1. 实现跳过认证(PassToken)及需要认证(UserLoginToken)的注解类
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface PassToken {
boolean required() default true;
}
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface UserLoginToken {
boolean required() default true;
}
2. 实现获取Token类(TokenService)
@Service
public class TokenService {
public String getToken(User user) {
// 过期时间,5分钟
long EXPIRE_TIME = 5*60*1000;
// 生成过期时间
Date expireDate = new Date(System.currentTimeMillis()+EXPIRE_TIME);
String token = JWT.create().withAudience(user.getId())// 将 user id 保存到 token 里面
.withExpiresAt(expireDate)// 设置过期时间
.sign(Algorithm.HMAC256(user.getPassword()));// 以 password 作为 token 的密钥
return token;
}
}
3. 实现token认证类(AuthenticationInterceptor)
public class AuthenticationInterceptor implements HandlerInterceptor {
@Autowired
UserService userService;
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws Exception {
String token = httpServletRequest.getHeader("token");// 从 http 请求头中取出 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;
}
}
//检查有没有需要用户权限的注解
if (method.isAnnotationPresent(UserLoginToken.class)) {
UserLoginToken userLoginToken = method.getAnnotation(UserLoginToken.class);
if (userLoginToken.required()) {
// 执行认证
if (token == null) {
throw new RuntimeException("尚未登录,请登录");
}
// 获取 token 中的 user id
String userId;
try {
userId = JWT.decode(token).getAudience().get(0);
} catch (JWTDecodeException j) {
throw new RuntimeException("token认证名称错误,请重新登录");
}
User user = userService.findUserById(userId);
if (user == null) {
throw new RuntimeException("用户不存在,请重新登录");
}
// 验证 token
JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(user.getPassword())).build();
try {
jwtVerifier.verify(token);
} catch (JWTVerificationException e) {
throw new RuntimeException("token认证密码错误或登录已过期,请重新登录");
}
return true;
}
}
return true;
}
@Override
public void postHandle(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse,
Object o, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse,
Object o, Exception e) throws Exception {
}
}
4. 因为需要抛出异常,实现一个全局异常捕获类()
@ControllerAdvice
public class GloablExceptionHandler {
@ResponseBody
@ExceptionHandler(Exception.class)
public Object handleException(Exception e) {
String msg = e.getMessage();
if (msg == null || msg.equals("")) {
msg = "服务器出错";
}
JSONObject jsonObject = new JSONObject();
jsonObject.put("status", 500);
jsonObject.put("message", msg);
return jsonObject;
}
}
5. 添加配置类(InterceptorConfig),让所有的映射路径都进行验证
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(authenticationInterceptor())
.addPathPatterns("/**"); // 拦截所有请求,通过判断是否有 @UserLoginToken 注解 决定是否需要登录
}
@Bean
public AuthenticationInterceptor authenticationInterceptor() {
return new AuthenticationInterceptor();
}
}
6. 实现访问类(UserController)
@RestController
@RequestMapping("/api")
public class UserController {
@Autowired
UserService userService;
@Autowired
TokenService tokenService;
//登录
@PostMapping("/login")
public Object login(@RequestBody User user){
JSONObject jsonObject=new JSONObject();
User userForBase=userService.findUserById("user");
if(userForBase==null){
jsonObject.put("message","登录失败,用户不存在");
return jsonObject;
}else {
if (!userForBase.getPassword().equals(user.getPassword())){
jsonObject.put("message","登录失败,密码错误");
return jsonObject;
}else {
String token = tokenService.getToken(userForBase);
jsonObject.put("token", token);
jsonObject.put("user", userForBase);
return jsonObject;
}
}
}
@UserLoginToken
@GetMapping("/getMessage")
public Object getMessage(){
Map<String, Object> retMap = new HashMap<>();
retMap.put("message", "你已通过验证");
retMap.put("status", 200);
return retMap;
}
}
7. 进行postman访问
* http://127.0.0.1:8080/api/login
* http://127.0.0.1:8080/api/getMessage
没登录之前访问效果
登录成功
登录成功带token访问
token错误或登录时间过期后访问
github项目源码地址
https://github.com/kenyonlover/spring_boot_jwt_test