当前位置:网站首页 > 技术博客 > 正文

Spring注解



要了解一个SpringSecurity模块,就是了解它如何身份认证、如何自定义、(至于如何鉴权是SpringSecurity通用部分),对应到代码就是:背后的相关 Configurer、Filter、Authentication、Provider。

本文以最新Spring Boot版本2.6.2 以授权码模式梳理;涉及源码太多,就不放源码对照了,可以自行fork查看;斜体表示可配置自定义替换的部分

  • 第一部分:先演示默认配置下 spring-boot-starter-oauth2-client 所带来的流程和效果,建立大概认知。对应代码 thirdpart-login 项目
  • 第二部分:全面解析 oauth2login、oauth2client 原理。
  • 第三部分:常见业务下我们自己用户系统也有token分发需求,因此也解读下提供JWT服务的 oauth2ResourceServer 模块
  • 第四部分:综上所述,实战定制SpringSecurity OAuth2。对应代码 thirdpart-login-custom

由 自动配置类引入的默认配置,可由代码 thirdpart-login 复现。

  1. 后端在 .yml 配置中做好相关配置
  2. 访问受限资源"/user",后端鉴权异常后,由重定向到登录页。
  3. 登录页则由根据相关配置自动构造页面String返回。
  4. 页面点击发出授权请求。
    后端匹配响应该模板路径,返回实际授权码请求的重定向响应,转入三方授权页面:
  5. 同意授权后,Gitee会向游览器返回重定向响应。
    游览器向 "redirect-uri" 发起访问,此时被后端匹配处理,其会用请求携带的 code 向配置的 "token-uri、user-info-uri" 发起一系列请求,最后构造出认证后的身份放入,以SESSION持久化等。再将先前保存在SESSION中的的受限资源访问请求拿出,重定向重新访问。

这两者都是在SpringSecurity中整合OAuth2的入口方法(例),对应、,只是引入Filter有所异同。简而言之:

  • 会在授权请求时进行认证(即设置安全上下文SecurityContext),背后会连续访问acc_token&user-info-url 将获取的用户信息构造填充 Authentication。
  • 而也会对授权请求进行处理,但只是获取到access_token后用repository存起来(要怎么使用自行处理),不会认证,这也意味着需要自行实现认证逻辑。
  • 对 授权码code 的请求:由响应"授权请求"向客户端返回重定向响应,定向到实际 "authorization-uri"
  • 对 access_token 和 user-info-uri 实际请求:会对回调地址(携带了code和state)进行处理,调用进行认证。背后会进行连续 token-uri、user-info-uri 请求,最后返回完全填充的。

太长就不贴了 参考Github项目代码,也内建了一些常见OAuth2提供方,在之内少配几个字段也没关系。

  • Client属性:用以处理application.yml中的相关属性,并构建代表OAuth2方的一个个。根据不同模式,对必须属性有不同要求。
  • provider属性:除了上图授权码模式下必须校验的 "authorization-uri、token-uri" 外,"user-info-uri"、也是必须的,在后续调用的内,必须需要这俩属性才能尝试访问 user-info-uri 并包装为。
  1. OAuth2LoginAuthenticationToken
    用以给Provider认证过渡用,最初仅含code,最终包含access_token、user等。
  2. OAuth2AuthorizationCodeAuthenticationToken
    用以给Provider认证过渡用,未填充时仅含code,经填充后包含access_token等。
  3. OAuth2AuthenticationToken
    认证后安全上下文实际保存的OAuth2用户认证,由将填充后的转换而来。

  1. OAuth2AuthorizationRequestRedirectFilter
    1. 通过调用用于判断是否为授权请求(默认为 "/oauth2/authorization/{registrationId}",可通过配置) ,并且请求包装为后由(默认基于SESSION实现)将授权请求保存(后有他用)
    2. 随后重定向到追加了参数(client-id、response_type)的真实授权码请求。
  2. OAuth2LoginAuthenticationFilter
    继承自,即负责身份认证的Filter。
    1. 当是(默认为/login/oauth2/code/*)请求且带了code和state时,尝试以这俩参数构建且调用去进行认证。
    2. 认证通过后,调用将认证后完全填充的转为authenticated=true的,用以代表认证后的身份。(该converter默认就是直接提取填充后的"principal、authorities、clientid"直接new)
    3. 将先前得到 "token、refreshToken" 等信息包装为调用 保存起来(默认是基于内存实现的ClientId和Principal为key的Map)
  3. OAuth2AuthorizationCodeGrantFilter
    (该Filter,在下会永远被跳过,因为该请求已被处理后通过重定向)
    匹配带code与state的请求(表示回调请求)且满足不为空时(表示经过了RedirectFilter,是先前授权请求发起的),会构造 交由(背后交由)进行认证,并将结果构造为交由保存,然后去除参数再将请求重定向到 "savedRequest 或者 redirect-url"。
    【注:不是很能理解该Filter这里为什么要重定向,这个重定向真的很恼火。如果API自身需要code,这重定向把参数清除了会报错;而即便API不要code了依附于它的逻辑使用,那也是无意义多一次请求。而且其基于SESSION的实现本来没什么问题,但非要重定向请求一次就导致单纯的多实例时会存在问题】
  1. OAuth2LoginAuthenticationProvider
    1. 对尝试认证,其内会进一步构造,然后调用 OAuth2AuthorizationCodeAuthenticationProvider 对其进行认证。
    2. 经过上述认证后拿到填充了 "access_token" 的,会构造成后传给负责进行实际的 "user-info-uri" 请求,并将结果包装成返回。(该User拥有两类authorities,一个是ROLE_USER(Spring在经过oauth2UserService时手动添加的),一类是Token中的SCOPE_{sopces})
  2. OAuth2AuthorizationCodeAuthenticationProvider
    对尝试认证,内部会构造对"token-uri"的实际请求,并调用进行请求返回,并根据返回结果(内含access_token/refreash_token),新new一个填充了"access_token"的返回。

由 引入,提供对请求中携带token校验解析、身份认证的服务。

  • JwtDecoder:在的时,会构建,它就需要以提供对"token"校验解析。
  • JwtEncoder:虽然不是必须的,但我们自己系统登录有令牌分发的需要。
  • BearerTokenAuthenticationFilter
    (虽没继承但却干着认证的事)
    首先通过判断是否含"token",然后构建并调用尝试认证。
    将认证后的结果设置到安全上下文中。如果中途出现了异常,则以该filter的(可通过配置) 处理。
  1. BearerTokenAuthenticationToken
    代表原始token的一个过渡身份。
  2. JwtAuthenticationToken
    其,进行实际系统访问的身份。由认证后,通过转换而来。
  • JwtAuthenticationProvider
    对 BearerTokenAuthenticationToken(带access_token)进行认证。
    1. 内部会调用(可通过配置)对 "token" 进行解析&验证为对象。
    2. 调用(可通过配置)尝试对进一步转换为进行实际系统访问的返回。(默认内部会调用解析填充 authorities(将"scpoe"/"scp"声明中空格分隔的字串转为);将"sub"字段作为 principal)

最终实现代码以及效果展示都放在Github上了:spring-security-oauth2-sample

登录流程,大致API流程:

从上文也能看出,不得不提用SpringSecurity很多时候宁愿迁出去自己写套Configuer/Filter/Provider…,官方虽提供了很多服务,而且也能看出在尽可能定制化,但背后还是强制引入了太多逻辑,很难与实际业务契合,即便稍有不同在它基础上定制也都需要付出很大代价。这代价不仅指新增代码行数,为了定制或运行稳定 你首先就得彻底清楚它原本引入了哪些逻辑(这点官网文档又做得不好),这就需要源码阅读和上手成本。

本文仍存在些许问题,特别是的重定向问题,还有与无状态相悖的涉及较少,也没一张清晰流程图,时间关系暂且就这样了,有什么问题还希望指正讨论。

关于 Spring Security 对 OAuth2 认证服务的实现,以及前言提到的 SpringSecurity原理、JWT等等,后面有时间的话也会慢慢更。

版权声明


相关文章:

  • 编码器和解码器2025-01-11 23:01:01
  • 工具类定义2025-01-11 23:01:01
  • python 离线安装第三方包2025-01-11 23:01:01
  • java中构造器有什么用2025-01-11 23:01:01
  • redis集群模式搭建2025-01-11 23:01:01
  • 电容耦合电路原理2025-01-11 23:01:01
  • 单片机c语言代码2025-01-11 23:01:01
  • java虚拟机规范官方文档2025-01-11 23:01:01
  • 移位指令怎么算2025-01-11 23:01:01
  • c++ opencv rect2025-01-11 23:01:01