应用场景:
登录授权
,它相比原先的session、cookie来说,更快更安全,跨域也不再是问题。错误代码 (keyword+"").trim();
会将空转为字符串“null” 正确代码: return StringUtils.isBlank(keyword)?keyword:keyword.trim();
JWT不是一个具体的技术实现,而更像是一种标准。JWT规定了数据传输的结构,一串完整的JWT由三段落组成,每个段落用英文句号连接(.)连接,他们分别是:Header
、Payload
、Signature
,所以,常规的JWT内容格式是这样的:Header.Payload.Signature。并且这一串内容会进行加密;解码就可以看到实际传输的内容。
{
“typ”: “JWT”,
“alg”: “HS256”
}
Header
、Payload
有没有被人篡改;如果被篡改,那么这条JWT将会被视为无效。内容前面的“Bearer”是固定的,并且还得多加一个空格做分割。
生成jwt:sign(Header+Playload+Signature+expiresAt
)
return create(header, claims, JWT_ISSUER, TOKEN_TIMEOUT);
头部存储认证类型和加密算法
{
“typ”: “JWT”,
“alg”: “HS256”
}
有效载荷中存放了token的签发者(iss)、签发时间(iat)、过期时间(exp)等以及一些我们需要写进token中的信息
{
"iss": "ios逆向",
"exp": 1638841050,
"iat": 1638840690,
"userId": "1",
"account": "admin"
}
将Header和Playload拼接生成一个字符串,使用HS256算法和我们提供的密钥(secret,服务器自己提供的一个字符串)对str进行加密生成最终的JWT,即我们需要的令牌(token)。
生成jwt:sign(Header+Playload+Signature+expiresAt
)
Algorithm algorithm = Algorithm.HMAC256(TOKENKEY);//使用HS256算法加密密钥
Date date = new Date(System.currentTimeMillis() + timeout);
JWTCreator.Builder builder = JWT.create()
.withHeader(header)
.withIssuer(issuer)
.withExpiresAt(date);
for (String key : claims.keySet()) {//Playload
builder.withClaim(key, claims.get(key));
}
token = builder.sign(algorithm);
builder.sign
public String sign(Algorithm algorithm) throws IllegalArgumentException, JWTCreationException {
if (algorithm == null) {
throw new IllegalArgumentException("The Algorithm cannot be null.");
} else {
this.headerClaims.put("alg", algorithm.getName());
if (!this.headerClaims.containsKey("typ")) {
this.headerClaims.put("typ", "JWT");
}
String signingKeyId = algorithm.getSigningKeyId();
if (signingKeyId != null) {
this.withKeyId(signingKeyId);
}
return (new JWTCreator(algorithm, this.headerClaims, this.payloadClaims)).sign();
}
}
完整例子
public static final long TOKEN_EXPIRE_TIME = 6 * 60 * 1000;
/**
* 生成token
*
* @param userId 用户ID
* @param account 登录名
* @param userName 用户名称
* @param role 角色ID集合
* @param department 部门ID集合
* @param jwtSecret 生成jwt的秘钥,由网关传入
* @return token
*/
public static String generateToken(Long userId, String account, String userName, List<Long> role, List<Long> department, String jwtSecret) {
Date now = new Date();
// 加密算法
Algorithm algorithm = Algorithm.HMAC256(jwtSecret);
return JWT.create()
//签发人
.withIssuer(ISSUER)
//签发时间
.withIssuedAt(now)
// .withSubject()
//过期时间
.withExpiresAt(new Date(now.getTime() + TOKEN_EXPIRE_TIME))
.withClaim("userId", userId)
.withClaim("userName", userName)
.withClaim("account", account)
.withClaim("role", role)
.withClaim("department", department)
.sign(algorithm);
}
/**
* 签发人
*/
private static final String ISSUER = "iOS逆向";
/**
* 验证token是否合法
*
* @param token
* @return
* JwtSecret为密钥,随机生成
*/
public static boolean verify(String token) {
try {
// 去掉token前缀
Algorithm algorithm = Algorithm.HMAC256("JwtSecret1");
JWTVerifier verifier = JWT.require(algorithm)
.withIssuer(ISSUER)
.build();
DecodedJWT jwtDecode= verifier.verify(token);
// 存储token信息
Map<String, Claim> claims=jwtDecode.getClaims();
LoginHelper.set(LoginHelper.USER_ID,claims.get(LoginHelper.USER_ID).asString());
return true;
} catch (Exception e) {
log.error("验证token失败 {}", e.getMessage());
}
return false;
}
/**
* 拦截器
*/
@Component
@Slf4j
public class JwtHandler implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws IOException {
Map<String,Object> map=new HashMap<>();
String token = Request.getHeaderParam(request, "token");
try {
Algorithm algorithm = Algorithm.HMAC256("JwtSecret");
JWTVerifier verifier = JWT.require(algorithm)
.withIssuer("baidu")
.build();
verifier.verify(token);
return true;
}catch (Exception e){
e.printStackTrace();
map.put("status",false);
map.put("msg","认证失败");
}
//jackson 将map转换为json
String json=new ObjectMapper().writeValueAsString(map);
response.getWriter().println(json);
return false;
}
}
拦截器注册
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
/**
* 注册拦截器
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new UserHandlerAdapter())
.addPathPatterns("/user/verify") //拦截请求路径
.excludePathPatterns("/user/login"); //不拦截请求路径
}
}