聊聊 OAuth 2.0 的 Token 续期处理
Token 校验逻辑
// CheckTokenEndpoint.checkToken
@RequestMapping(value = "/oauth/check_token")
@ResponseBody
public Map<String, ?> checkToken(@RequestParam("token") String value) {
// 根据 token 查询保存在 tokenStore 的令牌全部信息
OAuth2AccessToken token = resourceServerTokenServices.readAccessToken(value);
if (token == null) {
throw new InvalidTokenException("Token was not recognised");
}
if (token.isExpired()) {
throw new InvalidTokenException("Token has expired");
}
// 根据 token 查询保存的 认证信息 还有权限角色等 (业务信息)
OAuth2Authentication authentication = resourceServerTokenServices.loadAuthentication(token.getValue());
return accessTokenConverter.convertAccessToken(token, authentication);
}
- 当客户端带着
header token访问oauth2资源服务器,资源服务器会自动拦截token
- 当客户端带着
- 发送
token到 认证服务器 校验token合法性
- 发送
- 若认证服务器返回给资源服务器是
token不合法,则资源服务器返回给客户端对应的信息
- 若认证服务器返回给资源服务器是
Token 生成逻辑
//DefaultTokenServices.createAccessToken 代码逻辑
public OAuth2AccessToken createAccessToken(OAuth2Authentication authentication) throws AuthenticationException {
// 根据用户信息(username),查询已下发的token
OAuth2AccessToken existingAccessToken = tokenStore.getAccessToken(authentication);
OAuth2RefreshToken refreshToken = null;
// 存在已下发的token
if (existingAccessToken != null) {
// 1. token 已经被标志过期,则删除
if (existingAccessToken.isExpired()) {
if (existingAccessToken.getRefreshToken() != null) {
refreshToken = existingAccessToken.getRefreshToken();
tokenStore.removeRefreshToken(refreshToken);
}
tokenStore.removeAccessToken(existingAccessToken);
}
else {
// 直接返回存在的 token,并保存一下token 和 用户信息的关系 (username)
tokenStore.storeAccessToken(existingAccessToken, authentication);
return existingAccessToken;
}
}
// 不存在则创建新的 token
OAuth2AccessToken accessToken = createAccessToken(authentication, refreshToken);
tokenStore.storeAccessToken(accessToken, authentication);
// In case it was modified
refreshToken = accessToken.getRefreshToken();
if (refreshToken != null) {
tokenStore.storeRefreshToken(refreshToken, authentication);
}
return accessToken;
}
- 当我们通过oauth2 去获取
token时,若当前用户已经存在对应的token,直接返回而不不会创建新 token。
- 当我们通过oauth2 去获取
- 这就意味着,虽然设置了对应客户端获取 token 的有效时间,这里获取到的
token。
若是已下发旧token,有效时间不会和session机制一样自动续期。
- 这就意味着,虽然设置了对应客户端获取 token 的有效时间,这里获取到的
- 综上情况,在操作过程中
token过期是一个常态化的问题。
- 综上情况,在操作过程中
Token 刷新逻辑
curl --location --request POST 'http://auth-server/oauth/token?grant_type=refresh_token' \
--header 'Authorization: Basic dGVzdDp0ZXN0' \
--header 'VERSION: dev' \
--data-urlencode 'scope=server' \
--data-urlencode 'refresh_token=eccda61e-0c68-43af-8f67-6302cb389612'
若上,当 前端拿着正确的(未过期且未使用过)refresh_token 去调用 认证中心的刷新 端点刷新时,会 触发RefreshTokenGranter, 返回新的 Token
public class RefreshTokenGranter extends AbstractTokenGranter {
@Override
protected OAuth2AccessToken getAccessToken(ClientDetails client, TokenRequest tokenRequest) {
String refreshToken = tokenRequest.getRequestParameters().get("refresh_token");
return getTokenServices().refreshAccessToken(refreshToken, tokenRequest);
}
}
- refreshAccessToken 代码实现,调用
tokenStore生成新的token
@Transactional(noRollbackFor={InvalidTokenException.class, InvalidGrantException.class})
public OAuth2AccessToken refreshAccessToken(String refreshTokenValue, TokenRequest tokenRequest)
throws AuthenticationException {
createRefreshedAuthentication(authentication, tokenRequest);
if (!reuseRefreshToken) {
tokenStore.removeRefreshToken(refreshToken);
refreshToken = createRefreshToken(authentication);
}
OAuth2AccessToken accessToken = createAccessToken(authentication, refreshToken);
tokenStore.storeAccessToken(accessToken, authentication);
if (!reuseRefreshToken) {
tokenStore.storeRefreshToken(accessToken.getRefreshToken(), authentication);
}
return accessToken;
}
客户端(前端)何时刷新
被动刷新

客户端携带
token访问资源服务器资源资源服务器拦截
token去认证服务器check_token认证服务器返回
token过期错误,资源服务器包装错误信息返回给客户端客户端根据返回错误信息(响应码),直接调用认证服务器
refresh_token认证服务器返回新的
token给客户端, 然后再次发起 资源调用
被动请求的缺点是,用户当次请求会失败(返回token失败),对一些业务连贯的操作不是很友好
主动刷新

客户端存在计算逻辑,计算下发token 有效期
若token要过期之前,主动发起刷新
主动请求的缺点是,客户端占用部分计算资源来处理 token 失效问题
// 10S检测token 有效期
refreshToken() {
this.refreshTime = setInterval(() => {
const token = getStore({
name: 'access_token',
debug: true
})
if (this.validatenull(token)) {
return
}
if (this.expires_in <= 1000 && !this.refreshLock) {
this.refreshLock = true
this.$store
.dispatch('RefreshToken')
.catch(() => {
clearInterval(this.refreshTime)
})
this.refreshLock = false
}
this.$store.commit('SET_EXPIRES_IN', this.expires_in - 10)
}, 10000)
},
『★★★★★』 基于Spring Boot 2.2、 Spring Cloud Hoxton & Alibaba、 OAuth2 的RBAC 权限管理系统
聊聊 OAuth 2.0 的 Token 续期处理的更多相关文章
- 我也想聊聊 OAuth 2.0 —— Access Token
这是一篇待在草稿箱半年之久的文章 连我自己都不知道我的草稿箱有多少未发布的文章了.这应该是我在上一家公司未解散之前写的,记得当时是要做一个开发者中心,很不幸. 今天,打开草稿箱有种莫名的伤感,看到这个 ...
- 聊聊 OAuth 2.0 的 token expire_in 使用
问题背景 有同学私信问了这样的问题,访问 pig4cloud 的演示环境 查看登录请求 network 返回报文如下: { "access_token":"16d3579 ...
- 我也想聊聊 OAuth 2.0 —— 基本概念
这是一篇待在草稿箱半年之久的文章 连我自己都不知道我的草稿箱有多少未发布的文章了.这应该是我在上一家公司未解散之前写的,记得当时是要做一个开发者中心,很不幸. 今天,打开草稿箱有种莫名的伤感,看到这个 ...
- OAuth 2.0: Bearer Token Usage
Bearer Token (RFC 6750) 用于HTTP请求授权访问OAuth 2.0资源,任何Bearer持有者都可以无差别地用它来访问相关的资源,而无需证明持有加密key.一个Bearer代表 ...
- Bearer Token & OAuth 2.0
Bearer Token & OAuth 2.0 access token & refresh token http://localhost:8080/#/login HTTP Aut ...
- ASP.NET WebApi OWIN 实现 OAuth 2.0
OAuth(开放授权)是一个开放标准,允许用户让第三方应用访问该用户在某一网站上存储的私密的资源(如照片,视频,联系人列表),而无需将用户名和密码提供给第三方应用. OAuth 允许用户提供一个令牌, ...
- NET WebApi OWIN 实现 OAuth 2.0
NET WebApi OWIN 实现 OAuth 2.0 OAuth(开放授权)是一个开放标准,允许用户让第三方应用访问该用户在某一网站上存储的私密的资源(如照片,视频,联系人列表),而无需将用户名和 ...
- [转]ASP.NET WebApi OWIN 实现 OAuth 2.0
OAuth(开放授权)是一个开放标准,允许用户让第三方应用访问该用户在某一网站上存储的私密的资源(如照片,视频,联系人列表),而无需将用户名和密码提供给第三方应用. OAuth 允许用户提供一个令牌, ...
- IdentityServer4 ASP.NET Core的OpenID Connect OAuth 2.0框架学习保护API
IdentityServer4 ASP.NET Core的OpenID Connect OAuth 2.0框架学习之保护API. 使用IdentityServer4 来实现使用客户端凭据保护ASP.N ...
随机推荐
- 「NGK每日快讯」12.3日NGK公链第30期官方快讯!
- React组件复用的方式
React组件复用的方式 现前端的工程化越发重要,虽然使用Ctrl+C与Ctrl+V同样能够完成需求,但是一旦面临修改那就是一项庞大的任务,于是减少代码的拷贝,增加封装复用能力,实现可维护.可复用的代 ...
- C++教程01:计算机系统的组成
教程首发 | 公众号:lunvey 学习C++之前,需要先了解一点基础的计算机知识.毕竟C++是跑在计算机系统上的,我们写的程序都是一段段的指令集. 首台计算机ENIAC问世之后,缺少原理指导.冯诺依 ...
- MySQL:单表查询
在线数据库:http://sqlfiddle.com/ 建表脚本:https://gitee.com/bqzzz/sql/tree/master/Scripts 基本查询 ①.查询所有记录 SELEC ...
- MySQL注入时常用函数
注入常用函数 数据库相关 database() --- 返回当前数据库名 @@datadir --- 读取数据库路径 @@basedir --- 读取数据库安全路径 @@version_compile ...
- 1.2 Python3基础-规范
>>返回主目录 总的来说,如果安装的不是安装的Anaconda,pip命令还是经常会用到的(cmd模式使用),当然也可以在PyCharm中直接安装 PEP8规范,我另有一篇博客已经写好,可 ...
- 基于dlib+django+python 实现web端人脸打卡
face_recognition 基于python+django+dlib实现的人脸打卡系统 开始之前 windows用户需要安装 VS2017 其他VS版本也行 linux用户需要安装c++编译器( ...
- 面试必备——Java多线程与并发(二)
1.synchroized相关(锁的是对象,不是代码) (1)线程安全问题的主要原因 存在共享数据(也称临界资源) 存在多线程共同操作这些共享数据 解决:同一时刻有且只有一个线程在操作共享数据,其他线 ...
- java将数据生成csv文件
1,httpRequest接口触发进程[或者可以换成其他方式触发] /** * 出入库生成CSV文件 * @param req * @param params * @return */@Request ...
- 爬虫必知必会(7)_scrapy框架高级
一.请求传参 实现深度爬取:爬取多个层级对应的页面数据 使用场景:爬取的数据没有在同一张页面中 在手动请求的时候传递item:yield scrapy.Request(url,callback,met ...