当前位置: 首页 > news >正文

JAVA-JWT

JWT简介  

        JSON Web Token(JWT)是一个非常轻巧的规范,这个规范允许我们使用 JWT 在用户和服务器之间传递安全可靠的信息。一个 JWT 实际上就是一个字符串,它由三部分组成,头部、载荷与签名。前两部分需要经过 Base64 编码,后一部分通过前两部分 Base64 编码后再加密而成。

        JWT组成:Header + Payload + Signature

        Header:头部用于描述关于该 JWT 的最基本的信息,例如其类型以及签名所用的算法等,如{"type":"JWT","alg":"HS256"},Base64 加密header后的字符串为(JWT官网JSON Web Tokens - jwt.io 可以验证):eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

        Payload:一般添加用户的相关信息或其他业务需要的必要信息,但不建议添加敏感信息,因为该部分在客户端可解密。如:{"id":"1","name":"张三","sex":"male"},Base64 加密Payload后的字符串为(JWT官网 可以验证):eyJpZCI6IjEiLCJuYW1lIjoi5byg5LiJIiwic2V4IjoibWFsZSJ9

        Signature:这个部分需要 Base64 加密后的 header 和 Base64 加密后的 payload 使用 “.” 连接组成的字符串,然后通过 header 中声明的加密方式进行加盐 salt组合加密,然后就构成了 jwt 的第三部分。如:salt设置为abc,Signature字符串为:mZKsezNd5e5Q0Gi4vdeyEH3-ilxG_qEHkZp0gn7ayr0

        综上,公式如下:

        Token = Base64(Header).Base64(Payload).Base64(Signature)

        Signature = Header指定的签名算法(Base64(header).Base64(payload),秘钥)

        生成后的token信息为:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjEiLCJuYW1lIjoi5byg5LiJIiwic2V4IjoibWFsZSJ9.mZKsezNd5e5Q0Gi4vdeyEH3-ilxG_qEHkZp0gn7ayr0

JWT应用

        在分布式环境下,有状态(含session)可以采用分布式session解决方案,参考:分布式session解决方案-CSDN博客,无状态token+redis也可以实现分布式token验证校验,spring-session和token+redis 都借助了redis,如果实际项目不采用redis中间件,可以用JWT实现上述需求,架构示意图如下:

        项目中使用JWT步骤如下:

        1、添加项目依赖:java-jwt

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.gingko</groupId><artifactId>jwt</artifactId><version>0.0.1-SNAPSHOT</version><name>jwt</name><description>Demo project for Spring Boot</description><properties><java.version>1.8</java.version><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><spring-boot.version>2.7.6</spring-boot.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- jwt --><dependency><groupId>com.auth0</groupId><artifactId>java-jwt</artifactId><version>4.5.0</version></dependency><!-- fastjson --><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.83</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>runtime</scope><optional>true</optional></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies><dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>${spring-boot.version}</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement><build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.8.1</version><configuration><source>1.8</source><target>1.8</target><encoding>UTF-8</encoding></configuration></plugin><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><version>${spring-boot.version}</version><configuration><mainClass>com.gingko.jwt.JwtApplication</mainClass><skip>true</skip></configuration><executions><execution><id>repackage</id><goals><goal>repackage</goal></goals></execution></executions></plugin></plugins></build></project>

        2、编写配置

# 应用服务 WEB 访问端口
server:port: 8080#jwt key
jwt:key: gingko

        3、代码实现加密token,解密token。登录成功后,将加密后的token返回到前台,前台放入本地缓存,请求后台时,将token放入request header中,后台通过拦截器实现token的校验进而验证用户是否登录,代码如下:

        后台请求UserController:

package com.gingko.jwt.controller;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.gingko.jwt.common.GenericWebResult;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import java.util.Date;@RestController
@RequestMapping("/user")
public class UserController {@Value("${jwt.key}")private String jwtKey;@RequestMapping("/login")public GenericWebResult login(@RequestParam String userName,@RequestParam String password) {//到数据库校验通过Algorithm algorithm = Algorithm.HMAC256(jwtKey);String token = JWT.create().withClaim("userName",userName).withClaim("userId","001")//token半小时过期.withExpiresAt(new Date(System.currentTimeMillis() + 1800000)).sign(algorithm);return GenericWebResult.ok("登录成功",token);}@RequestMapping("/getInfo")public GenericWebResult getInfo(@RequestAttribute String userId) {return GenericWebResult.ok("获取信息成功 ",userId);}}

        登录拦截器LoginInterceptor:

package com.gingko.jwt.interceptor;
import com.alibaba.fastjson.JSON;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.gingko.jwt.common.GenericWebResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;/*** 登录拦截器*/
@Slf4j
@Component
public class LoginInterceptor implements HandlerInterceptor {@Value("${jwt.key}")private String jwtKey;//登录之前拦截@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {String token = request.getHeader("token");response.setContentType("application/json");response.setCharacterEncoding("UTF-8");log.info("登录校验开始,token:{}", token);if (token == null || token.isEmpty()) {log.info("token为空,请求被拦截");response.setStatus(HttpStatus.UNAUTHORIZED.value());GenericWebResult genericWebResult = GenericWebResult.error("token为空,请求被拦截");String resultStr = JSON.toJSONString(genericWebResult);response.getWriter().write(resultStr);return false;}try {Algorithm algorithm = Algorithm.HMAC256(jwtKey);JWTVerifier verifier = JWT.require(algorithm).build();//验证tokenDecodedJWT decodedJWT = verifier.verify(token);//将用户id设置到request中request.setAttribute("userId",decodedJWT.getClaim("userId").asString());} catch (JWTVerificationException exception) {log.warn("token无效,请求被拦截");GenericWebResult genericWebResult = GenericWebResult.error("token无效,请求被拦截");String resultStr = JSON.toJSONString(genericWebResult);response.getWriter().write(resultStr);return false;}return true;}
}

        配置类WebMvcConfig及统一返回值GenericWebResult

package com.gingko.jwt.config;
import com.gingko.jwt.interceptor.LoginInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import javax.annotation.Resource;/*** web配置类,配置拦截器*/
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {@Resourceprivate LoginInterceptor loginInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(loginInterceptor).addPathPatterns("/user/*").excludePathPatterns("/user/login");//登录不用拦截}
}
package com.gingko.jwt.common;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
/*** 向前台返回的统一格式的结果*/
@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class GenericWebResult {/*** 是否成功*/private boolean successFlag;/*** 成功或失败返回的信息*/private String msg;/*** /*** 返回列表或树形数据,用户可以自由添加*/private Object property;/*** 数据总条数,分页情况下使用*/private Long total;public static GenericWebResult ok() {return new GenericWebResult(true, null, null, null);}public static GenericWebResult ok(String msg) {return new GenericWebResult(true, msg, null, null);}public static GenericWebResult ok(Object property) {return new GenericWebResult(true, null, property, null);}public static GenericWebResult ok(String msg, Object property) {return new GenericWebResult(true, msg, property, null);}public static GenericWebResult ok(Object property, Long total) {return new GenericWebResult(true, null, property, total);}public static GenericWebResult ok(String msg, Object property, Long total) {return new GenericWebResult(true, msg, property, total);}public static GenericWebResult error(String msg) {return new GenericWebResult(false, msg, null, null);}public static GenericWebResult error(String msg, Object data) {return new GenericWebResult(false, msg, data, null);}
}

        4、后台启动2个服务,端口分别是8080和8082,通过8080服务登录,8082服务验证token的有效性,篡改token后验证token无效,符合预期。

 

 

http://www.lqws.cn/news/583777.html

相关文章:

  • Web3 + RWA 餐饮数字化解决方案白皮书(试点版)
  • 【Debian】2-1 frp内网穿透原理
  • 直播 APP 开发需要多少成本
  • 大模型在急性左心衰竭预测与临床方案制定中的应用研究
  • MIT 6.824学习心得(2) 浅谈多线程和RPC
  • leetcode:693. 交替位二进制数(数学相关算法题,python3解法)
  • 七天学会SpringCloud分布式微服务——06——Sentinel
  • Android阴影效果的艺术与实现:从入门到精通
  • WIFI 低功耗保活知识系列---三.WiFi AP如何广播自己的缓存区信息
  • 为何 SQL 性能调优的重要性更为突出
  • 工业级PHP任务管理系统开发:模块化设计与性能调优实践
  • Linux驱动学习day11(定时器)
  • 华为云Flexus+DeepSeek征文| 使用华为云CCE容器部署Dify-LLM高可用方案的验证与测试
  • Spring Security 鉴权与授权详解(前后端分离项目)
  • 基础算法合集-图论
  • 银河麒麟系统上利用WPS的SDK进行WORD的二次开发
  • 2025 年 6 月 TIOBE 指数
  • 浪潮和曙光服务器的ipmi配置教程
  • 带修莫队(三维莫队)
  • K8S初始化master节点不成功kubelet.service failed(cgroup driver配置问题)
  • Pyhton-EXCEL与Mysql数据对比
  • 引爆点:ImageNet、AlexNet与深度学习的惊雷
  • VBA代码解决方案第二十六讲:如何新建EXCEL工作簿文件
  • Windows Excel文档办公工作数据整理小工具
  • 飞纳台式扫描电镜能谱一体机:元素分析与高分辨率成像的完美结合
  • FPGA实现CameraLink视频解码转SDI输出,基于LVDS+GTX架构,提供2套工程源码和技术支持
  • 如何读取运行jar中引用jar中的文件
  • GitHub Actions 入门指南:从零开始自动化你的开发流程
  • Redis中的缓存击穿、缓存穿透和缓存雪崩是什么?
  • 如何提升 iOS App 全链路体验?从启动到退出的优化调试流程