项目初衷

以jwt(由header,payload和signature组成)为例,用户登录成功,后端返回accessToken。前端保存,请求接口携带,一切都是水到渠成的
可是在acessToken失效时,你正好请求一次接口,接口就挂了,可能你就跳到登录页了,用户体验也不好。我们希望虽然过期了,但是token偷偷地刷新重新设置,但又不影响当前接口运行。如果你的方案是把accessToken的有效期设置地久一点,比如100年。你真的是个机智Boy。

方案

生成accessToken和refreshToken。这两个token都是用jwt生成的,payload中关于用户信息,是一致的,是同一账号登录,过期时间是不一样的,accessToken短一点,refreshToken长一点;签名也可以不一样。但是两者解码后用户信息是一样(登录时统一生成的)。 jwt是不会保存在内存和数据库的;推荐redis缓存,现在理解成键值的关系(其实hash会更好)。项目中关系,是accessToken过期了,但是本地缓存的refreshToken是有效时,请求刷新。

  • 用户初次进入应用,无accessToken,则跳到登录页登录;有accessToken的话,先进行校验是否过期,过期再校验refreshToken。如果refreshToken也过期,则清除缓存进入登录页,未过期就进入应用,同时根据refreshToken刷新生成的accessToken;
  • 用户登录成功后返回accessToken与refreshToken。接口请求携带accessToken;接口通,本次请求结束;如果返回是accessToken过期的状态,根据refreshToken重新获取accesToken(重新走jwt.sign),redis也要更新refreshToken对应的accessToken值。当然两个都过期,就清除本地缓存,去登录页面。

前端

小demo来演示,accessToken过期后,如何无感刷新,重新请求接口。

演示

import axios from "axios";let token = "default"; // default在这里表示失效的token值function request(url, data = {}, callBack = null) {return new Promise(async (resolve, reject) => {axios(url, data).then((res) => {if (callBack) {console.log("进入 callBack");callBack(res.data);return;}if (token === "accessToken") { // 此时accessToken为更新后的值。实际业务按code值判断resolve(res.data);} else if (token === "default") { // token状态码失效判断,重新请求刷新token。 实际业务按code值判断console.log("无token / token 校验失败");setToken().then(() => {request(url, data, resolve); // 以resolve对象赋值给callBack});}}).catch((err) => {reject(err);});});
}function setToken() { // 此处是走刷新token的接口,当然如果接口说refreshToken也过期,则重置;重新登录return new Promise((resolve, reject) => {if (token === "default") { // console.log("进入 token");token = "accessToken";resolve();} else {reject();}});
}request("http://www.xxxx:8005/api/v1/category").then((res) => {console.log(res, 8888888);
});

运行结果如下

当然,你也可以模拟如果接口token未过期

let token = "accessToken";

后端

核心代码块

  • 登录成功返回accessToken, refreshToken;并将关联关系redis缓存起来
const redisClient = redis.createClient(REDIS_CONF.port, REDIS_CONF.host, {auth_pass: REDIS_CONF.password});// 管理登录
router.post("/login", async (ctx) => {const { email, password } = ctx.request.body// 验证账号密码是否正确const user = await UserModel.findOne({where: {email}})if(!user){throw new Error("账号不存在")}// 解密const correct = bcrypt.compareSync(password, user.password);if(!correct ){throw new Error("密码错误")}// 生成accessToken和refreshTokenconst access_token = generateToken(user.email)const refresh_token = generateReToken(user.email) addRefreshTokenToList(refresh_token,email,access_token,global.config.security.re_expiresIn);   ctx.response.status = 200;ctx.body = {code: 200,msg: "登录成功",access_token,refresh_token,expiresIn:global.config.security.expiresIn,token_type: "Basic",};
})// 颁布令牌
const generateToken = function (uid) {const secretKey = global.config.security.secretKey; // 全局变量控制const expiresIn = global.config.security.expiresIn;const token = jwt.sign({uid}, secretKey, {expiresIn: expiresIn})return token
}// 颁布刷新令牌
generateReToken (uid, scope) {const secretKey = global.config.security.re_secretKey;const expiresIn = global.config.security.re_expiresIn;const re_token = jwt.sign({uid}, secretKey, {expiresIn: expiresIn})return re_token
}// 重新刷新
router.post("/refresh", async (ctx) => {const body = ctx.request.body;const refreshToken = body.refresh_token;if (refreshToken) {const result = await redisClient.exists(refreshToken); // 是否refreshToken过期if (result) {let refreshTokenPayload;var decode = jwt.verify( // 根据refreshToken解码获取用户信息refreshToken,global.config.security.re_secretKey);const email = decode.uid;const accessToken = generateToken(email); // 重新生成accessTokenconst response = {access_token: accessToken,expires_in: global.config.security.expiresIn,token_type: "Basic",};updateRefreshTokenfromList(refreshToken, accessToken); // 更新refreshToken与accessToken的绑定关系ctx.response.status = 200;ctx.body = response;} else {ctx.response.status = 401;ctx.body = {error: "Unauthorized",msg: "The refresh token does not exist",};}} else {ctx.response.status = 400;ctx.body = {error: "Bad Request",msg: "The required parameters were not sent in the request",};}
});// 可能会用到溢出禁用
router.post("/revoke", async (ctx) => {const body = ctx.request.body;const refreshToken = body.refresh_token;if (refreshToken) {const result = await hasRefreshToken(refreshToken);if (result) {removeRefreshTokenfromList(refreshToken);ctx.response.status = 204;ctx.body = {msg: "refresh token revoke",};} else {ctx.response.status = 401;ctx.body = {error: "Unauthorized",msg: "The refresh token does not exist",};}} else {ctx.response.status = 401;ctx.body = {error: "Unauthorized",msg: "The refresh token does not exist",};}
});function addRefreshTokenToList(refreshToken, email, accessToken, exp) {redisClient.hmset(refreshToken, {email,accessToken,});redisClient.expire(refreshToken, exp);
}function updateRefreshTokenfromList(refreshToken, accessToken) {redisClient.hset(refreshToken, "accessToken", accessToken);
}function removeRefreshTokenfromList(refreshToken) {redisClient.del(refreshToken, redis.print);
}function hasRefreshToken(refreshToken) {return new Promise((resolve, reject) => {redisClient.exists(refreshToken, function (err, data) {if (err) {reject(err);} else {resolve(data);}});});
}

收工

前后端利用accessToken与refreshToken无感刷新相关推荐

  1. 关于实现token无感刷新的解决方案

    问题引入 在开发中为了安全或满足分布式场景,通常会舍弃原有的session认证手段,而采用jwt(json web token):但是使用token难免遇到token有效期的问题,如果token长期有 ...

  2. 使用Axios进行无感刷新Token

    前言 本人在开发项目时,在做登录模块时,参考了oauth2,在用户认证成功后会返回给前端一些令牌相关数据.接下来,再用进行接口请求时,前端根据令牌数据进行一系列的判断,然后做出最好的选择. 举个例子: ...

  3. 实现无感刷新token我是这样做的

    大家好,我是漫步,今天来分享一个登录常常遇到的难题,即登录超时时间与安全性的纠结问题. 原文: https://juejin.cn/post/6983582201690456071 前言 最近在做需求 ...

  4. 实现无感刷新 token 我是这样做的

    原文: https://juejin.cn/post/6983582201690456071 前言 最近在做需求的时候,涉及到登录token,产品提出一个问题:能不能让token过期时间长一点,我频繁 ...

  5. 关于无感刷新Token,我是这样子做的

    本文正在参加「金石计划 . 瓜分6万现金大奖」 什么是JWT JWT是全称是JSON WEB TOKEN,是一个开放标准,用于将各方数据信息作为JSON格式进行对象传递,可以对数据进行可选的数字加密, ...

  6. token过期?页面如何实现无感刷新?

    我们为什么要无感刷新呢? 我们都知道,后台返回的token是有时效性的,时间到了,你在交互后台的时候,后台会判断你的token是否过期(安全需要),如果过期了就会通过邪恶的手段逼迫你重新登陆! 无感刷 ...

  7. Vue 无感刷新token

    关于无感刷新的理解:  实现token无感刷新对于前端来说是一项非常常用的技术,其本质是为了优化用户体验,当token过期时不需要用户跳回登录页重新登录,而是当token失效时,进行拦截,发送刷新to ...

  8. uniapp 实现无感刷新token, 适应大多数项目

    不管你是用taro uni 还是vue-cli 或者 react-cli 刷新token这块一通百通 本质上 都一样 我之前讲了一个是 在响应拦截哪里做token刷新 其实这样做还是不好的,因为这样我 ...

  9. token过期怎么办 无感刷新token

    (1)可以通过响应拦截器或者全局前置守卫强制跳转登录页 // 全局前置守卫 router.beforeEach((to, from) => {let token = sessionStorage ...

  10. 无感刷新token方法

    通常,对于一些需要记录用户行为的系统,在进行网络请求的时候都会要求传递一下登录的token.不过,为了接口数据的安全,服务器的token一般不会设置太长,根据需要一般是1-7天的样子,token过期后 ...

最新文章

  1. Linux网络 - 数据包的发送过程
  2. android studio 链接编辑,Android Studio怎么连接手机测试程序?
  3. “百度”(baidu.com)的由来
  4. 你在中国有去过那些地区?
  5. Android SO逆向2-实例分析
  6. 树形结构:优先级队列,堆
  7. SAP Spartacus如何启用B2B feature
  8. mysql 启动_mysql安装、启动
  9. linux全网备份的原理,Linux面试题分享:Rsync(全网备份)和NFS(文件系统)
  10. python数据预处理
  11. java 接口入门,Java接口入门教程解读
  12. Open3d之颜色映射优化
  13. VoosteQ Material Comp for Mac - 尖端音频压缩器
  14. 应急指挥中心整体建设方案(ppt)
  15. 好好讲一讲:到底什么是Java架构师(含福利放送)
  16. 该网页无法正常运作 目前无法处理此请求。 HTTP ERROR 500
  17. 总线揭密 串行传输VS并行传输
  18. php地图找房代码,地图搜租房功能实现
  19. 数据库课程设计之服饰库存管理系统
  20. web端设计和web前端开发的区别

热门文章

  1. python爬取网易动态评论
  2. c语言实训报告 总结与展望,c语言实训报告总结范文四篇合集
  3. 联盟链之hyperledger-fabric
  4. 洪湖市计算机软件学校,湖北省教育厅关于公布“第十届湖北省中小学电脑制作作品评选”暨“第四届湖北省中小学信息技术创新与实践活动”获奖名单的通知...
  5. JAVA实现PDF合并、拆分代码工具类
  6. 计算机休眠状态和关,win7系统关于睡眠和休眠这两种状态的区别
  7. html中显示框框中对勾,如何打出方框里有对勾
  8. 求两个数最小公倍数的7种方法
  9. 杂项-公司:Apple
  10. 数字签名(Digital Signature)