Recently engaged in the Shiro security framework, found some online blog articles, but one to their own realization of the time encountered a variety of pits, need to look at the source of various data and various tests.
Then this article teaches you how to integrate Shiro into the springboot, and avoid some small pits, this time to achieve the basic landing and role permissions, the subsequent article also explains other functions, such as "teach you Shiro + springboot integration JWT"
Attached Source: Https://github.com/HowieYuan/shiro
Dependency Packages
<dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.3.2</version></dependency>
Database tables
Basics, user table, and Role roles table
Shiro Related class Shiro configuration class
@Configurationpublic class Shiroconfig {@Bean public shirofilterfactorybean shirfilter (SecurityManager Securitymana GER) {Shirofilterfactorybean Shirofilterfactorybean = new Shirofilterfactorybean (); SecurityManager Shirofilterfactorybean.setsecuritymanager (SecurityManager) must be set; Setloginurl If you do not set a value, the default is to automatically find the "/login.jsp" page or the "/login" Map Shirofilterfactorybean.setloginurl ("/notlogin") under the Web project root directory ; Set the URL of the no-limit jump; Shirofilterfactorybean.setunauthorizedurl ("/notrole"); Set Interceptor map<string, string> filterchaindefinitionmap = new linkedhashmap<> (); Visitors, Development Authority Filterchaindefinitionmap.put ("/guest/**", "anon"); User, Need role permission "user" Filterchaindefinitionmap.put ("/user/**", "Roles[user]"); admin, Need role permission "admin" filterchaindefinitionmap.put ("/admin/**", "roles[admin]"); Open Login Interface Filterchaindefinitionmap.put ("/login", "anon"); The rest of the interface is blocked//the main line of code mustPut at the end of all permission settings, otherwise it will cause all URLs to be intercepted filterchaindefinitionmap.put ("/**", "authc"); Shirofilterfactorybean.setfilterchaindefinitionmap (FILTERCHAINDEFINITIONMAP); System.out.println ("Shiro Interceptor factory class injection success"); return Shirofilterfactorybean; }/** * Inject SecurityManager */@Bean public SecurityManager SecurityManager () {Defaultwebsecuritym Anager SecurityManager = new Defaultwebsecuritymanager (); Set Realm. Securitymanager.setrealm (Customrealm ()); return SecurityManager; }/** * Custom identity authentication realm; * <p> * must write this class and add @Bean annotations to inject customrealm, * otherwise it will affect dependency injection of other classes in the Customrealm class */@Bean public Cust Omrealm Customrealm () {return new Customrealm (); }}
Note : The inside of the SecurityManager class should be imported, import org.apache.shiro.mgt.SecurityManager;
but if you are copying the code over, it will be imported by default java.lang.SecurityManager
here is a little bit of pit, other classes, but also belong to the Shiro package inside the class
The Shirfilter method is mainly set up some important jump URLs, such as non-landing, no right-time jump, and set the various types of URL interception, such as/user Start URL requires user permissions,/admin start URL requires Admin permissions, etc.
Permission Intercept Filter
When you run a Web application, Shiro creates some useful default filter instances and automatically makes them available, and these default filter instances are defined by the Defaultfilter enumeration class, and of course we can customize the filter instance. These will be discussed in a future article.
Filter |
explain |
Anon |
No parameters, open access, can be understood as anonymous users or visitors |
Authc |
No reference, requires authentication |
Logout |
No parameter, logoff, after execution will jump directly to the shiroFilterFactoryBean.setLoginUrl(); URL of the set |
Authcbasic |
No reference, indicating Httpbasic certification |
User |
No parameter, indicates that the user must exist and does not check when logged in operation |
Qs. |
No parameter, indicates a secure URL request, protocol is HTTPS |
Perms[user] |
Parameters can be written in multiple, indicating that one or some permissions are required to pass, multiple parameters are written perms["user, admin"], when there are multiple parameters must pass each parameter to calculate through |
Roles[admin] |
Parameters can be written in multiple, indicating that one or some of the roles can pass, multiple arguments are written roles["Admin,user"], when there are multiple arguments, each parameter must pass to count through |
Rest[user] |
According to the method requested, equivalent to Perms[user:method], wherein method is Post,get,delete, etc. |
PORT[8081] |
When the requested URL port is not 8081, jump to schemal://servername:8081?querystring where Schmal is protocol http or HTTPS and so on, ServerName is the host,8081 you are visiting is the port Port, QueryString is the URL you visited? The following parameters |
Commonly used mainly is the anon,authc,user,roles,perms and so on
Note : Anon, authc, Authcbasic, user is the first set of authentication filters, perms, port, rest, roles, SSL is the second set of authorization filters, to pass the authorization filter, First to complete the login authentication operation (that is, the first to complete the certification to seek authorization) to go to the second set of authorizations (such as access to the URL that requires roles permissions, if not logged, will jump directly to the shiroFilterFactoryBean.setLoginUrl();
URL set)
Custom Realm Class
We first have to inherit the Authorizingrealm class from defining our own realm for our custom identity, permission authentication operations.
Remember to override the two methods of overriding Dogetauthenticationinfo and Dogetauthorizationinfo (two method names are very similar, don't make a mistake)
public class Customrealm extends Authorizingrealm {private Usermapper usermapper; @Autowired private void Setusermapper (Usermapper usermapper) {this.usermapper = Usermapper; /** * Get authentication information * Shiro, the end is to get the user, role and permission information in the application through Realm. * * @param authenticationtoken User ID token * @return returns the AuthenticationInfo instance that encapsulates the user information */@Override Prot ected authenticationinfo dogetauthenticationinfo (Authenticationtoken authenticationtoken) throws authenticationexception {System.out.println ("———— identity authentication Method ————"); Usernamepasswordtoken token = (usernamepasswordtoken) Authenticationtoken; Gets the user String password = usermapper.getpassword (Token.getusername ()) from the database to the application username password; if (null = = password) {throw new Accountexception ("Incorrect user name"); } else if (!password.equals (New String ((char[)) token.getcredentials ())) {throw new accountexception ("Incorrect password") ; } return new SimpleauthenticationinfO (Token.getprincipal (), Password, getName ()); /** * Get authorization information * * @param principalcollection * @return */@Override protected Authorizationi NFO dogetauthorizationinfo (principalcollection principalcollection) {System.out.println ("———— Authority authentication ————"); String username = (string) securityutils.getsubject (). Getprincipal (); Simpleauthorizationinfo info = new Simpleauthorizationinfo (); Get the user role String role = Usermapper.getrole (username); set<string> set = new hashset<> (); You need to encapsulate the role into Set as a parameter of Info.setroles () set.add (role); Sets the user-owned role info.setroles (set); return info; }}
Rewrite the two methods are to implement identity authentication and authorization authentication, Shiro has a method of landing operations Subject.login()
, when we encapsulate the user name, password token as a parameter passed in, will run into the two methods inside (not necessarily two methods will enter)
Where the Dogetauthorizationinfo method is only required for authorization, such as the Administrator role configured in the previous configuration class filterChainDefinitionMap.put("/admin/**", "roles[admin]");
, when entering/admin will enter the Dogetauthorizationinfo method to check the permissions; The Dogetauthenticationinfo method is required for authentication (such as the previous Subject.login()
method) to enter
Besides the Usernamepasswordtoken class, we can get the user name and password (which will be used when logging in) from this object new UsernamePasswordToken(username, password);
, and the get username or password has the following methods
token.getUsername() //获得用户名 Stringtoken.getPrincipal() //获得用户名 Object token.getPassword() //获得密码 char[]token.getCredentials() //获得密码 Object
Note : A lot of people will find, usermapper and other classes, interfaces can not be injected through the @Autowired, run the program will be reported NullPointerException, Online said a lot of such as the Spring loading order, etc. But in fact there is a very important place to pay attention to, Customrealm this class is in the Shiro configuration class securityManager.setRealm()
method set in, and many people directly write securityManager.setRealm(new CustomRealm());
, this is not possible, you must use @Bean injected Myrealm, not directly new object:
@Bean public SecurityManager securityManager() { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); // 设置realm. securityManager.setRealm(customRealm()); return securityManager; } @Bean public CustomRealm customRealm() { return new CustomRealm(); }
The reason is also very simple, and Controller in the call Service, are Springbean, can not own new
Of course, the same principle can be written:
@Bean public SecurityManager securityManager(CustomRealm customRealm) { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); // 设置realm. securityManager.setRealm(customRealm); return securityManager; }
Then just add a @Component-like annotation to the Customrealm class
function implementation
The functionality of this article is all implemented in the same way that the interface returns JSON data
Assigning controllers based on URL permissions
游客@RestController@RequestMapping("/guest")public class GuestController{ @Autowired private final ResultMap resultMap; @RequestMapping(value = "/enter", method = RequestMethod.GET) public ResultMap login() { return resultMap.success().message("欢迎进入,您的身份是游客"); } @RequestMapping(value = "/getMessage", method = RequestMethod.GET) public ResultMap submitLogin() { return resultMap.success().message("您拥有获得该接口的信息的权限!"); }}
普通登陆用户@RestController@RequestMapping("/user")public class UserController{ @Autowired private final ResultMap resultMap; @RequestMapping(value = "/getMessage", method = RequestMethod.GET) public ResultMap getMessage() { return resultMap.success().message("您拥有用户权限,可以获得该接口的信息!"); }}
管理员@RestController@RequestMapping("/admin")public class AdminController { @Autowired private final ResultMap resultMap; @RequestMapping(value = "/getMessage", method = RequestMethod.GET) public ResultMap getMessage() { return resultMap.success().message("您拥有管理员权限,可以获得该接口的信息!"); }}
Suddenly notice the Customrealm class where the accountexception exception was thrown, and now build a class for exception capture
@RestControllerAdvicepublic class ExceptionController { private final ResultMap resultMap; @Autowired public ExceptionController(ResultMap resultMap) { this.resultMap = resultMap; } // 捕捉 CustomRealm 抛出的异常 @ExceptionHandler(AccountException.class) public ResultMap handleShiroException(Exception ex) { return resultMap.fail().message(ex.getMessage()); }}
And the logincontroller of landing and other processing.
@RestControllerpublic class Logincontroller {@Autowired private resultmap resultmap; Private Usermapper Usermapper; @RequestMapping (value = "/notlogin", method = requestmethod.get) public Resultmap Notlogin () {return resultmap. Success (). Message ("You are not logged in!"). "); } @RequestMapping (value = "/notrole", method = requestmethod.get) public Resultmap notrole () {return RESULTM Ap.success (). Message ("You don't have permission!"). "); } @RequestMapping (value = "/logout", method = requestmethod.get) public Resultmap logout () {Subject Subject = Securityutils.getsubject (); Deregistration of Subject.logout (); Return resultmap.success (). Message ("Log off successfully! "); /** * Login * * @param username username * @param password Password */@RequestMapping (value = "/login", met Hod = requestmethod.post) public resultmap Login (string username, string password) {//Create a sub from securityutils ject Subject Subject = Securityutils.getsubject (); //Prepare token (token) Usernamepasswordtoken token = new Usernamepasswordtoken (username, password) before the certificate is submitted; Execute Authentication Login Subject.login (token); According to the permission, specify the return data String role = Usermapper.getrole (username); if ("User". Equals (role)) {return resultmap.success (). Message ("Welcome login"); } if ("Admin". Equals (role)) {return resultmap.success (). Message ("Welcome to Admin page"); } return Resultmap.fail (). Message ("Permission Error! "); }}
Test
Teach you Shiro integrate springboot, avoid all kinds of pits