使用JWT双令牌机制进行接口请求鉴权

在前后端分离的开发过程中,前端发起请求,调用后端接口,后端在接收请求时,首先需要对收到的请求鉴权,在这种情况先我们可以采用JWT机制来鉴权。

JWT有两种机制,单令牌机制和双令牌机制。

单令牌机制服务端只生成一个 token,一般过期时间比较长,因此安全性稍差。

双令牌机制服务端生成两个token,一个access_token用来鉴权,过期时间一般较短(5分钟,15分钟等),另一个 refresh_token,只用来获取新的 access_token,过期时间较长(可设置为24小时或者更长)

单令牌机制一般步骤:

1.前端发起登录请求

2.服务端验证用户名密码,验证通过则下发token返回给前端。

3.前端收到返回的token进行保存。

4.前端后续请求头将携带 token 供服务端验证。

5.服务端收到请求首先验证 token,验证通过则正常提供服务,不通过则返回相应提示信息。

6.token 过期后重新登录。

双令牌机制与单令牌机制不同的是服务端生成两个token,一个access_token用来鉴权,一个 refresh_token 用来刷新 access_token

双令牌机制一般步骤:

1.前端发起登录请求

2.服务端验证用户名密码,验证通过则下发access_token和refresh_token 返回给前端。

3.前端收到返回的两个 token进行保存。

4.前端后续请求头将携带 access_token供服务端验证。

5.服务端收到请求首先验证 token,验证通过则正常提供服务,不通过则返回相应提示信息。

6.access_token过期后,前端请求头 将 access_token替换为  refresh_token,调用刷新 access_token的接口。获取到新的  access_token并保存,重新发起请求。

1.pom.xml添加 java-jwt

<dependency><groupId>com.auth0</groupId><artifactId>java-jwt</artifactId><version>3.10.1</version>
</dependency>

2.编写JwtUtil.java

import java.util.Calendar;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
public class JwtUtil {private final static String SIGNATURE = "sdf46fsdf"; //密钥private final static String ACCESS_TYPE = "access";private final static String REFRESH_TYPE = "refresh";private final static String SCOPE = "cms";private static int accessExpire = 15*60; //15分钟private static int refreshExpire = 60*60*24; //24小时/*** 生成  访问 token* @return 返回token*/public static String getAccessToken( String identity){JWTCreator.Builder builder = JWT.create();builder.withClaim("type", ACCESS_TYPE);builder.withClaim("identity", identity);builder.withClaim("scope", SCOPE);Calendar instance = Calendar.getInstance();instance.add(Calendar.SECOND, accessExpire);builder.withExpiresAt(instance.getTime());return builder.sign(Algorithm.HMAC256(SIGNATURE)).toString();}/*** 生成 刷新 token* @return 返回token*/public static String getRefreshToken(String identity){JWTCreator.Builder builder = JWT.create();builder.withClaim("type", REFRESH_TYPE);builder.withClaim("identity", identity);builder.withClaim("scope", SCOPE);Calendar instance = Calendar.getInstance();instance.add(Calendar.SECOND, refreshExpire);builder.withExpiresAt(instance.getTime());return builder.sign(Algorithm.HMAC256(SIGNATURE)).toString();}/*** 验证token* @param token*/public static void verify(String token){JWT.require(Algorithm.HMAC256(SIGNATURE)).build().verify(token);}/*** 获取token中payload* @param token* @return*/public static DecodedJWT getToken(String token){return JWT.require(Algorithm.HMAC256(SIGNATURE)).build().verify(token);}
}

3.过滤器校验token

import org.hibernate.annotations.common.util.StringHelper;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import com.auth0.jwt.exceptions.AlgorithmMismatchException;
import com.auth0.jwt.exceptions.InvalidClaimException;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.exceptions.SignatureVerificationException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.qyp.hpa.util.JwtUtil;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.*;
@Component
public class FilterConfig implements HandlerInterceptor{public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3)throws Exception {}public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2)throws Exception {}public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object arg2) throws Exception {response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));response.setHeader("Access-Control-Allow-Methods", "*");response.setHeader("Access-Control-Allow-Credentials", "true");response.setHeader("Access-Control-Allow-Headers", "Authorization,Origin, X-Requested-With, Content-Type, Accept,Access-Token,Content-length,Content-Range,Keep-Alive,Accept-Ranges,Connection");//Origin, X-Requested-With, Content-Type, Accept,Access-TokenString requestURL = request.getRequestURL().toString();if(requestURL.indexOf("login")!=-1 || requestURL.indexOf("refresh")!=-1){return true;}Map<String,Object> map = new HashMap<>();//令牌建议是放在请求头中,获取请求头中令牌AuthorizationString token = request.getHeader("Authorization");try{if(StringHelper.isEmpty(token)){map.put("msg","token不能为空");return false;}JwtUtil.verify(token);//验证令牌return true;//放行请求} catch (SignatureVerificationException e) {e.printStackTrace();map.put("msg","无效签名");} catch (TokenExpiredException e) {e.printStackTrace();map.put("code","10051");map.put("msg","token过期");} catch (AlgorithmMismatchException | JWTDecodeException | InvalidClaimException e) {e.printStackTrace();map.put("code","10041");map.put("msg","token算法不一致");} catch (Exception e) {e.printStackTrace();map.put("msg","token失效");}map.put("state",false);//设置状态//将map转化成json,response使用的是JacksonString json = new ObjectMapper().writeValueAsString(map);response.setContentType("application/json;charset=UTF-8");response.getWriter().print(json);return true;}
}

4.登录接口、刷新令牌接口

服务端登录接口

@RequestMapping(value="sys/login", method = RequestMethod.POST)public Map<String,Object> logIn(@RequestBody Map<String,Object> param) {Map<String,Object> map = new HashMap<String,Object>();String username = param.get("username")==null?"":param.get("username").toString();String password = param.get("password")==null?"":param.get("password").toString();try{//为了方便演示,直接写了固定的用户名和密码if("admin".equals(username) && "123456".equals(password)){String userId = "sadfsdfsdfsdfs";//生成JWT令牌String token = JwtUtil.getAccessToken(userId);String refresh_token = JwtUtil.getRefreshToken(userId);map.put(NS.MSG, "登录成功");map.put("access_token", token);map.put("refresh_token", refresh_token);map.put(NS.SUCC, NS.OK);}else {map.put(NS.MSG, NS.NO_USER);map.put(NS.SUCC, NS.ERROR);}} catch(Exception e){map.put(NS.SUCC, NS.ERROR);e.printStackTrace();}return map;}

服务端刷新令牌接口 

/*** 刷新令牌*/@GetMapping("sys/refresh")public Map<String,Object> getRefreshToken(HttpServletRequest request) {Map<String,Object> map = new HashMap<String,Object>();String tokenStr = request.getHeader("Authorization");DecodedJWT token = JwtUtil.getToken(tokenStr);String id = token.getClaim("identity").asString();//生成JWT令牌String access_token = JwtUtil.getAccessToken(id);map.put("access_token", access_token);return map;}

5. Vue前端调用

前端这里使用的是 vue3,以下是调用服务端登录接口,登录成功保存  access_token和 refresh_token

import { saveTokens
} from '@/util/token'
import { login} from '@/util/user'const _login= async () =>{const param = {username: 'admin',password: '123456',}const res:any = await login(param);console.log('login res', res);if(res.succ == '1'){saveTokens(res.access_token,res.refresh_token)}}

axios.js 封装处理  access_token过期,无感刷新 token

axios.js 封装处理当前端调用刷新 token的接口时,将请求头 headers.Authorization 字段替换为 refresh_token

T

util / token.js 工具类 

/*** 存储tokens* @param {string} accessToken* @param {string} refreshToken*/
export function saveTokens(accessToken, refreshToken) {localStorage.setItem('access_token', accessToken)localStorage.setItem('refresh_token', refreshToken)
}
/*** 存储access_token* @param {string} accessToken*/
export function saveAccessToken(accessToken) {localStorage.setItem('access_token', accessToken)
}
/*** 获得某个token* @param {string} tokenKey*/
export function getToken(tokenKey) {return localStorage.getItem(tokenKey)
}
/*** 移除token*/
export function removeToken() {localStorage.removeItem('access_token')localStorage.removeItem('refresh_token')
}

util / user.js 配置请求服务端登录接口

import {post,get} from '@/util/axios'
export async function login(data) {return await post('sys/login', data)
}

axios.js 完整代码

/*** 封装 axios*/
import axios from 'axios'
import {Message} from 'view-ui-plus'
import { getToken, saveAccessToken } from '@/util/token'
const baseUrl = "http://localhost:8089/"
const ErrorCode = {777: '前端错误码未定义',999: '服务器未知错误',10000: '未携带令牌',10020: '资源不存在',10030: '参数错误',10041: 'assessToken损坏',10042: 'refreshToken损坏',10051: 'assessToken过期',10052: 'refreshToken过期',10060: '字段重复',10070: '不可操作',
}
const config = {baseURL: baseUrl || '',timeout: 5 * 1000, // 请求超时时间设置crossDomain: true,// withCredentials: true, // Check cross-site Access-Control// 定义可获得的http响应状态码// return true、设置为null或者undefined,promise将resolved,否则将rejectedvalidateStatus(status) {return status >= 200 && status < 510},
}
/*** 错误码是否是refresh相关* @param { number } code 错误码*/
function refreshTokenException(code) {const codes = [10000, 10042, 10050, 10052, 10012]return codes.includes(code)
}
// 创建请求实例
const _axios = axios.create(config)
_axios.interceptors.request.use(originConfig => {// 有 API 请求重新计时///autoJump(router)const reqConfig = { ...originConfig }// step1: 容错处理if (!reqConfig.url) {console.error('request need url')}reqConfig.method = reqConfig.method.toLowerCase() // 大小写容错// 参数容错if (reqConfig.method === 'get') {if (!reqConfig.params) {reqConfig.params = reqConfig.data || {}}} else if (reqConfig.method === 'post') {if (!reqConfig.data) {reqConfig.data = reqConfig.params || {}}// 检测是否包含文件类型, 若包含则进行 formData 封装let hasFile = falseObject.keys(reqConfig.data).forEach(key => {if (typeof reqConfig.data[key] === 'object') {const item = reqConfig.data[key]if (item instanceof FileList || item instanceof File || item instanceof Blob) {hasFile = true}}})// 检测到存在文件使用 FormData 提交数据if (hasFile) {const formData = new FormData()Object.keys(reqConfig.data).forEach(key => {formData.append(key, reqConfig.data[key])})reqConfig.data = formData}}// step2: permission 处理if (reqConfig.url === 'sys/refresh') {const refreshToken = getToken('refresh_token')if (refreshToken) {reqConfig.headers.Authorization = refreshToken}} else {const accessToken = getToken('access_token')if (accessToken) {reqConfig.headers.Authorization = accessToken}}return reqConfig},error => Promise.reject(error),
)
// Add a response interceptor
_axios.interceptors.response.use(async res => {if (res.status.toString().charAt(0) === '2') {return res.data}const { code, msg } = res.datareturn new Promise(async (resolve, reject) => {let tipMessage = ''const { url } = res.config// refresh_token 异常,直接登出if (refreshTokenException(code)) {/** setTimeout(() => {store.dispatch('loginOut')const { origin } = window.locationwindow.location.href = origin}, 1500)return resolve(null)*/}// assessToken相关,刷新令牌console.log('msg',msg,'code',code)if (code === "10041" || code === "10051") {console.log('--msg',msg,'code',code)const cache = {}if (cache.url !== url) {cache.url = urlconst refreshResult = await _axios('sys/refresh')saveAccessToken(refreshResult.access_token)// 将上次失败请求重发const result = await _axios(res.config)return resolve(result)}}// 弹出信息提示的第一种情况:直接提示后端返回的异常信息(框架默认为此配置);// 特殊情况:如果本次请求添加了 handleError: true,用户自行通过 try catch 处理,框架不做额外处理if (res.config.handleError) {return reject(res)}if (typeof msg === 'string') {tipMessage = msg}if (Object.prototype.toString.call(msg) === '[object Object]') {;[tipMessage] = Object.values(msg).flat()}if (Object.prototype.toString.call(msg) === '[object Array]') {;[tipMessage] = msg}//ElMessage.error(tipMessage)Message.error(tipMessage)reject(res)})},error => {if (!error.response) {//ElMessage.error('请检查 API 是否异常')console.log('error', error)}// 判断请求超时if (error.code === 'ECONNABORTED' && error.message.indexOf('timeout') !== -1) {//ElMessage.warning('请求超时')}return Promise.reject(error)},
)
// 导出常用函数
/*** @param {string} url* @param {object} data* @param {object} params*/
export function post(url, data = {}, params = {}) {return _axios({method: 'post',url,data,params,})
}
/*** @param {string} url* @param {object} params*/
export function get(url, params = {}) {return _axios({method: 'get',url,params,})
}
/*** @param {string} url* @param {object} data* @param {object} params*/
export function put(url, data = {}, params = {}) {return _axios({method: 'put',url,params,data,})
}
/*** @param {string} url* @param {object} params*/
export function _delete(url, params = {}) {return _axios({method: 'delete',url,params,})
}
export default _axios

6.效果展示

调用登录接口

请求

响应

调用 list 接口,请求时将 token 放在  headers的  Authorization字段即可

在 access_token过期而  refresh_token未过期时,调用任意接口会刷新  access_token 

由上图可以看到,第一次调用 list 接口时,报 token 已过期,此时会自动调用 刷新 access_token的 refresh接口,成功刷新 access_token后,再次自动调用 list  接口,成功返回,达到了无感刷新token的效果。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/380300.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

IDEA的详细设置

《IDEA破解、配置、使用技巧与实战教程》系列文章目录 第一章 IDEA破解与HelloWorld的实战编写 第二章 IDEA的详细设置 第三章 IDEA的工程与模块管理 第四章 IDEA的常见代码模板的使用 第五章 IDEA中常用的快捷键 第六章 IDEA的断点调试&#xff08;Debug&#xff09; 第七章 …

《绝区零》是一款什么类型的游戏,Mac电脑怎么玩《绝区零》苹果电脑玩游戏怎么样

米哈游的《绝区零》最近在网上爆火呀&#xff0c;不过很多人都想知道mac电脑能不能玩《绝区零》&#xff0c;今天麦麦就给大家介绍一下《绝区零》是一款什么样的游戏&#xff0c;Mac电脑怎么玩《绝区零》。 一、《绝区零》是一款什么样的游戏 《绝区零》是由上海米哈游自主研发…

常见排序算法总结

文章目录 比较排序冒泡排序选择排序插入排序归并排序快速排序堆排序希尔排序 非比较排序&#xff08;桶排序&#xff09;计数排序基数排序 比较排序 冒泡排序 嵌套循环&#xff0c;每次内层循环执行时&#xff0c;数组的每两个元素交换&#xff0c;将一个最大/小的数排到数组…

技术成神之路:设计模式(八)责任链模式

介绍 责任链模式&#xff08;Chain of Responsibility Pattern&#xff09;是一种行为设计模式&#xff0c;它允许多个对象依次处理请求&#xff0c;避免请求的发送者和接收者之间的显式耦合。该模式通过将多个可能处理请求的对象连接成一条链&#xff0c;并沿着这条链传递请求…

达梦数据库 DISQL连接数据库与执行SQL、脚本的方法

DISQL连接数据库与执行SQL、脚本的方法 1.DISQL介绍2.DISQL连接数据库的方法2.1 本地连接2.2 远程连接2.3 CONN连接 3.执行SQL、脚本的方法3.1 通过DISQL登录后在字符界面3.2 启动DISQL时运行脚本3.3 进入DISQL后&#xff0c;通过start命令运行脚本3.4 使用EDIT命令编辑脚本 1.…

ubuntu 24 PXE Server (bios+uefi) 批量部署系统

pxe server 前言 PXE&#xff08;Preboot eXecution Environment&#xff0c;预启动执行环境&#xff09;是一种网络启动协议&#xff0c;允许计算机通过网络启动而不是使用本地硬盘。PXE服务器是实现这一功能的服务器&#xff0c;它提供了启动镜像和引导加载程序&#xff0c;…

解决npm install(‘proxy‘ config is set properly. See: ‘npm help config‘)失败问题

摘要 重装电脑系统后&#xff0c;使用npm install初始化项目依赖失败了&#xff0c;错误提示&#xff1a;‘proxy’ config is set properly…&#xff0c;具体的错误提示如下图所示&#xff1a; 解决方案 经过报错信息查询解决办法&#xff0c;最终找到了两个比较好的方案&a…

git commit报错: pre-commit hook failed (add --no-verify to bypass)

原因&#xff1a; 在提交前做代码风格检查&#xff0c;若检查不通过&#xff0c;则提交失败 解决方案&#xff1a;进入项目的.git>hooks目录&#xff0c;找到pre-commit文件&#xff0c;删除即可

【银河麒麟服务器操作系统】java进程oom现象分析及处理建议

了解银河麒麟操作系统更多全新产品&#xff0c;请点击访问麒麟软件产品专区&#xff1a;https://product.kylinos.cn 现象描述 某服务器系统升级内核至4.19.90-25.22.v2101版本后仍会触发oom导致java进程被kill。 现象分析 oom现象分析 系统messages日志分析&#xff0c;故…

python--实验15 数据分析与可视化

目录 知识点 1 数据分析概述 1.1流程 1.2定义 1.3数据分析常用工具 2 科学计算 2.1numpy 2.1.1定义 2.1.2创建数组的方式 2.1.3np.random的随机数函数 3 数据可视化 3.1定义 3.2基本思想 3.3Matplotlib库 3.3.1模块 4 数据分析 4.1Pandas 4.2数据结构 4.3基…

html+canvas 实现签名功能-手机触摸

手机上的效果图 需要注意&#xff0c;手机触摸和鼠标不是一个事件&#xff0c;不能通用&#xff0c;上一篇是关于使用鼠标的样例 相关代码 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewpo…

Large Language Model系列之一:语言模型与表征学习(Language Models and Representation Learning)

语言模型与表征学习&#xff08;Language Models and Representation Learning&#xff09; 1 语言模型 N-Gram模型 from collections import defaultdictsentences [The swift fox jumps over the lazy dog.,The swift river flows under the ancient bridge.,The swift br…

Java关于JDBC的理解

JDBC Java Database Connectivity&#xff1a;意为Java数据库连接。是Java提供的一组独立于任何数据库管理系统的API。Java提供接口规范&#xff0c;由各个数据库厂商提供接口的实现&#xff0c;厂商提供的实现类封装成jar文件&#xff0c;也就是我们俗称的数据库驱动jar包。我…

PyTorch张量数值计算

文章目录 1、张量基本运算2、阿达玛积3、点积运算4、指定运算设备⭐5、解决在GPU运行PyTorch的问题 &#x1f343;作者介绍&#xff1a;双非本科大三网络工程专业在读&#xff0c;阿里云专家博主&#xff0c;专注于Java领域学习&#xff0c;擅长web应用开发、数据结构和算法&am…

LDR6020:重塑iPad一体式有线键盘体验的创新力量

在移动办公与娱乐日益融合的时代&#xff0c;iPad凭借其强大的性能和便携性&#xff0c;成为了众多用户不可或缺的生产力工具。然而&#xff0c;为了进一步提升iPad的使用体验&#xff0c;一款高效、便捷的键盘成为了不可或缺的配件。今天&#xff0c;我们要介绍的&#xff0c;…

云计算复习--虚拟化技术

文章目录 虚拟化技术定义与原理虚拟机监视器&#xff08;VMM&#xff09;虚拟化技术服务器虚拟化存储虚拟化网络虚拟化应用虚拟化 关键技术新型虚拟化技术发展进展作业 虚拟化技术定义与原理 定义&#xff1a;虚拟化技术是一种将计算机物理实体&#xff08;如服务器、存储设备…

【BUG】已解决:OSError: [Errno 22] Invalid argument

已解决&#xff1a;OSError: [Errno 22] Invalid argument 目录 已解决&#xff1a;OSError: [Errno 22] Invalid argument 【常见模块错误】 错误原因&#xff1a; 解决方法如下&#xff1a; 欢迎来到英杰社区https://bbs.csdn.net/topics/617804998 欢迎来到我的主页&…

利用AI辅助制作ppt封面

如何利用AI辅助制作一个炫酷的PPT封面 标题使用镂空字背景替换为动态视频 标题使用镂空字 1.首先&#xff0c;新建一个空白的ppt页面&#xff0c;插入一张你认为符合主题的图片&#xff0c;占满整个可视页面。 2.其次&#xff0c;插入一个矩形&#xff0c;右键选择设置形状格式…

贝锐蒲公英远程运维方案:即装即用、无需专线,断网也可远程维护

目前&#xff0c;公路、隧道、桥梁、航道&#xff0c;甚至是施工现场和工业生产环境等&#xff0c;都采用了实时监测方案。 通过部署各类传感器和摄像头等设备&#xff0c;现场视频画面和控制单元&#xff08;如PLC、工控机等&#xff09;数据可以实时回传&#xff0c;用于集中…

智能优化算法之灰狼优化算法(GWO)

智能优化算法是一类基于自然界中生物、物理或社会现象的优化技术。这些算法通过模拟自然界中的一些智能行为&#xff0c;如遗传学、蚁群觅食、粒子群体运动等&#xff0c;来解决复杂的优化问题。智能优化算法广泛应用于各种工程和科学领域&#xff0c;因其具有全局搜索能力、鲁…