JSON Web Token(JWT)是一个轻量级的认证规范,这个规范允许我们使用JWT在用户和服务器之间传递安全可靠的信息。其本质是一个token,是一种紧凑的URL安全方法,用于在网络通信的双方之间传递。
1 JWT的组成
JWT头是一个描述JWT元数据的JSON对象,通常如下所示:
1 2 3 4
| { "alg": "HS256", "typ": "JWT" }
|
alg:表示签名使用的算法,默认为HMAC SHA256
typ:表示令牌的类型,JWT令牌统一写为JWT
1.2 载荷(playload)
有效载荷,是JWT的主体内容部分,也是一个JSON对象,包含需要传递的数据。
有效载荷部分规定有如下七个默认字段供选择:
1 2 3 4 5 6 7
| iss:发行人 exp:到期时间 sub:主题 aud:用户 nbf:在此之前不可用 iat:发布时间 jti:JWT ID用于标识该JWT
|
除以上默认字段外,还可以自定义私有字段。一般不建议存放敏感信息,因为base64是对称解密的,意味着该部分信息可以归类为明文信息
1.3 签证(signature)
签名实际上是一个加密的过程,是对上面两部分数据通过指定的算法生成哈希,以确保数据不会被篡改。
1 2 3 4 5
| header (base64后的)
payload (base64后的)
secret
|
在计算出签名哈希后,JWT头,有效载荷和签名哈希的三个部分组合成一个字符串,每个部分用”.”分隔,就构成整个JWT对象。
2 使用JWT
JJWT是一个提供端到端的JWT创建和验证的Java库
Maven:
1 2 3 4 5
| <dependency> <groupld>io.jsonwebtoken</groupld> <artifactld>jjwt</artifactld> <version>0.9.1</version> </dependency>
|
Maven依赖可以到MVNREPOSITORY网站查找:
https://mvnrepository.com/
3 Spring boot 整合 JWT
3.1 pom.xml文件添加依赖Maven JJWT 依赖
见本文 2 使用JWT
3.2 自定义注解
1 2 3 4 5 6 7 8 9 10
|
@Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface ConfirmToken { boolean required() default true; }
|
1 2 3 4 5 6 7 8 9 10 11
|
@Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface PassToken { boolean required() default true; }
|
3.3 JWT 工具类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
|
public class JwtUtil {
private static final Date DATE = new Date(System.currentTimeMillis()+ 3600*1000);
public static String getJwt(User user) { Map<String,Object> claims = new HashMap<>(8); claims.put("userId",user.getUserId()); claims.put("userName",user.getUserName()); return Jwts.builder() .setClaims(claims) .signWith(SignatureAlgorithm.HS256, user.getPassword()) .setExpiration(DATE) .compact(); } }
|
写个main方法测试一下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public static void main(String[] args) { User user = new User(); user.setUserName("张三"); user.setPassword("123456"); user.setUserId("00101"); String jwt = getJwt(user); System.out.println(jwt); Claims body = Jwts.parser() .setSigningKey(user.getPassword()) .parseClaimsJws(jwt) .getBody(); System.out.println(body); }
|
得到结果为;
1 2
| eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyTmFtZSI6IuW8oOS4iSIsImV4cCI6MTY4MDUxNjkyMSwidXNlcklkIjoiMDAxMDEifQ.DsFFFKzkUChFcSDgjLYtRbKvH-svCiffzoT6J2nmM1E {userName=张三, exp=1680516921, userId=00101}
|
使用
https://jwt.io/
解密获取荷载结果:
1 2 3 4 5
| { "userName": "张三", "exp": 1680516921, "userId": "00101" }
|
exp为JWT有效时间,其余两项为自定义荷载
3.4 自定义拦截器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
|
public class AuthenticationInterceptor implements HandlerInterceptor {
private static final String OPTIONS = "OPTIONS";
@Override public boolean preHandle(HttpServletRequest request, @NotNull HttpServletResponse httpServletResponse, @NotNull Object object) { if (OPTIONS.equalsIgnoreCase(request.getMethod())) { return true; } httpServletResponse.setHeader("Access-Control-Allow-Origin", "*"); httpServletResponse.setHeader("Access-Control-Allow-Methods", "*"); httpServletResponse.setHeader("Access-Control-Max-Age", "3600"); httpServletResponse.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Authorization," + " Content-Type, Accept, Connection, User-Agent, Cookie,token"); String token = request.getHeader("Authorization"); if (!(object instanceof HandlerMethod)) { return true; } HandlerMethod handlerMethod = (HandlerMethod) object; Method method = handlerMethod.getMethod(); if (method.isAnnotationPresent(PassToken.class)) { PassToken passToken = method.getAnnotation(PassToken.class); if (passToken.required()) { return true; } } if (method.isAnnotationPresent(ConfirmToken.class)) { ConfirmToken confirmToken = method.getAnnotation(ConfirmToken.class); if (confirmToken.required()) { if (token == null) { return false; } Claims body = Jwts.parser() .setSigningKey(user.getPassword()) .parseClaimsJws(token) .getBody();
if (body.get("userId") != user.getUserId() || body.get("userName") != user.getUserName()) { return false; } System.out.println("JWT验证码完毕"); } } return true; }
@Override public void postHandle(@NotNull HttpServletRequest httpServletRequest, @NotNull HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
}
@Override public void afterCompletion(@NotNull HttpServletRequest httpServletRequest, @NotNull HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
} }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
|
@Configuration public class InterceptorConfig implements WebMvcConfigurer {
@Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(authenticationInterceptor()) .addPathPatterns("/**"); }
@Bean public AuthenticationInterceptor authenticationInterceptor() { return new AuthenticationInterceptor(); } }
|
最后附上本文所写源代码:JWT 的简单使用