在 SpringBoot 项目中简单实现 JWT 验证

[复制链接]
发表于 2023-2-7 17:53:59 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
使用 SpringBoot 提供 api 的时候,我更喜欢使用 jwt 的方式来做验证。网上有会多 Spring Security 整合 jwt 的,也有 Shiro 整合 jwt 的,感觉有点复杂。这里分享一下自己在项目中的简单实现。
依赖包

除了 SpringBoot 基本的依赖,需要一个生成 jwt 和序列化的包。生成 jwt 的包依赖很多,因为我项目里使用了 hutool 这个包,就只用用它了。
  1.         <dependency>
  2.             <groupId>cn.hutool</groupId>
  3.             <artifactId>hutool-all</artifactId>
  4.             <version>5.8.9</version>
  5.         </dependency>
  6.         <dependency>
  7.             <groupId>cn.hutool</groupId>
  8.             <artifactId>hutool-all</artifactId>
  9.             <version>5.8.9</version>
  10.         </dependency>
复制代码
jwt用户模型

定义一个 Jwt 的 sub 字段模型,存储用户:
  1. import lombok.Data;
  2. import org.springframework.web.context.request.RequestAttributes;
  3. import org.springframework.web.context.request.RequestContextHolder;
  4. import org.springframework.web.context.request.ServletRequestAttributes;
  5. import javax.servlet.http.HttpServletRequest;
  6. @Data
  7. public class JwtUser {
  8.     /**
  9.      * 用户编号
  10.      */
  11.     private Integer id;
  12.     /**
  13.      * 用户名
  14.      */
  15.     private String name;
  16.     /**
  17.      * 角色
  18.      */
  19.     private String role;
  20.     /**
  21.      * 获取当前请求用户
  22.      * @return
  23.      */
  24.     public static JwtUser getCurrentUser() {
  25.         RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes();
  26.         HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
  27.         return (JwtUser) request.getAttribute("user");
  28.     }
  29. }
复制代码
验证注解

定义一个用于请求类和方法的注解
  1. import java.lang.annotation.*;
  2. @Inherited
  3. @Target({ElementType.TYPE,ElementType.METHOD})
  4. @Retention(RetentionPolicy.RUNTIME)
  5. public @interface Authorize {
  6.     /**
  7.      * 是否匿名可以访问
  8.      * @return
  9.      */
  10.     boolean anonymous() default false;
  11.     /**
  12.      * 角色
  13.      * @return
  14.      */
  15.     String[] roles() default {};
  16. }
复制代码
JWT 帮助类

用于生成 jwt 和 解析 JwtUser 对象。
  1. import cn.hutool.jwt.JWT;
  2. import cn.hutool.jwt.JWTUtil;
  3. import cn.hutool.jwt.signers.JWTSigner;
  4. import cn.hutool.jwt.signers.JWTSignerUtil;
  5. import com.google.gson.Gson;
  6. import com.mpyf.xapi.security.JwtUser;
  7. import lombok.var;
  8. import java.util.Date;
  9. import java.util.HashMap;
  10. import java.util.Map;
  11. import java.util.UUID;
  12. public class JwtTokenUtils {
  13.     public static final String SECRET = "your_secret";
  14.     public static final String ISS = "com.your.cn";
  15.     private static final int EXPIRATIONHOURS = 24; //过期时间24小时
  16.    
  17.     //创建token
  18.     public static String createToken(JwtUser user) {
  19.         return createToken(user, EXPIRATIONHOURS);
  20.     }
  21.     public static String createToken(JwtUser user, int hours) {
  22.         String subJson = new Gson().toJson(user);
  23.         JWTSigner jwtSigner = JWTSignerUtil.hs512(SECRET.getBytes());
  24.         JWT jwt = JWT.create().setSigner(jwtSigner);
  25.         jwt
  26.                 .setJWTId(UUID.randomUUID().toString().replace("-", ""))
  27.                 .setSubject(subJson) //用户信息
  28.                 .setIssuer(ISS)      //签发者
  29.                 //.setAudience("受众")
  30.                 //.setNotBefore(new Date())
  31.                 .setIssuedAt(new Date())
  32.                 .setExpiresAt(new Date(System.currentTimeMillis() + hours * 3600 * 1000));
  33.         return jwt.sign();
  34.     }
  35.     public static JwtUser getUser(String token) {
  36.         if (StringHelper.isNullOrEmpty(token)) return null;
  37.         var jwt = JWTUtil.parseToken(token);
  38.         JWTSigner jwtSigner = JWTSignerUtil.hs512(SECRET.getBytes());
  39.         jwt.setSigner(jwtSigner);
  40.         if (jwt.validate(10)) {
  41.             var subJson = jwt.getPayload("sub").toString();
  42.             JwtUser user = new Gson().fromJson(subJson, JwtUser.class);
  43.             return user;
  44.         } else {
  45.             return null;
  46.         }
  47.     }
  48. }
复制代码
验证拦截器

定义jwt的验证拦截器,从请求头获取 token 解析并验证。
  1. import com.mpyf.xapi.helper.JwtTokenUtils;
  2. import com.mpyf.xapi.helper.StringHelper;
  3. import org.springframework.stereotype.Component;
  4. import org.springframework.web.method.HandlerMethod;
  5. import org.springframework.web.servlet.HandlerInterceptor;
  6. import javax.servlet.http.HttpServletRequest;
  7. import javax.servlet.http.HttpServletResponse;
  8. import java.util.Arrays;
  9. /**
  10. * jwt 验证拦截器
  11. */
  12. @Component
  13. public class JwtAuthInterceptor implements HandlerInterceptor {
  14.     @Override
  15.     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
  16.         //Authorization:Bearer+空格+token
  17.         String token = request.getHeader("Authorization");
  18.         if (token != null) {
  19.             token = token.replace("Bearer ", "");
  20.         }
  21.         //处理模拟登录的jwt
  22.         if (StringHelper.isNullOrEmpty(token)) {
  23.             token = request.getParameter("jwt");
  24.         }
  25.         if (StringHelper.isNullOrEmpty(token)) {
  26.             //兼容从请求参数传token
  27.             Object jwt = request.getAttribute("jwt");
  28.             if (jwt != null) {
  29.                 token = jwt.toString();
  30.             }
  31.         }
  32.         JwtUser user = JwtTokenUtils.getUser(token);
  33.         request.setAttribute("user", user);
  34.         if (handler instanceof HandlerMethod) {
  35.             HandlerMethod h = (HandlerMethod) handler;
  36.             Authorize authorize = h.getMethodAnnotation(Authorize.class);
  37.             if (authorize == null) {
  38.                 authorize = h.getMethod().getDeclaringClass().getAnnotation(Authorize.class);
  39.             }
  40.             //如果没有Authorize或者可以匿名访问,直接返回
  41.             if (authorize != null && !authorize.anonymous()) {
  42.                 {
  43.                     if (user == null) {
  44.                         response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
  45.                         return false;
  46.                     } else if (authorize.roles() != null && authorize.roles().length > 0 &&
  47.                             Arrays.stream(authorize.roles()).allMatch(s -> !s.equalsIgnoreCase(user.getRole()))) {
  48.                         //没权限
  49.                         response.sendError(HttpServletResponse.SC_FORBIDDEN);
  50.                         return false;
  51.                     }
  52.                 }
  53.             }
  54.         }
  55.         return true;
  56.     }
  57. }
复制代码
注册拦截器

在 WebMvc 配置中注册拦截器,并支持跨域请求
  1. import com.mpyf.xapi.security.JwtAuthInterceptor;
  2. import org.springframework.beans.factory.annotation.Autowired;
  3. import org.springframework.context.annotation.Configuration;
  4. import org.springframework.web.servlet.config.annotation.*;
  5. @Configuration
  6. public class WebMvcConfig implements WebMvcConfigurer {
  7.     @Autowired
  8.     JwtAuthInterceptor jwtAuthInterceptor;
  9.     @Override
  10.     public void addInterceptors(InterceptorRegistry registry) {
  11.         registry.addInterceptor(jwtAuthInterceptor).addPathPatterns("/api/**");
  12.         WebMvcConfigurer.super.addInterceptors(registry);
  13.     }
  14.     @Override
  15.     public void addCorsMappings(CorsRegistry registry) {
  16.         registry.addMapping("/api/**")
  17.                 .allowedOriginPatterns("*")
  18.                 .allowedMethods("*")
  19.                 .allowedHeaders("*")
  20.                 //.maxAge(3600)
  21.                 .allowCredentials(true);
  22.         WebMvcConfigurer.super.addCorsMappings(registry);
  23.     }
  24. }
复制代码
Controller中使用
  1. @RestController
  2. @RequestMapping("/api/test")
  3. @Authorize(roles = {"admin", "user"})
  4. public class TestController {
  5.     @GetMapping("admin_and_user")
  6.     public String admin_and_user(){
  7.         return "admin 和 user 角色都可以访问";
  8.     }
  9.     @GetMapping("admin_only")
  10.     @Authorize(roles = "admin") //覆盖Controller的设置
  11.     public String admin_only(){
  12.         return "只有 admin 角色可以访问";
  13.     }
  14.     @GetMapping("public_all")
  15.     @Authorize(anonymous = true)
  16.     public String public_all(){
  17.         return "匿名可以访问";
  18.     }
  19. }
复制代码
不用 Spring Security 和 Shiro ,是不是更简单呢!

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
继续阅读请点击广告
回复

使用道具 举报

×
登录参与点评抽奖,加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表