微服务OAuth 2.1认证授权可行性方案(Spring Security 6)
一、背景
Oauth2
停止维护,基于OAuth 2.1
和 OpenID Connect 1.0
的Spring Authorization Server
模块独立于SpringCloud
。
本文开发环境如下:
Version | |
---|---|
Java | 17 |
SpringCloud | 2023.0.0 |
SpringBoot | 3.2.1 |
Spring Authorization Server | 1.2.1 |
Spring Security | 6.2.1 |
mysql | 8.2.0 |
https://spring.io/projects/spring-security#learn
https://spring.io/projects/spring-authorization-server#learn
二、微服务架构介绍
一个认证服务器(也是一个微服务),专门用于颁发JWT。
一个网关(也是一个微服务),用于白名单判断和JWT校验。
若干微服务。
本文的关键在于以下几点:
- 搭建认证服务器
- 网关白名单判断
- 网关验证JWT
- 认证服务器如何共享公钥,让其余微服务有JWT自校验的能力。
三、认证服务器
这里是官方文档https://spring.io/projects/spring-authorization-server#learn
基本上跟着Getting Started
写完就可以。
1. 数据库创建
新建一个数据库xc_users
。
然后执行jar
里自带的三个sql
。
这一步官方并没有给出,大概因为可以使用内存存储,在简单demo省去了持久化。不建立数据库可能也是可行的,我没试过。
2. 新建模块
新建一个auth
模块,作为认证服务器。
3. 导入依赖和配置
1 | <dependency> |
1 | server: |
4. 安全认证配置类
1 | @Configuration |
里面包含诸多内容,有来自Spring Security
的,也有来自的Spring Authorization Server
的。
UserDetailsService
的实例,用于检索用户进行身份验证。
1 | @Bean |
- 密码编码器(可选,本文不用)
1 | @Bean |
- 协议端点的
Spring Security
过滤器链
1 | @Bean |
- 用于身份验证的
Spring Security
过滤器链。
至于哪些要校验身份,哪些不用,根据自己需求写。
1 | @Bean |
- 自定义验证转化器(可选)
1 | private JwtAuthenticationConverter jwtAuthenticationConverter() { |
- 用于管理客户端的
RegisteredClientRepository
实例
1 | @Bean |
- 用于对访问令牌进行签名的实例
1 | @Bean |
- 用于解码签名访问令牌的
JwtDecoder
实例
1 | @Bean |
- 用于配置
Spring Authorization Server
的AuthorizationServerSettings
实例
1 | @Bean |
这里可以设置各种端点的路径,默认路径点开builder()即可看到,如下
1 | public static Builder builder() { |
这里我必须吐槽一下,qnmd /.well-known/jwks.json,浪费我一下午。获取公钥信息的端点现在已经替换成了/oauth2/jwks。
四、认证服务器测试
基本上跟着Getting Started
走就行。只不过端点的变动相较于Oauth2
很大,还有使用方法上不同。
在配置RegisteredClient
的时候,我们设置了三种GrantType
,这里只演示两种AUTHORIZATION_CODE
和CLIENT_CREDENTIALS
。
1. AUTHORIZATION_CODE(授权码模式)
1. 获取授权码
用浏览器打开以下网址,
1 | http://localhost:63070/auth/oauth2/authorize?client_id=XcWebApp&response_type=code&scope=all&redirect_uri=http://www.51xuecheng.cn |
对应oauth2/authorize
端点,后面的参数和当时设置RegisteredClient
保持对应就行。response_type
一定是code
。
进入到登陆表单,输入lisi
- 456
登陆。
选择all,同意请求。
url
被重定向到http://www.51xuecheng.cn
,并携带一个code
,这就是授权码。
1 | http://www.51xuecheng.cn/?code=9AexK_KFH1m3GiNBKsc0FU2KkedM2h_6yR-aKF-wPnpQT5USKLTqoZiSkHC3GUvt-56_ky-E3Mv5LbMeH9uyd-S1UV6kfJO6znqAcCAF43Yo4ifxTAQ8opoPJTjLIRUC |
2. 获取JWT
使用apifox
演示,postman
,idea-http
都可以。
向localhost:63070/auth
服务的/oauth2/token
端点发送Post
请求,同时需要携带认证信息。
认证信息可以如图所填的方法,也可以放到Header
中,具体做法是将客户端ID和客户端密码用冒号(:)连接成一个字符串,进行Base64
编码放入HTTP
请求的Authorization
头部中,前缀为Basic
。比如Authorization: Basic bXlDbGllbnRJZDpteUNsaWVudFNlY3JldA==
得到JWT
2. CLIENT_CREDENTIALS(客户端凭证模式)
不需要授权码,直接向localhost:63070/auth
服务的/oauth2/token
端点发送Post
请求,同时需要携带认证信息。
五、Gateway
至于gateway
基础搭建步骤和gateway
管理的若干微服务本文不做指导。
相较于auth
模块(也就是Authorization Server
),gateway
的角色是Resource Server
。
1. 引入依赖
1 | <dependency> |
2. 添加白名单文件
在resource
下添加security-whitelist.properties
文件。
写入以下内容
1 | /auth/**=???? |
3. 全局过滤器
在全局过滤器中,加载白名单,然后对请求进行判断。
1 | @Component |
4. 获取远程JWKS
在yml
配置中添加jwk-set-uri
属性。
1 | spring: |
新建配置类,自动注入JwtDecoder
。
1 | @Configuration |
5. 校验JWT
在全局过滤器中补全逻辑。
1 | @Component |
6. 测试(如何携带JWT)
携带一个正确的JWT
向gateway
发送请求。
把JWT
写到Header
的Authorization
字段中,添加前缀Bearer
(用空格隔开),向gateway
微服务所在地址发送请求。
gateway日志输出。
六、后记
颁发JWT都归一个认证服务器管理,校验JWT都归Gateway管理,至于授权,则由各个微服务自己定义。耦合性低、性能较好。
关于授权,可以接着这篇文章。
微服务OAuth 2.1认证授权Demo方案(Spring Security 6)