JWT与Token

一、Token介绍

        代替Session,就是一个加密的字符串。

        Token是当用户第一次访问服务端,由服务端生成的一串加密字符串,以作后续客户端进行请求的一个通行令牌。当第一次登录后,服务器生成一个Token字符串,并将此字符串返回给客户端,以后客户端请求需要带上这个Token发送服务器,进行请求数据即可,无需再次带上用户名和密码。
    类似于城主发放的路引,进城需要出示路引做身份证明。

使用场景:

  • 接口使用限制(聚合数据,天行数据,阿里云接口等);
  • 登录场景(客户端登录后,服务器签发token返回客户端);
  • 有时效的url链接控制(密保邮箱找回密码;邮箱激活账号); 

二、jjwt——生成Token的组件

        生成解析token字符串的常用组件有auth0,jjwt,这里选用jjwt进行学习

        JJWT旨在成为最易于使用和理解的库,用于在jvm和Android上创建和验证JSON Web Token(JWT)。对JWT进行加密签名后,称为JWS。

        官网:https://github.com/jwtk/jjwt

        JWT表示形式是一个字符串,该字符串包含三个部分,每个部分之间都用.进行分隔,每个部分都是Base64URL编码的。如下:

eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJKb2UifQ.ipevRNuRP6HflG8cFKnmUPtypruRC4fb1DWtoLL62SY

第一部分:header标头,需要指定用于签署JWT的算法

{

"alg": "HS256"

}

第二部分:body身体,jwt中需要包含的Claims认证数据,claims分为标准cliams与自定义。

{

"sub": "Joe"

}

第三部分:密文,它是通过将标头和正文的组合通过标头中指定的算法加密来计算的。起到鉴伪作用。

三、使用示例

1.引入依赖

<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-api</artifactId><version>0.11.2</version>
</dependency>
<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-impl</artifactId><version>0.11.2</version><scope>runtime</scope>
</dependency>
<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-jackson</artifactId> <!-- or jjwt-gson if Gson is preferred --><version>0.11.2</version><scope>runtime</scope>
</dependency>

2.Token工具类

public class TokenUtil {// 服务端密钥,混淆的字符串,一旦确定,就不能改变,如果密钥改变,所有的token会集体失效private static final String keystr = "1PFRmsM8buvtsMVVIRz6tARYhioOWXL6AJPrlgsVoeQ=";// 服务端ID 加密使用private static final String UID = "root";// 加密的字符串,生效多长时间private static final Integer expTime = 100 * 60 * 60 * 24 * 15;// token头public static final String TOKEN_HEADER = "Authorization";// 生成秘钥private static void generateKey() {// 指定算法SecretKey key = Keys.secretKeyFor(SignatureAlgorithm.HS256);// 生产加密字符串String encode = Encoders.BASE64.encode(key.getEncoded());System.out.println(encode);}// 加密,生成Tokenpublic static String generateToken(String uid) {Map<String, Object> map = new HashMap<>();map.put(UID, uid);// 因为加密的字符串有有效期,获取当前的时间戳Date time = new Date();// 过期时间Date lastTime = new Date(time.getTime() + expTime);// 生成keyKey secretKey = Keys.hmacShaKeyFor(Decoders.BASE64.decode(keystr));String token = Jwts.builder().setClaims(map)// 自定义签名.setIssuedAt(time)// 标准签名.setExpiration(lastTime)// 过期时间.signWith(secretKey)// 加密的密钥.compact();return token;}//解密,还原Tokenpublic static String parse(String token) {Key secretKey = Keys.hmacShaKeyFor(Decoders.BASE64.decode(keystr));try {Jws<Claims> jws = Jwts.parserBuilder().setSigningKey(secretKey).build().parseClaimsJws(token);Claims body = jws.getBody();return body.get(UID, String.class);} catch (Exception e) {throw new JavasmException(JavasmExceptionEnum.SessionExpired);}}public static void main(String[] args) {// 测试生成密钥
//        generateKey();// 测试生成token
//        String token = TokenUtil.generateToken("1001");
//        System.out.println(token);// eyJhbGciOiJIUzI1NiJ9.eyJyb290IjoiMTAwMSIsImlhdCI6MTczNjMyNTI1MCwiZXhwIjoxNzM2NDU0ODUwfQ.mMpS6SBntWJDDyOOucJyZ_8IlKPea4rCPCGGyCsb6Hg// 测试还原tokenString uid = TokenUtil.parse("eyJhbGciOiJIUzI1NiJ9" +".eyJyb290IjoiMTAwMSIsImlhdCI6MTczNjMyNTI1MCwiZXhwIjoxNzM2NDU0ODUwfQ" +".mMpS6SBntWJDDyOOucJyZ_8IlKPea4rCPCGGyCsb6Hg");System.out.println(uid); // 1001}
}

3.后端接口中引入Token影响登录

登录的逻辑:

  • 如果用户传入了用户名密码,按照用户名密码登录
  • 登录成功之后,生成token,返回token给用户
  • 如果用户没有传入用户名密码,检查是否有token
  • 如果token有数据,返回用户信息,
  • 如果token 没有数据,返回错误信息,
  • 如果token、用户名、密码都空,返回错误信息

代码:

@PostMapping("/doUnamePwdTokenLogin")
public ResponseEntity<R> doUnamePwdTokenLogin(String username, String password) {WebUser webUser = null;if (!StringUtils.isEmpty(username) && !StringUtils.isEmpty(password)) {// 说明传入了用户名密码webUser = loginService.doUnameLogin(username, password);} else {// 没有传入用户名密码,检查是否有token// 从header中获取前端传入的tokenString clientToken = request.getHeader(TokenUtil.TOKEN_HEADER);// 如果token为空,说明没有传入用户名或密码,返回错误信息if (StringUtils.isEmpty(clientToken)) {throw new JavasmException(JavasmExceptionEnum.ParameterNull);} else {// 走到这里说明至少是传入了token的,需要校验tokenString uid = TokenUtil.parse(clientToken);webUser = loginService.queryByUid(uid);       }}if (webUser != null && webUser.getUid() != null) {// 生成token信息String uid = webUser.getUid().toString();String token = TokenUtil.generateToken(uid);// 将token返回给用户// token不会跟随body返回,所以要放到header中HttpHeaders httpHeaders = new HttpHeaders();// 把token存入header中httpHeaders.add(TokenUtil.TOKEN_HEADER, token);// 默认情况下,浏览器的js中,只会处理系统自带的header属性,自定义的属性是无法通过js获取的// 需要设置一下,特殊处理的属性是哪个httpHeaders.add("Access-Control-Expose-Headers", TokenUtil.TOKEN_HEADER);return new ResponseEntity<>(R.ok(webUser), httpHeaders, HttpStatus.OK);}return ResponseEntity.ok(R.ok());
}

 

4.前端接口中引入Token实现首页自动登录

src\stores\TokenStore.js:

import { ref, computed } from "vue";
import { defineStore } from "pinia";export default defineStore("tokenStore",() => {const token = ref("");return { token };},{persist: {storage: localStorage, // 默认是localStoragepaths: ["token"], // 将token的属性存入localStorage},}
);

src\plugins\axios.js: 

//请求
axios.interceptors.request.use((config) => {// 这里表示每次请求的时候,都会携带tokenlet token = tokenStore().token;console.log("token请求:" +token);if (token != null && token != undefined && token !== "") {config.headers.Authorization = token;}return config;},(error) => {return Promise.reject(error);}
);
//响应
axios.interceptors.response.use(response => {let token = response.headers.authorization; // 注意这里要小写    console.log("token响应:" + token);if (token != null && token != undefined && token !== "") {tokenStore().token = token;}if (response.status === 200) {//服务器 给前端的内容return Promise.resolve(response);} else {return Promise.reject(response);}},(error) => {alert(`异常请求:${JSON.stringify(error.message)}`);}
);

src\views\Home.vue: 

let user;
let isLogin = ref(false)
let checkLogin = () => {// 如果未登录,跳转到登录页面user = loginUser().userModelconsole.log(user.uid);if (user.uid !== -1) {isLogin.value = true} else {// 自动登录axios.post('/login/doUnamePwdTokenLogin').then(result => {if (result.code === 200) {// 登录成功loginUser().userModel = result.data;isLogin.value = trueuser = loginUser().userModel} else {// 登录失败,跳转到登录页面router.push('/login')}})}
}
onMounted(() => {checkLogin();
});

四、拦截器

给某一些接口添加加密功能:

只有登录的用户才能访问指定接口,接口需要加密,即加上指定的注解。

com.javaplay.playPal.common.annoation.Auth: 

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Auth {
}

com.javaplay.playPal.common.interceptor.LoginInterceptor: 

@Component
public class LoginInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request,HttpServletResponse response,Object handler) throws Exception {// 获取被访问的方法对象HandlerMethod handlerMethod = (HandlerMethod) handler;Method method = handlerMethod.getMethod();// 获取被访问的方法上的注解对象Auth annotation = method.getAnnotation(Auth.class);if (annotation == null) {// 没有注解,说明不加密,放行return true;}// 有注解,说明需要加密,判断是否登录String token = request.getHeader(TokenUtil.TOKEN_HEADER);if (StringUtils.isEmpty(token)){// 没有token,说明没有登录,返回错误信息throw new JavasmException(JavasmExceptionEnum.PermissionDenied);}// 解析tokenString uid = TokenUtil.parse(token);return true;}
}

com.javaplay.playPal.common.interceptor.PlayWebConfig:

@Component
public class PlayWebConfig implements WebMvcConfigurer {@Resourceprivate LoginInterceptor loginInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(loginInterceptor).addPathPatterns("/**").excludePathPatterns("/login/**");}
}

加了注解的请求方法必须要登录以后才能看见: 

/*** 分页查询所有数据*/
@GetMapping("/page")
@Auth
public R selectAll(@RequestParam(value = "pageNum", defaultValue = "1") Integer pageNum,@RequestParam(value = "pageSize", defaultValue = "3") Integer pageSize) {PageInfo<Dynamics> pageInfo = dynamicsService.getPageInfo(pageNum, pageSize);return R.ok(pageInfo);
}

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

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

相关文章

Kubernetes集群架构

Kubernetes集群架构 Kubernetes 集群架构控制平面组件kube-apiserveretcdkube-schedulerkube-controller-managercloud-controller-manager 节点组件kubeletkebe-proxy&#xff08;可选&#xff09;容器运行时 插件DNSWeb UI&#xff08;Dashboard&#xff09;容器资源监控集群…

esp32开发笔记之一:esp32开发环境搭建vscode+ubuntu

最近想用esp32做一个物联网项目&#xff0c;踩坑N个终于有点心得&#xff0c;写下来避免和我一样的小白踩无谓的坑。 写在前面&#xff1a; 第一&#xff0c;大家一定要用linux系统作为编译工具&#xff0c;速度上是windows无法比的&#xff0c;不要因为不熟悉linux而选择win…

探索大型语言模型新架构:从 MoE 到 MoA

探索大型语言模型新架构&#xff1a;从 MoE 到 MoA 当前&#xff0c;商业科技公司纷纷投身于一场激烈的竞赛&#xff0c;不断扩大语言模型的规模&#xff0c;并为其注入海量的高质量数据&#xff0c;试图逐步提升模型的准确性。然而&#xff0c;这种看似顺理成章的发展路径逐渐…

数据结构:LinkedList与链表—面试题(三)

目录 1、移除链表元素 2、反转链表 3、链表的中间结点 4、返回倒数第k个结点 5、合并两个有序链表 1、移除链表元素 习题链接https://leetcode.cn/problems/remove-linked-list-elements/description/ 描述&#xff1a;给你一个链表的头节点 head 和一个整数 val &#xff…

AI赋能R-Meta分析核心技术:从热点挖掘到高级模型、助力高效科研与论文发表

Meta分析是针对某一科研问题&#xff0c;根据明确的搜索策略、选择筛选文献标准、采用严格的评价方法&#xff0c;对来源不同的研究成果进行收集、合并及定量统计分析的方法&#xff0c;现已广泛应用于农林生态&#xff0c;资源环境等方面&#xff0c;成为Science、Nature论文的…

LAMP搭建

LAMP搭建 引子&#xff1a;本篇文章为LAMP的搭建流程&#xff0c;其中L&#xff08;Ubuntu&#xff09;、A&#xff08;Apache&#xff09;、M&#xff08;Mysql&#xff09;、P&#xff08;PHP&#xff09;。 一、L → Ubuntu Step 1&#xff1a;在Vmware Workstation中使…

基于高斯混合模型的数据分析及其延伸应用(具体代码分析)

一、代码分析 &#xff08;一&#xff09;清除工作区和命令行窗口 clear; clc;clear;&#xff1a;该命令用于清除 MATLAB 工作区中的所有变量&#xff0c;确保代码运行环境的清洁&#xff0c;避免之前遗留的变量对当前代码运行产生干扰。例如&#xff0c;如果之前运行的代码中…

成为LabVIEW自由开发者

成为LabVIEW自由开发者的体验可以非常丰富且具有挑战性&#xff0c;同时也充满了自我成长和多样化项目的机会。 ​ 1. 高度的灵活性与自由度 工作时间与地点&#xff1a;作为自由开发者&#xff0c;你可以自由选择工作时间和地点。你可以在家工作&#xff0c;也可以选择在咖啡…

33.3K 的Freqtrade:开启加密货币自动化交易之旅

“ 如何更高效、智能地进行交易成为众多投资者关注的焦点。” Freqtrade 是一款用 Python 编写的免费开源加密货币交易机器人。它就像一位不知疲倦的智能交易助手&#xff0c;能够连接到众多主流加密货币交易所&#xff0c;如 Binance、Bitmart、Bybit 等&#xff08;支…

maven之插件调试

当使用maven进行项目管理的时候&#xff0c;可能会碰到一些疑难问题。网上资料很少&#xff0c;可能会想着直接调试定位问题。这里以maven-compiler-plugin为例&#xff1a; &#xff08;1&#xff09;准备maven-compiler-plugin源码 进入maven 官网-》Maven Plugins-》找到对…

如何配置Cursor的显示主题模式

cursor打开代码后&#xff0c;默认主题显示的主要代码颜色是白色&#xff0c;注解是黑色的&#xff0c;很不习惯&#xff0c;摸索一下&#xff0c;如何配置成与VSOCDE一样的主题&#xff0c;方案如下。 选择菜单 "File"--"Preferences 选择“ Theme" ---&…

Windows 系统中的任务管理器是什么,打开快捷键是什么?

任务管理器是 Windows 操作系统中一个强大的工具&#xff0c;它允许用户监控系统的性能、启动和停止进程、管理服务、以及查看网络活动等。掌握任务管理器的快捷键可以帮助你更高效地进行这些操作。本文中简鹿办公将教你如何利用任务管理器中的快捷键来提升你的工作效率。 一、…

论文导读 | 数据库中的连接操作

1. 连接操作的背景与问题定义 在关系型数据库中&#xff0c;我们通常面对以下问题&#xff1a; 给定一个数据库实例 I \mathcal{I} I&#xff0c;包含若干关系&#xff08;表&#xff09; R { R 1 , R 2 , ⋯ , R n } \mathcal{R}\{R_1, R_2, \cdots, R_n\} R{R1​,R2​,⋯…

最近在盘gitlab.0.先review了一下docker

# 正文 本猿所在产品的代码是保存到了一个本地gitlab实例上&#xff0c;实例是别的同事搭建的。最近又又又想了解一下&#xff0c;而且已经盘了一些了&#xff0c;所以写写记录一下。因为这个事儿没太多的进度压力&#xff0c;索性写到哪儿算哪儿&#xff0c;只要是新了解到的…

【搜索】【推荐】大 PK

引言 在当今信息爆炸的时代&#xff0c;如何从海量数据中精准地为用户推荐最相关的内容成为了科技领域的关键挑战。搜推技术作为推荐系统的核心组件&#xff0c;扮演着至关重要的角色。本文将深入探讨这两种技术背后的方法论&#xff0c;剖析它们各自面临的难点&#xff0c;并…

多模态大模型初探索:通过ollama部署多模态大模型

文章目录 前言模型下载 前言 今天和同事聊天&#xff0c;聊到多模态大模型&#xff0c;感觉可以作为2025年的一个新的探索方向。希望和大家一起学习&#xff0c;一起进步。 今天也是尝试了我能想到的最基本最快速地本地部署多模态大模型的方式&#xff0c;那便是使用ollama。…

maven如何从外部导包

1.找到你项目的文件位置&#xff0c;将外部要导入的包复制粘贴进你当前要导入的项目下。 2.从你的项目目录下选中要导入的包的pom文件即可导包成功 注意一定是选中对应的pom文件 导入成功之后对应的pom.xml文件就会被点亮

流媒体内网穿透/组网/网络映射EasyNTS上云网关启动失败如何解决?

在当今的网络视频监控和远程通信领域&#xff0c;设备的远程访问和数据共享需求日益增长。通过EasyNTS平台&#xff0c;用户无需开放内网端口&#xff0c;即可实现内网应用的外网访问&#xff0c;极大地简化了网络配置和维护工作。 EasyNTS上云网关的主要作用是解决异地视频共…

力扣刷题:数组OJ篇(下)

大家好&#xff0c;这里是小编的博客频道 小编的博客&#xff1a;就爱学编程 很高兴在CSDN这个大家庭与大家相识&#xff0c;希望能在这里与大家共同进步&#xff0c;共同收获更好的自己&#xff01;&#xff01;&#xff01; 目录 1.轮转数组&#xff08;1&#xff09;题目描述…

flink cdc oceanbase(binlog模式)

接上文&#xff1a;一文说清flink从编码到部署上线 环境&#xff1a;①操作系统&#xff1a;阿里龙蜥 7.9&#xff08;平替CentOS7.9&#xff09;&#xff1b;②CPU&#xff1a;x86&#xff1b;③用户&#xff1a;root。 预研初衷&#xff1a;现在很多项目有国产化的要求&#…