JWT官网: https://jwt.io/
JWT(Java版)的github地址:https://github.com/jwtk/jjwt
什么是JWT
JWT 介绍参考:【JWT】JSON Web Token
Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON
的开放标准((RFC 7519).定义了一种简洁的,自包含的方法用于通信双方之间以JSON
对象的形式安全的传递信息。因为数字签名的存在,这些信息是可信的,JWT可以使用HMAC
算法或者是RSA
的公私秘钥对进行签名。
JWT请求流程
-
1. 用户使用账号和面发出post请求;
-
2. 服务器使用私钥创建一个jwt;
-
3. 服务器返回这个jwt给浏览器;
-
4. 浏览器将该jwt串在请求头中像服务器发送请求;
-
5. 服务器验证该jwt;
-
6. 返回响应的资源给浏览器。
JWT的主要应用场景
身份认证在这种场景下,一旦用户完成了登陆,在接下来的每个请求中包含JWT,可以用来验证用户身份以及对路由,服务和资源的访问权限进行验证。由于它的开销非常小,可以轻松的在不同域名的系统中传递,所有目前在单点登录(SSO)中比较广泛的使用了该技术。 信息交换在通信的双方之间使用JWT对数据进行编码是一种非常安全的方式,由于它的信息是经过签名的,可以确保发送者发送的信息是没有经过伪造的。
优点
-
1.简洁(Compact): 可以通过
URL
,POST
参数或者在HTTP header
发送,因为数据量小,传输速度也很快
-
2.自包含(Self-contained):负载中包含了所有用户所需要的信息,避免了多次查询数据库
-
3.因为
Token
是以JSON
加密的形式保存在客户端的,所以JWT
是跨语言的,原则上任何web形式都支持。 -
4.不需要在服务端保存会话信息,特别适用于分布式微服务。
JWT的集成使用
JWT
依赖,由于是基于Java
,所以需要的是java-jwt
<dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>3.8.3</version> </dependency>
自定义两个注解
用来跳过验证的PassToken
1 @Target({ElementType.METHOD, ElementType.TYPE}) 2 @Retention(RetentionPolicy.RUNTIME) 3 public @interface PassToken { 4 boolean required() default true; 5 }
需要登录才能进行操作的注解UserLoginToken
1 @Target({ElementType.METHOD, ElementType.TYPE}) 2 @Retention(RetentionPolicy.RUNTIME) 3 public @interface UserLoginToken { 4 boolean required() default true; 5 }
简单自定义一个实体类User
1 package com.test.springboot.jwt.entity; 2 3 public class User { 4 String Id; 5 String username; 6 String password; 7 8 public User() { 9 } 10 11 public User(String id, String username, String password) { 12 Id = id; 13 this.username = username; 14 this.password = password; 15 } 16 17 public String getId() { 18 return Id; 19 } 20 21 public void setId(String id) { 22 Id = id; 23 } 24 25 public String getUsername() { 26 return username; 27 } 28 29 public void setUsername(String username) { 30 this.username = username; 31 } 32 33 public String getPassword() { 34 return password; 35 } 36 37 public void setPassword(String password) { 38 this.password = password; 39 } 40 41 @Override 42 public String toString() { 43 return "User{" + 44 "Id=‘" + Id + ‘\‘‘ + 45 ", username=‘" + username + ‘\‘‘ + 46 ", password=‘" + password + ‘\‘‘ + 47 ‘}‘; 48 } 49 }
需要写token
的生成方法
1 /** 2 * token的生成方法 3 */ 4 public String getToken(User user) { 5 String token=""; 6 // 创建JWT 7 token= JWT.create() 8 // 设置听众信息,也可以这是其他信息,如:withSubject主题 9 .withAudience(user.getId()) 10 // 签名 11 .sign(Algorithm.HMAC256(user.getPassword())); 12 return token; 13 }
Algorithm.HMAC256()
:使用HS256
生成token
,密钥则是用户的密码,唯一密钥的话可以保存在服务端。withAudience()
存入需要保存在token
的信息,这里我把用户ID
存入token
中
拦截器去获取token
并验证token
1 package com.test.springboot.jwt.interceptor; 2 3 import com.auth0.jwt.JWT; 4 import com.auth0.jwt.JWTVerifier; 5 import com.auth0.jwt.algorithms.Algorithm; 6 import com.auth0.jwt.exceptions.JWTDecodeException; 7 import com.auth0.jwt.exceptions.JWTVerificationException; 8 import com.test.springboot.jwt.annotation.PassToken; 9 import com.test.springboot.jwt.annotation.UserLoginToken; 10 import com.test.springboot.jwt.entity.User; 11 import com.test.springboot.jwt.service.UserService; 12 import org.springframework.beans.factory.annotation.Autowired; 13 import org.springframework.web.method.HandlerMethod; 14 import org.springframework.web.servlet.HandlerInterceptor; 15 import org.springframework.web.servlet.ModelAndView; 16 17 import javax.servlet.http.HttpServletRequest; 18 import javax.servlet.http.HttpServletResponse; 19 import java.lang.reflect.Method; 20 21 public class AuthenticationInterceptor implements HandlerInterceptor { 22 23 @Autowired 24 UserService userService; 25 26 @Override 27 public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws Exception { 28 String token = httpServletRequest.getHeader("token");// 从 http 请求头中取出 token 29 // 如果不是映射到方法直接通过 30 if (!(object instanceof HandlerMethod)) { 31 return true; 32 } 33 HandlerMethod handlerMethod = (HandlerMethod) object; 34 Method method = handlerMethod.getMethod(); 35 //检查是否有passtoken注释,有则跳过认证 36 if (method.isAnnotationPresent(PassToken.class)) { 37 PassToken passToken = method.getAnnotation(PassToken.class); 38 if (passToken.required()) { 39 return true; 40 } 41 } 42 //检查有没有需要用户权限的注解 43 if (method.isAnnotationPresent(UserLoginToken.class)) { 44 UserLoginToken userLoginToken = method.getAnnotation(UserLoginToken.class); 45 if (userLoginToken.required()) { 46 // 执行认证 47 if (token == null) { 48 throw new RuntimeException("无token,请重新登录"); 49 } 50 // 获取 token 中的 user id 51 String userId; 52 try { 53 // 从jwt中获取信息 54 userId = JWT.decode(token).getAudience().get(0); 55 } catch (JWTDecodeException j) { 56 throw new RuntimeException("401"); 57 } 58 User user = userService.findUserById(userId); 59 if (user == null) { 60 throw new RuntimeException("用户不存在,请重新登录"); 61 } 62 // 验证 token 63 JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(user.getPassword())).build(); 64 try { 65 jwtVerifier.verify(token); 66 } catch (JWTVerificationException e) { 67 throw new RuntimeException("401"); 68 } 69 return true; 70 } 71 } 72 return true; 73 } 74 75 @Override 76 public void postHandle(HttpServletRequest httpServletRequest, 77 HttpServletResponse httpServletResponse, 78 Object o, ModelAndView modelAndView) throws Exception { 79 80 } 81 82 @Override 83 public void afterCompletion(HttpServletRequest httpServletRequest, 84 HttpServletResponse httpServletResponse, 85 Object o, Exception e) throws Exception { 86 } 87 88 }
配置拦截器
package com.test.springboot.jwt.config; import com.test.springboot.jwt.interceptor.AuthenticationInterceptor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration public class InterceptorConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(authenticationInterceptor()) .addPathPatterns("/**"); } @Bean public AuthenticationInterceptor authenticationInterceptor() { return new AuthenticationInterceptor(); } }
Controller
1 @RestController 2 @RequestMapping("/user") 3 public class UserController { 4 5 @Autowired 6 UserService userService; 7 8 // 登录 9 @PostMapping("/login") 10 public Map<String, Object> login(@RequestBody User user) { 11 Map<String, Object> result = new HashMap<>(); 12 13 User user1 = userService.findOne(user.getUsername(), user.getPassword()); 14 if (user1 != null) { 15 String token = getToken(user1); 16 result.put("token", token); 17 result.put("user", user1); 18 return result; 19 } else { 20 result.put("message", "登录失败"); 21 return result; 22 } 23 } 24 25 @UserLoginToken 26 @GetMapping("/getMessage") 27 public Map<String, Object> getMessage() { 28 Map<String, Object> result = new HashMap<>(); 29 result.put("content", "你已通过验证"); 30 return result; 31 } 32 33 @PassToken 34 @GetMapping("/getPublicMessage") 35 public Map<String, Object> getPublicMessage() { 36 Map<String, Object> result = new HashMap<>(); 37 result.put("content", "公共信息"); 38 return result; 39 } 40 41 42 /** 43 * token的生成方法 44 */ 45 public String getToken(User user) { 46 String token=""; 47 // 创建JWT 48 token= JWT.create() 49 // 设置听众信息,也可以这是其他信息,如:withSubject主题 50 .withAudience(user.getId()) 51 // 签名 52 .sign(Algorithm.HMAC256(user.getPassword())); 53 return token; 54 } 55 }
Service
1 package com.test.springboot.jwt.service; 2 3 import com.test.springboot.jwt.entity.User; 4 import org.springframework.stereotype.Service; 5 6 import java.util.HashMap; 7 import java.util.Map; 8 import java.util.concurrent.ConcurrentHashMap; 9 10 @Service 11 public class UserService { 12 13 private static Map<String, User> userMap = new ConcurrentHashMap<>(); 14 15 static { 16 userMap.put("1001", new User("1001", "小白", "123456")); 17 userMap.put("1002", new User("1002", "小黑", "123456")); 18 userMap.put("1003", new User("1003", "小黄", "123456")); 19 } 20 21 public User findOne(String username, String password) { 22 for (Map.Entry<String, User> entry : userMap.entrySet()) { 23 User value = entry.getValue(); 24 if (value.getUsername().equals(username) 25 && value.getPassword().equals(password)) { 26 return value; 27 } 28 } 29 return null; 30 } 31 32 public User findUserById(String userId) { 33 return userMap.get(userId); 34 } 35 }
使用postman测试接口