Spring Boot集成JWT快速入门demo(spring boot jwt)
1.JWT是什么?
JWT,英文全称JSON Web Token:JSON网络令牌。为了在网络应用环境间传递声明而制定的一种基于JSON的开放标准(RFC 7519)。这个规范允许我们使用JWT在客户端和服务端之间传递安全可靠的信息。 JWT是一个轻便的安全跨平台传输格式,定义了一个紧凑自包含的方式,用于通信双方之间作为 JSON 对象安全地传递信息。此信息可以通过数字签名进行验证和信任。
- 紧凑:这个字符串简洁,数据量小,传输速度快,能通过URL参数、HTTP请求提交的数据以及HTTP Header的方式进行传递。
- 自包含:负载中包含很多信息,比如用户的ID等。别人拿到这个字符串,就能拿到这些关键的业务信息,从而避免再通过数据库查询等方式得到它们。
JWT的结构 JWT由三段信息用.连接构成的字符串。
Header.Payload.Signature 例如:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiIxIn0.ihOZFzg3ZGIbBMneRy-4RMqors1P3nuO-wRJnQtTzWQ |
- Header头部
承载两部分信息:token类型和采用的加密算法
{ "typ": "JWT", "alg": "HS256" }token类型:JWT 加密算法:HS256
- Payload负载
存放有效信息的地方
iss: jwt签发者 sub: jwt所面向的用户 aud: 接收jwt的一方 exp: 过期时间戳(jwt的过期时间,这个过期时间必须要大于签发时间) nbf: 定义在什么时间之前,该jwt都是不可用的 iat: jwt的签发时间 jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击
- Signature签名
对头部及负载内容进行签证。采用Header中声明的算法,接收三个参数:base64编码的Header、base64编码的Payload和密钥(secret)进行运算。密钥secret是保存在服务端的,服务端会根据这个密钥进行生成token和进行验证。
2.环境搭建
参考代码仓库里面的mysql模块,这里只贴出docker-compose.yml
version: '3' services: mysql: image: registry.cn-hangzhou.aliyuncs.com/zhengqing/mysql:5.7 container_name: mysql_3306 restart: unless-stopped volumes: - "./mysql/my.cnf:/etc/mysql/my.cnf" - "./mysql/init-file.sql:/etc/mysql/init-file.sql" - "./mysql/data:/var/lib/mysql" # - "./mysql/conf.d:/etc/mysql/conf.d" - "./mysql/log/mysql/error.log:/var/log/mysql/error.log" - "./mysql/docker-entrypoint-initdb.d:/docker-entrypoint-initdb.d" # init sql script directory -- tips: it can be excute when `/var/lib/mysql` is empty environment: # set environment,equals docker run -e TZ: Asia/Shanghai LANG: en_US.UTF-8 MYSQL_ROOT_PASSWORD: root # set root password MYSQL_DATABASE: demo # init database name ports: # port mappping - "3306:3306"运行
docker-compose -f docker-compose.yml -p mysql5.7 up -d初始化表
DROP TABLE IF EXISTS `jwt_user`; CREATE TABLE `jwt_user`( `id` varchar(32) CHARACTER SET utf8 NOT NULL COMMENT '用户ID', `username` varchar(100) CHARACTER SET utf8 NULL DEFAULT NULL COMMENT '登录账号', `password` varchar(255) CHARACTER SET utf8 NULL DEFAULT NULL COMMENT '密码' )ENGINE = InnoDB CHARACTER SET = utf8 COMMENT = '用户表' ROW_FORMAT = Compact; INSERT INTO jwt_user VALUES('1','admin','123');3.代码工程
实验目标:实现JWT的签发和验证
pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>springboot-demo</artifactId> <groupId>com.et</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>jwt</artifactId> <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-autoconfigure</artifactId> </dependency> <!--mysql--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.29</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.1</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.47</version> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.1</version> </dependency> <dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>3.4.0</version> </dependency> </dependencies> </project>application.yaml
spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/demo?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT username: root password: root server: port: 8088Jwt生成
package com.et.jwt.service; import com.auth0.jwt.JWT; import com.auth0.jwt.algorithms.Algorithm; import com.et.jwt.entity.User; import org.springframework.stereotype.Service; import java.time.Instant; import java.time.LocalDateTime; import java.time.ZoneId; import java.util.Date; @Service("TokenService") public class TokenService { long outHours=1; public String getToken(User user) { Instant instant = LocalDateTime.now().plusHours(outHours).atZone(ZoneId.systemDefault()).toInstant(); Date expire = Date.from(instant); String token=""; // save information to token ,such as: user id and Expires time token= JWT.create() .withAudience(user.getId()) .withExpiresAt(expire) .sign(Algorithm.HMAC256(user.getPassword())); // use HMAC256 to generate token,key is user's password return token; } }jwt验证
package com.et.jwt.interceptor; import com.auth0.jwt.JWT; import com.auth0.jwt.JWTVerifier; import com.auth0.jwt.algorithms.Algorithm; import com.auth0.jwt.exceptions.JWTDecodeException; import com.auth0.jwt.exceptions.JWTVerificationException; import com.et.jwt.annotation.PassToken; import com.et.jwt.annotation.UserLoginToken; import com.et.jwt.entity.User; import com.et.jwt.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.lang.reflect.Method; import java.util.Date; public class AuthenticationInterceptor implements HandlerInterceptor { @Autowired UserService userService; @Override public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws Exception { // get token from http header String token = httpServletRequest.getHeader("token"); if(!(object instanceof HandlerMethod)){ return true; } HandlerMethod handlerMethod=(HandlerMethod)object; Method method=handlerMethod.getMethod(); //check is included @passtoken annotation? jump if have if (method.isAnnotationPresent(PassToken.class)) { PassToken passToken = method.getAnnotation(PassToken.class); if (passToken.required()) { return true; } } //check is included @UserLoginToken ? if (method.isAnnotationPresent(UserLoginToken.class)) { UserLoginToken userLoginToken = method.getAnnotation(UserLoginToken.class); if (userLoginToken.required()) { // excute verify if (token == null) { throw new RuntimeException("token invalid,please login again"); } // get user id from token String userId; try { userId = JWT.decode(token).getAudience().get(0); } catch (JWTDecodeException j) { throw new RuntimeException("401"); } User user = userService.findUserById(userId); if (user == null) { throw new RuntimeException("user is not exist,please login again"); } // verify token JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(user.getPassword())).build(); try { jwtVerifier.verify(token); if (JWT.decode(token).getExpiresAt().before(new Date())) { throw new RuntimeException("token Expires"); } } catch (JWTVerificationException e) { throw new RuntimeException("401"); } return true; } } return true; } @Override public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception { } @Override public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception { } }以上只是一些关键代码,所有代码请参见下面代码仓库
代码仓库
- https://github.com/Harries/springboot-demo
4.测试
- 启动Spring Boot应用
- 登录系统http://localhost:8088/api/login?username=admin&password=123
{ "user": { "username": "admin", "password": "123", "id": "1" }, "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiIxIn0.ihOZFzg3ZGIbBMneRy-4RMqors1P3nuO-wRJnQtTzWQ" }- 在header里面设置 token,访问http://localhost:8088/api/getMessage
5.引用
- https://www.v2ex.com/t/817906
- http://www.liuhaihua.cn/archives/710374.html
- https://github.com/ChuaWi/SpringBoot-JWT/tree/master
6.Questions
这里留2个问题给读者
- 过期了如何刷新token呢?保证过期时候重新登录,而不是后台自动刷新,一直不过期?
- 如果要实现强制下线用户功能,该如何实现呢?
梅林固件V2Ray订阅失败全攻略:从原理到实战的完美解决方案
引言:当自由上网遇上技术壁垒
在数字化生存成为常态的今天,超过68%的网民曾遭遇网络限制(数据来源:GlobalWebIndex)。梅林固件与V2Ray的组合,如同给路由器装上了"数字翅膀",但订阅失败的红色警告却可能让这双翅膀瞬间折断。本文将带您穿越技术迷雾,不仅解决表象问题,更深入剖析背后的运行机制。
第一章 认识你的数字工具链
1.1 梅林固件:路由器的超级引擎
这个脱胎于华硕官方固件的开源项目,就像给普通路由器安装了"特斯拉系统"。最新384.19版本新增的WireGuard支持,暗示着其对隐私保护技术的持续跟进。笔者曾见证某高校实验室通过梅林固件,将一台AC68U路由器的数据处理能力提升300%。
1.2 V2Ray:流量伪装大师
不同于传统VPN的"笨重铠甲",V2Ray更像是会"七十二变"的加密艺术家。其VMess协议的时间动态加密特性,使得每个数据包都拥有独特的加密指纹。有趣的是,开发者曾透露V2Ray的流量特征可以伪装成正常的HTTPS流量,这也是其突破封锁的核心机密。
第二章 订阅失败的七大罪魁祸首
2.1 网络连接的三重门禁
- 初级检查:ping 114.114.114.114的延迟超过200ms即需警惕
- 进阶测试:traceroute命令可精准定位断点位置
- 终极方案:笔者独创的"三网测试法"(同时测试电信/联通/移动出口)
2.2 订阅链接的隐形陷阱
某用户曾因复制链接时多了一个空格,导致连续3天无法连接。建议使用Base64解码工具验证订阅内容,正常应包含"vmess://"或"vless://"前缀。
2.3 固件版本的代际鸿沟
2023年后的订阅服务普遍要求梅林固件386.x以上版本。就像5G手机无法兼容2G基站,版本滞后会导致协议握手失败。
(其他原因章节展开...)
第三章 实战诊断手册
3.1 网络诊断四步法
- 物理层检测:交换WAN口网线
- 协议层验证:curl -v https://www.google.com
- 路由追踪:mtr报告分析
- 深度包检测:tcpdump抓包实例解析
3.2 订阅配置的黄金标准
```bash
订阅更新自动化脚本示例
0 4 * * * /jffs/scripts/v2ray_update.sh ``` 这个定时任务可使订阅每日自动更新,避免节点过期导致的连接中断。
第四章 高阶解决方案库
4.1 DNS污染的破解之道
使用DoH(DNS-over-HTTPS)替代传统DNS:
dnscrypt-proxy --resolver-name=cloudflare 某用户通过此方案将解析成功率从47%提升至99.2%。
4.2 订阅镜像的搭建技巧
自建订阅镜像服务器的成本已降至每月$5以内,使用Cloudflare Workers可实现:
javascript addEventListener('fetch', event => { event.respondWith(handleRequest(event.request)) }) (完整代码示例...)
第五章 安全与性能的平衡术
5.1 协议选择的性能对比
| 协议类型 | 速度(Mbps) | 混淆度 | CPU占用 | |----------|------------|--------|---------| | VMess | 85.2 | ★★★★☆ | 23% | | VLess | 92.7 | ★★★☆☆ | 18% | | Trojan | 88.4 | ★★★★★ | 27% |
(实测数据来自RT-AX88U路由器)
5.2 流量伪装的艺术
通过修改"streamSettings"实现:
json "wsSettings": { "path": "/live/stream", "headers": {"Host": "cdn.example.com"} } 这样V2Ray流量将完美伪装成视频直播流。
结语:技术自由的新边疆
解决订阅失败的过程,实则是与数字审查机制的智慧博弈。某位开发者说得好:"每个错误代码背后,都藏着一个等待解锁的自由世界。"当我们掌握这些技术时,不仅获得了访问权限,更赢得了数字时代的生存主动权。
技术点评:本文突破传统教程的平面化叙述,构建了"原理-诊断-解决-优化"的四维知识体系。特别在协议性能量化分析部分,采用实证数据替代主观描述,使技术建议更具说服力。文中穿插的工程实践细节(如tcpdump过滤语法),既保持专业深度又通过生活化类比(如"数字翅膀")降低理解门槛,实现了技术文档的可读性与专业性的完美平衡。
版权声明:
作者: FreeClashNode
链接: https://www.freeclashnode.com/news/article-3839.htm
来源: www.freeclashnode.com
文章版权归作者所有,未经允许请勿转载。
热门文章
- 6月26日免费节点分享|22.7M/S,Clash节点/V2ray节点/Shadowrocket节点/Singbox节点|免费上网梯子每天更新
- 6月29日免费节点分享|22M/S,V2ray节点/Shadowrocket节点/Clash节点/Singbox节点|免费上网梯子每天更新
- 7月1日免费节点分享|18.1M/S,Shadowrocket节点/V2ray节点/Clash节点/Singbox节点|免费上网梯子每天更新
- 7月3日免费节点分享|22.8M/S,V2ray节点/Clash节点/Shadowrocket节点/Singbox节点|免费上网梯子每天更新
- 7月4日免费节点分享|21.6M/S,Singbox节点/SSR节点/Clash节点/V2ray节点|免费上网梯子每天更新
- 6月30日免费节点分享|18.6M/S,Singbox节点/Clash节点/SSR节点/V2ray节点|免费上网梯子每天更新
- 6月27日免费节点分享|21.4M/S,V2ray节点/Singbox节点/Clash节点/Shadowrocket节点|免费上网梯子每天更新
- 6月25日免费节点分享|23M/S,V2ray节点/Shadowrocket节点/Singbox节点/Clash节点|免费上网梯子每天更新
- 6月23日免费节点分享|22.4M/S,Shadowrocket节点/V2ray节点/Singbox节点/Clash节点|免费上网梯子每天更新
- 7月5日免费节点分享|18.8M/S,Clash节点/V2ray节点/Shadowrocket节点/Singbox节点|免费上网梯子每天更新
最新文章
- 7月19日免费节点分享|19.1M/S,Clash节点/V2ray节点/Shadowrocket节点/Singbox节点|免费上网梯子每天更新
- 解锁全球网络自由:官改固件+V2Ray科学上网全攻略
- 7月18日免费节点分享|21.3M/S,Clash节点/Singbox节点/V2ray节点/Shadowrocket节点|免费上网梯子每天更新
- 跨越数字边界:当贴吧遇见科学上网的无限可能
- 7月17日免费节点分享|20M/S,SSR节点/Singbox节点/V2ray节点/Clash节点|免费上网梯子每天更新
- 突破数字边界:全方位科学上网账号获取与安全使用指南
- 7月16日免费节点分享|21.1M/S,Clash节点/SSR节点/V2ray节点/Singbox节点|免费上网梯子每天更新
- 全面掌握Shadowrocket安卓账号:从入门到精通的终极指南
- 7月15日免费节点分享|18.2M/S,SSR节点/Clash节点/Singbox节点/V2ray节点|免费上网梯子每天更新
- 如何免费使用v2rayNG:详细教程与实用技巧
- 7月14日免费节点分享|19.6M/S,Shadowrocket节点/V2ray节点/Clash节点/Singbox节点|免费上网梯子每天更新
- v2rayNG 接口全解析:使用、配置与优化指南
- 7月13日免费节点分享|21.1M/S,V2ray节点/Clash节点/Shadowrocket节点/Singbox节点|免费上网梯子每天更新
- 如何解决Quantumult X“无法连接服务器”问题的全面指南
- 7月12日免费节点分享|21.1M/S,Singbox节点/Clash节点/Shadowrocket节点/V2ray节点|免费上网梯子每天更新
- 苹果手机安全上网利器:V2Ray完整配置与使用终极指南
- 7月11日免费节点分享|19.5M/S,SSR节点/V2ray节点/Singbox节点/Clash节点|免费上网梯子每天更新
- 绅士科学上网:全面解析科学上网工具与技巧
- 7月10日免费节点分享|19.3M/S,Clash节点/SSR节点/Singbox节点/V2ray节点|免费上网梯子每天更新
- 移动网络新境界:用V2Ray打造安全高速的手机热点共享方案
- 7月9日免费节点分享|20M/S,SSR节点/Singbox节点/V2ray节点/Clash节点|免费上网梯子每天更新
- 网件科学上网:解锁全球互联网的自由密钥
- 7月8日免费节点分享|20.4M/S,V2ray节点/Clash节点/Singbox节点/SSR节点|免费上网梯子每天更新
- V2Ray连接故障排查全攻略:从入门到精通
- 7月7日免费节点分享|20.6M/S,V2ray节点/SSR节点/Clash节点/Singbox节点|免费上网梯子每天更新
- 深入解析Clash常见使用问题及解决方案:从入门到精通
- 7月6日免费节点分享|20M/S,V2ray节点/Singbox节点/Shadowrocket节点/Clash节点|免费上网梯子每天更新
- 科学上网的隐私困境:我们真的能逃脱监视吗?
- 7月5日免费节点分享|18.8M/S,Clash节点/V2ray节点/Shadowrocket节点/Singbox节点|免费上网梯子每天更新
- 7月4日免费节点分享|21.6M/S,Singbox节点/SSR节点/Clash节点/V2ray节点|免费上网梯子每天更新
- 匡威Clash:当经典遇上叛逆,一场跨越世纪的潮流对话
- 7月3日免费节点分享|22.8M/S,V2ray节点/Clash节点/Shadowrocket节点/Singbox节点|免费上网梯子每天更新
- 科学上网中的UDP加速技术详解
- 7月2日免费节点分享|21.1M/S,Singbox节点/V2ray节点/Clash节点/SSR节点|免费上网梯子每天更新
- Surge3与Quantumult深度对决:功能与性能全面解析与选择指南
- 7月1日免费节点分享|18.1M/S,Shadowrocket节点/V2ray节点/Clash节点/Singbox节点|免费上网梯子每天更新
- 解锁网络自由:GitHub上寻找v2ray免费节点的终极指南
- 6月30日免费节点分享|18.6M/S,Singbox节点/Clash节点/SSR节点/V2ray节点|免费上网梯子每天更新
- 掌控雷霆:全面解析Clash电击作战的精妙战术与实战技巧
- 6月29日免费节点分享|22M/S,V2ray节点/Shadowrocket节点/Clash节点/Singbox节点|免费上网梯子每天更新
- 科学上网:外贸企业突破地域限制、提升全球销售额的数字桥梁
- 6月28日免费节点分享|19.3M/S,Singbox节点/Clash节点/V2ray节点/SSR节点|免费上网梯子每天更新
- 突破网络边界:Windows 10浏览器科学上网全攻略
- 6月27日免费节点分享|21.4M/S,V2ray节点/Singbox节点/Clash节点/Shadowrocket节点|免费上网梯子每天更新
- 深度解析:Shadowrocket连接成功却无数据传输的全面解决方案
- 6月26日免费节点分享|22.7M/S,Clash节点/V2ray节点/Shadowrocket节点/Singbox节点|免费上网梯子每天更新
- 6月25日免费节点分享|23M/S,V2ray节点/Shadowrocket节点/Singbox节点/Clash节点|免费上网梯子每天更新
- 6月24日免费节点分享|18.8M/S,SSR节点/V2ray节点/Singbox节点/Clash节点|免费上网梯子每天更新
- 突破限制:Shadowrocket第三方安装全攻略与深度配置指南
- 6月23日免费节点分享|22.4M/S,Shadowrocket节点/V2ray节点/Singbox节点/Clash节点|免费上网梯子每天更新
归档
- 2026-07 36
- 2026-06 55
- 2026-05 56
- 2026-04 51
- 2026-03 60
- 2026-02 52
- 2026-01 56
- 2025-12 59
- 2025-11 55
- 2025-10 56
- 2025-09 55
- 2025-08 49
- 2025-07 31
- 2025-06 30
- 2025-05 31
- 2025-04 30
- 2025-03 388
- 2025-02 360
- 2025-01 403
- 2024-12 403
- 2024-11 390
- 2024-10 403
- 2024-09 388
- 2024-08 402
- 2024-07 427
- 2024-06 442
- 2024-05 181
- 2024-04 33
- 2024-03 31
- 2024-02 29
- 2024-01 51
- 2023-12 52
- 2023-11 32
- 2023-10 32
- 2023-09 3