• 【案例实战】分布式应用下登录检验解决方案(JWT)


    1.需求背景以及JWT简介
    • 现实场景中,有些功能是需要登录才能访问的,比如购物车,个人订单等等。登录功能是最常见的功能。

    (1)什么是JWT

    • JWT 是一个开放标准,它定义了一种用于简洁,自包含的用于通信双方之间以 JSON 对象的形式安全传递信息的方法。 可以使用 HMAC 算法或者是 RSA 的公钥密钥对进行签名。

    • 简单来说: 就是通过一定规范来生成token,然后可以通过解密算法逆向解密token,这样就可以获取用户信息

    • 优点

      • 生产的token可以包含基本信息,比如id、用户昵称、头像等信息,避免再次查库
      • 存储在客户端,不占用服务端的内存资源
    • 缺点

      • token是经过base64编码,所以可以解码,因此token加密前的对象不应该包含敏感信息,如用户权限,密码等

      • 如果没有服务端存储,则不能做登录失效处理,除非服务端改秘钥

    • JWT格式组成 头部、负载、签名

      • header+payload+signature
        • 头部:主要是描述签名算法
        • 负载:主要描述是加密对象的信息,如用户的id等,也可以加些规范里面的东西,如iss签发者,exp 过期时间,sub 面向的用户
        • 签名:主要是把前面两部分进行加密,防止别人拿到token进行base解密后篡改token
    • 关于jwt客户端存储

      • 可以存储在cookie,localstorage和sessionStorage里面

    (2)用户登录流程图

    在这里插入图片描述

    2.创建Maven项目,搭建SpringBoot项目

    (1)添加maven依赖

        <parent>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-parentartifactId>
            <version>2.6.7version>
            <relativePath/> 
        parent>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-webartifactId>
            dependency>
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-testartifactId>
                <scope>testscope>
            dependency>
            <dependency>
                <groupId>org.projectlombokgroupId>
                <artifactId>lombokartifactId>
                <version>1.18.20version>
                <scope>compilescope>
            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

    (2)创建yml配置文件

    server:
      port: 8001
    spring:
      application:
        name: login-server
    
    • 1
    • 2
    • 3
    • 4
    • 5

    (3)创建运行主类

    @SpringBootApplication
    public class LoginApplication {
        public static void main(String[] args) {
            SpringApplication.run(LoginApplication.class, args);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    3.容器化急速部署MySQL

    (1)创建目录

    mkdir -p /usr/local/docker/mysql/conf
    mkdir -p /usr/local/docker/mysql/logs
    mkdir -p /usr/local/docker/mysql/data
    
    • 1
    • 2
    • 3

    (2)容器启动mysql服务

    docker run -p 3306:3306 --name mysql \ 
    -e MYSQL_ROOT_PASSWORD=123456 \
    -d mysql:8.0
    
    • 1
    • 2
    • 3
    #查看容器
    docker ps
    
    • 1
    • 2

    在这里插入图片描述

    (3)可视化工具连接

    在这里插入图片描述

    4.数据库表准备

    (1)创建数据库user库

    在这里插入图片描述

    (2)创建测试用户表

    在这里插入图片描述

    • 创建表sql脚本
    /*
     Navicat Premium Data Transfer
    
     Source Server         : mysql_test
     Source Server Type    : MySQL
     Source Server Version : 80027
     Source Host           : 192.168.139.100:3306
     Source Schema         : user
    
     Target Server Type    : MySQL
     Target Server Version : 80027
     File Encoding         : 65001
    
     Date: 15/11/2022 09:14:11
    */
    
    SET NAMES utf8mb4;
    SET FOREIGN_KEY_CHECKS = 0;
    
    -- ----------------------------
    
    -- Table structure for user
    
    -- ----------------------------
    
    DROP TABLE IF EXISTS `user`;
    CREATE TABLE `user`  (
      `id` int(0) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
      `username` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '用户名',
      `password` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '密码',
      `phone` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '手机号',
      `name` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '用户姓名',
      `sex` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '用户性别',
      `create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
      `age` int(0) NULL DEFAULT NULL COMMENT '年龄',
      PRIMARY KEY (`id`) USING BTREE
    ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin ROW_FORMAT = Dynamic;
    
    -- ----------------------------
    
    -- Records of user
    
    -- ----------------------------
    
    SET FOREIGN_KEY_CHECKS = 1;
    
    • 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
    • 41
    • 42
    • 43
    • 44
    • 45

    (3)创建测试登录用户

    • **注意:**这里只是做演示,正常企业密码不会设置明文的,按照企业自己的加密方式去加密密码,我们现在主要是为了开发登录鉴权这一套流程。
    INSERT INTO `user`.`user`(`id`, `username`, `password`, `phone`, `name`, `sex`, `create_time`, `age`) VALUES (1, 'lixiang', '1234567890', '13830567835', '李祥', '男', '2022-11-15 09:19:26', 18);
    
    • 1
    5.SpringBoot整合MySQL+MyBatisPlus

    (1)添加maven依赖

            
            <dependency>
                <groupId>com.baomidougroupId>
                <artifactId>mybatis-plus-boot-starterartifactId>
                <version>3.4.0version>
            dependency>
            <dependency>
                <groupId>mysqlgroupId>
                <artifactId>mysql-connector-javaartifactId>
            dependency>
            <dependency>
                <groupId>com.alibabagroupId>
                <artifactId>druid-spring-boot-starterartifactId>
                <version>1.1.18version>
            dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    (2)配置yml文件

      datasource:
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://192.168.139.100:3306/user?allowPublicKeyRetrieval=true&characterEncoding=UTF-8&allowMultiQueries=true&useUnicode=true&useSSL=false&serverTimezone=Asia/Shanghai
        username: root
        password: 123456
        type: com.alibaba.druid.pool.DruidDataSource
        druid:
          initial-size: 8
          max-active: 20
          max-wait: 60000
          min-evictable-idle-time-millis: 30000
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    (3)启动主类添加MapperScan()注解

    @MapperScan("com.lixiang.mapper")
    
    • 1

    (4)启动验证

    在这里插入图片描述

    6.MyBatisPlus逆向工程自动生成

    (1)加入maven依赖

            
            <dependency>
                <groupId>com.baomidougroupId>
                <artifactId>mybatis-plus-generatorartifactId>
                <version>3.4.1version>
            dependency>
            
            <dependency>
                <groupId>org.apache.velocitygroupId>
                <artifactId>velocity-engine-coreartifactId>
                <version>2.0version>
            dependency>
            
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    (2)运行代码

    package com.lixiang.db;
    
    import com.baomidou.mybatisplus.annotation.DbType;
    import com.baomidou.mybatisplus.annotation.IdType;
    import com.baomidou.mybatisplus.generator.AutoGenerator;
    import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
    import com.baomidou.mybatisplus.generator.config.GlobalConfig;
    import com.baomidou.mybatisplus.generator.config.PackageConfig;
    import com.baomidou.mybatisplus.generator.config.StrategyConfig;
    import com.baomidou.mybatisplus.generator.config.rules.DateType;
    import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
    
    /**
     * @description mybatis自定生成工具
     */
    public class MyBatisPlusGenerator {
        public static void main(String[] args) {
            //1. 全局配置
            GlobalConfig config = new GlobalConfig();
            // 是否支持AR模式
            config.setActiveRecord(true)
                    // 作者
                    .setAuthor("lixiang")
                    // 生成路径,最好使用绝对路径,window路径是不一样的
                    //TODO  TODO  TODO  TODO
                    .setOutputDir("D:\\IDEAWork\\springboot-login\\src\\test\\java")
                    // 文件覆盖
                    .setFileOverride(true)
                    // 主键策略
                    .setIdType(IdType.AUTO)
    
                    .setDateType(DateType.ONLY_DATE)
                    // 设置生成的service接口的名字的首字母是否为I,默认Service是以I开头的
                    .setServiceName("%sService")
    
                    //实体类结尾名称
                    .setEntityName("%sDO")
    
                    //生成基本的resultMap
                    .setBaseResultMap(true)
    
                    //不使用AR模式
                    .setActiveRecord(false)
    
                    //生成基本的SQL片段
                    .setBaseColumnList(true);
    
            //2. 数据源配置
            DataSourceConfig dsConfig = new DataSourceConfig();
            // 设置数据库类型
            dsConfig.setDbType(DbType.MYSQL)
                    .setDriverName("com.mysql.cj.jdbc.Driver")
                    //TODO 修改数据库对应的配置
                    .setUrl("jdbc:mysql://121.36.81.39:3306/user?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai")
                    .setUsername("root")
                    .setPassword("123456");
    
            //3. 策略配置globalConfiguration中
            StrategyConfig stConfig = new StrategyConfig();
    
            //全局大写命名
            stConfig.setCapitalMode(true)
                    // 数据库表映射到实体的命名策略
                    .setNaming(NamingStrategy.underline_to_camel)
    
                    //使用lombok
                    .setEntityLombokModel(true)
    
                    //使用RestController注解
                    .setRestControllerStyle(true)
    
                    // 生成的表, 支持多表一起生成,以数组形式填写
                    //TODO  TODO  TODO  TODO
                    .setInclude("user");
    
            //4. 包名策略配置
            PackageConfig pkConfig = new PackageConfig();
            pkConfig.setParent("com.lixiang")
                    .setMapper("mapper")
                    .setService("service")
                    .setController("controller")
                    .setEntity("model")
                    .setXml("mapper");
    
            //5. 整合配置
            AutoGenerator ag = new AutoGenerator();
            ag.setGlobalConfig(config)
                    .setDataSource(dsConfig)
                    .setStrategy(stConfig)
                    .setPackageInfo(pkConfig);
    
            //6. 执行操作
            ag.execute();
            System.out.println("=======  相关代码生成完毕  ========");
        }
    }
    
    
    • 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
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    (3)启动验证

    在这里插入图片描述

    7.SpringBoot整合JWT

    (1)添加maven依赖

                
                <dependency>
                    <groupId>io.jsonwebtokengroupId>
                    <artifactId>jjwtartifactId>
                    <version>0.7.0version>
                dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    (2)编写登录用户类

    /**
     * 登录user实体bean
     * @author lixiang
     * @since 2022-01-13
     */
    @Data
    @Builder
    @NoArgsConstructor
    @AllArgsConstructor
    public class LoginUser {
    
        /**
         * 主键
         */
        private Long id;
    
        /**
         * 用户名
         */
        private String username;
    
        /**
         * 姓名
         */
        private String name;
    
        /**
         * 手机号
         */
        private String phone;
    
        /**
         * 用户性别
         */
        private String sex;
    
        /**
         * 年龄
         */
        private Integer age;
    
    }
    
    • 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
    • 41
    • 42

    (3)编写JWTUtil

    /**
     * JWT工具类
     * @author lixiang
     * @since 2022-01-13
     */
    @Slf4j
    public class JWTUtil {
    
        /**
         * token过期时间,正常是7天
         */
        private static final long EXPIRE = 1000L * 60 * 60 * 24 * 7;
    
        /**
         * 加密的密钥
         */
        private static final String SECRET = "lixiang.com";
    
        /**
         * 令牌前缀
         */
        private static final String TOKEN_PREFIX = "LONGIN-TEST";
    
        /**
         * subject 颁布地址
         */
        private static final String SUBJECT = "lixiang";
    
        /**
         * 根据用户信息生成token
         * @param loginUser
         * @return
         */
        public static Map<String,Object> geneJsonWebToken(LoginUser loginUser){
    
            if(loginUser == null){
                throw new NullPointerException("loginUser对象为空");
            }
    
            Date endDate = new Date(System.currentTimeMillis() + EXPIRE);
    
            String token = Jwts.builder().setSubject(SUBJECT)
                    .claim("age",loginUser.getAge())
                    .claim("id",loginUser.getId())
                    .claim("name",loginUser.getName())
                    .claim("phone",loginUser.getPhone())
                    .claim("sex",loginUser.getSex())
                    .setIssuedAt(new Date())
                    .setExpiration(endDate)
                    .signWith(SignatureAlgorithm.HS256,SECRET)
                    .compact();
    
            token = TOKEN_PREFIX+token;
    
            Map<String,Object> map = new HashMap<>();
            map.put("accessToken",token);
            map.put("accessTokenExpires",endDate);
    
            return map;
        }
    
        /**
         * 检验token方法
         * @param token
         * @return
         */
        public static Claims checkJWT(String token){
    
            try{
                return Jwts.parser().setSigningKey(SECRET)
                        .parseClaimsJws(token.replace(TOKEN_PREFIX, ""))
                        .getBody();
            }catch (Exception e){
                log.info("JWT token解密失败");
                return null;
            }
        }
    
    }
    
    • 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
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79

    (4)测试JWT,编写测试方法

    public class Main {
        public static void main(String[] args) {
            LoginUser loginUser = LoginUser.builder()
                    .age(18)
                    .id(1L)
                    .name("李祥")
                    .phone("13820934720")
                    .sex("男")
                    .username("lixiang")
                    .build();
    
            Map<String, Object> objectMap = JWTUtil.geneJsonWebToken(loginUser);
    
            System.out.println("LoginUser加密:");
            objectMap.forEach((k,v)->{
                System.out.println("---key:"+k+",value:"+v);
            });
    
            String accessToken = String.valueOf(objectMap.get("accessToken"));
            System.out.println("Token解密:");
            Claims claims = JWTUtil.checkJWT(accessToken);
            System.out.println("---name:"+claims.get("name"));
            System.out.println("---age:"+claims.get("age"));
            System.out.println("---phone:"+claims.get("phone"));
            System.out.println("---username:"+claims.get("username"));
            System.out.println("---sex:"+claims.get("sex"));
        }
    }
    
    • 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

    在这里插入图片描述

    8.开发测试接口
    • 开发两个接口,我们的目的是一个用于不需要登录就能访问,一个需要登录才能访问

    • 查看商品信息列表,查看个人订单信息两个接口

    • 开发测试的UserController,这块全部用的测试接口,主要是给大家演示效果,现在我们想让商品列表的接口可以随便访问,订单列表的接口只有用户登录之后才能访问。

    /**
     * @description 测试Controller
     * @author lixiang
     */
    @RestController
    @RequestMapping("/user")
    public class UserController {
    
        /**
         * 查询商品列表
         * @return
         */
        @GetMapping("/product_list")
        public Object getProductList(){
            return getResult(200,"查询商品列表");
        }
    
        /**
         * 查询订单列表
         * @return
         */
        @GetMapping("/order_list")
        public Object getOrderList(){
            return getResult(200,"查询订单列表");
        }
    
        /**
         * 测试返回结果
         * @param code
         * @param msg
         * @return
         */
        private Object getResult(int code, String msg) {
            Map<String,Object> result = new HashMap<>();
            result.put("code",code);
            result.put("msg",msg);
            return result;
        }
    
    }
    
    • 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
    • 测试

    在这里插入图片描述

    在这里插入图片描述

    • 两个接口访问正常,但是对于订单接口我们是想增加Token检验,才会给予访问,这块我们就需要写一个拦截器,但是我们现在应该先去开发一下登录的接口。
    9.开发登录接口
    • 这块我们采用手机号和密码登录

    (1)创建登录请求类

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class LoginReq {
    
        /**
         * 手机号
         */
        private String phone;
    
        /**
         * 密码
         */
        private String password;
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    (2)创建login方法在UserServie

    public interface UserService {
    
        /**
         * 登录方法
         * @param req
         * @return
         */
        Map<String, Object> login(LoginReq req);
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    (3)login实现类编写

    @Service
    public class UserServiceImpl implements UserService {
    
        @Autowired
        private UserMapper userMapper;
    
        @Override
        public Map<String, Object> login(LoginReq req) {
    
            UserDO user = userMapper.selectOne(new QueryWrapper<UserDO>().eq("phone", req.getPhone()));
    
            Map<String,Object> result = new HashMap<>();
    
            //判断是否已经注册的
            if (user == null) {
                //未注册
                result.put("code",10000);
                result.put("msg","用户未注册");
                return result;
            }
    
            if (req.getPassword().equals(user.getPassword())) {
    
                //登录成功,生成token,UUID生成token,存储到redis中并设置过期时间
                LoginUser loginUser = LoginUser.builder().build();
                BeanUtils.copyProperties(user, loginUser);
                return JWTUtil.geneJsonWebToken(loginUser);
            }
            result.put("code",10000);
            result.put("msg","密码错误");
            return result;
        }
    }
    
    • 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

    (4)编写Controller

        @Autowired
        private UserService userService;
    
        /**
         * 登录
         * @return
         */
        @PostMapping("/login")
        public Object login(@RequestBody LoginReq req){
            return userService.login(req);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    (5)测试登录接口

    在这里插入图片描述

    10.开发登录拦截器
    • 登录接口开发完成了,那么我们需要开发一个登录拦截器。
    /**
     * 全局登录拦截器
     * @author lixiang
     */
    @Slf4j
    public class LoginInterceptor implements HandlerInterceptor {
    
        public static ThreadLocal<LoginUser> threadLocal = new ThreadLocal<>();
    
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    
            //从请求头中拿token
            String accessToken = request.getHeader("token");
    
            //从请求参数中拿token
            if (accessToken == null){
                accessToken = request.getParameter("token");
            }
    
            if(accessToken!=null && !accessToken.equals("")){
                //不为空,判断是否登录过期
                Claims claims = JWTUtil.checkJWT(accessToken);
                if (claims == null){
                    sendJsonMessage(response, "账号已过期");
                    return false;
                }
                Long userId = Long.valueOf(claims.get("id").toString());
                String headImg = (String) claims.get("username");
                String name = (String) claims.get("name");
                String phone = (String) claims.get("phone");
                String sex = (String) claims.get("sex");
                Integer age = (Integer) claims.get("age");
    
                //设置LoginUser对象属性,建造者模式
                LoginUser loginUser = LoginUser.builder()
                        .name(name)
                        .username(headImg)
                        .id(userId)
                        .phone(phone)
                        .sex(sex)
                        .age(age).build();
    
                //通过threadLocal共享用户登录信息
                threadLocal.set(loginUser);
                return true;
            }
            sendJsonMessage(response, "账号未登录");
            return false;
        }
    
        /**
         *
         * @param response
         * @param msg
         */
        private void sendJsonMessage(HttpServletResponse response, String msg) {
    
            Map<String,Object> result = new HashMap<>();
            result.put("code",10000);
            result.put("msg",msg);
            ObjectMapper objectMapper = new ObjectMapper();
            response.setContentType("application/json;charset=utf-8");
            PrintWriter writer = null;
    
            try {
                writer = response.getWriter();
                writer.print(objectMapper.writeValueAsString(result));
                response.flushBuffer();
            } catch (IOException e) {
                log.warn("响应json数据给前端异常");
            }finally {
                if(writer!=null){
                    writer.close();
                }
            }
        }
    }
    
    • 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
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78

    (2)配置接口拦截

    /**
     * 登录拦截配置类
     * @author lixiang
     */
    @Configuration
    @Slf4j
    public class InterceptorConfig implements WebMvcConfigurer {
    
        @Bean
        public LoginInterceptor loginInterceptor() {
            return new LoginInterceptor();
        }
    
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(loginInterceptor())
                    .addPathPatterns("/user/*")
                    //排查不拦截的路径
                    .excludePathPatterns("/user/login","/user/product_list");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    配置拦截器使用户登录的接口和商品列表查询的接口不进行token验证,将用户的信息放在ThreadLocal中保证每个线程独立内存空间。

    11.启动验证

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    至此,整个登录整合JWT功能已经开发完成,这块其实还可以根据自己的业务去返回一个RefreshToken,Token过期刷新的token。

  • 相关阅读:
    AJAX学习日记——Day 3
    python-PyMySQ mysql数据库的操作和使用
    学习笔记-配置备份静态路由及优先级
    Vector容器介绍
    [spark] RDD 编程指南(翻译)
    Three导入dae格式模型实例
    解码2022中国网安强星丨从“移动应用”到“万物互联”,梆梆安全做物联网时代的安全“守门人”
    Ansys Mechanical|学习方法
    深夜小酌,50道经典SQL题,真香~
    内核开发0 --- 代码规范
  • 原文地址:https://blog.csdn.net/weixin_47533244/article/details/127883134