Token 校验逻辑

  1. // CheckTokenEndpoint.checkToken
  2. @RequestMapping(value = "/oauth/check_token")
  3. @ResponseBody
  4. public Map<String, ?> checkToken(@RequestParam("token") String value) {
  5. // 根据 token 查询保存在 tokenStore 的令牌全部信息
  6. OAuth2AccessToken token = resourceServerTokenServices.readAccessToken(value);
  7. if (token == null) {
  8. throw new InvalidTokenException("Token was not recognised");
  9. }
  10. if (token.isExpired()) {
  11. throw new InvalidTokenException("Token has expired");
  12. }
  13. // 根据 token 查询保存的 认证信息 还有权限角色等 (业务信息)
  14. OAuth2Authentication authentication = resourceServerTokenServices.loadAuthentication(token.getValue());
  15. return accessTokenConverter.convertAccessToken(token, authentication);
  16. }
    1. 当客户端带着 header token 访问 oauth2 资源服务器,资源服务器会自动拦截 token
    1. 发送 token 到 认证服务器 校验 token 合法性
    1. 若认证服务器返回给资源服务器是token不合法,则资源服务器返回给客户端对应的信息

Token 生成逻辑

  1. //DefaultTokenServices.createAccessToken 代码逻辑
  2. public OAuth2AccessToken createAccessToken(OAuth2Authentication authentication) throws AuthenticationException {
  3. // 根据用户信息(username),查询已下发的token
  4. OAuth2AccessToken existingAccessToken = tokenStore.getAccessToken(authentication);
  5. OAuth2RefreshToken refreshToken = null;
  6. // 存在已下发的token
  7. if (existingAccessToken != null) {
  8. // 1. token 已经被标志过期,则删除
  9. if (existingAccessToken.isExpired()) {
  10. if (existingAccessToken.getRefreshToken() != null) {
  11. refreshToken = existingAccessToken.getRefreshToken();
  12. tokenStore.removeRefreshToken(refreshToken);
  13. }
  14. tokenStore.removeAccessToken(existingAccessToken);
  15. }
  16. else {
  17. // 直接返回存在的 token,并保存一下token 和 用户信息的关系 (username)
  18. tokenStore.storeAccessToken(existingAccessToken, authentication);
  19. return existingAccessToken;
  20. }
  21. }
  22. // 不存在则创建新的 token
  23. OAuth2AccessToken accessToken = createAccessToken(authentication, refreshToken);
  24. tokenStore.storeAccessToken(accessToken, authentication);
  25. // In case it was modified
  26. refreshToken = accessToken.getRefreshToken();
  27. if (refreshToken != null) {
  28. tokenStore.storeRefreshToken(refreshToken, authentication);
  29. }
  30. return accessToken;
  31. }
    1. 当我们通过oauth2 去获取 token 时,若当前用户已经存在对应的token,直接返回而不不会创建新 token。
    1. 这就意味着,虽然设置了对应客户端获取 token 的有效时间,这里获取到的token

      若是已下发旧token,有效时间不会和session 机制一样自动续期。
    1. 综上情况,在操作过程中token 过期是一个常态化的问题。

Token 刷新逻辑

  1. curl --location --request POST 'http://auth-server/oauth/token?grant_type=refresh_token' \
  2. --header 'Authorization: Basic dGVzdDp0ZXN0' \
  3. --header 'VERSION: dev' \
  4. --data-urlencode 'scope=server' \
  5. --data-urlencode 'refresh_token=eccda61e-0c68-43af-8f67-6302cb389612'

若上,当 前端拿着正确的(未过期且未使用过)refresh_token 去调用 认证中心的刷新 端点刷新时,会 触发RefreshTokenGranter, 返回新的 Token

  1. public class RefreshTokenGranter extends AbstractTokenGranter {
  2. @Override
  3. protected OAuth2AccessToken getAccessToken(ClientDetails client, TokenRequest tokenRequest) {
  4. String refreshToken = tokenRequest.getRequestParameters().get("refresh_token");
  5. return getTokenServices().refreshAccessToken(refreshToken, tokenRequest);
  6. }
  7. }
  • refreshAccessToken 代码实现,调用 tokenStore 生成新的token
  1. @Transactional(noRollbackFor={InvalidTokenException.class, InvalidGrantException.class})
  2. public OAuth2AccessToken refreshAccessToken(String refreshTokenValue, TokenRequest tokenRequest)
  3. throws AuthenticationException {
  4. createRefreshedAuthentication(authentication, tokenRequest);
  5. if (!reuseRefreshToken) {
  6. tokenStore.removeRefreshToken(refreshToken);
  7. refreshToken = createRefreshToken(authentication);
  8. }
  9. OAuth2AccessToken accessToken = createAccessToken(authentication, refreshToken);
  10. tokenStore.storeAccessToken(accessToken, authentication);
  11. if (!reuseRefreshToken) {
  12. tokenStore.storeRefreshToken(accessToken.getRefreshToken(), authentication);
  13. }
  14. return accessToken;
  15. }

客户端(前端)何时刷新

被动刷新

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

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

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

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

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

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

主动刷新

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

  2. 若token要过期之前,主动发起刷新

主动请求的缺点是,客户端占用部分计算资源来处理 token 失效问题

  1. // 10S检测token 有效期
  2. refreshToken() {
  3. this.refreshTime = setInterval(() => {
  4. const token = getStore({
  5. name: 'access_token',
  6. debug: true
  7. })
  8. if (this.validatenull(token)) {
  9. return
  10. }
  11. if (this.expires_in <= 1000 && !this.refreshLock) {
  12. this.refreshLock = true
  13. this.$store
  14. .dispatch('RefreshToken')
  15. .catch(() => {
  16. clearInterval(this.refreshTime)
  17. })
  18. this.refreshLock = false
  19. }
  20. this.$store.commit('SET_EXPIRES_IN', this.expires_in - 10)
  21. }, 10000)
  22. },

『★★★★★』 基于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. Mybatis-04 日志、分页

    Mybatis-04 日志.分页 日志 1.日志工厂 如果数据库操作出现异常,就需要打印日志来排错. 日志工厂会把日志工作委托实现: SLF4J Apache Commons Logging Log4 ...

  2. Power Query 导入多源数据

    导入方法: 导入数据库文件: 修改加载方式: 其他类型数据处理方式类似

  3. Typescript快速入门

    目录 什么是Typescript 为什么学习Typescript 快速搭建开发环境 1.安装node.js 2.使用node自带的npm安装Typescript编译器 3.配置vscode编辑环境 4 ...

  4. Yarn框架的一般过程

    基本过程图: Clinet向ResouceManager发送Job请求 ResouceManager接受到请求后在自身开启一个Container 来运行的ApplicationManager组件,Ap ...

  5. Go中的if-else判断

    目录 go中的if-else判断 一.语法 go中的if-else判断 一.语法 if 条件 { //符合上面条件的执行 } else if 条件{ //符合上面条件的执行 } else { // 不 ...

  6. 翻译:《实用的 Python 编程》02_07_Objects

    目录 | 上一节 (2.6 列表推导式) | 下一节 (3 从程序组织) 2.7 对象 本节介绍有关 Python 内部对象模型的更多详细信息,并讨论一些与内存管理,拷贝和类型检查有关的问题. 赋值 ...

  7. 微信小程序左右滚动公告栏效果

    <view class='notice-wrap' hidden='{{hideNotice}}'> <view class='tongzhitext'> <text c ...

  8. 184. 部门工资最高的员工 + join + in

    184. 部门工资最高的员工 LeetCode_MySql_184 题目描述 题解分析 1.首先需要使用group by找出工资最高的值 2. 然后考虑到最高工资的可能有多位,所以使用in语句找到所有 ...

  9. go中errgroup源码解读

    errgroup 前言 如何使用 实现原理 WithContext Go Wait 错误的使用 总结 errgroup 前言 来看下errgroup的实现 如何使用 func main() { var ...

  10. DRF(django rest-framework)

    1.什么是DRF django组件,快速帮助我们开发遵循restful规范的一个组件 2.什么是restful规范 RESTful的URL用于指定资源,URL中只能使用名词的组合来标识资源," ...