JWT详解
下文笔者讲述JWT的相关简介说明,如下所示

测试--未登录

登录-获取token

再次访问index页面,带上token
JWT简介
JWT(Json Web Token) 是使用json的方式 作为web应用中的令牌 用于在各方之间安全地将信息作为json对象创数 在数据的传输过程中还可以完成数据的加密、签名等相关处理 JWT特点: 1.跨语言:支持主流语言 2.自包含:包含必要的所有信息,如用户信息和签名等 3.易传递:很方便通过HTTP头部传递
以前的Session认证
Session 原始认证方式
原始认证方式的缺点
1.用户量越多,服务器的开销越大 2.cookie本身不安全 3.在集群的情况下 用户必须在同一服务器或通过session共享机制实现认证 4.前后端分离增加了复杂性 如:代理、路由网关
JWT认证模式简介
JWT认证流程
JWT认证流程图
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优点
1.简单(Compact):可使用URL,POST参数或者在HTTP header中发送,数据量小,传输速度快 2.自包含(Self-contained):负载中包含用户所需要的信息,减少多次查询数据库-可减少数据库的压力 3.使用Token使用JSON加密的形式保存在客户端,即JWT与语言无关,所有web都支持 4.服务器端无需存储会话信息,所以JWT非常适用于分布式及微服务
JWT结构
JWT是一个Token 它通常由 标头(header)、有效载荷(payload)、签名(signature)三部分组成 2.Header 标头通常由两部分组成: 令牌的类型(即JWT)和所使用的算法 它会使用Base64编码组成JWT结构的第一部分 注意事项: Base64是一种编码 { "alg": "HS256", "typ": "JWT" } 3.payload 令牌的第二部分是有效负载 其中包含声明 生命是有关实体(通常是用户和其他数据的声明) 它使用Base64编码组成了JWT结构的第二部分 注意事项: 不要存放敏感的信息(如:password) { "id": "0008", "usename": "maomao", "admin":"true" } 4.signature 前面的两部分都是Base64进行编码 即前端可以解开知道里面的信息 signature需要使用编码后的Header和payload以及自己提供的一个密钥(即盐值) 使用header中指定的算法(HS256)进行签名 注意事项: 签名的功能用于确保JWT没被篡改过 如: 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.java265.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; /** * @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.java265.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.java265.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; /** * @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.java265.jwt.config; import com.java265.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; @Configuration public class InterceptorConfig extends WebMvcConfigurationSupport { @Override protected void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new JwtInterceptor()) .addPathPatterns("/user/index") .excludePathPatterns("/user/login"); } }

测试--未登录

登录-获取token

再次访问index页面,带上token

版权声明
本文仅代表作者观点,不代表本站立场。
本文系作者授权发表,未经许可,不得转载。