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呢?保证过期时候重新登录,而不是后台自动刷新,一直不过期?
- 如果要实现强制下线用户功能,该如何实现呢?
番茄Clash终极指南:从零开始掌握高效网络代理工具
在当今数字化时代,网络访问的自由与安全显得尤为重要。番茄Clash作为一款新兴的网络代理工具,凭借其强大的功能和简洁的设计,正迅速成为众多用户的首选。无论你是网络新手还是技术专家,本指南将带你全面了解番茄Clash,从安装到高级配置,一步步掌握这款工具的精髓。
番茄Clash简介:为何选择它?
番茄Clash是一款集网络代理与管理于一体的多功能工具,旨在为用户提供高效、安全的网络访问体验。它不仅支持多种代理协议(如HTTP、SOCKS5等),还具备实时流量监控、跨平台兼容等实用功能。与其他同类工具相比,番茄Clash的优势在于其用户友好的界面设计,即使是初次接触代理工具的用户也能快速上手。
核心功能一览
- 多协议支持:轻松切换HTTP、SOCKS5等代理协议,满足不同场景需求。
- 实时流量监控:直观显示网络活动,帮助用户掌握数据流向。
- 跨平台兼容:支持Windows 7及以上系统及主流Linux发行版。
- 规则自定义:通过灵活的配置,实现流量筛选和访问控制。
安装番茄Clash:详细步骤解析
系统需求检查
在安装前,请确保设备满足以下最低配置:
- 操作系统:Windows 7或更高版本,或最新Linux发行版。
- 内存:至少2GB RAM。
- 存储空间:100MB可用空间。
下载与安装
获取安装包:
- 访问番茄Clash官网或GitHub开源页面下载最新版本。
- 选择与系统匹配的安装包(如Windows的.exe文件或Linux的.tar.gz压缩包)。
安装流程:
- Windows用户:双击安装包,按照向导提示完成安装。
- Linux用户:解压后运行终端命令
./install.sh完成安装。
首次启动:安装完成后,桌面或应用菜单中会出现番茄Clash图标,点击即可启动。
使用入门:界面与基础操作
主界面导航
番茄Clash的主界面设计简洁明了,分为三个主要区域:
- 左侧功能菜单:包含项目列表、设置和帮助选项。
- 中间监控区:实时显示网络流量和连接状态。
- 右侧配置面板:用于调整代理规则和高级设置。
创建第一个代理项目
- 点击“新建项目”按钮,输入项目名称(例如“工作代理”)。
- 选择代理协议(如SOCKS5),填写服务器地址和端口。
- 保存后,项目将出现在左侧菜单中,点击“启动”即可激活代理。
流量监控与连接管理
通过主界面的监控区,用户可以实时查看:
- 当前上传/下载速度。
- 活跃的连接数及目标地址。
- 代理服务的延迟和稳定性指标。
高级配置:优化你的代理体验
首次配置向导
初次启动时,番茄Clash会引导用户完成基础设置:
1. 选择语言和主题。
2. 配置默认代理模式(全局或按规则分流)。
3. 设置本地监听端口(建议保留默认值)。
网络与安全设置
- 代理服务器配置:支持手动输入或从文件导入服务器列表。
- DNS设置:推荐使用加密DNS(如Cloudflare的1.1.1.1)以提升隐私性。
- 规则管理:通过自定义规则,实现特定网站走指定代理。
性能优化技巧
- 启用流量压缩:减少数据传输量,提升速度。
- 调整并发连接数:根据网络状况优化(默认值通常已够用)。
- 定期清理缓存:避免历史数据积累影响性能。
常见问题与解决方案
1. 番茄Clash无法启动?
- 检查权限:以管理员身份运行程序。
- 重新安装:下载最新版本覆盖安装。
- 查看日志:日志文件通常位于安装目录的
logs文件夹中。
2. 代理速度慢怎么办?
- 尝试切换不同的代理服务器。
- 关闭不必要的后台应用释放带宽。
- 检查本地网络是否稳定。
3. 如何更新到最新版本?
- 官网或GitHub下载新版安装包,直接覆盖安装即可。
- 部分Linux系统支持通过命令行更新(如
sudo apt upgrade tomato-clash)。
结语:番茄Clash,你的网络自由助手
番茄Clash以其强大的功能和易用性,成为网络代理工具中的佼佼者。无论是为了突破地域限制,还是提升网络安全性,它都能提供可靠的解决方案。通过本指南的学习,相信你已经能够熟练安装、配置和使用番茄Clash。未来,随着开发团队的持续更新,这款工具的功能还将进一步丰富。现在就行动起来,开启你的高效网络之旅吧!
语言点评:
本文以清晰的结构和通俗的语言,将番茄Clash的复杂功能拆解为易于理解的步骤。通过使用标题分级、列表和加粗关键词,增强了内容的可读性。同时,穿插实用技巧和问题解答,既照顾了新手用户,也为进阶玩家提供了有价值的信息。整体风格专业而不失亲和力,符合技术类教程的传播需求。
版权声明:
作者: freeclashnode
链接: https://www.freeclashnode.com/news/article-3839.htm
来源: FreeClashNode
文章版权归作者所有,未经允许请勿转载。
热门文章
- 12月6日|19.8M/S,V2ray节点/Clash节点/SSR节点/Singbox节点|免费订阅机场|每天更新免费梯子
- 11月22日|20.2M/S,Shadowrocket节点/V2ray节点/Clash节点/Singbox节点|免费订阅机场|每天更新免费梯子
- 12月5日|23M/S,Singbox节点/V2ray节点/Clash节点/SSR节点|免费订阅机场|每天更新免费梯子
- 12月11日|23M/S,Singbox节点/V2ray节点/Clash节点/Shadowrocket节点|免费订阅机场|每天更新免费梯子
- 11月25日|20.3M/S,Clash节点/V2ray节点/Singbox节点/SSR节点|免费订阅机场|每天更新免费梯子
- 12月9日|20M/S,Singbox节点/V2ray节点/Clash节点/SSR节点|免费订阅机场|每天更新免费梯子
- 12月12日|18.6M/S,Singbox节点/Clash节点/Shadowrocket节点/V2ray节点|免费订阅机场|每天更新免费梯子
- 12月8日|21.6M/S,Singbox节点/SSR节点/V2ray节点/Clash节点|免费订阅机场|每天更新免费梯子
- 12月13日|18.1M/S,V2ray节点/SSR节点/Clash节点/Singbox节点|免费订阅机场|每天更新免费梯子
- 11月20日|19.3M/S,Singbox节点/Shadowrocket节点/V2ray节点/Clash节点|免费订阅机场|每天更新免费梯子
最新文章
- 12月17日|20.7M/S,Singbox节点/V2ray节点/Shadowrocket节点/Clash节点|免费订阅机场|每天更新免费梯子
- 12月16日|21M/S,SSR节点/Singbox节点/Clash节点/V2ray节点|免费订阅机场|每天更新免费梯子
- 12月15日|20.8M/S,SSR节点/Singbox节点/Clash节点/V2ray节点|免费订阅机场|每天更新免费梯子
- 12月14日|21.5M/S,V2ray节点/Shadowrocket节点/Singbox节点/Clash节点|免费订阅机场|每天更新免费梯子
- 12月13日|18.1M/S,V2ray节点/SSR节点/Clash节点/Singbox节点|免费订阅机场|每天更新免费梯子
- 12月12日|18.6M/S,Singbox节点/Clash节点/Shadowrocket节点/V2ray节点|免费订阅机场|每天更新免费梯子
- 12月11日|23M/S,Singbox节点/V2ray节点/Clash节点/Shadowrocket节点|免费订阅机场|每天更新免费梯子
- 12月10日|19.9M/S,Clash节点/V2ray节点/Singbox节点/SSR节点|免费订阅机场|每天更新免费梯子
- 12月9日|20M/S,Singbox节点/V2ray节点/Clash节点/SSR节点|免费订阅机场|每天更新免费梯子
- 12月8日|21.6M/S,Singbox节点/SSR节点/V2ray节点/Clash节点|免费订阅机场|每天更新免费梯子
归档
- 2025-12 30
- 2025-11 55
- 2025-10 56
- 2025-09 55
- 2025-08 49
- 2025-07 31
- 2025-06 30
- 2025-05 31
- 2025-04 31
- 2025-03 383
- 2025-02 360
- 2025-01 403
- 2024-12 403
- 2024-11 390
- 2024-10 403
- 2024-09 388
- 2024-08 402
- 2024-07 424
- 2024-06 446
- 2024-05 184
- 2024-04 33
- 2024-03 32
- 2024-02 29
- 2024-01 50
- 2023-12 53
- 2023-11 32
- 2023-10 32
- 2023-09 3