基于spring的安全框架实现了ioc以及aop减轻程序员的工作量,通过导入依赖编写配置类的方式实现认证授权.
    SpringSecurity提供了一个配置类WebSecurityConfigurerAdapter用来提供给程序员对SpringSecurity做自定义配置,我们需要配置如下几个信息
    创建UserDetailService的Bean,该组件是用来加载用户认证信息
    配置编码器,通过该编码器对密码进行加密匹配
    授权规则配置,哪些资源需要什么权限..
    (编写一个配置类继承WebSecurityConfigurerAdapter重写方法userservice用于提供用户信息,configure用于配置具体拦截以及放行请求,成功页面以及授权……)
    认证流程:

    • 请求过来会被过滤器链中的UsernamePasswordAuthenticationFilter拦截到,请求中的用户名和密码被封装成UsernamePasswordAuthenticationToken(Authentication的实现类)
    • 过滤器将UsernamePasswordAuthenticationToken提交给认证管理器(AuthenticationManager)进行认证.
    • AuthenticationManager委托AuthenticationProvider(DaoAuthenticationProvider)进行认证,AuthenticationProvider通过调用UserDetailsService获取到数据库中存储的用户信息(UserDetails),然后调用passwordEncoder密码编码器对UsernamePasswordAuthenticationToken中的密码和UserDetails中的密码进行比较
    • AuthenticationProvider认证成功后封装Authentication并设置好用户的信息(用户名,密码,权限等)返回
    • Authentication被返回到UsernamePasswordAuthenticationFilter,通过调用SecurityContextHolder工具把Authentication封装成SecurityContext中存储起来。然后UsernamePasswordAuthenticationFilter调用AuthenticationSuccessHandler.onAuthenticationSuccess做认证成功后续处理操作
    • 最后SecurityContextPersistenceFilter通过SecurityContextHolder.getContext()获取到SecurityContext对象然后调用SecurityContextRepository将SecurityContext存储起来,然后调用SecurityContextHolder.clearContext方法清理SecurityContext。

    注意:SecurityContext是一个和当前线程绑定的工具,在代码的任何地方都可以通过SecurityContextHolder.getContext()获取到登陆信息。
    用户信息封装:自定义实现类实现UserDetailsService 重写loaduserbyusername方法,提供自定义的用户验证来源;(数据库中的用户信息)
    security认证流程:访问服务的请求到达服务器时会先被usernamepasswordauthenticationfilter拦截到,将请求中的用户信息(用户名以及密码)封装成对应的token,该过滤器将生成的token交给authenticationmanager进行认证(authenticationmanager调用provider,调用userdetailsservice获取到数据库中的用户信息在通过密码编码器比对密码),认证成功后封装一个Authentication返回到最初始的拦截器中,将Authentication封装成securitycontext储存起来,然后拦截器调用成功后的AuthenticationSuccessHandler.onAuthenticationSuccess做后续处理,
    最后SecurityContextPersistenceFilter通过SecurityContextHolder.getContext()获取到SecurityContext对象然后调用SecurityContextRepository将SecurityContext存储起来,然后调用SecurityContextHolder.clearContext方法清理SecurityContext.
    (授权流程)

    • 在FilterSecurityInterceptor中会调用其父类AbstractSecurityInterceptor

    的beforeInvocation方法做授权之前的准备工作

    • 该方法中通过SecurityMetadataSource..getAttributes(object);获得资源所需要的访问权限 ,通过SecurityContextHolder.getContext().getAuthentication()获取当前认证用户的认证信息,即Authentication对象 。
    • 然后通过调用AccessDecisionManager.decide(authenticated, object, attributes);进行授权,该方法使用了投票机制来决定用户是否有资源访问权限

    AccessDecisionManager接口有三个实现类,他们通过通过AccessDecisionVoter投票
    器完成投票,三种投票策略如下:
    AffirmativeBased : 只需有一个投票赞成即可通过
    ConsensusBased:需要大多数投票赞成即可通过,平票可以配置
    UnanimousBased:需要所有的投票赞成才能通过

    • 而投票器也有很多,如RoleVoter通过角色投票,如果ConfigAttribute是以“ROLE_”开头的,则将使用RoleVoter进行投票,AuthenticatedVoter 是用来区分匿名用户、通过Remember-Me认证的用户和完全认证的用户(登录后的)
    • 投票通过,放心请求响应的资源

    (记住我流程:)
    用户发起认证请求,会被RememberMeAuthenticationFilter拦截,生成token保存用户的信息,一份存在数据库中,一份存在数据库中,下一次访问时只需要通过比对请求中携带的token与数据库中的token是否一致

    • 认证成功UsernamePasswordAuthenticationFilter会调用RememberMeServices创建Token

    (见其父类AbstractAuthenticationProcessingFilter.successfulAuthentication),同时 RemeberMeService 会调用TokenRepository将Token写入数据库(persistent_logins),然后 RemeberMeService通 过Reponse.addCookie把Token写到浏览器的Cookies中

    • 当浏览器再次发起请求会进入RemeberMeAuthenticationFilter,该Filter获取到请求cookies中的token交给RemeberMeService
    • RemeberMeService调用TokenRepository去数据库中根据Token查询用户名
    • 调用UserDetilasService.loadUserByUsername根据用户名获取用户认证信息
    • 通过authenticationManager.authenticate,做一次认证,然后把用户信息放入上下文对象中SecurityContext

    (验证码登录流程)
    SpringSecurity - 图1
    用户发起验证码登录请求security的smscode拦截器会拦截请求校验验证码,验证码验证成功后做登录验证通过用户的手机号去认证是否有具体的用户信息;