导入java-jwt包

  1. <!--jwt for java 权限认证-->
  2. <!-- https://mvnrepository.com/artifact/com.auth0/java-jwt -->
  3. <dependency>
  4. <groupId>com.auth0</groupId>
  5. <artifactId>java-jwt</artifactId>
  6. <version>3.18.2</version>
  7. </dependency>

编写jwt工具类

  1. package com.example.demo2.springbootmybatis.utils;
  2. import com.auth0.jwt.JWT;
  3. import com.auth0.jwt.JWTVerifier;
  4. import com.auth0.jwt.algorithms.Algorithm;
  5. import com.auth0.jwt.exceptions.JWTDecodeException;
  6. import com.auth0.jwt.interfaces.Claim;
  7. import com.auth0.jwt.interfaces.DecodedJWT;
  8. import com.example.demo2.springbootmybatis.exception.TokenUnavailableException;
  9. import java.util.Calendar;
  10. import java.util.Date;
  11. public class JwtUtils {
  12. /**
  13. * 签发对象:用户的id
  14. * 签发时间:现在
  15. * 有效时间:30分钟
  16. * 载荷内容:用户名和用户编号
  17. * 加密秘钥:id+一段字符串
  18. * */
  19. public static String createToken(Integer userId,String userName,String userNumber){
  20. Calendar nowTime = Calendar.getInstance();
  21. nowTime.add(Calendar.MINUTE,30);
  22. Date expireDate = nowTime.getTime();
  23. /**
  24. * withAudience: 签发对象
  25. * withIssuedAt: 签发时间
  26. * withExpiredAt: 有效时间
  27. * withClaim: 荷载内容
  28. * sign: 签名
  29. * */
  30. return JWT.create().withAudience(userId.toString())
  31. .withIssuedAt(new Date())
  32. .withExpiresAt(expireDate)
  33. .withClaim("userName",userName)
  34. .withClaim("userNumber",userNumber)
  35. .sign(Algorithm.HMAC256(userId + "HelloWorld"));
  36. }
  37. /**
  38. * 校验 token 的合法性
  39. * @param token token字符串
  40. * @param secret 用户id
  41. * @throws TokenUnavailableException
  42. * **/
  43. public static void verifyToken(String token,String secret) throws TokenUnavailableException {
  44. DecodedJWT jwt = null;
  45. try {
  46. JWTVerifier verifier = JWT.require(Algorithm.HMAC256(secret + "HelloWorld")).build();
  47. // 如果校验过程中出现问题,会抛出TokenUnavailableException异常
  48. jwt = verifier.verify(token);
  49. } catch (Exception e){
  50. throw new TokenUnavailableException(4003,e.getMessage());
  51. }
  52. }
  53. /**
  54. * 获取签发对象
  55. * @param token token字符串
  56. * @throws TokenUnavailableException
  57. * */
  58. public static String getAudience(String token) throws TokenUnavailableException {
  59. String audience = null;
  60. try{
  61. audience = JWT.decode(token).getAudience().get(0);
  62. System.out.println(audience);
  63. }catch (JWTDecodeException e){
  64. throw new TokenUnavailableException(4002,e.getMessage());
  65. }
  66. return audience;
  67. }
  68. /**
  69. * 通过载荷名字获取载荷的值
  70. */
  71. public static Claim getClaimByName(String token, String name){
  72. return JWT.decode(token).getClaim(name);
  73. }
  74. }

注解类编写

免验证注解

@PassToken: 表明该接口可以跳过验证

拦截器的编写

拦截器配置类

  1. package com.example.demo2.springbootmybatis.config;
  2. import com.example.demo2.springbootmybatis.service.impl.JwtAuthenticationInterceptor;
  3. import org.springframework.context.annotation.Bean;
  4. import org.springframework.context.annotation.Configuration;
  5. import org.springframework.stereotype.Component;
  6. import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
  7. import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
  8. /**
  9. * @author 小喻同学
  10. */
  11. @Configuration
  12. public class JwtInterceptorConfig implements WebMvcConfigurer {
  13. @Override
  14. public void addInterceptors(InterceptorRegistry registry) {
  15. /**
  16. * @addPathPatterns 配置拦截的路径
  17. * 如果写成 .addPathPatterns("/**")
  18. * 那么就会拦截所有的controller
  19. * **/
  20. registry.addInterceptor(authenticationInterceptor())
  21. .addPathPatterns("/admin","/admin/*");
  22. }
  23. @Bean
  24. public JwtAuthenticationInterceptor authenticationInterceptor() {
  25. return new JwtAuthenticationInterceptor();
  26. }
  27. }

拦截器

  1. package com.example.demo2.springbootmybatis.service.impl;
  2. import com.example.demo2.springbootmybatis.annotation.PassToken;
  3. import com.example.demo2.springbootmybatis.entiy.Admin;
  4. import com.example.demo2.springbootmybatis.exception.AdminNotExistException;
  5. import com.example.demo2.springbootmybatis.exception.NeedToLoginException;
  6. import com.example.demo2.springbootmybatis.service.AdminService;
  7. import com.example.demo2.springbootmybatis.utils.JwtUtils;
  8. import org.springframework.beans.factory.annotation.Autowired;
  9. import org.springframework.stereotype.Component;
  10. import org.springframework.web.method.HandlerMethod;
  11. import org.springframework.web.servlet.HandlerInterceptor;
  12. import org.springframework.web.servlet.ModelAndView;
  13. import javax.servlet.http.HttpServletRequest;
  14. import javax.servlet.http.HttpServletResponse;
  15. import java.lang.reflect.Method;
  16. // @Component 泛指组件,当组件不好归类的时候,一般使用这个注解进行标注
  17. @Component
  18. public class JwtAuthenticationInterceptor implements HandlerInterceptor {
  19. @Autowired
  20. private AdminService adminService;
  21. @Override
  22. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object object) throws Exception {
  23. String token = request.getHeader("token");
  24. if(!(object instanceof HandlerMethod)){
  25. return true;
  26. }
  27. HandlerMethod handlerMethod = (HandlerMethod) object;
  28. Method method = handlerMethod.getMethod();
  29. // 如果是被注解了@PassToken的话,那么就不进行校验
  30. if(method.isAnnotationPresent(PassToken.class)){
  31. PassToken passToken = method.getAnnotation(PassToken.class);
  32. if(passToken.required()){
  33. return true;
  34. }
  35. }else{
  36. System.out.println("被jwt拦截了,需要进行token权限认证");
  37. // 请求头中没有找到token
  38. if(token == null){
  39. throw new NeedToLoginException(4001,"需要先登录");
  40. }
  41. String userId = JwtUtils.getAudience(token);
  42. Admin admin = adminService.getOne(Integer.valueOf(userId));
  43. if(admin == null){
  44. System.out.println("AdminNotExist");
  45. throw new AdminNotExistException(4004,"不存在该管理员");
  46. }
  47. JwtUtils.verifyToken(token,userId);
  48. String userName = JwtUtils.getClaimByName(token, "userName").asString();
  49. String userNumber = JwtUtils.getClaimByName(token, "userNumber").asString();
  50. System.out.println("name:" + userName);
  51. System.out.println("number:" + userNumber);
  52. request.setAttribute("userName",userName);
  53. request.setAttribute("userNumber",userNumber);
  54. return true;
  55. }
  56. return true;
  57. }
  58. @Override
  59. public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
  60. HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
  61. }
  62. @Override
  63. public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
  64. HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
  65. }
  66. }

拦截器的逻辑大概如下

  1. 目标方法是否有注解?如果有@PassToken注解就不用执行后面的验证,直接放行,否则需要进行验证。
  2. 请求头中有没有token?没有的话直接返回错误。
  3. 从token中获取签发的对象。从数据库中查询这个用户是否存在(有可能是客户端造假,也有可能是这个用户的账户被冻结了)。查询的话直接调用对应的Service层
  4. 检验Jwt的有效性。判断令牌是否无效或者过期了。
  5. 校验成功,把载荷内容获取到,可以在controller中使用。

image.png

去Controller上测试一下
image.png

postman测试
image.png

不附带token去请求接口
image.png
使用过期的token去请求接口
image.png
使用正确的token去请求接口
image.png