springboot + security + redis + jwt 实现验证登录上

发布于:2025-04-14 ⋅ 阅读:(25) ⋅ 点赞:(0)
前言:

通过实践而发现真理,又通过实践而证实真理和发展真理。从感性认识而能动地发展到理性认识,又从理性认识而能动地指导革命实践,改造主观世界和客观世界。实践、认识、再实践、再认识,这种形式,循环往复以至无穷,而实践和认识之每一循环的内容,都比较地进到了高一级的程度。

前置操作:添加依赖
//JWT框架 java8版本
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
</dependency>
//JWT框架 java8以上的版本使用下面这个
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
     <version>0.12.3</version>
</dependency>


//redis驱动
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
//Security框架,在完成Redis,jwt前,为了更好完成代码功能,先不添加Security框架
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-core</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-config</artifactId>
</dependency>

注释的格式打错了,复制完后请删掉

 

上面的3个框架,都是为了这一套流程更好更容易的实现

JWT:创建JWT

Redis:存储JWT

Security:判断JWT

运行,报错

org.springframework.data.redis.RedisConnectionFailureException: Unable to connect to Redis

良性报错,我们像注册mysql一样,注册redis即可

配置完后,就可以解决bug了

Redis配置:

spring:
#mysql配置
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/my_project?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useSSL=false&zeroDateTimeBehavior=convertToNull
    username: root
    password: 123456
#redis配置
  data:
    redis:
      database: 0
      host: 127.0.0.1
      port: 6379
      #  password: xxxxx 如果有密码添加

database:为redis数据名称

mybsql的可以用英文,redis的只有数字,可以根据idea右边的数据添加

正片:

先从JWT开始,JWT从生成一个JWT开始

最简单的JWT生成

        //文章来源 https://developer.aliyun.com/article/1613854

        String compact = Jwts.builder()
                .subject("严欣铷") //用户标签
                .expiration(new Date(System.currentTimeMillis() + 7200 * 1000)) //设置过期时间
                .signWith(Jwts.SIG.HS512.key().build()) //设置加密算法
                .compact();
        System.out.println(compact);

JWT分为三部分

再结合一个来看

① 标准载荷:
JwtBuilder为JWT规范中定义的标准载荷提供了方便的构建器方法。
● issuer: sets the iss(Issuer) Claim jwt签发者
● subject: sets the sub(Subject) Claim jwt针对的用户
● audience: sets the aud(Audience) Claim 校验jwt的一方
● expiration: sets the exp(Expiration Time) Claim jwt过期时间
● notBefore: sets the nbf(Not Before) Claim 定义在某个时间前该jwt是不可用的
● issuedAt: sets the iat(Issued At) Claim 签发时间
● id: sets the jti(JWT ID) Claim jwt的唯一身份标识,作一次性token,防重放攻击。
② 自定义载荷:
● claime(key,value)
● claime(Map对象)

现在需要将jwt传入到redis中

在写入redis前,先简单的写一串内容进去

@SpringBootTest
class SecurityApplicationTests {

    @Autowired
    StringRedisTemplate template;  //添加这句代码,自动装载,即可解决文章三处代码报错

    @Test
    void contextLoads() {
//        String compact = Jwts.builder()
//                .signWith(Jwts.SIG.HS512.key().build())
//                .subject("严欣铷")
//                .expiration(new Date(System.currentTimeMillis() + 7200 * 1000))
//                .compact();
//        System.out.println(compact);

        template.opsForValue().set("name","yanxinru");
        System.out.println(template.opsForValue().get("name"));
    }

}

这三条代码即可添加数据

现在有了JWT和Redis,我们试着将JWT传入Redis中

@SpringBootTest
class SecurityApplicationTests {

    @Autowired
    StringRedisTemplate template;  //添加这句代码,自动装载,即可解决文章三处代码报错

    @Test
    void contextLoads() {
        String compact = Jwts.builder()
                .signWith(Jwts.SIG.HS512.key().build())
                .subject("严欣铷")
                .expiration(new Date(System.currentTimeMillis() + 7200 * 1000))
                .compact();
        System.out.println(compact);

        template.opsForValue().set("key",compact); //调用compact
    }

}

根据这个源码,我们可以设置一个timeout,让它自动删除

@SpringBootTest
class SecurityApplicationTests {

    @Autowired
    StringRedisTemplate template;  //添加这句代码,自动装载,即可解决文章三处代码报错

    @Test
    void contextLoads() {
        String compact = Jwts.builder()
                .signWith(Jwts.SIG.HS512.key().build())
                .subject("严欣铷")
                .expiration(new Date(System.currentTimeMillis() + 6000))
                .compact();
        System.out.println(compact);
        long date = 6000; //6秒过期,6秒后自动删除
        template.opsForValue().set("yanxinru",compact,date, TimeUnit.MILLISECONDS);
        template.opsForValue().set("chentiantian",compact,date, TimeUnit.MILLISECONDS);
    }

}

等六秒后,它会自动删除

我们可以将用户的ID,账号,作用唯一标识,将时间放长

该添加Security了

添加一个配置


/**
 * Security配置类
 */
@Configuration(proxyBeanMethods = false)
@EnableWebSecurity
@EnableMethodSecurity(securedEnabled = true)
public class SecurityConfig {

}

为了方便测试

  security:
    user:
      name: yanxinru
      password: 123456
      roles:
        User

做完后,我们对JWT进行修改

@SpringBootTest
class SecurityApplicationTests {

    @Autowired
    StringRedisTemplate template;  //添加这句代码,自动装载,即可解决文章三处代码报错

    @Test
    void contextLoads() {
        String compact = Jwts.builder()
                .signWith(Jwts.SIG.HS512.key().build())
                .subject("严欣铷")
                .expiration(new Date(System.currentTimeMillis() + 6000))
                .compact();

        long date = 600000; // 60秒过期
        if (set(compact,date)){
            System.out.println("导入成功");
        }
        else {
            System.out.println("导入失败");
        }

    }
    Boolean set(String token, long date){
        try{
            template.opsForValue().set("yanxinru",token,date,TimeUnit.MILLISECONDS);
            return true;
        }catch (Exception e){
            return false;
        }
    }
}

接下来,将内容添加至登录接口中

先做一个简单的登录接口

@RestController
@RequestMapping("/web/user")
public class userWebController {

    @Resource
    userMapper userMapper;

    @Autowired
    JWTUnit unit;//工具类都可以这么写

    @GetMapping("/login")
    public userToken login(String username, String password){
        userTokenEntity userTokenEntity = userMapper.userToken(username, password);

        userToken userToken = new userToken();
        userToken.setId(userTokenEntity.getId());
        userToken.setNickname(userTokenEntity.getNickname());
        userToken.setUsername(userTokenEntity.getUsername());
        userToken.setRole(userTokenEntity.getRole());

        String token = unit.getToken(userTokenEntity.getUsername());
        userToken.setToken(token);
        return userToken;
    }

    @GetMapping("/id")
    public userTokenEntity id(int id){
        return userMapper.userTokenById(id);
    }
}
@Component
public class JWTUnit {

    @Autowired
    StringRedisTemplate template;

    public String getToken(String username){
        String token = Jwts.builder()
                .signWith(Jwts.SIG.HS512.key().build())
                .subject(username)
                .expiration(new Date(System.currentTimeMillis() + 7200 * 1000))
                .compact();
        long date = System.currentTimeMillis() + 7200 * 1000;
        if (set(username,token,date)){
            System.out.println("成功导入");
            return token;
        }
        else {
            System.out.println("导入失败");
            return  token;
        }

    }

    //将key和token添加至redis中
    Boolean set(String username,String token, long date){
        try{
            template.opsForValue().set(username,token,date, TimeUnit.MILLISECONDS);
            return true;
        }catch (Exception e){
            return false;
        }
    }
}

我们完成了生成WJT

添加至redis中

要对token解析了,那要从redis里读取

先读取
userTokenEntity userTokenEntity = user.userTokenById(1);
jwtUnit.getToken(userTokenEntity.getUsername());
jwtUnit.getToken("asd");
jwtUnit.getToken("asdasd");
jwtUnit.getToken("asdasdxzca");
template.opsForValue().get(userTokenEntity.getUsername());
System.out.println(template.opsForValue().get(userTokenEntity.getUsername()));
System.out.println(template.opsForValue().get("asd"));//读取参数为表名
System.out.println(template.opsForValue().get("asdasd"));//读取参数为表名
System.out.println(template.opsForValue().get("asdasdxzca"));//读取参数为表名