zoukankan      html  css  js  c++  java
  • 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);
            }
    
        }
    }
  • 相关阅读:
    Java设计模式——单例模式
    重新学习MyBatis-逆向工程
    重新学习MyBatis(六)
    重新学习MyBatis(五)
    重新学习Mybatis(四)
    重新学习MyBatis(三)
    重新学习Mybatis(二)
    Java设计模式重新出发
    回归问题常用的损失函数总结
    Matlab绘图局部放大
  • 原文地址:https://www.cnblogs.com/haiyangwu/p/10358485.html
Copyright © 2011-2022 走看看