-
2021-12-24 11:39:31
vue实际运用六:处理token过期
1. 后端为了安全,token一般存在有效时间,当token过期,所有请求失效
解决方案:
1)在请求发起前拦截每个请求,判断token的有效时间是否已经过期,若已过期,则将请求挂起,先刷新token后再继续请求。
- 优点: 在请求前拦截,能节省请求,省流量
- 缺点: 需要后端额外提供一个token过期时间的字段;使用了本地时间判断,若本地时间被篡改,特别是本地时间比服务器时间慢时,拦截会失败
- 使用方法:axios.interceptors.request.use() 这个请求前拦截方法
2)根据拦截返回后的数据判断,若token过期,先刷新token,再进行一次请求。
- 优点:不需额外的token过期字段,不需判断时间
- 缺点: 会消耗多一次请求,耗流量
- 使用方法:axios.interceptors.response.use() 这个响应拦截方法
最简单方法:获取到过期code,直接跳到登录页
2. 实现方法二
1)封装axios基本结构
- token是存在localStorage中
//在request.js import axios from 'axios' // 创建一个实例 const service = axios.create({ baseURL: process.env.VUE_APP_BASE_API, timeout: 5000 // request timeout }) // 从localStorage中获取token function getLocalToken () { const token = window.localStorage.getItem('token') return token } // 给实例添加一个setToken方法,用于登录后将最新token动态添加到header,同时将token保存在localStorage中 service.setToken = (token) => { instance.defaults.headers['X-Token'] = token window.localStorage.setItem('token', token) } // 拦截返回的数据 service.interceptors.response.use(response => { // 接下来会在这里进行token过期的逻辑处理 return response }, error => { return Promise.reject(error) }) //暴露 export default service
假如后端接口token过期返回的code是401
//获取新的token请求 function refreshToken () { return service.post('/refreshtoken').then(res => res.data) } // 拦截返回的数据 service.interceptors.response.use(response => { // 接下来会在这里进行token过期的逻辑处理 const { code } = response.data ----------------------------------------------------------- // 说明token过期了,获取新的token if (code === 401) { return refreshToken().then(res => { // 刷新token成功,将最新的token更新到header中,同时保存在localStorage中 const { token } = res.data service.setToken(token) // 获取当前失败的请求 const config = response.config //重置失败请求的配置 config.headers['X-Token'] = token config.baseURL = '' " //重试当前请求并返回promise return service(config) }).catch( res=>{ //重新请求token失败,跳转到登录页 window.location.href = '/login ' } ) } -------------------------------------------------------------- return response }, error => { return Promise.reject(error) })
2)问题和优化
- 如果token失效时,存在多个请求,这就会导致多次执行刷新token的接口
在request.js中用一个变量来标记当前是否正在刷新token的状态,如果正在刷新则不再调用刷新token的接口
在request.js // 是否正在刷新的标记 let isRefreshing = false ----------------------------------------------------------- // 说明token过期了,获取新的token if (code === 401) { //判断一下状态 if( !isRefreshing ){ //修改状态,进入更新token阶段 isRefreshing = true return refreshToken().then(res => { // 刷新token成功,将最新的token更新到header中,同时保存在localStorage中 const { token } = res.data service.setToken(token) // 获取当前失败的请求 const config = response.config //重置失败请求的配置 config.headers['X-Token'] = token config.baseURL = '' " //重试当前请求并返回promise return service(config) }).catch( res=>{ //重新请求token失败,跳转到登录页 window.location.href = '/login ' } ).finally( ()=>{ //完成之后在关闭状态 isRefreshing = false } ) } } --------------------------------------------------------------
- 同时发起两个或以上的请求时,其他接口如何重试
两个接口几乎同时发起和返回,第一个接口会进入刷新token后重试的流程,而第二个接口需要先存起来,然后等刷新token后再重试。同样,如果同时发起三个请求,此时需要缓存后两个接口,等刷新token后再重试;
当第二个过期的请求进来,token正在刷新,我们先将这个请求存到一个数组队列中,想办法让这个请求处于等待中,一直等到刷新token后再逐个重试清空请求队列。
将请求存进队列中后,同时返回一个Promise,让这个Promise一直处于Pending状态(即不调用resolve),此时这个请求就会一直等啊等,只要我们不执行resolve,这个请求就会一直在等待。当刷新请求的接口返回来后,我们再调用resolve,逐个重试。// 是否正在刷新的标记 let isRefreshing = false // 重试队列,每一项将是一个待执行的函数形式 let requests = [] ----------------------------------------------------------- // 说明token过期了,获取新的token if (code === 401) { const config = response.config //判断一下状态 if( !isRefreshing ){ //修改状态,进入更新token阶段 isRefreshing = true // 获取当前的请求 return refreshToken().then(res => { // 刷新token成功,将最新的token更新到header中,同时保存在localStorage中 const { token } = res.data service.setToken(token) //重置失败请求的配置 config.headers['X-Token'] = token config.baseURL = '' " //已经刷新了token,将所有队列中的请求进行重试 requests.forEach(cb => cb(token)) // 重试完了别忘了清空这个队列 requests = [] return service(config) }).catch( res=>{ //重新请求token失败,跳转到登录页 window.location.href = '/login ' } ).finally( ()=>{ //完成之后在关闭状态 isRefreshing = false } ) } else{ // 正在刷新token,返回一个未执行resolve的promise return new Promise((resolve) => { // 将resolve放进队列,用一个函数形式来保存,等token刷新后直接执行 requests.push((token) => { config.baseURL = '' config.headers['X-Token'] = token resolve(instance(config)) }) }) } } --------------------------------------------------------------
完整版
//在request.js import axios from 'axios' // 是否正在刷新的标记 let isRefreshing = false // 重试队列,每一项将是一个待执行的函数形式 let requests = [] // 创建一个实例 const service = axios.create({ baseURL: process.env.VUE_APP_BASE_API, timeout: 5000 // request timeout }) // 从localStorage中获取token function getLocalToken () { const token = window.localStorage.getItem('token') return token } // 给实例添加一个setToken方法,用于登录后将最新token动态添加到header,同时将token保存在localStorage中 service.setToken = (token) => { instance.defaults.headers['X-Token'] = token window.localStorage.setItem('token', token) } //获取新的token请求 function refreshToken () { return service.post('/refreshtoken').then(res => res.data) } // 拦截返回的数据 service.interceptors.response.use(response => { // 接下来会在这里进行token过期的逻辑处理 const { code } = response.data ----------------------------------------------------------- // 说明token过期了,获取新的token if (code === 401) { const config = response.config //判断一下状态 if( !isRefreshing ){ //修改状态,进入更新token阶段 isRefreshing = true // 获取当前的请求 return refreshToken().then(res => { // 刷新token成功,将最新的token更新到header中,同时保存在localStorage中 const { token } = res.data service.setToken(token) //重置失败请求的配置 config.headers['X-Token'] = token config.baseURL = '' " //已经刷新了token,将所有队列中的请求进行重试 requests.forEach(cb => cb(token)) // 重试完了别忘了清空这个队列 requests = [] return service(config) }).catch( res=>{ //重新请求token失败,跳转到登录页 window.location.href = '/login ' } ).finally( ()=>{ //完成之后在关闭状态 isRefreshing = false } ) } else{ // 正在刷新token,返回一个未执行resolve的promise return new Promise((resolve) => { // 将resolve放进队列,用一个函数形式来保存,等token刷新后直接执行 requests.push((token) => { config.baseURL = '' config.headers['X-Token'] = token resolve(instance(config)) }) }) } } -------------------------------------------------------------- return response }, error => { return Promise.reject(error) }) //暴露 export default service
更多相关内容 -
请求时token过期自动刷新token操作
2020-10-14 18:55:08主要介绍了请求时token过期自动刷新token操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧 -
Vue 拦截器对token过期处理方法
2020-08-28 04:50:26下面小编就为大家分享一篇Vue 拦截器对token过期处理方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧 -
Vue利用路由钩子token过期后跳转到登录页的实例
2020-11-26 23:38:40在Vue2.0中的路由钩子主要是用来拦截导航,让它完成跳转或前取消,可以理解为路由...这里我使用了组件内钩子进行判断token过期后跳转到登录页,其他两种钩子可以去官网查看。 //路由前验证 beforeRouteEnter(to, f -
Android token过期刷新处理的方法示例
2020-08-26 21:52:43主要介绍了Android token过期刷新处理的方法示例,本文详细的介绍了2种方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下 -
.Net微信开发之如何解决access_token过期问题
2020-10-22 00:55:55主要为大家详细介绍了.Net微信开发之如何解决access_token过期问题的方法,感兴趣的小伙伴们可以参考一下 -
token 过期后,如何自动续期?
2022-05-03 00:18:24点击关注公众号,利用碎片时间学习JWT token的 payload 部分是一个json串,是要传递数据的一组声明,这些声明被JWT标准称为claims。JWT标准里面定义的标准claim包括:iss(Issuser):JWT的签发主体;sub(Subject):...点击关注公众号,利用碎片时间学习
JWT token的 payload 部分是一个json串,是要传递数据的一组声明,这些声明被JWT标准称为claims。
JWT标准里面定义的标准claim包括:
iss(Issuser)
:JWT的签发主体;sub(Subject)
:JWT的所有者;aud(Audience)
:JWT的接收对象;exp(Expiration time)
:JWT的过期时间;nbf(Not Before)
:JWT的生效开始时间;iat(Issued at)
:JWT的签发时间;jti(JWT ID)
:是JWT的唯一标识。
除了以上标准声明以外,我们还可以自定义声明。以 com.auth0 为例,下面代码片段实现了生成一个带有过期时间的token.
String token = JWT.create() .withIssuer(ISSUER) .withIssuedAt(new Date(currentTime))// 签发时间 .withExpiresAt(new Date(currentTime + EXPIRES_IN * 1000 * 60))// 过期时间戳 .withClaim("username", username)//自定义参数 .sign(Algorithm.HMAC256(user.getPassword()));
其中:
withIssuer()
设置签发主体;withIssuedAt()
设置签发时间;withExpiresAt()
设置过期时间戳,过期的时长为 EXPIRES_IN (单位秒);withClaim()
设置自定义参数。
JWT设置了过期时间以后,一定超过,那么接口就不能访问了,需要用户重新登录获取token。如果经常需要用户重新登录,显然这种体验不是太好,因此很多应用会采用token过期后自动续期的方案,只有特定条件下才会让用户重新登录。
token过期的续期方案
解决token过期的续期问题可以有很多种不同的方案,这里举一些比较有代表性的例子。首先我们看一个单token方案,这个方案除了可以实现token续期以外,还可以实现某些条件下的强制重新登录。
单token方案
将 token 过期时间设置为15分钟;
前端发起请求,后端验证 token 是否过期;如果过期,前端发起刷新token请求,后端为前端返回一个新的token;
前端用新的token发起请求,请求成功;
如果要实现每隔72小时,必须重新登录,后端需要记录每次用户的登录时间;用户每次请求时,检查用户最后一次登录日期,如超过72小时,则拒绝刷新token的请求,请求失败,跳转到登录页面。
另外后端还可以记录刷新token的次数,比如最多刷新50次,如果达到50次,则不再允许刷新,需要用户重新授权。
上面介绍的单token方案原理比较简单。下面我们再看一个双token方案。
双token方案
登录成功以后,后端返回
access_token
和refresh_token
,客户端缓存此两种token;使用
access_token
请求接口资源,成功则调用成功;如果token超时,客户端携带refresh_token
调用token刷新接口获取新的access_token
;后端接受刷新token的请求后,检查
refresh_token
是否过期。如果过期,拒绝刷新,客户端收到该状态后,跳转到登录页;如果未过期,生成新的access_token
返回给客户端。客户端携带新的
access_token
重新调用上面的资源接口。客户端退出登录或修改密码后,注销旧的token,使
access_token
和refresh_token
失效,同时清空客户端的access_token
和refresh_toke
。
微信网页授权是通过OAuth2.0机制实现的,也使用了双token方案。
微信网页授权方案
用户在第三方应用的网页上完成微信授权以后,第三方应用可以获得 code(授权码)。code的超时时间为10分钟,一个code只能成功换取一次access_token即失效。
第三方应用通过code获取网页授权凭证access_token和刷新凭证 refresh_token。
access_token是调用授权关系接口的调用凭证,由于access_token有效期(2个小时)较短,当access_token超时后,可以使用refresh_token进行刷新。
refresh_token拥有较长的有效期(30天),当refresh_token失效的后,需要用户重新授权。
后端实现token过期还可以利用Redis来存储token,设置redis的键值对的过期时间。如果发现redis中不存在token的记录,说明token已经过期了。
来源:toutiao.com/article/6995179162675790350
推荐:
PS:因为公众号平台更改了推送规则,如果不想错过内容,记得读完点一下“在看”,加个“星标”,这样每次新文章推送才会第一时间出现在你的订阅列表里。点“在看”支持我们吧!
-
Laravel Passport token过期后判断refresh_token是否过期
2021-05-07 08:45:08需求:前后端分离状态下,登录失效(token过期)后,前端需要知道下一步是跳转到登录页面还是使用refresh_token刷新token。这就需要后端根据是否可以刷新token(refresh_token是否过期)返回不同的标识,以供前端进行下...需求:前后端分离状态下,登录失效(token过期)后,前端需要知道下一步是跳转到登录页面还是使用refresh_token刷新token。
这就需要后端根据是否可以刷新token(refresh_token是否过期)返回不同的标识,以供前端进行下一步操作。
具体做法如下:
1、新建RefreshToken中间件,每次登陆成功后查询是否保存了token和id的对应关系(登录失效后无法通过token获取对应id)
namespace App\Http\Middleware;useClosure;useAuth;useIlluminate\Support\Facades\DB;classRefreshToken
{/**
* @param $request
* @param Closure $next
* @return mixed*/
public function handle($request, Closure $next)
{$token=$request->header('authorization');$has=DB::table('oauth_refresh_tokens_ids')->where('access_token',$token)->value('access_token_id');if(!$has){
DB::table('oauth_refresh_tokens_ids')->insert(['access_token'=>$token,'access_token_id'=>Auth::user()->token()->id]);
}return $next($request);
}
}
View Code
2、kerne.php文件内为RefreshToken中间件分配一个key
protected $routeMiddleware =['auth' => \App\Http\Middleware\Authenticate::class,
'refresh'=>\App\Http\Middleware\RefreshToken::class,//刷新token
];
View Code
3、在路由中使用RefreshToken中间件
4、重写Authenticate中间件登录失效后的跳转方法redirectTo
protected function redirectTo($request)
{$token=$_SERVER['HTTP_AUTHORIZATION'];$accessTokenId=DB::table('oauth_refresh_tokens_ids')->where('access_token',$token)->value('access_token_id');$RefreshTokensExpiresAt=DB::table('oauth_refresh_tokens')->where('access_token_id',$accessTokenId)->value('expires_at');if(strtotime($RefreshTokensExpiresAt)>strtotime(now())){//可刷新token
return route('login',['code'=>402]);
}else{return route('login',['code'=>401]);
}
}
View Code
5、新建路由
Route::get('login/{code}', function ($code) {return response()->json(['code'=>$code,'msg'=>'login timeout.']);
})->name('login');
View Code
实现:
1、登录超时后refresh_token未过期时返回
{"code": "402",
"msg": "login timeout."}
2、登录超时后refresh_token过期时返回
{"code": "401",
"msg": "login timeout."}
说明:虽然数据表中存了token和refresh_token的创建时间和失效时间,但是框架验证的时候并没有从数据表中取值,而是直接通过token计算出来,不得不说安全性是真的高。所以通过修改token过期时间来进行登录状态续期的办法不可行,必须对token进行刷新。
Enjoy it !
-
处理token过期的问题,无感知刷新token
2021-12-05 22:07:08登录用户的token过期了 整体目标是:通过axios响应拦截器来处理401问题。 理解token过期 登陆成功之后,接口会返回一个token值,这个值在后续请求时通过请求头时带上(就像是开门钥匙)。但是,这个值一般会有有效...401错误的场景
有如下两种情况会出现401错误:
- 未登陆用户做一些需要权限才能做的操作,代码会报出401错误。这种情况下,应该让用户回到登陆页。
- 登录用户的token过期了
整体目标是:通过axios响应拦截器来处理401问题。
理解token过期
登陆成功之后,接口会返回一个token值,这个值在后续请求时通过请求头时带上(就像是开门钥匙)。但是,这个值一般会有有效期(具体是多长,是由后端决定),假如在我这里有效期是2小时。
如果你上午8点登陆成功,到了10:01分,则token就会失效,再去发请求时,就会报401错误。
refresh_token和token的作用
当用户登陆成功之后,返回的token中有两个值,说明如下:
- token:
- 作用:在访问一些接口时,需要传入token,就是它。
- 有效期:2小时(安全)。
- refresh_token
- 作用: 当token的有效期过了之后,可以使用它去请求一个特殊接口(这个接口也是后端指定的,明确需要传入refresh_token),并返回一个新的token回来(有效期还是2小时),以替换过期的那个token。
- 有效期:14天。(最理想的情况下,一次登陆可以持续14天。)
响应拦截器功能
axios中提供了响应拦截器功能:所有从后端回来的响应都会集中进入响应拦截器中。所以,我们可以在响应拦截器中去写代码来统一解决。
request的响应拦截器中:- 对于某次请求A,如果是401错误 (2)
- 有refresh_token,用refresh_token去请求回新的token (3)
- 新token请求成功 (4)
- 更新本地token (5)
- 再发一次请求A (6)
- 新token请求失败
- 携带请求地址,跳转到登陆页
- 新token请求成功 (4)
- 没有refresh_token,说明没有登陆
- 携带请求地址,跳转到登陆页
- 有refresh_token,用refresh_token去请求回新的token (3)
封装独立的history
src/utils/history
import { createBrowserHistory } from 'history' const history = createBrowserHistory() export default history
App.tsx
import history from '@/utils/history' function App () { return ( <div className="App"> <Router history={history}>
处理401问题,token无感知刷新
request.ts
// 添加响应拦截器 instance.interceptors.response.use( function (response) { // 对响应数据做点什么 return response }, async function (error: AxiosError<{ message: string }>) { // 对响应错误做点什么 // console.dir(error) // 1. 处理网络异常状况 // 如果请求延迟,response为undefined if (!error.response) { Toast.show('网络异常') return Promise.reject(error) } // 2. 返回401(token过期)处理 if (error.response.status !== 401) { Toast.show('操作异常') return Promise.reject(error) } if (error.response.status === 401) { // 2.1 获取refresh_token, 发送请求,获取最新的token const { refresh_token } = getToken() // 从本地中获取refresh_token if (refresh_token) { try { // 发请求重新获取token const res = await axios.put(baseURL + 'authorizations', null, { headers: { Authorization: `Bearer ${refresh_token}` } }) // 2.2 保存到redux store.dispatch( savaToken({ token: res.data.data.token, refresh_token }) ) // 2.3 这个时候已经有token了,重新发请求 return instance(error.config) } catch (error) { // 如果使用refresh_token获取token的请求都失败了,就直接跳转到login history.replace('/login', { from: location.pathname }) // 清空redux中的所有token store.dispatch(savaToken({ token: '', refresh_token: '' })) // 将错误抛出 return Promise.reject(error) } } else { // 2.4 没有refresh_token 跳转到login,并携带当前页面的地址,支持回跳 history.replace('/login', { from: location.pathname }) } // 2.5 清除 token 和 refresh_token 相关的数据 store.dispatch(savaToken({ token: '', refresh_token: '' })) // 2.6 抛出错误 return Promise.reject(error) } return Promise.reject(error) } )
-
vue token过期后自动刷新token
2022-04-11 17:42:08半小时后你再调接口,会报401错误,代表token过期,这个时候前端有两种解决方案,第一种也就是退出登录,让用户重新登录,这种比较简单。但是我们的经理不希望用户再次登录,而是希望这个token能自动更新,我就稍微... -
token过期后刷新token并重新发起请求
2022-05-26 21:05:29系统登录成功后,后端返回token和refreshToken,所有请求都携带token,token如果过期接口将返回401,此时前端需要拿着refreshToken去刷新token,刷新后将拿到新的token和refreshToken,并将token过期的请求重新发起,... -
token过期处理,js处理登录身份过期,vue处理token登录过期,前端本地token过期处理,前后端分离token超时...
2020-07-28 10:57:38* 监听token过期时间 * @return: Boolean */ export const tokenExpressInTime = () => { // let reg = /[\u4e00-\u9fa5]/g // let time = localStorage.getItem('tokens') ? JSON.parse(localStorage.... -
jwt的token过期刷新,过期时间
2022-04-22 16:05:06token是否过期,如果没有过期就刷新token的时间为30min,反之则会重新登录,需要注意的是这种方式我是在登 录以后就将token存在了redis. //登录方法中将token存在redis String token =JwtUtil.sign(userName,user.... -
vue项目token过期
2021-11-01 17:58:08问题:vue项目token过期,后端会在token过期之前两秒钟在控制台的返给前端一个新的authorization(token) token过期分析:1.jwt过期(其中一个过期)===> 后端返回新的token,重新赋值存储setToken 2.jwt过期... -
TOKEN过期
2020-07-16 13:42:37问题:登录系统提示token过期 解决:登录提示token过期。跟踪显示在登录后更新token。在访问某个系统接口提示token过期,原因为:在登录时候将token存入rides,与在访问时候获取的token不统一。在各子项目之间配置... -
vue控制token过期处理
2022-03-04 09:58:04后端控制登录用户的token是否存在过期行为,如在一个页面长期停留不操作后端则返回一个状态码如 401,前端可在二次封装的axios中统一错误处理那里检测401并清除该token,跳回白名单中的登录页。 详情看下图: ... -
Vue 消除Token过期时刷新页面的重复提示
2022-03-03 15:30:15”提示,然后跳转到登录页面,接下来又弹出了n个“Token已过期”的后端返回消息提示。 2、原因分析 当前页面初始化,有多个向后端查询系统参数的调用,代码如下: created () { // ======================... -
请求时token过期自动刷新token
2020-12-18 21:41:051.在开发过程中,我们都会接触到token,token的作用是什么呢?主要的作用就是为了安全,用户登陆时,服务器会随机生成一个有时效性的token,用户的每一次请求都需要携带上token,证明其请求的合法性,服务器会验证... -
token 过期解决
2019-10-24 11:22:18vue如何在token过期之后跳转到登录页面,且不影响其他无需携带token的接口数据访问 事情是这样的,最近做了一个类似于商城的项目。本来测试是没有问题的,后来过了大概三四天的时间没有在浏览器中打开过,再打开... -
前端怎么判断token过期或无token问题
2022-01-07 20:47:25登录用户的token过期了 ( token会有有效期(具体是多长,是由后端决定)) refresh_token的作用 作用: 当token的有效期过了之后,可以使用它去请求一个特殊接口(这个接口也是后端指定的,明确需要传入refresh_... -
【Java】后台判断token过期,后台刷新token,接下来该如何处理
2021-03-16 20:27:31如标题所示我再详细描述一下我遇到的问题,如果我再发送请求的试试,后台验证header中的token,如果这时候,发现token过期,然后一系列判断其为合法token,允许token刷新,后台主动刷新token,并且成功获得新的token,... -
数据埋点之记录token过期登出行为的前端解决方案
2022-03-22 21:17:29问题描述:通过调用接口的方式实现数据埋点(事件跟踪),但需要记录因token过期导致的登出行为时会遇到问题:token已经过期了,无法用这个过期的token去调用数据埋点接口来记录用户行为。 思路: 让后端同学在返回... -
后端设置的token过期后,前端的处理方法
2022-03-02 20:18:46后端设置的token过期后,前端的处理方法 -
Laravel 安全:CSRF Token 过期时间
2021-04-26 11:04:06问题是:CSRF _token 的过期时间是多久?回答会在每一次 Session 创建时重新生成,也就是说,跟会话的时间一致,Laravel 默认为 120 分钟,可以通过修改 config/session.php 文件里的 lifetime 修改。来源...class.....