Permission Subsystem--Shiro Security Framework

Introduction to Shiro Security Framework

Overview of Shiro

Shiro is an open source security framework under apache ( http://shiro.apache.org/) It extracts the functions related to the security authentication of the software system, realizes the functions of user identity authentication, authority authorization, encryption, session management, etc., and forms a general security authentication framework. Using shiro, you can quickly complete the development of functions such as authentication and authorization, and reduce system costs.
When a user accesses resources, the system is required to control the user's authority. The specific process is shown in the figure:

Shiro outline architecture

At the conceptual level, the Shiro architecture consists of three main ideas, as shown in the figure:

in:

  1. Subject : The principal object responsible for submitting user authentication and authorization information.
  2. SecurityManager: Security manager, responsible for business implementation such as authentication and authorization.
  3. Realm: Domain object, responsible for obtaining business data from the data layer.

Shiro detailed architecture

When the Shiro framework performs authority management, some core objects are involved, including: authentication management objects, authorization management objects, session management objects, cache management objects, encryption management objects, and Realm management objects (domain objects: responsible for processing authentication and authorization) data access problems in the field), etc., and its specific architecture is shown in the figure:

in:

  1. Subject: A specific entity (user, third-party service, etc.) that interacts with the software.
  2. SecurityManager (Security Manager): The core of Shiro, used to coordinate the work of management components.
  3. Authenticator (Authentication Manager): Responsible for performing authentication operations.
  4. Authorizer (authorization manager): responsible for authorization detection.
  5. SessionManager (session management): Responsible for creating and managing the user's Session life cycle, providing a powerful Session experience.
  6. SessionDAO: Performs Session Persistence (CRUD) actions on behalf of the SessionManager, which allows any stored data to be hooked into the session management base.
  7. CacheManager: Provides functions for creating cache instances and managing cache life cycle.
  8. Cryptography (encryption manager): provides the design and management of encryption methods.
  9. Realms (realm objects): are the bridge between shiro and your application security data.

Shiro framework authentication interception implementation (filter)

Shiro basic environment configuration

add shiro dependencies

When using spring to integrate shiro, you need to add the following dependencies to pom.xml:

<dependency>
     <groupId>org.apache.shiro</groupId>
     <artifactId>shiro-spring</artifactId>
     <version>1.5.3</version>
</dependency>
Shiro Core Object Configuration

In the project based on SpringBoot implementation, the basic configuration of our shiro application is as follows: .

Step 1: Create the SpringShiroConfig class. The key code is as follows:

package com.cy.pj.common.config;

@Configuration//The class described by the annotation is a configuration object, which is also handed over to spring management (a special bean object)
public class SpringShiroConfig {
}

Step 2: Add the SecurityManager configuration in the Shiro configuration class (the interface object org.apache.shiro.mgt.SecurityManager must be used here), the key code is as follows:

@Bean//The method described by this annotation, its return value (object) will be handed over to spring management
public SecurityManager securityManager(){//This object is the core of the shiro framework
 DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
 return securityManager;
}

Step 3: Add the configuration of the ShiroFilterFactoryBean object in the Shiro configuration class. Use this object to set anonymous access and authenticated access to resources. The key code is as follows:

//Configure ShiroFilterFactoryBean (use this object to set resources for anonymous access and authenticated access to resources)
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager){
     ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
     shiroFilterFactoryBean.setSecurityManager(securityManager);//The securityManager object is responsible for checking whether the request has been authenticated
     Map<String,String> map=new LinkedHashMap<>();
     //Static resources allow anonymous access: "anon"
     map.put("/bower_components/**","anon");
     map.put("/build/**","anon");
     map.put("/dist/**","anon");
     map.put("/plugins/**","anon");
     //Except for anonymously accessed resources, all other resources must be accessed after authentication ("authc").
     //map.put("/**","authc");//Remember me function is added, and the authentication level is modified shiroFilterFactoryBean.setFilterChainDefinitionMap(map);//Definition of filter chain
     return shiroFilterFactoryBean;
}

During the configuration process, the object relationship is shown in the following figure:

Shiro landing page rendering

Server Controller Implementation
  • Business description and design implementation

When the server intercepts the user request, it determines whether the request has been authenticated. If there is no authentication, it should jump to the login page first.

  • Key code analysis and implementation.

Step 1: Add a method to present the login page in PageController. The key code is as follows:

@RequestMapping("doLoginUI")
public String doLoginUI(){
    return "login";
}

Step 2: Modify the configuration of the shiroFilterFactorybean in the SpringShiroConfig class and add the settings for the login url. See the sfBean.setLoginUrl("/doLoginUI") section for the key code.

@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager){
    ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
     shiroFilterFactoryBean.setSecurityManager(securityManager);//The securityManager object is responsible for checking whether the request has been authenticated
     //The third step is to add the settings of the login url
     shiroFilterFactoryBean.setLoginUrl("/doLoginUI");
     //Define map to specify request filtering rules (which resources allow anonymous access and which must authenticate access)
     Map<String,String> map=new LinkedHashMap<>();
     //Static resources allow anonymous access: "anon"
     map.put("/bower_components/**","anon");
     map.put("/build/**","anon");
     map.put("/dist/**","anon");
     map.put("/plugins/**","anon");
     //Except for anonymously accessed resources, all other resources must be accessed after authentication ("authc").
     //map.put("/**","authc");//Remember me function is added, and the authentication level is modified shiroFilterFactoryBean.setFilterChainDefinitionMap(map);//Definition of filter chain
     return shiroFilterFactoryBean;
}
Client page implementation
  • Business description and design implementation.

Add a login.html page to /templates/pages/, then deploy the project to the web server and start the test run.

  • Key code analysis and implementation.

For the specific code, see login.html in the project.

Shiro Framework Authentication Business Implementation

Certification Process Analysis

Identity authentication is to determine whether the user is a legitimate user of the system. The authentication (authentication of user identity information) when the user accesses system resources is shown in the flowchart:

**

The certification process is analyzed as follows:

  1. The system calls the login method of the subject to submit the user information to the SecurityManager
  2. SecurityManager delegates authentication operations to the authenticator object Authenticator
  3. Authenticator passes the identity information entered by the user to Realm.
  4. Realm accesses the database to obtain user information and then encapsulates and returns the information.
  5. Authenticator authenticates the information returned by realm.

Thinking: How to complete the authentication operation without using the shiro framework? filter, interceptor.

Authentication server implementation

Core business analysis

Authentication business API processing flow analysis, as shown in the figure:

DAO interface definition
  • Business description and design implementation.

In the user data layer object SysUserDao, query user information according to specific conditions and encapsulate it.

  • Key code analysis and implementation.

In the SysUserDao interface, add a method to obtain the user object according to the user name. The key code is as follows:

/**Find user information based on user name*/
@Select("select * from sys_users where username=#{username}")
SysUser findUserByUserName(String username);
Service interface and implementation
  • Business description and design implementation.

The business of this module is implemented in Realm type objects. When we write realm, we must inherit AuthorizingRealm and rewrite related methods to complete the acquisition and encapsulation of authentication and authorization business data.

  • Key code analysis and implementation.

The first step: define the ShiroUserRealm class, the key code is as follows:

package com.cy.pj.sys.service.realm;
import com.cy.pj.sys.dao.SysMenuDao;
import com.cy.pj.sys.dao.SysRoleMenuDao;
import com.cy.pj.sys.dao.SysUserDao;
import com.cy.pj.sys.dao.SysUserRoleDao;
import com.cy.pj.sys.pojo.SysUser;
import org.apache.shiro.authc.*;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.thymeleaf.util.StringUtils;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@Component
public class ShiroUserRealm extends AuthorizingRealm {
    @Autowired
     private SysUserDao sysUserDao;
     /**The credential encryption object is returned in this method, and the password entered by the user is encrypted based on this object.*/
     @Override
     public CredentialsMatcher getCredentialsMatcher() {
         HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
         credentialsMatcher.setHashAlgorithmName("MD5");//Encryption
         credentialsMatcher.setHashIterations(1);//Encryption times
         return credentialsMatcher;
     }//For this get method, you can also override the set method
    // Write get or set, just write one
    //    @Override
    //    public void setCredentialsMatcher(CredentialsMatcher credentialsMatcher) {
    //       HashedCredentialsMatcher matcher=new HashedCredentialsMatcher();
    //        matcher.setHashAlgorithmName("MDS");
    //        matcher.setHashIterations(1);
    //        super.setCredentialsMatcher(matcher);
    //    }
     /**Obtain and encapsulate authentication information*/
     @Override
     protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {

         //1. Get the username entered when logging in
         UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
         String username=token.getUsername();
         //2. Query the user information in the database based on the user name
         SysUser user = sysUserDao.findUserByUserName(username);
         //3. Check if the user exists
         if(user==null)throw new UnknownAccountException();
         //4. Verify that the user has been disabled
         if(user.getValid()==0)throw new LockedAccountException();
         //5. Encapsulate user information for password verification by the bottom layer
         Object principal=user;//identity
         Object hashedCredentials=user.getPassword();//encrypted password
         ByteSource credentialsSalt=ByteSource.Util.bytes(user.getSalt());//credential salt
         String realmName=this.getName();//The name of the current shiro object (you can name it arbitrarily)
         return new SimpleAuthenticationInfo(principal, hashedCredentials, credentialsSalt, realmName);
     }
}

Step 2: For this realm, you need to inject it into the SecurityManager object in the SpringShiroConfig configuration class, and modify the securityManager method. See the yellow background section, for example:

@Bean//The method described by this annotation, its return value (object) will be handed over to spring management
public SecurityManager securityManager(/*@Qualifier("shiroUserRealm") Used to specify when multiple realm implementation classes*/ Realm realm){//This object is the core of the shiro framework
     DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
     securityManager.setRealm(realm);
     return securityManager;
}
Controller class implementation
  • Business description and design implementation.

Define related methods in this object to process the client's login request, such as obtaining the user name, password, etc. and then submit the shiro framework for authentication.

  • Key code analysis and implementation.

Step 1: Add a method to handle login in SysUserController. The key code is as follows:

@RequestMapping("/user/doLogin")
public JsonResult doLogin(boolean isRememberMe,String username,String password){
     //1. Get the subject object
     UsernamePasswordToken token = new UsernamePasswordToken(username, password);
     if (isRememberMe){token.setRememberMe(true);}
     //2. Submit the token to SecurityManger based on the subject object
     //2.1 Encapsulate the user
     Subject subject = SecurityUtils.getSubject();
     //2.2 Authentication of user information
     subject.login(token);//The token will be submitted to the securityManager
     //analyze:
     //1) The token will be passed to shiro's SecurityManager
     //2)SecurityManager passes the token to the authentication manager
     //3) The authentication manager will pass the token to realm
     return new JsonResult("login ok");
}

Step 2: Modify the configuration of shiroFilterFactory, configure anonymous access to the path /user/doLogin, and view the code in the yellow marked part below:

@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager){
     ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
     shiroFilterFactoryBean.setSecurityManager(securityManager);//The securityManager object is responsible for checking whether the request has been authenticated
     //The third step is to add the settings of the login url
     shiroFilterFactoryBean.setLoginUrl("/doLoginUI");
     //Define map to specify request filtering rules (which resources allow anonymous access and which must authenticate access)
     Map<String,String> map=new LinkedHashMap<>();
     //Static resources allow anonymous access: "anon"
     map.put("/bower_components/**","anon");
     map.put("/build/**","anon");
     map.put("/dist/**","anon");
     map.put("/plugins/**","anon");
   
     map.put("/user/doLogin", "anon");
     //Except for anonymously accessed resources, all other resources must be accessed after authentication ("authc").
     map.put("/**","user");
     shiroFilterFactoryBean.setFilterChainDefinitionMap(map);//Definition of filter chain
     return shiroFilterFactoryBean;
}

Step 3: When we perform the login operation, in order to improve the user experience, we can process the exception information in the system. For example, add the following methods to the unified exception handling class:

package com.cy.pj.common.web;
import com.cy.pj.common.pojo.vo.JsonResult;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.ShiroException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authz.AuthorizationException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
@Slf4j
@ControllerAdvice
public class GlobalExceptionHandler {
     //The logging API that comes with the JDK
     @ExceptionHandler(RuntimeException.class)
     @ResponseBody
     public JsonResult doHandlerRuntimeException(RuntimeException e){
            e.printStackTrace();//You can also write log exception information
     log.error("exception msg {}"+e.getMessage());
     return new JsonResult(e);
     }
     @ExceptionHandler(ShiroException.class)
     @ResponseBody
     public JsonResult doHandleShiroException(ShiroException e) {
            JsonResult r=new JsonResult();
     r.setState(0);
     if(e instanceof UnknownAccountException) {
                r.setMessage("Account does not exist");
     }else if(e instanceof LockedAccountException) {
                r.setMessage("Account is disabled");
     }else if(e instanceof IncorrectCredentialsException) {
                r.setMessage("Incorrect password");
     }else if(e instanceof AuthorizationException) {
                r.setMessage("No permission for this operation");
     }else {
                r.setMessage("System maintenance");
     }
            e.printStackTrace();
     return r;
     }
}

Authentication client implementation

Writing user landing pages

Add a login page (login.html) under the /templates/pages/ directory.

Asynchronous login operation implementation

When the login operation is clicked, the entered user name and password are asynchronously submitted to the server.

$(function () {
    $(".login-box-body").on("click",".btn",doLogin);
  });
  function doLogin(){
      var params={
         username:$("#usernameId").val(),
         password:$("#passwordId").val()
      }
      var url="user/doLogin";
      $.post(url,params,function(result){
          if(result.state==1){
            //Jump to the page corresponding to indexUI
            location.href="doIndexUI?t="+Math.random();
          }else{
            $(".login-box-msg").html(result.message); 
          }
      });
  }

Exit Action Configuration Implementation

In the SpringShiroConfig configuration class, modify the filtering rules and add the configuration of the yellow marked part of the code, please see the following code:

@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager){
    ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
     shiroFilterFactoryBean.setSecurityManager(securityManager);//The securityManager object is responsible for checking whether the request has been authenticated
     //The third step is to add the settings of the login url
     shiroFilterFactoryBean.setLoginUrl("/doLoginUI");
     //Define map to specify request filtering rules (which resources allow anonymous access and which must authenticate access)
     Map<String,String> map=new LinkedHashMap<>();
     //Static resources allow anonymous access: "anon"
     map.put("/bower_components/**","anon");
     map.put("/build/**","anon");
     map.put("/dist/**","anon");
     map.put("/plugins/**","anon");
     map.put("/doLogout", "logout");//logout is an exit filter provided by the shiro framework. Once the exit operation is performed, the bottom layer will clear the user information stored on the system server and directly jump to the login page
     //Release, allow login operation
     map.put("/user/doLogin", "anon");
     //Except for anonymously accessed resources, all other resources must be accessed after authentication ("authc").
     //map.put("/**","authc");//Add remember me function and modify the authentication level
     map.put("/**","user");
     shiroFilterFactoryBean.setFilterChainDefinitionMap(map);//Definition of filter chain
     return shiroFilterFactoryBean;
}

Shiro framework authorization process implementation

Authorization Process Analysis

Authorization is the authorization of user resource access (whether or not the user is allowed to access this resource). The authorization process when a user accesses system resources is shown in the figure:

The authorization process analysis is as follows:
1) The system calls subject-related methods to submit user information (such as isPermitted) to the SecurityManager.
2) The SecurityManager delegates the permission detection operation to the Authorizer object.
3) Authorizer delegates user information to realm.
4) Realm accesses the database to obtain user permission information and encapsulate it.
5) Authorizer determines the user authorization information.

Thinking: Thinking about how to complete the authorization operation without using shiro? intercetor, aop.

Add authorization configuration

In the SpringShiroConfig configuration class, add the relevant configuration for authorization:
The first step: configure the life cycle management of the bean object (SpringBoot can not be configured).

@Bean
public LifecycleBeanPostProcessor   lifecycleBeanPostProcessor() {
         return new LifecycleBeanPostProcessor();
}

Step 2: Create a proxy object for the target business object through the following configuration (can be omitted in SpringBoot).

@DependsOn("lifecycleBeanPostProcessor")
@Bean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
         return new DefaultAdvisorAutoProxyCreator();
}

Step 3: Configure the advisor object. The bottom layer of the shiro framework will decide whether to create a proxy object and perform permission control through the return value of the matches method of this object (similar to the entry point).

@Bean
public AuthorizationAttributeSourceAdvisor 
authorizationAttributeSourceAdvisor (
                    SecurityManager securityManager) {
                AuthorizationAttributeSourceAdvisor advisor=
                new AuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(securityManager);
    return advisor;
}

Explanation: The most important respect rules for using the framework, the framework rules specify what way to use.

Authorization server implementation

Core business analysis

During authorization, the server-side core business and API analysis are as shown in the figure:

Dao implementation
  • Business description and design implementation.

Based on the login user ID, the authentication information obtains the permission information of the login user and encapsulates it.

  • Key code analysis and implementation.

Step 1: Define a method for finding the role id based on the user id in SysUserRoleDao (if the method already exists, there is no need to write it again). The key code is as follows:

List<Integer> findRoleIdsByUserId(Integer id);

Step 2: Define a method for finding menu id based on role id in SysRoleMenuDao. The key code is as follows:

List<Integer> findMenuIdsByRoleIds(@Param("roleIds") List<Integer> roleIds);

Step 3: In SysMenuDao, the method to find the permission ID based on the menu id, the key code is as follows:

List<String> findPermissions(@Param("menuIds") List<Integer> menuIds);
Mapper implementation
  • Business description and design implementation.

Based on the method in Dao, define the mapping element.

  • Key code analysis and implementation.

Step 1: Define the findRoleIdsByUserId element in SysUserRoleMapper. The key code is as follows:

<select id="findRoleIdsByUserId"
            resultType="int">
           select role_id
           from sys_user_roles
           where user_id=#{userId}        
</select>

Step 2: Define the findMenuIdsByRoleIds element in SysRoleMenuMapper. The key code is as follows:

<select id="findMenuIdsByRoleIds"
         resultType="int">
         select menu_id
         from sys_role_menus
         where role_id in 
         <foreach collection="roleIds"
                  open="("
                  close=")"
                  separator=","
                  item="item">
               #{item}
         </foreach>
</select>

Step 3: Define the findPermissions element in SysMenuMapper, the key code is as follows:

<select id="findPermissions"
           resultType="string">
       select permission <!-- sys:user:update -->
       from sys_menus
       where id in 
       <foreach collection="menuIds"
                open="("
                close=")"
                separator=","
                item="item">
            #{item}
       </foreach>
   </select>
Service implementation
  • Business description and design implementation.

In the ShiroUserReam class, rewrite the doGetAuthorizationInfo method of the object realm, and complete the acquisition and encapsulation of user authorization information, and finally pass the information to the authorization manager to complete the authorization operation.

  • Key code analysis and implementation.

Modify the doGetAuthorizationInfo method in the ShiroUserRealm class. The key code is as follows:

//authority management
@Autowired
private SysUserRoleDao sysUserRoleDao;
@Autowired
private SysRoleMenuDao sysRoleMenuDao;
@Autowired
private SysMenuDao sysMenuDao;
/**Obtain and encapsulate authorization information*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
    System.out.println("==doGetAuthorizationInfo==");
     //1. Get login user information, such as user id
     SysUser user=(SysUser) principalCollection.getPrimaryPrincipal();//Get the user's primary identity (determined by the package at login)
     Integer userId=user.getId();
     //2. Get the roles owned by the user based on the user id (sys_user_roles)
     List<Integer> roleIds=sysUserRoleDao.findRoleIdsByUserId(userId);
     if (roleIds==null||roleIds.size()==0)throw new AuthorizationException();//Permission exception
     //3. Get menu id based on role id (sys_role_menus)
     List<Integer> menuIds=sysRoleMenuDao.findMenuIdsByRoleIds(roleIds);
     if (menuIds==null||menuIds.size()==0)throw new AuthorizationException();
     //4. Obtain permission representation based on menu id (sys_menus)
     List<String> permissions=sysMenuDao.findPermissions(menuIds);
     if (permissions==null||permissions.size()==0)throw new AuthorizationException();
     //5. Encapsulate and return the permission identification information
     Set<String> set=new HashSet<>();
     for (String per:permissions){
            if (!StringUtils.isEmpty(per)) {
                set.add(per);
     }
        }
        System.out.println("set = " + set);
     SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();
     info.setStringPermissions(set);
     return info;//Return to Authorization Manager
}

Authorized Access Implementation Description Implementation

On the business layer (Service) method that requires authorized access, add the permission identifier required to execute this method, refer to the code @RequiresPermissions("sys:user:update")

Note: This annotation must be added to the business layer method.

Shiro extension function application

Shiro cache configuration

When we perform an authorization operation, the user permission information will be queried from the database every time. In order to improve the authorization performance, the user permission information can be queried and then cached, and the data can be retrieved from the cache during the next authorization.

The built-in cache application implementation in Shiro, the steps are as follows:

Step 1: Configure the cached Bean object (provided by the Shiro framework) in SpringShiroConfig.

@Bean
public CacheManager shiroCacheManager(){
     return new MemoryConstrainedCacheManager();
}

Note: The name of this CacheManager object cannot write cacheManager, because an object named cacheManager already exists in the spring container.

Step 2: Modify the configuration of the securityManager and inject the cache object into the SecurityManager object.

@Bean
public SecurityManager securityManager(
            Realm realm,
            CacheManager cacheManager) {
         DefaultWebSecurityManager sManager=
         new DefaultWebSecurityManager();
         sManager.setRealm(realm);
         sManager.setCacheManager(cacheManager);
         return sManager;
}

Note: For the shiro framework, the user's permission information can also be cache d with the help of third-party caching products (such as redis).

Shiro remember me

The Remember Me function is that after the user logs in successfully, if the browser is closed, the next time the user accesses system resources (such as the home page doIndexUI), there is no need to perform the login operation.

Client business implementation

Select Remember me on the page, and then perform the submit operation to submit the corresponding values ​​of username, password, and remember me to the control layer, as shown in the figure:

The key JS implementation in its client login.html:

 function doLogin(){
      var params={
         username:$("#usernameId").val(),
         password:$("#passwordId").val(),
         isRememberMe:$("#rememberId").prop("checked"),
      }
      var url="user/doLogin";
      console.log("params",params);
      $.post(url,params,function(result){
          if(result.state==1){
            //Jump to the page corresponding to indexUI
            location.href="doIndexUI?t="+Math.random();
          }else{
            $(".login-box-msg").html(result.message); 
          }
          return false;//Prevent duplicate commits on refresh
      });
  }
Server-side business implementation

The specific steps of server-side business implementation are as follows:

Step 1: In the doLogin method in SysUserController, set the token's setRememberMe method based on whether remember me is selected.

@RequestMapping("/user/doLogin")
public JsonResult doLogin(boolean isRememberMe,String username,String password){
    //1. Get the subject object
 UsernamePasswordToken token = new UsernamePasswordToken(username, password);
 if (isRememberMe){token.setRememberMe(true);}
    //2. Submit the token to SecurityManger based on the subject object
 Subject subject = SecurityUtils.getSubject();
 subject.login(token);//The token will be submitted to the securityManager
 return new JsonResult("login ok");
}

Step 2: Add remember me configuration to the SpringShiroConfig configuration class. The key code is as follows:

@Bean
     public RememberMeManager rememberMeManager() {
         CookieRememberMeManager cManager=
         new CookieRememberMeManager();
  SimpleCookie cookie=new SimpleCookie("rememberMe");
         cookie.setMaxAge(7*24*60*60);
         cManager.setCookie(cookie);
         return cManager;
     }

Step 3: Modify the configuration of the securityManager in SpringShiroConfig and inject the rememberManager object into the securityManager. Refer to the yellow part of the code.

@Bean
     public SecurityManager securityManager(
            Realm realm,CacheManager cacheManager
RememberMeManager rememberManager) {
         DefaultWebSecurityManager sManager=
         new DefaultWebSecurityManager();
         sManager.setRealm(realm);
         sManager.setCacheManager(cacheManager);
         sManager.setRememberMeManager(rememberManager);
         return sManager;
     }

Step 4: Modify the filtering authentication level of shiro, change /=author to /=user, and view the yellow background part.

@Bean
     public ShiroFilterFactoryBean shiroFilterFactory(
             SecurityManager securityManager) {
         ShiroFilterFactoryBean sfBean=
         new ShiroFilterFactoryBean();
         sfBean.setSecurityManager(securityManager);
         //If there is no authentication request, first visit the url of this authentication
         sfBean.setLoginUrl("/doLoginUI");
         //Define map to specify request filtering rules (which resources allow anonymous access and which must authenticate access)
         LinkedHashMap<String,String> map=
                 new LinkedHashMap<>();
         //Static resources allow anonymous access: "anon"
         map.put("/bower_components/**","anon");
         map.put("/build/**","anon");
         map.put("/dist/**","anon");
         map.put("/plugins/**","anon");
         map.put("/user/doLogin","anon");
         map.put("/doLogout", "logout");//Automatically check LoginUrl
         //Except for anonymously accessed resources, all other resources must be accessed after authentication ("authc").
         map.put("/**","user");//authc
         sfBean.setFilterChainDefinitionMap(map);
         return sfBean;
     }

Shiro session management configuration

The shiro framework is used to implement the authentication operation. If the user logs in successfully, the user information will be written into the session object. The default duration is 30 minutes. If you need to configure this, you can refer to the following configuration:

Step 1: In the SpringShiroConfig class, add the session manager configuration. The key code is as follows:

@Bean   
public SessionManager sessionManager() {
         DefaultWebSessionManager sManager=
                 new DefaultWebSessionManager();
         sManager.setGlobalSessionTimeout(60*60*1000);
         return sManager;
}

Step 2: In the SpringShiroConfig configuration class, add sessionManager value injection to the security manager securityManager. The key code is as follows:

@Bean
public SecurityManager securityManager(
            Realm realm,CacheManager cacheManager,
RememberMeManager rememberManager,
SessionManager sessionManager) {
         DefaultWebSecurityManager sManager=
         new DefaultWebSecurityManager();
         sManager.setRealm(realm);
         sManager.setCacheManager(cacheManager);
         sManager.setRememberMeManager(rememberMeManager);
         sManager.setSessionManager(sessionManager);
         return sManager;
}

Get the user login information and display the login user name on the system homepage (starter.html).

Step 1: Define a tool class (ShiroUtils) to obtain user login information.

package com.cy.pj.common.util;
import org.apache.shiro.SecurityUtils;
import com.cy.pj.sys.entity.SysUser;
public class ShiroUtils {
      public static String getUsername() {
          return getUser().getUsername();
      }
      public static SysUser getUser() {
          return  (SysUser)
           SecurityUtils.getSubject().getPrincipal();
      }
}

Step 2: Modify the doIndexUI method in PageController, the code is as follows:

@RequestMapping("doIndexUI")
    public String doIndexUI(Model model) {
        SysUser user=ShiroUtils.getUser();
        model.addAttribute("user",user);
        return "starter";
    }

Step 3: Get the login user information directly on the page (starter.html) with the help of the expression in thymeleaf

<span class="hidden-xs" id="loginUserId">[[${user.username}]]</span>

Change the password of the login user? (Refer to User Module Documentation)

analyze:

1) Who do you want to modify? (password, salt value, modification time)

2) Design and implementation of the server? (dao,service,controller)

3) Design and implementation of the client? (Submit user password information asynchronously)

Shiro Summary

Analysis of key points and difficulties

  1. Analysis and implementation of shiro authentication process (determining the legitimacy of user identity).
  2. Shiro authorization process analysis and implementation (permission detection and authorization for resource access).
  3. Shiro cache, session duration, remember me and other functions are implemented.

Common FAQ

  1. Talk about the core components of shiro?
  2. Talk about the certification process of shiro, how do you know it, and why do you want to be certified?
  3. Talking about the authorization process of shiro, how do you know the process is like this, and why do you need to authorize it?
  4. Built-in cache application implementation in Shiro? Why use this cache? Is it possible to use a third-party cache?
  5. How does the Remember Me feature in Shiro work? Why use this feature?
  6. What is the default duration of a session in Shiro, how do you know?

Bug Analysis

  1. SecurityManager package name is wrong.
  2. The MD5 encryption algorithm is set incorrectly.
  3. Realm objects are not handed over to spring management
  4. Username and password received incorrectly
  5. The CacheManager name conflicts with the built-in CacheManager name in Spring.
  6. Filter rule configuration error?

Tags: MySQL JQuery Spring Boot

Posted by battlesiege on Sat, 07 May 2022 05:13:50 +0300