上述准备工作以及完成
6:pojo 实体类
@Data public class Users { private String id; private String username; private String password; }
7: annotation 自定义注解
/** * @author xia * @version 1.0 * @Data 用来跳过验证的 PassToken * @date 2023/2/13 16:16 */ @Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface PassToken { boolean required() default true; }
/** * @author xia * @version 1.0 * @DATA 用于登录后才能操作的token * @date 2023/2/13 16:16 */ @Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface UserLoginToken { boolean required() default true; }
8:拦截器
@Configuration public class InterceptorConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(jwtInterceptor()) .excludePathPatterns("/user/**") .addPathPatterns("/**"); } @Bean public JWTInterceptor jwtInterceptor() { return new JWTInterceptor(); } }
@Component @SuppressWarnings("all") public class JWTInterceptor implements HandlerInterceptor { @Autowired User user; // 请求前到达之前拦截 @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object object) throws Exception { // 从请求头获取 token String token = request.getHeader("token"); //首先映射是不是方法,如果不是则返回 if(!(object instanceof HandlerMethod)){ return true; } HandlerMethod handlerMethod=(HandlerMethod) object; Method method=handlerMethod.getMethod(); //检查是否有passtoken注释,有则跳过认证 if (method.isAnnotationPresent(PassToken.class)) { PassToken passToken = method.getAnnotation(PassToken.class); if (passToken.required()) { return true; } } // token 验证失败后的返回信息 if (method.isAnnotationPresent(UserLoginToken.class)) { UserLoginToken userLoginToken = method.getAnnotation(UserLoginToken.class); if (userLoginToken.required()) { if (StrUtil.isBlank(token)) { throw new RuntimeException( "无token,请重新登录"); } // 获取 token 中的 user id int userId; try { DecodedJWT decode = JWT.decode(token); String id = decode.getClaim("id").asString(); userId = Integer.parseInt(id); } catch (JWTDecodeException j) { throw new RuntimeException("401"); } System.out.println(userId); Users users = user.findUserById(userId); if (users == null) { throw new RuntimeException("用户不存在,请重新登录"); } try { // JWT 工具包验证 token JWTUtils.verify(token); return true; } catch (TokenExpiredException e) { throw new RuntimeException("Token已经过期!!!"); } catch (SignatureVerificationException e){ throw new RuntimeException("签名错误!!!"); } catch (AlgorithmMismatchException e){ throw new RuntimeException("加密算法不匹配!!!"); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException("无效token~~"); } } } return true; } }
9:controller 层
@RestController @Slf4j @SuppressWarnings("all") public class JwtController { @Resource private UsersMapper mapper; // 认证 @PassToken @PostMapping("/user/login") public Result login(@RequestBody Users user) { System.out.println("前端传来的用户数据:"+user); try { // 查询数据库 QueryWrapper<Users> queryWrapper = new QueryWrapper<>(); HashMap<String, Object> queryMap = new HashMap<>(); queryMap.put("username", user.getUsername()); queryMap.put("password", user.getPassword()); queryWrapper.allEq(queryMap); Users userDb = mapper.selectOne(queryWrapper); // 认证失败 if(userDb == null) { throw new RuntimeException("没有此用户。请重新登录"); } // 认证成功 Map<String, String> tokenClaimsMap = new HashMap<>(); //用来存放payload tokenClaimsMap.put("id",userDb.getId()); tokenClaimsMap.put("username", userDb.getUsername()); String token = JWTUtils.getToken(tokenClaimsMap); // 返回的数据 return new Result(ResultCode.SUCCESS,token); } catch (Exception e) { // 认证失败,返回的数据 e.printStackTrace(); return new Result(ResultCode.ERROR,e); } } @UserLoginToken @GetMapping("/private/info") public String info(HttpServletRequest request){ String token = request.getHeader("token"); DecodedJWT tokenJWT = JWTUtils.getToken(token); System.out.println(tokenJWT.getClaim("username").asString()); System.out.println(tokenJWT.getClaim("userId").asInt()); return "这是一段私人信息。只有登录才能显示"; } @UserLoginToken @GetMapping("/private/Test") public Result info(){ String code = "这是一段私人信息。只有登录才能显示"; return new Result(ResultCode.SUCCESS,code); } @PassToken() @PostMapping("/private/Test2") public Result info2(){ String code = "这是一段普通信息。不登录也能显示"; return new Result(ResultCode.SUCCESS,code); } }
10: service 以及实现类
@Service public interface User { Users findUserById(int userId); } @Service public class UserService implements User{ @Resource private UsersMapper mapper; @Override public Users findUserById(int userId) { Users users = mapper.selectById(userId); System.out.println("------------>"+ users); return users; } }
11: mapper层
@Mapper public interface UsersMapper extends BaseMapper<Users> { }
启动项目,演示效果如下:
1:账号登录成功生成Token (登录请求,使用@PassToken() 注解 跳过token验证)
@PassToken @PostMapping("/user/login") public Result login(@RequestBody Users user) { System.out.println("前端传来的用户数据:"+user); try { // 查询数据库 QueryWrapper<Users> queryWrapper = new QueryWrapper<>(); HashMap<String, Object> queryMap = new HashMap<>(); queryMap.put("username", user.getUsername()); queryMap.put("password", user.getPassword()); queryWrapper.allEq(queryMap); Users userDb = mapper.selectOne(queryWrapper); // 认证失败 if(userDb == null) { throw new RuntimeException("没有此用户。请重新登录"); } // 认证成功 Map<String, String> tokenClaimsMap = new HashMap<>(); //用来存放payload tokenClaimsMap.put("id",userDb.getId()); tokenClaimsMap.put("username", userDb.getUsername()); String token = JWTUtils.getToken(tokenClaimsMap); // 返回的数据 return new Result(ResultCode.SUCCESS,token); } catch (Exception e) { // 认证失败,返回的数据 e.printStackTrace(); return new Result(ResultCode.ERROR,e); }
2:使用这个注解 :@UserLoginToken(登录需要验证)
@UserLoginToken @GetMapping("/private/info") public String info(HttpServletRequest request){ String token = request.getHeader("token"); DecodedJWT tokenJWT = JWTUtils.getToken(token); System.out.println(tokenJWT.getClaim("username").asString()); System.out.println(tokenJWT.getClaim("userId").asInt()); return "这是一段私人信息。只有登录才能显示"; } @UserLoginToken @GetMapping("/private/Test") public Result info(){ String code = "这是一段私人信息。只有登录才能显示"; return new Result(ResultCode.SUCCESS,code); }
不携带Token 打印结果:
Token 错误:
3: 使用@PassToken() 放行请求 ,即不需要Token 也可登录
@PassToken() @PostMapping("/private/Test2") public Result info2(){ String code = "这是一段普通信息。不登录也能显示"; return new Result(ResultCode.SUCCESS,code); }
源码地址如下: