# JWT 图解
Posted 爱学习的大鱼
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了# JWT 图解相关的知识,希望对你有一定的参考价值。
JWT简介
JWT(Json Web Token
)通过json形式,作为web应用中的令牌,用于在各方之间安全地将信息作为json对象创数,在数据的传输过程中还可以完成数据的加密、签名等相关处理,特点如下:
- 跨语言:支持主流语言
- 自包含:包含必要的所有信息,如用户信息和签名等
- 易传递:很方便通过HTTP头部传递
传统的session认证
传统方案存在问题:
- 用户量越多,服务器的开销越大
- cookie本身不安全
- 在集群的情况下,用户必须在同一服务器或通过session共享机制实现认证
- 前后端分离增加了复杂性,如:代理、路由网关
基于JWT认证
认证流程
- 首先,前端通过Web表单将自己的用户名和密码发送到后端。这一过程一般是一个HTTP POST请求。建议的方式是通过SSL加密的传输(https协议),从而避免敏感信息被嗅探
- 后端核对用户名和密码成功后,将用户的id等其他信息作为JWT Payload(负载),将其与头部分别进行Base64编码拼接后签名,形成一个形同xxx.yyy.zzz字符串的JWT(Token)
- 后端将JWT字符串作为登录成功的返回结果返回给前端。前端可以将返回的结果保存在localStorage或sessionStorage上,退出登录时前端删除保存的JWT即可
- 前端在每次请求时将JWT放入HTTP Header中的Authorization位。(解决XSS和XSRF问题)
- 后端检查JWT是否存在,如存在验证其有效性。例如,检查签名是否正确;检查Token是否过期;检查Token的接收方是否是自己(可选)
- 验证通过后后端使用JWT中包含的用户信息进行其他逻辑操作,返回相应结果
JWT的优势
- 简洁(Compact): 可以通过URL,POST参数或者在HTTP header中发送,数据量小,传输速度快
- 自包含(Self-contained):负载中包含了用户所需要的信息,避免了多次查询数据库
- 因为Token是以JSON加密的形式保存在客户端,所以JWT是跨语言的,原则上任何web形式都支持
- 不需要在服务端保存会话信息,特别适用于分布式微服务
JWT的结构
Token 是 String ====> header.payload.signature
1、令牌(token)组成
- 标头(header)
- 有效载荷(payload)
- 签名(signature)
2、Header
- 标头通常由两部分组成:
令牌的类型
(即JWT)和所使用的算法
- 它会使用
Base64
编码组成JWT结构的第一部分 - 注意:Base64是一种编码,也就是说,他是可以翻译回原来的数据,它并不是一种加密过程
"alg": "HS256",
"typ": "JWT"
3、payload
- 令牌的第二部分是
有效负载
,其中包含声明,生命是有关实体
(通常是用户和其他数据的声明) - 它使用
Base64
编码组成了JWT结构的第二部分 - 注意:不要存放敏感的信息(eg.password)
"id": "0001",
"usename": "jack",
"admin":"true"
4、signature
- 前面的两部分都是Base64进行编码的,即前端可以解开知道里面的信息
- signature需要使用编码后的Header和payload以及自己
提供的一个密钥(即盐值)
,使用header中指定的算法(HS256)进行签名 - 签名的作用
保证了JWT没有被篡改过
eg.
HMASH256(base64urlEncode(header) + "." + base64urlEncode(payload),secret)
即验证: header + payload + 盐值 == signature的数据
JWT中常见的异常信息
AlgorithmMismatchException
算法不匹配异常(即使用的算法不一致)SignatureVerificationException
签名不一致异常(即使用的签名不一致)InvalidClaimException
失效的payload异常(即数据被篡改)TokenExpiredException
令牌过期异常
Java使用JWT实战
- 导入JWT依赖
<!-- https://mvnrepository.com/artifact/com.auth0/java-jwt -->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>4.0.0</version>
</dependency>
- 编写JWT工具类
package com.example.jwt.utils;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import java.util.Calendar;
import java.util.Map;
/**
* @author 谢阳
* @version 1.8.0_131
* @date 2022/7/22 14:34
* @description JWT token 工具类
* 包含token的生成、验证、获取数据等...
*/
public class JWTUtils
// 私有签名 (盐)
private static final String SECRET = "token!xie@yang";
// 默认token存活时间
private static final Integer DEFAULT_DAY = 7;
/**
* 获取默认时间的token数据
*
* @param map 数据,请勿传入用户敏感信息
* @return token令牌
*/
public static String getToken(Map<String, String> map)
return getToken(map, DEFAULT_DAY, SECRET);
/**
* 获取指定时间的token数据
*
* @param map 数据,请勿传入用户敏感信息
* @param day 指定 token 存活时间 (天)
* @return token令牌
*/
public static String getToken(Map<String, String> map, Integer day)
return getToken(map, day, SECRET);
/**
* 获取指定签名的 token
*
* @param map 数据,请勿传入用户敏感信息
* @param secret 签名
* @return token令牌
*/
public static String getToken(Map<String, String> map, String secret)
return getToken(map, DEFAULT_DAY, secret);
/**
* 自定义token令牌
*
* @param map 数据
* @param day token 存活时间 (天)
* @param secret 签名
* @return token令牌
*/
public static String getToken(Map<String, String> map, Integer day, String secret)
JWTCreator.Builder builder = JWT.create();
map.forEach((key, value) ->
builder.withClaim(key, value); // 保存数据
);
Calendar instance = Calendar.getInstance();
instance.add(Calendar.DATE, day);
return builder.withExpiresAt(instance.getTime()) // 设置token过期时间
.sign(Algorithm.HMAC256(secret)); // 设置签名
/**
* 验证默认签名的 token
*
* @param token 令牌
*/
public static void verify(String token)
verify(token, SECRET);
/**
* 验证指定签名的token
*
* @param token 令牌
* @param secret 自定义签名
*/
public static void verify(String token, String secret)
JWT.require(Algorithm.HMAC256(secret)).build().verify(token);
/**
* 获取指定签名 DecodedJWT 数据
*
* @param token 令牌
* @param secret 签名
* @return DecodedJWT
*/
public static DecodedJWT getDecodedJWT(String token, String secret)
return JWT.require(Algorithm.HMAC256(secret)).build().verify(token);
/**
* 获取默认签名 DecodedJWT 数据
*
* @param token 令牌
* @return DecodedJWT
*/
public static DecodedJWT getDecodedJWT(String token)
return getDecodedJWT(token, SECRET);
/**
* 获取默认签名的单个数据
*
* @param token 令牌
* @param claim 声明名
* @param slazz 声明类型
* @param <T> 方法类型
* @return 数据<T>
*/
public static <T> T getClaim(String token, String claim, Class<T> slazz)
return getClaim(token, SECRET, claim, slazz);
/**
* 获取指定签名的单个数据
*
* @param token 令牌
* @param secret 签名
* @param claim 声明名
* @param slazz 声明类型
* @param <T> 方法类型
* @return 数据<T>
*/
public static <T> T getClaim(String token, String secret, String claim, Class<T> slazz)
return getDecodedJWT(token, secret).getClaim(claim).as(slazz);
/**
* 获取默认所有数据
*
* @param token 令牌
* @return Map<String, Claim>
*/
public static Map<String, Claim> getClamins(String token)
return getClamins(token, SECRET);
/**
* 过去指定签名所有数据
*
* @param token 令牌
* @param secret 签名
* @return Map<String, Claim>
*/
public static Map<String, Claim> getClamins(String token, String secret)
return getDecodedJWT(token, secret).getClaims();
- 编写拦截器
package com.example.jwt.interceptors;
import com.auth0.jwt.exceptions.AlgorithmMismatchException;
import com.auth0.jwt.exceptions.InvalidClaimException;
import com.auth0.jwt.exceptions.SignatureVerificationException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.example.jwt.utils.JWTUtils;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
/**
* @author 谢阳
* @version 1.8.0_131
* @date 2022/7/22 16:55
* @description 拦截器
*/
public class JwtInterceptor implements HandlerInterceptor
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception
String token = request.getHeader("token");
Map<String, Object> map = new HashMap<>();
try
JWTUtils.verify(token);
return true;
catch (AlgorithmMismatchException | SignatureVerificationException | InvalidClaimException e)
map.put("msg","请勿修改数据");
map.put("state","4001");
catch (TokenExpiredException e)
map.put("msg", "登录已过期");
map.put("state", "4002");
catch (Exception e)
map.put("msg", "请先登录");
map.put("state", "4003");
String json = new ObjectMapper().writeValueAsString(map);
response.setContentType("application/json;charset=UTF-8");
response.getWriter().println(json);
return false;
- 注册拦截器
package com.example.jwt.config;
import com.example.jwt.interceptors.JwtInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
/**
* @author 谢阳
* @version 1.8.0_131
* @date 2022/7/22 17:02
* @description
*/
@Configuration
public class InterceptorConfig extends WebMvcConfigurationSupport
@Override
protected void addInterceptors(InterceptorRegistry registry)
registry.addInterceptor(new JwtInterceptor())
.addPathPatterns("/user/index")
.excludePathPatterns("/user/login");
- 其他相关配置(略.)
测试结果:
- 不登陆访问index数据
- 登录用户,获取token
- 使用token访问index
以上是关于# JWT 图解的主要内容,如果未能解决你的问题,请参考以下文章