一、认证流程

1、集成过程详情看

2、首先讲默认配置的流程,我们加了spring security的配置之后,对没有特殊处理的接口发起请求,会跳到默认的登录页

      

spring security权限控制实现原理 springsecurity权限控制流程_自定义

  • ① 当请求发起后,会被 UsernamePasswordAuthenticationFilter拦截,实际上幕后主使是它的爸爸AbstractAuthenticationProcessingFilter,它爸爸是掌控这个认证流程的主线

spring security权限控制实现原理 springsecurity权限控制流程_自定义_02

spring security权限控制实现原理 springsecurity权限控制流程_ide_03

  • ② 接着它儿子 UsernamePasswordAuthenticationFilter开始干活执行attemptAuthentication方法,儿子根据用户输入的账户密码,构建UsernamePasswordAuthenticationToken,然后叫 AuthenticationManager工作

spring security权限控制实现原理 springsecurity权限控制流程_权限控制_04

  • ③ AuthenticationManager接口的实现类ProviderManager承担起责任,这里ProviderManager 有对具体使用哪个provider来应战有做判断,后面我们讲自定义provider的时候再说

spring security权限控制实现原理 springsecurity权限控制流程_自定义_05

  • ④ 接着 provider家族的 AbstractUserDetailsAuthenticationProvider和DaoAuthenticationProvider父子兵上阵

spring security权限控制实现原理 springsecurity权限控制流程_权限控制_06

spring security权限控制实现原理 springsecurity权限控制流程_自定义_07

  • ⑤ 跟着开始调动UserDetailsService的实现类进行查询用户信息,UserDetailsService是我们集成spring security需实现的

spring security权限控制实现原理 springsecurity权限控制流程_ide_08

  • ⑥ 查询到相应数据之后,视线转回到步骤④的 provider家族的 AbstractUserDetailsAuthenticationProvider,开始进行校验,它的校验有默认的内部类DefaultPreAuthenticationChecks 和DefaultPostAuthenticationChecks,利用前端用户传入的密码和数据库查询到的密码进行匹配,用户传入的我们已经在前面构建到UsernamePasswordAuthenticationToken 里了

spring security权限控制实现原理 springsecurity权限控制流程_权限控制_09

spring security权限控制实现原理 springsecurity权限控制流程_自定义_10

  • ⑦ 至于使用什么密码校验规则,在配置中传入

spring security权限控制实现原理 springsecurity权限控制流程_自定义_11

spring security权限控制实现原理 springsecurity权限控制流程_ide_12

  • ⑧ 匹配成功后存入权限信息之类的,然后回到幕后主使AbstractAuthenticationProcessingFilter,做相应的后勤工作

spring security权限控制实现原理 springsecurity权限控制流程_自定义_13

spring security权限控制实现原理 springsecurity权限控制流程_自定义_14

spring security权限控制实现原理 springsecurity权限控制流程_ide_15

  • ⑨ 假设我们在配置类配置了成功登录处理类,则会相应的调用

spring security权限控制实现原理 springsecurity权限控制流程_自定义_16

3、下面讲一下我们自定义filter和provider的流程,就比如最常用的我们微信或授权登录,后台拿到前端传过来的code,我们去获取openid

  • ① 首先我们大概看一下自定义filter和provider需要做哪些工作,这里我们弄微信登录和小程序登录两个来说明,总的增加六个类,这里只截图,具体源码请看这里

WxLoginFilter(模仿UsernamePasswordAuthenticationFilter继承AbstractAuthenticationProcessingFilter) 

spring security权限控制实现原理 springsecurity权限控制流程_自定义_17

WxAuthProvider(模仿AbstractUserDetailsAuthenticationProvider实现AuthenticationProvider)

spring security权限控制实现原理 springsecurity权限控制流程_权限控制_18

WxAuthenticationToken(模仿UsernamePasswordAuthenticationToken继承AbstractAuthenticationToken)

spring security权限控制实现原理 springsecurity权限控制流程_ide_19

MiniProgramLoginFilter(模仿UsernamePasswordAuthenticationFilter继承AbstractAuthenticationProcessingFilter)

spring security权限控制实现原理 springsecurity权限控制流程_ide_20

MiniProgramAuthProvider(模仿AbstractUserDetailsAuthenticationProvider实现AuthenticationProvider)

spring security权限控制实现原理 springsecurity权限控制流程_ide_21

MiniProgramAuthenticationToken(模仿UsernamePasswordAuthenticationToken继承AbstractAuthenticationToken)

spring security权限控制实现原理 springsecurity权限控制流程_自定义_22

修改配置类SecurityConfig

spring security权限控制实现原理 springsecurity权限控制流程_权限控制_23

spring security权限控制实现原理 springsecurity权限控制流程_自定义_24

spring security权限控制实现原理 springsecurity权限控制流程_ide_25

  • ② 那跟上面默认配置的差别在哪里了,它是怎么自动匹配我们的请求呢,用postman请求授权接口http://127.0.0.1:8090/mini/auth,这个其实就是我们配置在自定义的MiniProgramLoginFilter里,幕后主使AbstractAuthenticationProcessingFilter操控着这一切,对url进行匹配,调用最适合的儿子来处理请求

spring security权限控制实现原理 springsecurity权限控制流程_ide_26

spring security权限控制实现原理 springsecurity权限控制流程_ide_27

spring security权限控制实现原理 springsecurity权限控制流程_自定义_28

  • ③ 前面说了儿子干活执行attemptAuthentication方法,那儿子构建好token之后调用AuthenticationManager接口的实现类ProviderManager,这里ProviderManager 怎么知道来调用我们自定义的哪个provider呢?
  • 主要靠provider.supports,它根据我们在filter那里传入的token类型,匹配provider实现类中supports校验的类型,一致则可以调用

spring security权限控制实现原理 springsecurity权限控制流程_权限控制_29

spring security权限控制实现原理 springsecurity权限控制流程_权限控制_30

spring security权限控制实现原理 springsecurity权限控制流程_权限控制_31

  • 至于其他的流程也就跟上面的没两样啦

4、总结认证流程:filter->AuthenticationManager->provider->UserDetailService->provider->filter

  1. 如果自定义了filter则根据匹配进去相应的filter,当然入口还是父类 AbstractAuthenticationProcessingFilter 的doFilter,这里的 !this.requiresAuthentication(request, response) 则是对多个自定义filter进行请求匹配
  2. 接着到AuthenticationManager接口的实现类ProviderManager,具体选择对应 AuthenticationProvider的实现,默认是DaoAuthenticationProvider,判断是用 provider.supports(),每个自定义provider都会继承supports,根据信息token类(如UsernamePasswordAuthenticationToken)和在filter传进来的类型行判断
  3. DaoAuthenticationProvider中的retrieveUser开始查数据库验证,查到之后就回去它的爸爸AbstractUserDetailsAuthenticationProvider开始check,最主要还是这个this.additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken)authentication)
  4. 最后校验成功,this.createSuccessAuthentication(principalToReturn, authentication, user)返回,里面还有对权限的初始化
  5. 上面步骤3和步骤4不是必须的,假如自己自定义了 provider,比如写了微信登录之类的,那就不需要数据库验证的过程,这时候没有步骤3和4
  6. 最后的最后,返回到初始层,AbstractAuthenticationProcessingFilter中,接着将登录信息放入session还有上下文,this.successfulAuthentication(request, response, chain, authResult)根据是否自定义了SuccessHandler或者FailHandler来调用

二、权限控制

1、权限流程

  • ① 加入权限控制,增加三个类

MyAccessDecisionManager(实现AccessDecisionManager) 

spring security权限控制实现原理 springsecurity权限控制流程_权限控制_32

MyFilterSecurityInterceptor(继承AbstractSecurityInterceptor)

spring security权限控制实现原理 springsecurity权限控制流程_自定义_33

SecurityMetadataSource(实现FilterInvocationSecurityMetadataSource)

spring security权限控制实现原理 springsecurity权限控制流程_ide_34

修改配置类SecurityConfig

spring security权限控制实现原理 springsecurity权限控制流程_权限控制_35

  • ② 当请求被MyFilterSecurityInterceptor拦截后,调用父类AbstractSecurityInterceptor的beforeInvocation方法

spring security权限控制实现原理 springsecurity权限控制流程_ide_36

  • ③ 根据子类注入的 SecurityMetadataSource实现类进行处理

spring security权限控制实现原理 springsecurity权限控制流程_ide_37

spring security权限控制实现原理 springsecurity权限控制流程_ide_38

  • ④ 查询到相应的系统权限,返回给AbstractSecurityInterceptor

spring security权限控制实现原理 springsecurity权限控制流程_ide_39

  • ⑤ 接着调用accessDecisionManager开始进行匹配, accessDecisionManager我们开始就已经注入

spring security权限控制实现原理 springsecurity权限控制流程_ide_40

spring security权限控制实现原理 springsecurity权限控制流程_ide_41

  • ⑥ 匹配用户拥有的权限和用户的请求,如果包含则可以访问,否则不行

spring security权限控制实现原理 springsecurity权限控制流程_自定义_42

2、权限控制总结:

  1. 自定义的filter并注入AccessDecisionManager的实现类,接着调用filter的父类AbstractSecurityInterceptor中的 beforeInvocation 进行操作
  2. 先获取FilterInvocationSecurityMetadataSource的实现类中的 getAttributes 方法获取数据库中的权限列表,返回用户请求的
  3. 接着调用 AccessDecisionManager 的实现类中的 decide 方法用上下文中用户的权限和用户请求的进行匹配,达到权限控制的目的