• Java - SpringBoot整合JWT


    前言

    Java - JWT的简单介绍和使用

    一. SpringBoot整合JWT

    1.1 基础配置

    1.pom依赖:

    <parent>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-parentartifactId>
        <version>2.3.12.RELEASEversion>
    parent>
    <dependencies>
    	
    	<dependency>
    	   <groupId>org.springframework.bootgroupId>
    	    <artifactId>spring-boot-configuration-processorartifactId>
    	    <optional>trueoptional>
    	dependency>
    	
    	<dependency>
    	    <groupId>com.auth0groupId>
    	    <artifactId>java-jwtartifactId>
    	    <version>3.19.1version>
    	dependency>
    	<dependency>
    	    <groupId>io.jsonwebtokengroupId>
    	    <artifactId>jjwtartifactId>
    	    <version>0.7.0version>
    	dependency>
    	<dependency>
    	    <groupId>org.springframework.bootgroupId>
    	    <artifactId>spring-boot-starter-webartifactId>
    	dependency>
    	
    	<dependency>
            <groupId>org.apache.commonsgroupId>
            <artifactId>commons-lang3artifactId>
        dependency>
        
        <dependency>
            <groupId>org.projectlombokgroupId>
            <artifactId>lombokartifactId>
            <version>1.18.10version>
            <scope>providedscope>
        dependency>
    dependencies>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    2.application.yml文件:

    server:
      port: 8080
    
    config:
      jwt:
        secret: ASD!@#F^%A
        expire: 600
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    1.2 Token配置类

    Token配置类JwtUtil

    @Component
    @Data
    @ConfigurationProperties(prefix = "config.jwt")
    public class JwtUtil {
        private String secret;
        private int expire;
    
        public String buildToken(Long userId) {
            Calendar expires = Calendar.getInstance();
            expires.add(Calendar.SECOND, expire);
    
            String token = JWT
                    .create()
                    .withSubject(String.valueOf(userId))
                    .withExpiresAt(expires.getTime())
                    // 第三部分Signature
                    .sign(Algorithm.HMAC256(secret));
            return token;
        }
    
        public Long getSubject(String token) {
            Long res = null;
            if (StringUtils.isBlank(token)) {
                return res;
            }
            JWTVerifier verifier = JWT.require(Algorithm.HMAC256(secret)).build();
            // 验证失败会抛出异常
            DecodedJWT verify = verifier.verify(token);
    
            String subject = verify.getSubject();
            if (StringUtils.isNoneBlank(subject)) {
                res = Long.parseLong(subject);
            }
            return res;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • @ConfigurationProperties(prefix = "config.jwt"):用于获取配置文件中的属性定义并绑定到Bean的属性中。
    • @Data:因为属性的装配注入,需要依赖于对应的get/set方法。因此通过这个注解来快速实现get/set

    TokenContext类,用来存储userId,这样当次请求就可以在任意一个地方拿到这个变量了。

    public class TokenContext {
        public static final ThreadLocal<Long> USER_ID = new ThreadLocal<>();
    
        public static Long getUserId() {
            return USER_ID.get();
        }
    
        public static void setUserId(Long userId) {
            USER_ID.set(userId);
        }
    
        public static void clear(){
            USER_ID.remove();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    1.3 拦截器的注册

    拦截器TokenInterceptor

    @Component
    public class TokenInterceptor extends HandlerInterceptorAdapter {
     	// 过滤登录等特殊请求地址,真实情况要比这个多的多,比如静态资源的等等。
     	// Header的key
        public static final String LOGIN_URL = "/login";
        public static final String TOKEN = "token";
    
        @Autowired
        private JwtUtil jwtUtil;
    
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            String requestURI = request.getRequestURI();
            // 排除登录等特殊请求
            if (requestURI.contains(LOGIN_URL)) {
                return true;
            }
            String token = request.getHeader(TOKEN);
            if (StringUtils.isBlank(token)) {
                throw new SignatureException("Token不能为空!");
            }
            // 获取JWT中存储的userId
            Long userId = jwtUtil.getSubject(token);
            if (userId == null) {
                throw new SignatureException("Token失效,请重新登录!");
            }
            TokenContext.setUserId(userId);
            return true;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    WebMvc配置WebConfig:配置拦截器生效。

    @Configuration
    public class WebConfig implements WebMvcConfigurer {
        @Autowired
        private TokenInterceptor tokenInterceptor;
    
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(tokenInterceptor).addPathPatterns("/**");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    1.4 异常类处理和接口实现

    写一个异常类处理ExceptionController

    @RestControllerAdvice
    public class ExceptionController {
        @ExceptionHandler(value = {SignatureException.class})
        @ResponseBody
        public String authorizationException(SignatureException e) {
            return e.getMessage();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    写两个接口:

    @RestController
    public class MyController {
        @Autowired
        private JwtUtil jwtUtil;
    
        @PostMapping("/login")
        public String login(@RequestParam("userId") Long userId) {
            String token = jwtUtil.buildToken(userId);
            return "Success, Jwt Token : " + token;
        }
    
        @PostMapping("/getUser")
        public String getUser() {
            try {
                Long userId = TokenContext.getUserId();
                if (userId != null) {
                    return "成功拿到用户信息: " + userId;
                }
                return "用户信息为空";
            } finally {
                TokenContext.clear();
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    1.5 测试

    登录结果如下:
    在这里插入图片描述


    测试带Token请求:

    在这里插入图片描述

    测试不带Token请求:

    在这里插入图片描述

    文章到这里就结束啦,后面会出一篇文章,整合Shiro + JWT + SpringCloudSpringBoot

  • 相关阅读:
    5.【平衡二叉树(AVL)】定义、结构体 + 插入【LL、RR、LR、RL型】+ 查找效率分析
    深聊测试开发之:从订单支付流程来聊一聊,如何预防重复支付,建议收藏。
    干货 | 每日十道Java集合面试题
    Jenkins简介及Docker Compose部署
    PHP:NULL 合并运算符
    频繁的需求变更,让你痛苦过吗?
    计算机毕业设计Python+Django的闲置物品交易系统+二手商城网站(源码+系统+mysql数据库+Lw文档)
    【Python微信机器人】第六七篇: 封装32位和64位Python hook框架实战打印微信日志
    软件外包开发需求文档编写
    数据结构之单链表的模拟实现
  • 原文地址:https://blog.csdn.net/Zong_0915/article/details/127751639