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);
}
    1. 当客户端带着 header token 访问 oauth2 资源服务器,资源服务器会自动拦截 token
    1. 发送 token 到 认证服务器 校验 token 合法性
    1. 若认证服务器返回给资源服务器是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; }
    1. 当我们通过oauth2 去获取 token 时,若当前用户已经存在对应的token,直接返回而不不会创建新 token。
    1. 这就意味着,虽然设置了对应客户端获取 token 的有效时间,这里获取到的token

      若是已下发旧token,有效时间不会和session 机制一样自动续期。
    1. 综上情况,在操作过程中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;
}

客户端(前端)何时刷新

被动刷新

  1. 客户端携带 token 访问资源服务器资源

  2. 资源服务器拦截 token 去认证服务器 check_token

  3. 认证服务器返回 token 过期错误,资源服务器包装错误信息返回给客户端

  4. 客户端根据返回错误信息(响应码),直接调用认证服务器 refresh_token

  5. 认证服务器返回新的 token 给客户端, 然后再次发起 资源调用

被动请求的缺点是,用户当次请求会失败(返回token失败),对一些业务连贯的操作不是很友好

主动刷新

  1. 客户端存在计算逻辑,计算下发token 有效期

  2. 若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 权限管理系统

项目推荐: Spring Cloud 、Spring Security OAuth2的RBAC权限管理系统 欢迎关注

聊聊 OAuth 2.0 的 Token 续期处理的更多相关文章

  1. 我也想聊聊 OAuth 2.0 —— Access Token

    这是一篇待在草稿箱半年之久的文章 连我自己都不知道我的草稿箱有多少未发布的文章了.这应该是我在上一家公司未解散之前写的,记得当时是要做一个开发者中心,很不幸. 今天,打开草稿箱有种莫名的伤感,看到这个 ...

  2. 聊聊 OAuth 2.0 的 token expire_in 使用

    问题背景 有同学私信问了这样的问题,访问 pig4cloud 的演示环境 查看登录请求 network 返回报文如下: { "access_token":"16d3579 ...

  3. 我也想聊聊 OAuth 2.0 —— 基本概念

    这是一篇待在草稿箱半年之久的文章 连我自己都不知道我的草稿箱有多少未发布的文章了.这应该是我在上一家公司未解散之前写的,记得当时是要做一个开发者中心,很不幸. 今天,打开草稿箱有种莫名的伤感,看到这个 ...

  4. OAuth 2.0: Bearer Token Usage

    Bearer Token (RFC 6750) 用于HTTP请求授权访问OAuth 2.0资源,任何Bearer持有者都可以无差别地用它来访问相关的资源,而无需证明持有加密key.一个Bearer代表 ...

  5. Bearer Token & OAuth 2.0

    Bearer Token & OAuth 2.0 access token & refresh token http://localhost:8080/#/login HTTP Aut ...

  6. ASP.NET WebApi OWIN 实现 OAuth 2.0

    OAuth(开放授权)是一个开放标准,允许用户让第三方应用访问该用户在某一网站上存储的私密的资源(如照片,视频,联系人列表),而无需将用户名和密码提供给第三方应用. OAuth 允许用户提供一个令牌, ...

  7. NET WebApi OWIN 实现 OAuth 2.0

    NET WebApi OWIN 实现 OAuth 2.0 OAuth(开放授权)是一个开放标准,允许用户让第三方应用访问该用户在某一网站上存储的私密的资源(如照片,视频,联系人列表),而无需将用户名和 ...

  8. [转]ASP.NET WebApi OWIN 实现 OAuth 2.0

    OAuth(开放授权)是一个开放标准,允许用户让第三方应用访问该用户在某一网站上存储的私密的资源(如照片,视频,联系人列表),而无需将用户名和密码提供给第三方应用. OAuth 允许用户提供一个令牌, ...

  9. IdentityServer4 ASP.NET Core的OpenID Connect OAuth 2.0框架学习保护API

    IdentityServer4 ASP.NET Core的OpenID Connect OAuth 2.0框架学习之保护API. 使用IdentityServer4 来实现使用客户端凭据保护ASP.N ...

随机推荐

  1. java的单例模式小知识点

    单例模式 目的 为了让一个类有且仅有一个实例 优点 只允许一个,节省空间 不用频繁创建删除,提高性能 缺点 不容易扩展 长期不使用会被系统当作垃圾回收,造成系统状态的丢失 实现 要点 防止外界随意的创 ...

  2. 关于MVCC,我之前写错了,这次我改好了!

    关于MVCC的原理,在<我想进大厂>之mysql夺命连环13问写过一次,但是当时写的其实并不准确,这个理解可以应付面试,帮助快速理解,但是他的真正实现原理我想再次拿出来说一说. 简单理解版 ...

  3. WPF -- 自定义按钮

    本文介绍WPF一种自定义按钮的方法. 实现效果 使用图片做按钮背景: 自定义鼠标进入时效果: 自定义按压效果: 自定义禁用效果 实现效果如下图所示: 实现步骤 创建CustomButton.cs,继承 ...

  4. PAT-1140(Look-and-say Sequence)字符串处理

    Look-and-say Sequence PAT-1140 #include<iostream> #include<cstring> #include<string&g ...

  5. 大数据实战-Spark实战技巧

    1.连接mysql --driver-class-path mysql-connector-java-5.1.21.jar 在数据库中,SET GLOBAL binlog_format=mixed; ...

  6. CVE-2017-12149-JBoss 5.x/6.x 反序列化

    漏洞分析 https://www.freebuf.com/vuls/165060.html 漏洞原理 该漏洞位于JBoss的HttpInvoker组件中的 ReadOnlyAccessFilter 过 ...

  7. Keytool 工具使用

    Keytool 管理私钥仓库(keystore)和与之相关的 X.509 证书链(用以验证与私钥对应的公钥),也可以用来管理其他信任实体 keytool 将密钥和证书存储在一个所谓的密钥仓库中,缺省的 ...

  8. FreeBSD ports 基本用法

    首先获取portsnap#portsnap fetch extract---------------------------------------使用whereis 查询软件地址如#whereis ...

  9. Java8 BiFunction 简单用用

    最近来了新公司,主要用到了ElasitcSearch,大家都知道在底层查询代码中往往需要判断传入某个参数是否为空来判断设置查询,例如下方代码: BoolQueryBuilder query = Que ...

  10. vue-cli3.0 开发环境构建

    vue-cli3.0官网 1.node版本 node版本要求node>=8.9以上(推荐 8.11.0+)使用以下命令查看node版本 node -v 如果不是最新的请到node下载下载最新版本 ...