敞开成长之旅!这是我参加「日新方案 12 月更文应战」的第26天,点击检查活动概况
引言
应用场景:
-
登录授权
,它比较原先的session、cookie来说,更快更安全,跨域也不再是问题。 - 传递数据
I. 准备常识
1.1 关键字去空格处理
错误代码 (keyword+"").trim();
会将空转为字符串“null”
正确代码: return StringUtils.isBlank(keyword)?keyword:keyword.trim();
1.2 JWT认证流程
JWT不是一个详细的技能完成,而更像是一种规范。JWT规则了数据传输的结构,一串完整的JWT由三阶段组成,每个阶段用英文句号连接(.)连接,他们分别是:Header
、Payload
、Signature
,所以,常规的JWT内容格局是这样的:Header.Payload.Signature。而且这一串内容会进行加密;解码就可以看到实践传输的内容。
- Header: 加密的方法、type
{
“typ”: “JWT”,
“alg”: “HS256”
}
-
Payload: 实践传递的参数内容,推荐对payload内容进行加密。
-
Signature: 签名,用于判别
Header
、Payload
有没有被人篡改;假如被篡改,那么这条JWT将会被视为无效。
内容前面的“Bearer”是固定的,而且还得多加一个空格做分割。
II token组成
生成jwt:sign(Header+Playload+Signature+expiresAt
)
return create(header, claims, JWT_ISSUER, TOKEN_TIMEOUT);
2.1 头部(Header)
头部存储认证类型和加密算法
{
“typ”: “JWT”,
“alg”: “HS256”
}
2.2 有效载荷(Playload)
有效载荷中存放了token的签发者(iss)、签发时刻(iat)、过期时刻(exp)等以及一些咱们需求写进token中的信息
{
"iss": "ios逆向",
"exp": 1638841050,
"iat": 1638840690,
"userId": "1",
"account": "admin"
}
2.3 签名(Signature)
将Header和Playload拼接生成一个字符串,运用HS256算法和咱们供给的密钥(secret,服务器自己供给的一个字符串)对str进行加密生成终究的JWT,即咱们需求的令牌(token)。
2.4 代码完成:生成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);
}
III 验证token
3.1 网关验证token
/**
* 签发人
*/
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;
}
3.2 运用阻拦器验证token
/**
* 阻拦器
*/
@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"); //不阻拦恳求路径
}
}