Header+Playload+Signature
头部(Header),格式如下:
{
“typ”: “JWT”,
“alg”: “HS256”
}
头部存储认证类型和加密算法,将此json使用Base64编码可得到如下个格式的字符串:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
有效载荷(Playload):
{
"role": [],
"iss": "baidu",
"exp": 1638841050,
"userName": "管理员",
"department": [],
"iat": 1638840690,
"userId": "1",
"account": "admin"
}
有效载荷中存放了token的签发者(iss)、签发时间(iat)、过期时间(exp)等以及一些我们需要写进token中的信息。有效载荷也使用Base64编码得到如下格式的字符串:
eyJyb2xlIjpbXSwiaXNzI
签名(Signature):
将Header和Playload拼接生成一个字符串str=“eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlIjpbXSwiaXNzI”,
使用HS256算法和我们提供的密钥(secret,服务器自己提供的一个字符串)对str进行加密生成最终的JWT,
即我们需要的令牌(token),形如:str.”签名字符串”。
生成token
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);
}
/**
* 获取用户名称
*
* @param token toke
* @return 用户姓名
*/
public static String getUserName(String token) {
try {
return JWT.decode(token).getClaim("userName").asString();
} catch (Exception ex) {
ex.printStackTrace();
}
return "";
}
/**
* 获取用户角色
*
* @param token token
* @return 角色ID集合
*/
public static List<Long> getRole(String token) {
try {
return JWT.decode(token).getClaim("role").asList(Long.class);
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
验证token
3.1 网关验证token
/**
* 签发人
*/
private static final String ISSUER = "baidu";
/**
* 验证token是否合法
*
* @param token
* @return
* JwtSecret为密钥,随机生成
*/
public static boolean verify(String token) {
try {
Algorithm algorithm = Algorithm.HMAC256("JwtSecret");
JWTVerifier verifier = JWT.require(algorithm)
.withIssuer(ISSUER)
.build();
verifier.verify(token);
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"); //不拦截请求路径
}
}
4,更多优秀文章
https://blog.csdn.net/wd521521/article/details/82856203
https://blog.csdn.net/zunguitiancheng/article/details/90417574
https://blog.csdn.net/houmenghu/article/details/99181326
https://zhuanlan.zhihu.com/p/91420328