Shiro integrates some configurations and problems of springboot

Keywords: Java Shiro Spring Boot

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
@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
  • 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
    • 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

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

Posted by andy666 on Tue, 07 Dec 2021 01:12:31 -0800