TA的每日心情 | 开心 2021-12-13 21:45 |
---|
签到天数: 15 天 [LV.4]偶尔看看III
|
1.首先了解一下Token
1、token也称作令牌,由uid+time+sign[+固定参数]组成:
uid: 用户唯一身份标识
time: 当前时间的时间戳
sign: 签名, 使用 hash/encrypt 压缩成定长的十六进制字符串,以防止第三方恶意拼接
固定参数(可选): 将一些常用的固定参数加入到 token 中是为了避免重复查数据库
2.token 验证的机制(流程)
用户登录校验,校验成功后就返回Token给客户端。
客户端收到数据后保存在客户端
客户端每次访问API是携带Token到服务器端。
服务器端采用filter过滤器校验。校验成功则返回请求数据,校验失败则返回错误码
3.使用Springboot搭建基于token验证
3.1 引入 POM 依赖
- <dependency>
- <groupId>com.auth0</groupId>
- <artifactId>java-jwt</artifactId>
- <version>3.4.0</version>
- </dependency>
复制代码
3.2 新建一个拦截器配置 用于拦截前端请求 实现 WebMvcConfigurer
- 1 /***
- 2 * 新建Token拦截器
- 3 * @Title: InterceptorConfig.java
- 4 * @author MRC
- 5 * @date 2019年5月27日 下午5:33:28
- 6 * @version V1.0
- 7 */
- 8 @Configuration
- 9 public class InterceptorConfig implements WebMvcConfigurer {
- 10 @Override
- 11 public void addInterceptors(InterceptorRegistry registry) {
- 12 registry.addInterceptor(authenticationInterceptor())
- 13 .addPathPatterns("/**"); // 拦截所有请求,通过判断是否有 @LoginRequired 注解 决定是否需要登录
- 14 }
- 15 @Bean
- 16 public AuthenticationInterceptor authenticationInterceptor() {
- 17 return new AuthenticationInterceptor();// 自己写的拦截器
- 18 }
-
- //省略其他重写方法
- 19
- 20 }
复制代码
3.3 新建一个 AuthenticationInterceptor 实现HandlerInterceptor接口 实现拦截还是放通的逻辑
- 1 public class AuthenticationInterceptor implements HandlerInterceptor {
- 2 @Autowired
- 3 UserService userService;
- 4 @Override
- 5 public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws Exception {
- 6 String token = httpServletRequest.getHeader("token");// 从 http 请求头中取出 token
- 7 // 如果不是映射到方法直接通过
- 8 if(!(object instanceof HandlerMethod)){
- 9 return true;
- 10 }
- 11 HandlerMethod handlerMethod=(HandlerMethod)object;
- 12 Method method=handlerMethod.getMethod();
- 13 //检查是否有passtoken注释,有则跳过认证
- 14 if (method.isAnnotationPresent(PassToken.class)) {
- 15 PassToken passToken = method.getAnnotation(PassToken.class);
- 16 if (passToken.required()) {
- 17 return true;
- 18 }
- 19 }
- 20 //检查有没有需要用户权限的注解
- 21 if (method.isAnnotationPresent(UserLoginToken.class)) {
- 22 UserLoginToken userLoginToken = method.getAnnotation(UserLoginToken.class);
- 23 if (userLoginToken.required()) {
- 24 // 执行认证
- 25 if (token == null) {
- 26 throw new RuntimeException("无token,请重新登录");
- 27 }
- 28 // 获取 token 中的 user id
- 29 String userId;
- 30 try {
- 31 userId = JWT.decode(token).getAudience().get(0);
- 32 } catch (JWTDecodeException j) {
- 33 throw new RuntimeException("401");
- 34 }
- 35 User user = userService.findUserById(userId);
- 36 if (user == null) {
- 37 throw new RuntimeException("用户不存在,请重新登录");
- 38 }
- 39 // 验证 token
- 40 JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(user.getPassword())).build();
- 41 try {
- 42 jwtVerifier.verify(token);
- 43 } catch (JWTVerificationException e) {
- 44 throw new RuntimeException("401");
- 45 }
- 46 return true;
- 47 }
- 48 }
- 49 return true;
- 50 }
- 51
- 52 @Override
- 53 public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
- 54
- 55 }
- 56 @Override
- 57 public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
- 58
- 59 }
- 60 }
复制代码
3.4 新建两个注解 用于标识请求是否需要进行Token 验证
- /***
- * 用来跳过验证的 PassToken
- * @author MRC
- * @date 2019年4月4日 下午7:01:25
- */
- @Target({ElementType.METHOD, ElementType.TYPE})
- @Retention(RetentionPolicy.RUNTIME)
- public @interface PassToken {
- boolean required() default true;
- }
复制代码
- /**
- * 用于登录后才能操作
- * @author MRC
- * @date 2019年4月4日 下午7:02:00
- */
- @Target({ElementType.METHOD, ElementType.TYPE})
- @Retention(RetentionPolicy.RUNTIME)
- public @interface UserLoginToken {
- boolean required() default true;
- }
复制代码
3.5 新建一个Server 用于下发Token
- /***
- * token 下发
- * @Title: TokenService.java
- * @author MRC
- * @date 2019年5月27日 下午5:40:25
- * @version V1.0
- */
- @Service("TokenService")
- public class TokenService {
- public String getToken(User user) {
- Date start = new Date();
- long currentTime = System.currentTimeMillis() + 60* 60 * 1000;//一小时有效时间
- Date end = new Date(currentTime);
- String token = "";
-
- token = JWT.create().withAudience(user.getId()).withIssuedAt(start).withExpiresAt(end)
- .sign(Algorithm.HMAC256(user.getPassword()));
- return token;
- }
- }
复制代码
3.6 新建一个工具类 用户从token中取出用户Id
- 1 /*
- 2 * @author MRC
- 3 * @date 2019年4月5日 下午1:14:53
- 4 * @version 1.0
- 5 */
- 6 public class TokenUtil {
- 7
- 8 public static String getTokenUserId() {
- 9 String token = getRequest().getHeader("token");// 从 http 请求头中取出 token
- 10 String userId = JWT.decode(token).getAudience().get(0);
- 11 return userId;
- 12 }
- 13
- 14 /**
- 15 * 获取request
- 16 *
- 17 * @return
- 18 */
- 19 public static HttpServletRequest getRequest() {
- 20 ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder
- 21 .getRequestAttributes();
- 22 return requestAttributes == null ? null : requestAttributes.getRequest();
- 23 }
- 24 }
复制代码
3.7 新建一个简单的控制器 用于验证
- @RestController
- public class UserApi {
- @Autowired
- UserService userService;
- @Autowired
- TokenService tokenService;
- // 登录
- @GetMapping("/login")
- public Object login(User user, HttpServletResponse response) {
- JSONObject jsonObject = new JSONObject();
- User userForBase = new User();
- userForBase.setId("1");
- userForBase.setPassword("123");
- userForBase.setUsername("mrc");
- if (!userForBase.getPassword().equals(user.getPassword())) {
- jsonObject.put("message", "登录失败,密码错误");
- return jsonObject;
- } else {
- String token = tokenService.getToken(userForBase);
- jsonObject.put("token", token);
- Cookie cookie = new Cookie("token", token);
- cookie.setPath("/");
- response.addCookie(cookie);
- return jsonObject;
- }
- }
- /***
- * 这个请求需要验证token才能访问
- *
- * @author: MRC
- * @date 2019年5月27日 下午5:45:19
- * @return String 返回类型
- */
- @UserLoginToken
- @GetMapping("/getMessage")
- public String getMessage() {
- // 取出token中带的用户id 进行操作
- System.out.println(TokenUtil.getTokenUserId());
- return "你已通过验证";
- }
- }
复制代码
3.8 开始测试
## 成功登陆后保存token到前端cookie 以后的请求带上token即可区别是哪个用户的请求!
我们下一个请求在请求的时候带上这个token试试
成功通过验证! 我们看一下后端控制台打印的结果!
打印出带这个token的用户
DEMO测试版本:https://gitee.com/mrc1999/springbootToken
参考博客:https://www.jianshu.com/p/310d307e44c6
|
|