用 Spring Security Oauth2 + JWT 搭建一个认证中心
前言
在很多微服务分布式项目架构中,一般都会有一个独立的认证中心服务,负责用户身份认证和授权,正好近期公司的一个项目用到了,记录总结下。
在此之前,先了解一些概念。
第三方认证
互联网项目经常需要访问外部资源,同样本地系统也经常需要访问外部资源,比如用户使用我们的 APP,但是用户嫌手机号 + 验证码方式注册登录太麻烦,为了用户体验,集成了QQ,微信授权登录。
流程如下:
由图可知,微信不属于我们的系统,我们没有存储用户的账号、密码等信息,我们的系统想要获取用户相关信息需要通过微信的认证,认证通过后可以获得用户的昵称、头像等基本信息,之后该用户就可以在我们的系统登录使用了。
上述过程就是第三方认证。
认证技术方案
单点登录
单点登录,英文SSO,在分布式系统要实现单点登录,通常将认证系统抽取出来作为一个认证中心,用户认证通过后,给用户发放 token,并且将用户身份 token 单独存储(通常是 Redis)这样每次用户访问其他系统时判断 Redis 中是否有合适的 token。
特点:
- 认证系统独立
- 各子系统与认证系统通信,完成身份认证。
- 用户 token 存储在 Redis 中。
单点登录框架(不借助框架,自己也能实现):
CAS
Spring Security CAS
OAuth2 认证
第三方认证技术方案最主要是解决认证协议的通用标准 问题,因为要实现 跨系统认证,各系统之间要遵循一定的接口协议,OAuth2 协议为用户资源的授权提供了一个安全的、开放而又简易的标准。
微信认证流程
微信认证、授权也是基于 OAuth2 的,流程如下:
Oauth2 认证流程
Oauth2 包括以下角色:
客户端:本身不存储资源,需要通过资源拥有者的授权去请求资源服务器的资源。
资源拥有者:通常为用户,也可以是应用程序,即该资源的拥有者。
认证授权服务器:用来对资源拥有的身份进行认证、对访问资源进行授权。客户端要想访问资源需要通过认证服务器由资源拥有者授权后方可访问。
资源服务器:存储资源的服务器,比如我们的系统存储了用户、业务相关信息,微信存储了用户的头像、昵称信息。客户端最终访问资源服务器获取资源信息。
Oauth2 在我们项目中的应用
- 使用微信登录,获取用户的昵称、头像等基本信息。
- 系统客户端访问服务端资源需要请求认证中心获取 token。
- 服务端微服务之间的相互访问。
Spring Security Oauth2
我们使用 Spring Security Oauth2 进行构建认证服务,并在其基础上做了扩展,Spring Security Oauth2 生成的 token 功能不够强大,所以在此基础上整合了 JWT 令牌。
搭建认证中心服务器
- 创建 Spring Security Oauth2 需要的数据库表,如图:
sql:https://www.zhengyk.cn/images/oauth2.sql
主要关注 “oauth_client_details” 表:
client_id:客户端id
resource_id:资源id(暂时不用)
client_secret:客户端密码
scop:范围
access_token_validity:访问token的有效期(秒)
refresh_token_validity:刷新token的有效期(秒)
authorized_grant_type:授权类型,authorization_code,password,refresh_token,client_credentials
Oauth2 授权类型
authorized_grant_type 为 Oauth2 的授权模式,其中授权码(authorization_code)和密码(password)模式应用的比较多,可配置多个,英文逗号分隔:
Oauth2 授权码模式
上面画的微信认证授权流程其实就是授权码模式。
申请授权码
启动认证中心服务,发送 Get 请求:
1 | localhost:8080/auth/oauth/authorize?client_id=app&response_type=code&scope=app&redirect_uri=http://localhost |
client_id:客户端id,和数据库中授权配置类中设置的客户端id一致。
response_type:授权码模式固定为code,数据库中配置为 authorization_code
scope:客户端范围,和授权配置类中设置的 scope 一致。
redirect_uri:跳转uri,当授权码申请成功后会跳转到此地址,并在后边带上code参数(授权码)。
首先跳转到登录界面:
这个账户密码是请求认证服务器的时需要输入的 client_id 和 client_secret
输入后提示是否授权
点击 “Authorize” 获得授权码 code:
申请令牌
post请求:http://localhost:8080/auth/oauth/token
grant_type:授权类型,填写 authorization_code,表示授权码模式
code:授权码,就是刚刚获取的授权码,注意:授权码只使用一次就无效了,需要重新申请。
redirect_uri:申请授权码时的跳转url,一定和申请授权码时用的redirect_uri一致。
需要使用 Basic 认证:http 协议定义的一种认证方式,将客户端id和客户端密码按照“客户端ID:客户端密码”的格式拼接,并用 base64 编码,放在 header 中 Authorization 请求服务端,认证失败返回401错误。
获取到 token:
access_token:访问令牌,携带此令牌访问资源
token_type:token 类型,Oauth2 建议使用 bearer
refresh_token:刷新令牌,使用此令牌可以延长访问令牌的过期时间(这样可以不需要用户重新登录)
expires_in:过期时间,单位为秒。
scope:范围,与定义的客户端范围一致。
jti:access_token 和 refresh_token 的唯一标识。
Oauth2 密码模式
与授权码模式区别是申请令牌不再使用授权码,而是直接通过用户名和密码即可申请令牌
测试如下:
post请求:http://localhost:8080/auth/oauth/token
参数:
grant_type:密码模式授权填写 password
username:用户注册的账号
password:用户注册的密码
与授权码模式一样,也是需要 Http Basic 认证。
如图:
刷新令牌
当用户申请的 access_token 令牌快过期时,要重新生成一个令牌,这是有两种选择:
用户重新登录,再获取 access_token
使用 refresh_token 刷新一个新令牌
使用刷新令牌的方式的好处是不需要用户重新登录即可获得新的令牌,具体采用哪种方式视具体业务来定。
测试:post: http://localhost:40400/auth/oauth/token
grant_type::固定为 refresh_token
refresh_token:申请令牌时获得的 access_token
JWT
什么是JWT?
JSON Web Token(JWT)是一个开放的行业标准(RFC 7519),它定义了一种简介的、自包含的协议格式,用于 在通信双方传递json对象,传递的信息经过数字签名可以被验证和信任。JWT可以使用HMAC算法或使用RSA的公 钥/私钥对来签名,防止被篡改。
JWT令牌的优点:
jwt基于json,非常方便解析。
可以在令牌中自定义丰富的内容(如用户权限),易扩展。
通过非对称加密算法及数字签名技术,JWT防止篡改,安全性高。
资源服务使用 JWT 可不依赖认证服务即可完成授权。
缺点:
JWT令牌较长,占存储空间比较大。
Spring Security 整合了 JWT,上述示例中返回的 token 就是 JWT 令牌,具体实现细节可参考官方文档。
至此,一个微服务中简易版的认证中心的搭建和核心接口测试已完成, 可在此基础上进行扩展自己的业务。
- Title: 用 Spring Security Oauth2 + JWT 搭建一个认证中心
- Author: 薛定谔的汪
- Created at : 2018-11-01 13:20:16
- Updated at : 2023-11-17 19:37:36
- Link: https://www.zhengyk.cn/2018/11/01/springcloud/spring-security-oauth2-jwt/
- License: This work is licensed under CC BY-NC-SA 4.0.