当前位置:网站首页>token过期自动续费方案和实现
token过期自动续费方案和实现
2022-07-02 05:51:00 【想吃凤梨酥】
解决方案
方案1: 每一次请求都进行重新生成一个新的token【频率过高,性能不好】
方案2: 每次登录的时候生成两个token给前端进行返回,一个是用于鉴别用户身份的token,另外一个token则是用于刷新token用的
方案3: 登录过后给前端进行返回token并设置了过期时间30分钟,每次请求的时候前端把token存在请求头里面进行发请求,后端接收请求的时候获取请求头出来进行jwt解析判断过期时间是否小于10分钟,如果小于10分钟就生成新的token在responseHearde进行返回即可
方案二实现
实现过程:
- 登录成功以后,后端返回 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。
后端
LoginController控制器里
package com.liu.token.controller;
import com.liu.token.domain.Login;
import com.liu.token.utlis.JwtUtil;
import com.liu.token.utlis.ResponseResult;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
@RestController
@RequestMapping("/user")
public class LoginController {
@PostMapping("/login")
public ResponseResult login(@RequestBody Login login){
// 验证凭证,1分钟
String access_token = JwtUtil.createJWT(login.getUsername(), 60*1000l);
// 验证凭证过期使用这个来刷新凭证,30天
String refresh_token = JwtUtil.createJWT(login.getUsername(), 30*24*60*60*1000l);
HashMap<String,String> map=new HashMap<>();
map.put("access_token",access_token);
map.put("refresh_token",refresh_token);
return new ResponseResult(200,"登录成功!",map);
}
// 验证路由,不重要,只是提供访问一下
@PostMapping("/logout")
public ResponseResult logout(){
HashMap<String,Object> map=new HashMap<>();
map.put("access_token",123456);
map.put("refresh_token",123456);
return new ResponseResult(200,"llalaallaal",map);
}
}
LoginFilter过滤器里
package com.liu.token.filter;
import com.alibaba.fastjson.JSON;
import com.liu.token.common.CustomException;
import com.liu.token.utlis.JwtUtil;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.AntPathMatcher;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;
@Slf4j
@WebFilter
public class LoginFilter implements Filter {
// 路径匹配器
public static final AntPathMatcher PATH_MATCHER = new AntPathMatcher();
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
//1、获取本次请求的URI
String requestURI = request.getRequestURI();
log.info("拦截到请求:{}", requestURI);
// 定义不需要处理的请求路径
String[] urls = new String[]{
"/user/login"
};
//2、判断本次请求是否需要处理
boolean check = check(urls, requestURI);
//3、如果不需要处理,则直接放行
if (check) {
log.info("本次请求{}不需要处理", requestURI);
filterChain.doFilter(request, response);
return;
}
String access_token = request.getHeader("access_token");
String refresh_token = request.getHeader("refresh_token");
if (access_token==null||refresh_token==null){
throw new CustomException("没有登录,请登录!");
}
HashMap<String,Object> map = new HashMap();
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json");
try {
// 过期了会报错,就会执行catch代码块
Claims claims = JwtUtil.parseJWT(access_token);
} catch (Exception e) {
try {
Claims claims = JwtUtil.parseJWT(refresh_token);
System.out.println(claims);
String id = claims.get("sub").toString();
String jwt = JwtUtil.createJWT(id, 60 * 1000l);
map.put("code",401);
map.put("msg","刷新token");
map.put("data",jwt);
response.getWriter().write(JSON.toJSONString(map));
response.getWriter().close();
} catch (Exception ex) {
map.put("code",402);
map.put("msg","token已经过期,请重新登录!");
map.put("data",null);
response.getWriter().write(JSON.toJSONString(map));
response.getWriter().close();
}
}
filterChain.doFilter(request, response);
}
/** * 路径匹配,检查本次请求是否需要放行 * * @param urls * @param requestURL * @return */
public boolean check(String[] urls, String requestURL) {
for (String url : urls) {
boolean match = PATH_MATCHER.match(url, requestURL);
if (match) {
return true;
}
}
return false;
}
}
前端
login.vue登录测试页面
<template>
<div class="home">
<input type="text" v-model="username">
<input type="text" v-model="password">
<button @click="login">登录</button>
</div>
<div>
<button @click="logout">测试</button>
</div>
</template>
<script> import request from "@/config/request" export default {
data() {
return {
username:"123456", password:"123456", } }, methods: {
login() {
request.post("/user/login", {
username: this.username, password:this.password }).then(res => {
let {
code, msg, data } = res; console.log(data); if (code == 200) {
// 保存token window.localStorage.setItem("access_token",data.access_token) window.localStorage.setItem("refresh_token",data.refresh_token) } }) }, logout() {
request.post("/user/logout", {
username: this.username, password:this.password }).then(res => {
let {
code, msg, data } = res; console.log(res); }) } } } </script>
request.js
//导入axios 和 qs 插件
import axios from "axios"
import qs from "qs"
//配置全局公共域名
// const baseURL = process.env.NODE_ENV === "production" ? "https://www.xxxxx.com" : "";
//创建axios实例
let request = axios.create({
baseURL: "/api", //赋值公共域名
timeout: 5000 //设置延迟时间(单位:毫秒)
})
//拦截request的发送请求和响应请求,并做一定的配置
request.interceptors.request.use(
//拦截发送请求,并给请求头信息headers加上token令牌
config => {
config.headers["access_token"] = localStorage.getItem("access_token");
config.headers["refresh_token"] = localStorage.getItem("refresh_token");
return config
},
err => {
Promise.reject(err)
}
)
request.interceptors.response.use(
//拦截响应请求 , 这里直接返回数据
response => {
console.log(response)
// console.log(response.headers)
if (response.data.code == 200) {
return response.data;
} else if (response.data.code == 401) {
console.log("--------------------------------")
window.localStorage.setItem("access_token", response.data.data);
return request.request(response.config);
} else if (response.data.code == 402) {
alert("请重新登录!跳转路由逻辑")
}
},
err => {
Promise.reject(err)
}
)
//导出request
export default request;
方案三实现
后端
LoginController控制器里
package com.liu.token.controller;
import com.liu.token.domain.Login;
import com.liu.token.utlis.JwtUtil;
import com.liu.token.utlis.ResponseResult;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
@RestController
@RequestMapping("/user")
public class UserController {
@PostMapping("/login")
public ResponseResult login(@RequestBody Login login){
// 验证凭证,11分钟
String access_token = JwtUtil.createJWT(login.getUsername(), 30*60*1000l);
HashMap<String,String> map=new HashMap<>();
map.put("access_token",access_token);
return new ResponseResult(200,"登录成功!",map);
}
// 验证路由,不重要,只是提供访问一下
@PostMapping("/logout")
public ResponseResult logout(){
HashMap<String,Object> map=new HashMap<>();
map.put("access_token",123456);
map.put("refresh_token",123456);
return new ResponseResult(200,"llalaallaal",map);
}
}
LoginFilter过滤器里
package com.liu.token.filter;
import com.alibaba.fastjson.JSON;
import com.liu.token.common.CustomException;
import com.liu.token.utlis.JwtUtil;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.AntPathMatcher;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;
@Slf4j
@WebFilter
public class LoginFilter implements Filter {
// 路径匹配器
public static final AntPathMatcher PATH_MATCHER = new AntPathMatcher();
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
//1、获取本次请求的URI
String requestURI = request.getRequestURI();
log.info("拦截到请求:{}", requestURI);
// 定义不需要处理的请求路径
String[] urls = new String[]{
"/user/login"
};
//2、判断本次请求是否需要处理
boolean check = check(urls, requestURI);
//3、如果不需要处理,则直接放行
if (check) {
log.info("本次请求{}不需要处理", requestURI);
filterChain.doFilter(request, response);
return;
}
String access_token = request.getHeader("access_token");
if (access_token==null){
throw new CustomException("没有登录,请登录!");
}
try {
Claims claims = JwtUtil.parseJWT(access_token);
int exp = (int)claims.get("exp");
String id = claims.get("sub").toString();
long minute = (exp*1000l - System.currentTimeMillis())/1000/60;
// 还有10分钟过期时就更新token
if (minute<10){
String token = JwtUtil.createJWT(id, 30 * 60 * 1000l);
response.addHeader("token",token);
}
} catch (Exception e) {
HashMap<String,Object> map = new HashMap();
map.put("code",402);
map.put("msg","token已过期,重新登录!");
map.put("data",null);
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json");
response.getWriter().write(JSON.toJSONString(map));
response.getWriter().close();
}
filterChain.doFilter(request, response);
}
/** * 路径匹配,检查本次请求是否需要放行 * * @param urls * @param requestURL * @return */
public boolean check(String[] urls, String requestURL) {
for (String url : urls) {
boolean match = PATH_MATCHER.match(url, requestURL);
if (match) {
return true;
}
}
return false;
}
}
前端
login.vue登录测试页面
<template>
<div class="home">
<input type="text" v-model="username">
<input type="text" v-model="password">
<button @click="login">登录</button>
</div>
<div>
<button @click="logout">测试</button>
</div>
</template>
<script> import request from "@/config/request" export default {
data() {
return {
username:"123456", password:"123456", } }, methods: {
login() {
request.post("/user/login", {
username: this.username, password:this.password }).then(res => {
let {
code, msg, data } = res; console.log(data); if (code == 200) {
window.localStorage.setItem("access_token",data.access_token) } }) }, logout() {
request.post("/user/logout", {
username: this.username, password:this.password }).then(res => {
let {
code, msg, data } = res; console.log(res); }) } } } </script>
request.js
//导入axios 和 qs 插件
import axios from "axios"
import qs from "qs"
//配置全局公共域名
// const baseURL = process.env.NODE_ENV === "production" ? "https://www.xxxxx.com" : "";
//创建axios实例
let request = axios.create({
baseURL: "/api", //赋值公共域名
timeout: 5000 //设置延迟时间(单位:毫秒)
})
//拦截request的发送请求和响应请求,并做一定的配置
request.interceptors.request.use(
//拦截发送请求,并给请求头信息headers加上token令牌
config => {
config.headers["access_token"] = localStorage.getItem("access_token");
return config
},
err => {
Promise.reject(err)
}
)
request.interceptors.response.use(
//拦截响应请求 , 这里直接返回数据
response => {
console.log(response)
if (response.data.code == 200) {
// 如果响应头部有token就存起来
let token = response.headers["token"];
if (token) {
window.localStorage.setItem("access_token", token);
}
return response.data;
} else if (response.data.code == 402) {
alert("请重新登录!跳转路由逻辑")
}
},
err => {
Promise.reject(err)
}
)
//导出request
export default request;
边栏推荐
- H5 jump applet
- 1036 Boys vs Girls
- PHP 开发与测试 Webservice(SOAP)-Win
- Oled12864 LCD screen
- Minimum value ruler method for the length of continuous subsequences whose sum is not less than s
- 如何写出好代码 — 防御式编程指南
- [PHP是否安装了 SOAP 扩]对于php实现soap代理的一个常见问题:Class ‘SoapClient‘ not found in PHP的处理方法
- TypeScript的泛型和泛型约束
- Zzuli:1065 count the number of numeric characters
- centos8安装mysql8.0.22教程
猜你喜欢

With an amount of $50billion, amd completed the acquisition of Xilinx

Thunder on the ground! Another domestic 5g chip comes out: surpass Huawei and lead the world in performance?

2022-2-14 learning xiangniuke project - Section 7 account setting

Minimum value ruler method for the length of continuous subsequences whose sum is not less than s

File contains vulnerability (I)

如何写出好代码 — 防御式编程指南

Conglin environmental protection rushes to the scientific and Technological Innovation Board: it plans to raise 2billion yuan, with an annual profit of more than 200million yuan

DRM display framework as I understand it

3D 打印机 G 代码命令:完整列表和教程

Cambrian was reduced by Paleozoic venture capital and Zhike shengxun: a total of more than 700million cash
随机推荐
Cambrian was reduced by Paleozoic venture capital and Zhike shengxun: a total of more than 700million cash
2022-2-14 learning xiangniuke project - Section 7 account setting
1037 Magic Coupon
Financial portal related information
Résumé de la collection de plug - ins couramment utilisée dans les outils de développement idea
"Original, excellent and vulgar" in operation and maintenance work
I want to understand the swift code before I learn it. I understand it
Online music player app
“簡單”的無限魔方
JVM class loading mechanism
centos8安装mysql8.0.22教程
all3dp.com网站中全部Arduino项目(2022.7.1)
mysql的约束总结
Go language web development is very simple: use templates to separate views from logic
Several keywords in C language
运动健身的一些心得经验
Lingyunguang rushes to the scientific innovation board: the annual accounts receivable reaches 800million. Dachen and Xiaomi are shareholders
Brew install * failed, solution
[PHP是否安装了 SOAP 扩]对于php实现soap代理的一个常见问题:Class ‘SoapClient‘ not found in PHP的处理方法
Pytorch Chinese document