分享个人操作的JWT的工具类,基于jjwt库,这是Java圈子中最流行的JWT操作库。
文章目录
- 一、应用场景
- 二、实战案例
- 1. jjwt 开源地址
- 2. 加依赖
- 3. 工具类
- 4. 写配置
- 5. 使用
- 三、企业需求
一、应用场景
给用户不敏感信息生成token,作为用户登陆的唯一标识,访问后端都必须携带token,也就是说,后端处理前端请求时会先判断token是否合法,不合法直接响应状态码;合法正常处理请求。
前后端分离项目采用session 和cookie即可,前后端分离场景:建议采用token,因为有了跨域问题。
二、实战案例
1. jjwt 开源地址
GIthub:https://github.com/jwtk/jjwt
2. 加依赖
<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-api</artifactId><version>0.11.1</version></dependency><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-impl</artifactId><version>0.11.1</version><scope>runtime</scope></dependency><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-jackson</artifactId> <!-- or jjwt-gson if Gson is preferred --><version>0.11.1</version><scope>runtime</scope></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.12</version><scope>provided</scope></dependency><!-- https://mvnrepository.com/artifact/commons-codec/commons-codec --><dependency><groupId>commons-codec</groupId><artifactId>commons-codec</artifactId><version>1.14</version></dependency><dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>20.0</version></dependency>
3. 工具类
package com.itmuch.usercenter.util;import com.google.common.collect.Maps;
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;import javax.crypto.SecretKey;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;@Slf4j
@RequiredArgsConstructor
@SuppressWarnings("WeakerAccess")
@Component
public class JwtOperator {/*** 秘钥* - 默认aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrsssttt*/@Value("${secret:aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrsssttt}")private String secret;/*** 有效期,单位秒* - 默认2周*/@Value("${expire-time-in-second:1209600}")private Long expirationTimeInSecond;/*** 从token中获取claim** @param token token* @return claim*/public Claims getClaimsFromToken(String token) {try {return Jwts.parser().setSigningKey(this.secret.getBytes()).parseClaimsJws(token).getBody();} catch (ExpiredJwtException | UnsupportedJwtException | MalformedJwtException | IllegalArgumentException e) {log.error("token解析错误", e);throw new IllegalArgumentException("Token invalided.");}}/*** 获取token的过期时间** @param token token* @return 过期时间*/public Date getExpirationDateFromToken(String token) {return getClaimsFromToken(token).getExpiration();}/*** 判断token是否过期** @param token token* @return 已过期返回true,未过期返回false*/private Boolean isTokenExpired(String token) {Date expiration = getExpirationDateFromToken(token);return expiration.before(new Date());}/*** 计算token的过期时间** @return 过期时间*/public Date getExpirationTime() {return new Date(System.currentTimeMillis() + this.expirationTimeInSecond * 1000);}/*** 为指定用户生成token** @param claims 用户信息* @return token*/public String generateToken(Map<String, Object> claims) {Date createdTime = new Date();Date expirationTime = this.getExpirationTime();byte[] keyBytes = secret.getBytes();SecretKey key = Keys.hmacShaKeyFor(keyBytes);return Jwts.builder().setClaims(claims).setIssuedAt(createdTime).setExpiration(expirationTime)// 你也可以改用你喜欢的算法// 支持的算法详见:https://github.com/jwtk/jjwt#features.signWith(key, SignatureAlgorithm.HS256).compact();}/*** 判断token是否非法** @param token token* @return 未过期返回true,否则返回false*/public Boolean validateToken(String token) {return !isTokenExpired(token);}// public static void main(String[] args) {
// // 1. 初始化
// JwtOperator jwtOperator = new JwtOperator();
// jwtOperator.expirationTimeInSecond = 1209600L;
// jwtOperator.secret = "aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrsssttt";
//
// // 2.设置用户信息
// HashMap<String, Object> objectObjectHashMap = Maps.newHashMap();
// objectObjectHashMap.put("id", "1");
//
// // 测试1: 生成token
// String token = jwtOperator.generateToken(objectObjectHashMap);
// // 会生成类似该字符串的内容: eyJhbGciOiJIUzI1NiJ9.eyJpZCI6IjEiLCJpYXQiOjE1NjU1ODk4MTcsImV4cCI6MTU2Njc5OTQxN30.27_QgdtTg4SUgxidW6ALHFsZPgMtjCQ4ZYTRmZroKCQ
// System.out.println(token);
//
// // 将我改成上面生成的token!!!
// String someToken = "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6IjEiLCJpYXQiOjE1NjU1OTQ1NjIsImV4cCI6MTU2NjgwNDE2Mn0.PAvWPcQAZnSlYKNbZr4O1l9aA4LPphuq0OG2QIs7O5E\n";
// // 测试2: 如果能token合法且未过期,返回true
// Boolean validateToken = jwtOperator.validateToken(someToken);
// System.out.println(validateToken);
//
// // 测试3: 获取用户信息
// Claims claims = jwtOperator.getClaimsFromToken(someToken);
// System.out.println(claims);
//
// // 将我改成你生成的token的第一段(以.为边界)
// String encodedHeader = "eyJhbGciOiJIUzI1NiJ9";
// // 测试4: 解密Header
// byte[] header = Base64.decodeBase64(encodedHeader.getBytes());
// System.out.println(new String(header));
//
// // 将我改成你生成的token的第二段(以.为边界)
// String encodedPayload = "eyJpZCI6IjEiLCJpYXQiOjE1NjU1ODk1NDEsImV4cCI6MTU2Njc5OTE0MX0";
// // 测试5: 解密Payload
// byte[] payload = Base64.decodeBase64(encodedPayload.getBytes());
// System.out.println(new String(payload));
//
// // 测试6: 这是一个被篡改的token,因此会报异常,说明JWT是安全的
// jwtOperator.validateToken("eyJhbGciOiJIUzI1NiJ9.eyJpZCI6IjEiLCJpYXQiOjE1NjU1ODk3MzIsImV4cCI6MTU2Njc5OTMzMn0.nDv25ex7XuTlmXgNzGX46LqMZItVFyNHQpmL9UQf-aUxxx");
// }
}
4. 写配置
jwt:secret: aaaaaaabbbbbbcccccdddddaaaaaaabbbbbbcccccdddddaaaaaaabbbbbbcccccddddd# 有效期,单位秒,默认2周expire-time-in-second: 1209600
5. 使用
@Autowiredprivate final JwtOperator jwtOperator;
三、企业需求
1.首先写一个拦截器,拦截前端的所有请求
2.获取请求头部header中的token是否合法
1>合法正常处理请求
2>不合法抛出自定义异常和状态码
问题1:
为什么是自定义异常?
释义:
由于不能讲控制台的异常直接抛出到前端页面,这样不友好,定义全局异常处理类,根据需求自定义异常信息和状态码。
例如:token不合法,状态码是401
private void checkToken() {try {// 1. 从header里面获取tokenHttpServletRequest request = getHttpServletRequest();String token = request.getHeader("X-Token");// 2. 校验token是否合法&是否过期;如果不合法或已过期直接抛异常;如果合法放行Boolean isValid = jwtOperator.validateToken(token);if (!isValid) {throw new SecurityException("Token不合法!");}// 3. 如果校验成功,那么就将用户的信息设置到request的attribute里面Claims claims = jwtOperator.getClaimsFromToken(token);request.setAttribute("id", claims.get("id"));request.setAttribute("wxNickname", claims.get("wxNickname"));request.setAttribute("role", claims.get("role"));} catch (Throwable throwable) {throw new SecurityException("Token不合法");}}