使用双令牌(token)来提升站点的用户体验
封面图源:@takeru11275
前言
注意!本文并不十分严谨,只是以笔者自己的理解写的。请不要以本文为准,注意参考其他大佬的文章。
OAuto2.0
在开始介绍双 token(access token 和 refresh token)之前先了解一下what is OAuto2.0。
OAuto2.0 是一种授权框架(协议),起初我以为是一种插件框架之类的,不过详细看了一遍文档发现并没有这么麻烦,可以简单理解为他只是一种形式规范。
例如 OAuto2.0 规定了角色
的概念:
- 资源所有者:拥有受保护的资源(例如媒体文件,数据库数据)并且可以授权访问的用户或系统。
- 客户端:访问受保护资源的系统。访问资源时,需要拥有访问令牌。
- 授权服务器:服务器接收来自客户端的令牌请求,在身份验证通过后返回令牌。
- 资源服务器:存放资源并接收客户端访问请求的服务器,在用户发起请求时会验证访问令牌并返回资源。
在 OAuto2.0 中,令牌也有两个:
- 访问令牌:用户验证资源请求所用的验证令牌。
- 刷新令牌:当访问令牌过期时请求新的访问令牌。
我不想写的太冗余(其实就是我原本写了一大堆然后软件没有保存,不小心切换到其它文章让我白写了,现在我已经没耐心再写那么多东西了)。
所以写在前面,请忘记 OAuto2.0,这个东西不重要,因为他只是一种形式,如果你总要带入这个东西,那多半会对你的理解造成困扰。
接下来我会用我自己的理解去给你介绍两个令牌他们的工作方式。
(或许你会你问我既然如此为什么还要去写 OAuto2.0,而不是直接写双令牌。因为不写一下 OAuto2.0 会让人感觉双令牌是我自己发明的,显得我很装)
运作
我们从登录开始。
当客户端发起登录请求,授权服务器会校验用户名和密码,并且生成访问令牌
和刷新令牌
,将刷新令牌保存到数据库用于后期验证。
这里的两个令牌会有一些区别,例如访问令牌会包含用户名和 uid 一些用户的信息,而刷新令牌并不会保存用户的任何信息。访问令牌的有效期会比刷新令牌的短。
注意,刷新令牌不该保存任何用户相关的信息,这是危险的行为,这会使刷新令牌和访问令牌无异。
当页面挂载时,客户端会主动发起访问令牌有效性的验证请求。
如果无效就携带上刷新令牌请求新的访问令牌。如果授权服务器发现刷新令牌也无效,就返回指定内容让客户端跳转到登录重新获取令牌。
原理解释
看到这里,聪明的你或许就会知道为什么之前让你忘记 OAuto2.0 的原因了。因为所有的操作并没有用到什么库或者插件,可以说仅仅是一个习惯。
如果你还不明白,可以接着往下看。
我们设想一个场景(实际上就是我开发时遇到的问题):
站点只有一个 JWT 格式的令牌,用于作为身份验证资源请求。
此时令牌过期了,我们应该怎么办,是直接返回登录?还是用旧令牌去请求新令牌?
事实上这两个都不是好办法,前者会降低用户体验,例如你的令牌有效期是七天,而用户可能不会频繁访问你的网站,或者说每次访问间隔总在七天之后,那这样可能会导致每次访问都需要登录,这无疑降低了用户体验。而后者是根本行不通的,因为令牌已经过期了,是无法解析出任何有用的信息的。
可能你会想,那我直接把有效期设置长一些,直接设置一个月,或者根本不会过期!哦,亲爱的,这当然可以,不过可能会让你的用户发现自己的信息被扒光或者被修改了。
那么目前来看新增一个令牌是很好的方法,
原来的单个令牌变成访问令牌,用于身份验证资源请求。而新增的令牌是刷新令牌,他不会携带任何有用的信息,作用仅仅是用来请求新的令牌。
当然你可能会奇怪,刷新令牌不携带任何信息,那我怎么判断他是哪个用户的刷新令牌,我怎么给用户刷新访问令牌。
我的解决方法是直接在数据库新建了一个表,存放所有的刷新令牌和对应的 uid,添加一个字段用来作为刷新令牌是否有效的判断。(我曾经在某个文章还是评论区看见有人说不应该储存刷新令牌,但是我实在不知道该怎么做验证了,只能这样做)
实践操作
纸上得来终觉浅,主要是我觉得写也不好,把代码贴出来可能会让人感觉更清晰一些。
这里我使用了 vue3 + pinia + nodejs
1 |
|
1 |
|
1 |
|