1. Jwt 工具类
引入依赖
<dependency><groupId>com.auth0</groupId><artifactId>java-jwt</artifactId><version>4.4.0</version>
</dependency>
JwtUtil 类包含了两个静态方法:generateToken() 用于生成 JWT,validateToken() 用于验证 JWT
public class JwtUtil {//密钥private static final String SECRET_KEY = "xxxxxxxxx";// 过期时间60分钟private static final long EXPIRE_TIME = 60 * 60 * 1000;/*** 生成签名** @param claims* @return*/public static String generateToken(Map<String, Object> claims) {var jwtBuilder = JWT.create();// 设置传入的字典中的声明for (Map.Entry<String, Object> entry : claims.entrySet()) {jwtBuilder.withClaim(entry.getKey(), entry.getValue().toString());}//过期时间Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);Algorithm algorithm = Algorithm.HMAC256(SECRET_KEY);return jwtBuilder.withExpiresAt(date).sign(algorithm);}/*** 校验token是否正确** @param token* @return*/public static boolean validateToken(String token) {try {if (token.startsWith("Bearer ")) {token = token.replace("Bearer ", "");return false;}Algorithm algorithm = Algorithm.HMAC256(SECRET_KEY);DecodedJWT jwt = JWT.require(algorithm).build().verify(token);return true;} catch (Exception exception) {return false;}}/*** 获得token中的用户信息(无需secret解密也能获得)** @param token* @return*/public static String getUsername(String token) {try {if (token.startsWith("Bearer ")) {token = token.replace("Bearer ", "");}DecodedJWT jwt = JWT.decode(token);return jwt.getClaim("userName").asString();} catch (JWTDecodeException e) {return null;}}
}
2. 匿名访问注解
自定义匿名访问注解,接口添加该注解,则跳过Jwt权限验证。
/*** 自定义注解* 允许匿名访问*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AllowAnon {boolean required() default true;
}
3. Jwt验证拦截器
public class JwtInterceptor implements HandlerInterceptor {/*** 在请求处理之前进行调用(Controller方法调用之前)** @param request* @param response* @param handler* @return* @throws IOException*/@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {System.out.println("Jwt Interceptor preHandle");HandlerMethod handlerMethod = (HandlerMethod) handler;Method method = handlerMethod.getMethod();//检查有没有允许匿名访问的注解if (method.isAnnotationPresent(AllowAnon.class)) {AllowAnon allowAnon = method.getAnnotation(AllowAnon.class);if (allowAnon.required()) {//允许匿名访问(无需权限)return true;}}String authorizationHeader = request.getHeader("Authorization");if (authorizationHeader == null || !authorizationHeader.startsWith("Bearer ")) {ResponseErrorMessage(response);return false;}String token = authorizationHeader.substring(7);try {//验证tokenif (JwtUtil.validateToken(token)) {//验证成功return true;} else {//验证失败ResponseErrorMessage(response);return false;}} catch (Exception e) {ResponseErrorMessage(response);return false;}}/*** 请求处理之后进行调用,但是在视图被渲染之前(Controller方法调用之后)* preHandle方法处理之后这个方法会被调用,如果控制器Controller出现了异常,则不会执行此方法** @param request* @param response* @param handler* @param modelAndView* @throws Exception*/@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {System.out.println("Jwt Interceptor postHandle");}/*** 不管有没有异常,这个afterCompletion都会被调用** @param request* @param response* @param handler* @param ex* @throws Exception*/@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {System.out.println("Jwt Interceptor afterCompletion");}/*** 响应错误消息** @param response* @throws IOException*/private void ResponseErrorMessage(HttpServletResponse response) throws IOException {response.setStatus(javax.servlet.http.HttpServletResponse.SC_UNAUTHORIZED);response.setContentType("application/json; charset=UTF-8");response.getWriter().write("{\"success\":false,\"message\":\"授权失败\",\"data\":null}");}
}
4. 注册拦截器
//注册拦截器配置
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {/*** 重写addInterceptors()实现拦截器* 配置:要拦截的路径以及不拦截的路径** @param registry*/@Overridepublic void addInterceptors(InterceptorRegistry registry) {System.out.println("InterceptorConfig addInterceptors");registry.addInterceptor(jwtInterceptor()) //配置拦截规则.addPathPatterns("/api/**") // 拦截所有请求,通过判断token是否合法来决定是否需要登录.excludePathPatterns("/api/user/register" //注册);//允许匿名访问放行的请求}@Beanpublic JwtInterceptor jwtInterceptor() {return new JwtInterceptor();}
}
5. 控制器
@RestController
@RequestMapping("/api/user")
public class UserController {/*** 注册(无需jwt验证,InterceptorConfig定义排除JwtInterceptor验证路由)** @return*/@PostMapping("register")public String register() {return "注册成功";}/*** 登录(无需jwt验证,添加AllowAnon允许匿名访问注解)** @return*/@AllowAnon@PostMapping("login")public String login() {Map<String, Object> claims = new HashMap<>();claims.put("userId", user.getId());claims.put("userName", user.getUserName());//生成tokenvar token = JwtUtil.generateToken(claims);return token;}/*** 详情(需要验证)** @return*/@GetMapping("detail")public void getUserDetail(@RequestHeader("Authorization") String token) {//获取当前用户名var username = JwtUtil.getUsername(token);}
}