当前位置 : 主页 > 编程语言 > java >

聊聊SpringSecurity认证流程和授权流程

来源:互联网 收集:自由互联 发布时间:2022-09-02
@[TOc] 聊聊SpringSecurity认证流程和授权流程 认证流程 我们先聊一下SpringSecurity的认证流程 首先是AbstractAuthenticationProcessingFilter的doFilter()方法 public void doFilter(ServletRequest req, ServletResponse

@[TOc]

聊聊SpringSecurity认证流程和授权流程

认证流程

我们先聊一下SpringSecurity的认证流程

首先是AbstractAuthenticationProcessingFilter的doFilter()方法

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) res; if (!requiresAuthentication(request, response)) { chain.doFilter(request, response); return; } Authentication authResult; try { authResult = attemptAuthentication(request, response); if (authResult == null) { return; } sessionStrategy.onAuthentication(authResult, request, response); } catch (InternalAuthenticationServiceException failed) { logger.error( "An internal error occurred while trying to authenticate the user.", failed); unsuccessfulAuthentication(request, response, failed); return; } catch (AuthenticationException failed) { // Authentication failed unsuccessfulAuthentication(request, response, failed); return; } // Authentication success if (continueChainBeforeSuccessfulAuthentication) { chain.doFilter(request, response); } successfulAuthentication(request, response, chain, authResult); }

attemptAuthentication()

方法中调用抽象方法attemptAuthentication()获取认证信息,实现类是UsernamePasswordAuthenticationFilter,它的attemptAuthentication()方法:

  • 获取用户和密码
  • 创建UsernamePasswordAuthenticationToken对象,认证结果暂时设置为false
  • 调用getAuthenticationManager()方法获取认证管理器ProviderManager
  • 调用authenticate()方法
  • ProviderManager的authenticate()方法中获取所有的AuthenticationProvider实例,遍历看哪个,最终找到AbstractUserDetailsAuthenticationProvider的supports()方法,发现支持UsernamePasswordAuthenticationToken,发现支持后调用AbstractUserDetailsAuthenticationProvider的authenticate()方法,方法中根据用户名查找缓存有没有,没有的话调用retrieveUser()方法,方法中调用

    this.getUserDetailsService().loadUserByUsername(username);也就是实现UserDetailsService接口的自定义类loadUserByUsername()方法,获取用户信息和权限信息封装到UserDetails中并返回

    继续看AbstractUserDetailsAuthenticationProvider的authenticate()方法,获取完UserDetails信息后进行预检查,看一下用户是否被冻结等等,然后进入DaoAuthenticationProvider的additionalAuthenticationChecks(),这个方法里进行密码的比对,看数据库的密码和表单密码是否一致,默认比对逻辑是BCryptPasswordEncoder的matches()方法完成的

    然后是AbstractUserDetailsAuthenticationProvider的postAuthenticationChecks.check(user);进行后置检查,检查账户是否过期等,

    然后调用createSuccessAuthentication()方法,创建UsernamePasswordAuthenticationToken对象,构造方法中设置authenticated为true,表示已经通过认证,返回UsernamePasswordAuthenticationToken对象

    至此attemptAuthentication()

    successfulAuthentication()

    AbstractAuthenticationProcessingFilter的successfulAuthentication()方法中把认证信息写入SecurityContextHolder.getContext()中,然后调用AuthenticationSuccessHandler的onAuthenticationSuccess方法,我们可以实现AuthenticationSuccessHandler接口自定义认证成功的处理类

    unsuccessfulAuthentication()

    失败方法是unsuccessfulAuthentication(),方法中清空SecurityContextHolder保存的信息,调用AuthenticationFailureHandler的onAuthenticationFailure(),我们同样可以实现AuthenticationFailureHandler接口自定义认证失败处理类

    授权流程

    认证涉及到的过滤器为FilterSecurityInterceptor

    • 调用invoke方法,

      • 进入AbstractSecurityInterceptor的beforeInvocation()方法:

        通过obtainSecurityMetadataSource()获取系统中的权限的配置

        • 进入authenticateIfRequired()方法

          获取Authentication信息,这里面有用户的权限列表,并返回

        • 得到认证信息后调用AccessDecisionManager决策管理器的decide()方法

          方法中获取投票者进行投票

          如果投票不通过的话会抛出AccessDeniedException异常,被ExceptionTranslationFilter处理

    总结

    我们主要讲了一下SpringSecurity的表单认证流程和授权流程,主要是AbstractAuthenticationProcessingFilter过滤器的作用,我们对AbstractAuthenticationProcessingFilter进行代码分析,涉及过滤器UsernamePasswordAuthenticationFilter,并对其中的方法进行了分析,以及认证成功和认证失败的处理方法successfulAuthentication()和unsuccessfulAuthentication()进行了分析。授权流程主要涉及的是FilterSecurityInterceptor类,通过投票决策器进行投票来判断是否有相应的权限

    网友评论