SpringSecurity 5.0 認證、記住我、授權源碼分析


一、SpringSecurity 過濾器鏈:

  1、SecurityContextPersistenceFilter 會在請求開始時從配置好的SecurityContextRepository中獲取SecurityContext,然后把它設置給SecurityContextHolder。
  在請求完成后將SecurityContextHolder持有的SecurityContext再保存到配置好的SecurityContextRepository,同時清除SecurityContextHolder所持有的SecurityContext。
 
  2、 UsernamePasswordAuthenticationFilter用於處理來自表單提交的認證,認證成功由 AuthenticationSuccessHandler處理,反之由 AuthenticationFailureHandler處理。
 
  3、FilterSecurityInterceptor用於保護Http資源(授權),它需要引用AccessDecisionManager(決策管理器)、AuthenticationManager(認證管理器)、                                            SecurityMetadataSource(資源與權限的對應關系)。
 
  4、ExceptionTranslationFilter 只會處理 AuthenticationException、AccessDeniedException異常,其他的異常會拋出。
 

二、基於用戶名、密碼認證、授權流程:

1、AbstractAuthenticationProcessingFilter.attemptAuthentication()開始用戶認證流程,在這里會處理認證的成功(AuthenticationSuccessHandler)或失敗(AuthenticationFailureHandler);
 
2、重寫UsernamePasswordAuthenticationFilter.attemptAuthentication()調用retrieveUser()方法,自定義用戶名、密碼、驗證碼校驗、認證邏輯,返回Authentication對象實例存入SessionAuthenticationStrategy;
 
3、繼承AbstractUserDetailsAuthenticationProvider重寫retrieveUser方法,開始處理用戶認證,調用DetailsServiceImpl.loadUserByUsername()返回UserDetails對象實例;
 
4、實現UserDetailsService重寫loadUserByUsername,自定義用戶名密碼校驗流程,成功返回UserDetails對象實例,否則拋出AuthenticationException類異常;用戶認證結束;
 
5、FilterSecurityInterceptor.beforeInvocation()過濾器開始授權流程,實例化AccessDecisionManager(授權管理器)、AuthenticationManager(認證管理器)、FilterInvocationSecurityMetadataSource(處理url、權限關系的接口);
 
6、進入AbstractSecurityInterceptor.beforeInvocation(Object object)進行授權,分為三步開始處理。
 6.1.Collection<ConfigAttribute> attributes = this.obtainSecurityMetadataSource().getAttributes(object);//獲取當前url的權限
 6.2.Authentication authenticated = authenticateIfRequired();//獲取Authenticatio實例,拿到當前用戶信息
 6.3.this.accessDecisionManager.decide(authenticated, object, attributes);//沒有權限拋出AccessDeniedException類異常 ExceptionTranslationFilter處理異常
 
三、“記住我”功能實現原理:
  1、用戶第一次登陸,AbstractAuthenticationProcessingFilter  開始用戶認證,成功后 RememberMeServices 實現類 AbstractRememberMeServices 作業務處理。
  PersistentTokenBasedRememberMeServices 繼承了 AbstractRememberMeServices,重寫了 onLoginSucce(),存儲 PersistentTokenRepository 對象到DB。
public class PersistentTokenBasedRememberMeServices extends AbstractRememberMeServices {
  private PersistentTokenRepository tokenRepository = new InMemoryTokenRepositoryImpl();
    ......
  protected void onLoginSuccess(HttpServletRequest request, HttpServletResponse response, Authentication successfulAuthentication) {
        String username = successfulAuthentication.getName();
        this.logger.debug("Creating new persistent login for user " + username);
        PersistentRememberMeToken persistentToken = new PersistentRememberMeToken(username, this.generateSeriesData(), this.generateTokenData(), new Date());

        try {
            this.tokenRepository.createNewToken(persistentToken);
            this.addCookie(persistentToken, request, response);
        } catch (Exception var7) {
            this.logger.error("Failed to save persistent token ", var7);
        }

    }
   ......
}

  2、第二次登陸 RememberMeAuthenticationFilter 處理

public class RememberMeAuthenticationFilter extends GenericFilterBean implements ApplicationEventPublisherAware {
   
......
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest)req;
        HttpServletResponse response = (HttpServletResponse)res;
        if (SecurityContextHolder.getContext().getAuthentication() == null) {
            Authentication rememberMeAuth = this.rememberMeServices.autoLogin(request, response);// 解碼cookie獲取token,拿到UserDetails對象
            if (rememberMeAuth != null) {
                try {
                    rememberMeAuth = this.authenticationManager.authenticate(rememberMeAuth);//認證開始
                    SecurityContextHolder.getContext().setAuthentication(rememberMeAuth);
                    this.onSuccessfulAuthentication(request, response, rememberMeAuth);
                    if (this.logger.isDebugEnabled()) {
                        this.logger.debug("SecurityContextHolder populated with remember-me token: '" + SecurityContextHolder.getContext().getAuthentication() + "'");
                    }

                    if (this.eventPublisher != null) {
                        this.eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(SecurityContextHolder.getContext().getAuthentication(), this.getClass()));
                    }

                    if (this.successHandler != null) {
                        this.successHandler.onAuthenticationSuccess(request, response, rememberMeAuth);
                        return;
                    }
                } catch (AuthenticationException var8) {
                    if (this.logger.isDebugEnabled()) {
                        this.logger.debug("SecurityContextHolder not populated with remember-me token, as AuthenticationManager rejected Authentication returned by RememberMeServices: '" + rememberMeAuth + "'; invalidating remember-me token", var8);
                    }

                    this.rememberMeServices.loginFail(request, response);//失敗跳轉到登錄頁
                    this.onUnsuccessfulAuthentication(request, response, var8);
                }
            }

            chain.doFilter(request, response);
        } else {
            if (this.logger.isDebugEnabled()) {
                this.logger.debug("SecurityContextHolder not populated with remember-me token, as it already contained: '" + SecurityContextHolder.getContext().getAuthentication() + "'");
            }

            chain.doFilter(request, response);
        }

    }
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM