How to Realize Efficient Dynamic Authentication

 I. Overview

Spring Security is a highly customizable security framework within the Spring framework and a standard security framework for Spring applications, providing two major components including authentication and authentication. It is highly integrated in the Spring framework, without the need to introduce third-party extension modules, can avoid a large number of data interface adaptation problems, and greatly reduce development costs and time.

As shown in the figure below, Spring Security's authentication and authentication process is actually located in the request filter and interceptor, and the API adaptation will not be performed until the request passes through all the filters and interceptors. In other words, customizing Spring Security is to modify various filters and interceptors in the filtering chain.

The authentication process is the green part in the figure. Spring Security provides a lot of authentication methods, such as password authentication, pre-authentication, etc.; the orange part is the dynamic authentication part, and its built-in Security Interceptor will delegate the request to each specific AccessDecisionManager Authenticate.

1.jpg

The entire data flow of Spring Security

In Spring Security, the AccessDecisionManager is based on the voter for authentication: there will be multiple AccessDecisionVoters under the Manager, and each Voter will return the result to the Manager, and finally the Manager will determine whether to grant permission.

There are three types of Managers:

  • AffirmativeBased one-vote system (default option)

  • UnanimousBased one-vote opposition system

  • ConsensusBased minority obeys majority system

The reason why the voter is used is that it is convenient to add and expand voters, and the second is to simplify the implementation of the framework by quantifying the authentication results.

2. Construction of authentication module

According to the overview section, there are many places we can modify, such as FilterSecurityInterceptor, AccessDecisionManager, AccessDecisionVoter. However, it takes a lot of time to modify the Interceptor or Manager, so we choose to directly add a new Voter for dynamic authentication.

http.authorizeRequests().withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>(){
  @Override
  public < 0 extends FilterSecurityInterceptor > 0 postProcess(0 o){
     o.setAccessDecisionManager(new AffirmativeBased(Arrays.asList(new WebExpressionVotor(),userAccessDecisionVoter)));//决策管理器
     o.setSecurityMetadataSource(userFilterInvocationSecurityMetadataSource);//安全元数据
     return o;
  }
}};

Here we use the AffirmativeBased voter to vote because we did not include Spring's default attributes during the custom filtering process, so WebExpressionVoter will automatically abstain, and the remaining steps are naturally voted and authenticated by our own voter UserAccessDecisionVoter .

@Component
public class UserAccessDecisionVoter implements AccessDecisionVoter<FilterInvocation> { 
  @Override 
  public boolean supports(ConfigAttribute attribute) { 
     return true; 
  } 
                                    
  @Override 
  public boolean supports(Class<?> clazz) { 
    return true; 
  } 
                                                                             
  @Autowired
  AnalysisUserRoleUtil analysisUserRoleUtil; 
                                                                                             
  @Autowired
  JwtConfig jwtConfig;

  /**         
  * @param authentication   用户信息         
  * @param filterInvocation 请求信息         
  * @param attributes       安全配置属性,这里指的是角色         
  * @return 1:同意、-1:反对,返回1时表示有访问权限,-1表示没有访问权限         
  */        
  @Override        
  public int vote(Authentication authentication, FilterInvocation filterInvocation, Collection<ConfigAttribute> attributes) {                
    assert authentication != null;                
    assert filterInvocation != null;                         

    // 没有URL, 拒绝访问                
    String requestURL = getRequestURL(attributes);                
    if (null == requestURL) {                        
      return AccessDecisionVoter.ACCESS_DENIED;                
    }                         

    // 匿名用户, 拒绝访问                
    String userName = authentication.getPrincipal().toString();                
    if (userName.equals("anonymousUser")) {                        
      return AccessDecisionVoter.ACCESS_DENIED;                
    }                          

    // 获取用户信息                
    SystemUser systemUser = systemUserService.queryUserByName(userName);                         

    // 任何人不能删除自己                
    if (this.isDeletingSelf(requestURL, systemUser)) {                        
      return AccessDecisionVoter.ACCESS_DENIED;                
    }                         

    // 依据不同的权限判断是否需要同意操作                
    if (analysisUserRoleUtil.isSuperAdmin(systemUser)) {                        
      return this.superAdminPrivilegeCheck(requestURL, systemUser);                
    }                
    if (analysisUserRoleUtil.isInSuperAdminGroup(systemUser)) {                        
      return this.superAdminGroupMemberPrivilegeCheck(requestURL, systemUser);                
    }                
    if (analysisUserRoleUtil.isGroupAdmin(systemUser)) {                        
      return this.groupAdminPrivilegeCheck(requestURL, systemUser);                
    }                    
      return this.regularUserPrivilegeCheck(requestURL, systemUser);        
  }

The application grants different permissions based on roles. In the subsequent process of permission determination, there are but not limited to the following two solutions:

  1. Put all URIs that need to be judged into the database, and take them out when checking permissions;

  2. The operations and URI templates involved in the design document are independent and non-interfering with each other; regular expressions are used for judgment during authentication; new operations added later need to construct URIs based on the modified templates.

The first strategy has many application modules, and a large number of people need to be added later. It is not appropriate to put the URIs that need to be determined into the database and connect them with tables, which may take up a lot of database storage space. Therefore, we use the second strategy to determine the operation URI. The disadvantage is that the amount of encoding is relatively large, and the advantage is that it does not take up too much additional storage space.

In the process of building a custom authentication voter, you may find some operations that need to be released directly. We put the URIs involved in this part of the operation in the configuration file and inject them when building the filter chain to achieve bypassing. purpose of voting.

3. Summary

The above is an example of customizing dynamic authentication based on SpringBoot DecisionVoter. The specific authentication logic and various details need to be customized according to different needs. At the same time, attention should be paid to ensure the integrity of the authentication process when performing customized operations. Efficiency and security, avoiding possible security holes and performance problems. In addition, it is also necessary to consider the scalability and maintainability of the system, so that it can be easily expanded and maintained in the process of future demand changes or upgrades.

Guess you like

Origin blog.csdn.net/ZNBase/article/details/131942840