First, post the official website address https://shiro.apache.org/spring-framework.html
1, shiro configuration class
To use shiro, you must configure a shiroConfig configuration class, which requires three bean s:
- Realm, custom realm
- Defaultwebsecuritymanager needs to pass the Realm parameter
- ShiroFilterFactoryBean, you need to pass the defaultwebsecuritymanager parameter
- In ShiroFilterFactoryBean, we can choose to add the corresponding filter
- anon: access without authentication
- authc: must be authenticated to access
- user: you must have the "remember me" function to use it
- perms: you can only access a resource with permission
- Role: you can only access a role with permission
- In ShiroFilterFactoryBean, we can choose to add the corresponding filter
@Configuration public class ShiroConfig { @Bean public UserRealm userRealm(){ return new UserRealm(); } @Bean public DefaultWebSecurityManager defaultSecurityManager(@Qualifier("userRealm") UserRealm userRealm){ DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(userRealm); return securityManager; } @Bean public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("defaultSecurityManager") DefaultWebSecurityManager SecurityManager){ ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean(); bean.setSecurityManager(SecurityManager); //Add Shiro's built-in filter======================= /* anon : Access without authentication authc : Authentication is required to access user : You must have the "remember me" function to use perms : You must have permission on a resource to access it role : You must have a role permission to access */ Map<String, String> map = new LinkedHashMap<>(); map.put("/back/**","authc"); map.put("/front/**","anon"); // Set the request of / user/addUser, which can only be accessed after authentication // map.put("/user/addUser","authc"); // map.put("/user/deleteUser","authc"); // Set all the requests under / user / and access them only after authentication // map.put("/user/*","authc"); bean.setFilterChainDefinitionMap(map); // Set login request bean.setLoginUrl("/login"); //============================================ bean.setUnauthorizedUrl("/noPermission"); return bean; } //thymeleaf integrated shiro @Bean(name = "shiroDialect") public ShiroDialect shiroDialect(){ return new ShiroDialect(); } @Bean public ShiroExceptionResolver shiroExceptionResolver(){ return new ShiroExceptionResolver(); } }
Several built-in filters in shiro
anon : Access without authentication authc : Authentication is required to access user : You must have the "remember me" function to use perms : You must have permission on a resource to access it role : You must have a role permission to access
2, Shiro realm authentication and authorization
The customized Realm needs to be integrated with the authoringrealm. Override the following methods
- doGetAuthorizationInfo (principalcollection) Authorization
- First, doGetAuthorizationInfo needs to return an AuthorizationInfo object. Let's first create a new implementation class object: SimpleAuthorizationInfo
- Authorization requires us to get the login object - > login user: SecurityUtils.getSubject()*** (User)subject.getPrincipal();***
- SimpleAuthorizationInfo contains authorization and role granting functions
- addRole(String role)
- setRoles(Set<String> set)
- addStringPermissions(String permission)
- addObjectPermissions(Set<Permission>)
- Finally, after authorizing the user, return info
- First, doGetAuthorizationInfo needs to return an AuthorizationInfo object. Let's first create a new implementation class object: SimpleAuthorizationInfo
- doGetAuthenticationInfo (AuthenticationToken authenticationToken)
- First, doGetAuthenticationInfo needs to return an AuthenticationInfo object. Let's first create a new implementation class object: SimpleAuthenticationInfo
- Then look at the constructor of this implementation class
- The first two commonly used are passed to the constructor: a user principal, credentials, login credentials, and a realName. The currently logged in role name will be encapsulated in the simpleprincipal collection collection in the next layer
- Then look at the constructor of this implementation class
- Therefore, at this time, we need to obtain the user information of the database and submit it to SimpleAuthenticationInfo for verification
- During login verification, we encapsulate the received user name and password in the Controller as the UsernamePasswordToken token in shiro (not verified at this time), and then real can get the login token in doGetAuthenticationInfon later
- doGetAuthenticationInfon gets the login token and can get the user name. We can query the user name to get the password for verification
- The user name does not exist. An UnknownAccountException exception is thrown
- Incorrect password throws an IncorrectCredentialsException
- We can catch exceptions and handle them accordingly
- First, doGetAuthenticationInfo needs to return an AuthenticationInfo object. Let's first create a new implementation class object: SimpleAuthenticationInfo
LoginController
@PostMapping("/LoginConfirm") public String login(String username, String password, Model model) { //Get current user Subject subject = SecurityUtils.getSubject(); //Not certified //Encapsulate the user's login data and obtain the token UsernamePasswordToken token = new UsernamePasswordToken(username, password); //Login and exception handling try { //User login subject.login(token); Session subjectSession = subject.getSession(); model.addAttribute("loginUser",subjectSession.getAttribute("loginUser")); return "redirect:index"; } catch (UnknownAccountException uae) { log.warn("user name does not exist"); //If the user name does not exist System.out.println("user name does not exist"); model.addAttribute("exception", "user name does not exist"); return "redirect:login"; } catch (IncorrectCredentialsException ice) { log.warn("Password error"); //If the password is wrong System.out.println("Password error"); model.addAttribute("exception", "Password error"); return "redirect:login"; } }
UserRealm
public class UserRealm extends AuthorizingRealm { @Autowired UserService userService; @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { System.out.println("================Authorization executed===================="); SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); // Get the currently logged in object Subject subject = SecurityUtils.getSubject(); // Get User object User currentUser = (User) subject.getPrincipal(); // Set the permissions of the current user String permission = currentUser.getPermission(); System.out.println(currentUser.getUsername() + "The permissions for are " + permission); info.addStringPermission(permission); return info; } @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { System.out.println("=====================Certification performed AuthenticationToken==================="); UsernamePasswordToken userToken = (UsernamePasswordToken) authenticationToken; //Connect to a real database User user = userService.getUserByName(userToken.getUsername()); if(user == null){ //There is no such person return null; //Throw an exception UnknownAccountException } // Login succeeded. Save the user information into the session Subject currentSubject = SecurityUtils.getSubject(); Session session = currentSubject.getSession(); session.setAttribute("loginUser",user); // Password authentication, shiro do return new SimpleAuthenticationInfo(user,user.getPassword(),""); } }
3, Shiro thymeleaf
to configure:
To use shiro's label on the front end, configure it as follows
Add the following Bean in the shiro configuration class just now
@Configuration public class ShiroConfig { //thymeleaf integrated shiro @Bean(name = "shiroDialect") public ShiroDialect shiroDialect(){ return new ShiroDialect(); } }
Add the following in the < HTML > tag of thymeleaf front end:
xmlns:shiro="http://www.pollix.at/thymeleaf/shiro"
Common labels
<shiro:guest> Visitor visit <a href = "login.jsp"></a> </shiro:guest> user Label: the user has passed the authentication\Remember what the response is when I log in <shiro:user> welcome[<shiro:principal/>]Sign in <a href = "logout">sign out</a> </shiro:user> authenticated Tag: user authentication passed, i.e Subjec.login Login success is not to remember my login <shiro:authenticted> user[<shiro:principal/>] Authenticated </shiro:authenticted> notAuthenticated Tag: the user is not authenticated, that is, there is no call Subject.login Log in,include"Remember me"Also unauthenticated <shiro:notAuthenticated> Not authenticated(include"Remember me") </shiro:notAuthenticated> principal Tag: displays user identity information. Called by default Subjec.getPrincipal()Get, i.e Primary Principal <shiro:principal property = "username"/> hasRole Label: if current Subject If there are roles, they will be displayed body Body content <shiro:hashRole name = "admin"> user[<shiro:principal/>]Have role admin </shiro:hashRole> hasAnyRoles Labels: if Subject Have any role(Or relationship)Will show body Content in body <shiro:hasAnyRoles name = "admin,user"> user[<shiro:pricipal/>]Have role admin perhaps user </shiro:hasAnyRoles> lacksRole:If current Subjec No roles will be displayed body Body content <shiro:lacksRole name = "admin"> user[<shiro:pricipal/>]No role admin </shiro:lacksRole> hashPermission:If current Subject Permission will be displayed body Body content <shiro:hashPermission name = "user:create"> user[<shiro:pricipal/>] Have permissions user:create </shiro:hashPermission> lacksPermission:If current Subject No permission will be displayed body Body content <shiro:lacksPermission name = "org:create"> user[<shiro:pricipal/>] No permission org:create </shiro:lacksPermission>
4, Problems encountered in development
1. ShiroFilterFactoryBean is configured with setUnauthorizedUrl, which does not work
Inspiration: https://blog.csdn.net/bicheng4769/article/details/86680955
Problem recurrence:
Configure setUnauthorizedUrl in ShiroFilterFactoryBean, and jump to the corresponding request path if it is not authorized as the name suggests
However, it is found that in any case, those without permission will only report an error and will not jump to the configured request
Cause tracing:
We first click setUnauthorizedUrl to see the source code and find that this url is used in applyunthorizedurlifnecessary
We found that the authorization filter uses the url. Click to see the implementation class
It is found that the implementation classes under the AuthorizationFilter only have port, permission, role and SSL filters;
The FilterChainDefinitionMap I configured earlier is of authc and anon types. If it is not within the management scope of the filter, the exception org.apache.shiro.authz.AuthorizationException: Not authorized to invoke method will be thrown all the time
solve:
Manually capture exceptions for page Jump
ShiroExceptionResolver implements HandlerExceptionResolver. When this exception is encountered, manually jump to the page of bit authorization
public class ShiroExceptionResolver implements HandlerExceptionResolver { @Override public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { System.out.println("==============Abnormal start============="); //If shiro does not have permission to operate, shiro will not forward to the url without permission when operating auno if(ex instanceof UnauthorizedException){ ModelAndView mv = new ModelAndView("redirect:/noPermission"); return mv; } ex.printStackTrace(); System.out.println("==============Abnormal end============="); ModelAndView mv = new ModelAndView("redirect:/err"); mv.addObject("exception", ex.toString().replaceAll("\n", "<br/>")); return mv; } }
Summary:
Perms, roles, SSL, rest and port belong to the AuthorizationFilter
anon,authcBasic,auchc,user belong to AuthenticationFilter