Jwt(Json web token)——从Http协议到session+cookie到Token & Jwt介绍 & Jwt的应用:登陆验证的流程_jwt

img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上软件测试知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

需要这份系统化的资料的朋友,可以戳这里获取


### Payload 载荷:比如唯一的用户ID



> 
> **Payload** 部分也是一个 JSON 对象,用来存放实际需要传递的数据。JWT 规定了7个官方字段,供选用。
> 
> 
> Payload 也使用BASE64编码
> 
> 
> 


#### Registered claims



> 
> 这些是一组预先定义的声明,它们不是强制性的,但推荐使用,以提供一组有用的,可互操作的声明。 其中一些是:`iss`(发行者),`exp`(到期时间),`sub`(主题),`aud`(受众)等
> 
> 
> 




| 序号 | 名称 | 解释 |
| --- | --- | --- |
| 1 | iss (issuer) | 签发人 |
| 2 | exp (expiration time) | 过期时间 |
| 3 | sub (subject) | 主题 |
| 4 | aud (audience) | 受众(接收jwt的一方) |
| 5 | nbf (Not Before) | 生效时间 |
| 6 | iat (Issued At) | 签发时间 |
| 7 | jti (JWT ID) | jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击 |


#### Public claims


这些可以由使用JWT的人员随意定义。


#### Private claims


这些是为了同意使用它们但是既没有登记,也没有公开声明的各方之间共享信息,而创建的定制声明。


#### Payload案例



{ “sub”: “456781234”, “name”: “Mr.zhang”, “admin”: true}


### Signature:签名



> 
> **The signature** is used to verify the message wasn’t changed along the way, and, in the case of tokens signed with a private key, it can also verify that the sender of the JWT is who it says it is.
> 
> 
> Signature 部分是对前两部分的签名,防止数据篡改。
> 
> 
> https://jwt.io/
> 
> 
> 


Signature部分由编码后的Header、Payload和自定义的秘钥使用Header中指定的算法(HSA256)进行加密签名;


![在这里插入图片描述](https://img-blog.csdnimg.cn/9f7b8820be68408c88d83aa8b7378fa0.png)



> 
> 结构
> 
> 
> 


![在这里插入图片描述](https://img-blog.csdnimg.cn/0fa8bf3baca443efb7d6d169500f0ec1.png)


## jwt使用初步


![在这里插入图片描述](https://img-blog.csdnimg.cn/77c44ad9ba8d46cb9144d1daa5788f44.png)


### 导包



    <dependency>
        <groupId>com.auth0</groupId>
        <artifactId>java-jwt</artifactId>
        <version>4.3.0</version>
    </dependency>

### 生成token



package com.tianju.redisDemo.jwt;

import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

public class JavaJwtTest {
public static void main(String[] args) {
// 定义头部header
Map<String,Object> header = new HashMap<>();
header.put(“alg”, “HS256”);

    // 私钥
    String salt = "pet";
    
    String token = JWT.create()
            .withHeader(header)
            .withClaim("username", "tom")
            .withExpiresAt(new Date(System.currentTimeMillis() + 1000 \* 60)) // 60s之后过期
            .sign(Algorithm.HMAC256(salt.getBytes()));
    System.out.println(token);

}

}


获得token



> 
> eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2ODk3NzY0MTUsInVzZXJuYW1lIjoidG9tIn0.kPmgw03j9g3EJAo2LLzXHd1b7H3uF5zF-0EDEaeonMY
> 
> 
> 


![在这里插入图片描述](https://img-blog.csdnimg.cn/b95a109ecc314f568fa24cc587027f54.png)


### 解析token



DecodedJWT decode = JWT.decode(token);


### 获取token的内容



System.out.println("Header: " + decode.getHeader());
System.out.println("Payload: " + decode.getPayload());
System.out.println("Audience: " + decode.getAudience());
System.out.println("Signature: " + decode.getSignature());


### 验证token



JWTVerifier build = JWT.require(Algorithm.HMAC256(user.getPassword())).build();


获取信息



System.out.println(build.verify(token).getClaim(“data”));


{“name”:”tomcat”,”username”:”tom”}


### 几种token验证的情况


#### token的过期时间



> 
> com.auth0.jwt.exceptions.TokenExpiredException
> 
> 
> 



package com.tianju.redisDemo.jwt;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

public class JavaJwtTest1 {
public static void main(String[] args) throws InterruptedException {
// 定义头部header
Map<String,Object> header = new HashMap<>();
header.put(“alg”, “HS256”);

    // 私钥
    String salt = "pet";

    String token = JWT.create()
            .withHeader(header)
            .withClaim("username", "tom")
            .withExpiresAt(new Date(System.currentTimeMillis() + 1000 \* 3)) // 60s之后过期
            .sign(Algorithm.HMAC256(salt.getBytes()));
    System.out.println(token);

    Thread.sleep(1000\*5);

    JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256("pet")).build();
    DecodedJWT verify = jwtVerifier.verify(token);
    System.out.println(verify.getClaim("username"));
}

}


![在这里插入图片描述](https://img-blog.csdnimg.cn/f8f95d2d07694058837ccc89c868080f.png)


#### token的内容被改过



> 
> com.auth0.jwt.exceptions.JWTDecodeException
> 
> 
> 



package com.tianju.redisDemo.jwt;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

public class JavaJwtTest1 {
public static void main(String[] args) throws InterruptedException {
// 定义头部header
Map<String,Object> header = new HashMap<>();
header.put(“alg”, “HS256”);

    // 私钥
    String salt = "pet";

    String token = JWT.create()
            .withHeader(header)
            .withClaim("username", "tom")
            .withExpiresAt(new Date(System.currentTimeMillis() + 1000 \* 3)) // 60s之后过期
            .sign(Algorithm.HMAC256(salt.getBytes()));
    System.out.println(token);

    token = token.replaceAll("[a-zA-Z]","X");


    JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256("pet")).build();
    DecodedJWT verify = jwtVerifier.verify(token);
    System.out.println(verify.getClaim("username"));
}

}


![在这里插入图片描述](https://img-blog.csdnimg.cn/de8f8d72420242b08d5e839446230295.png)


#### token的载荷被更改


![在这里插入图片描述](https://img-blog.csdnimg.cn/d2af33d10c044fb0aefbd10dbf55ed1f.png)


![在这里插入图片描述](https://img-blog.csdnimg.cn/8f7c351cd5a643f6a78d1ba32f3b0fec.png)



package com.tianju.redisDemo.jwt;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

public class JavaJwtTest1 {
public static void main(String[] args) throws InterruptedException {
// 定义头部header
Map<String,Object> header = new HashMap<>();
header.put(“alg”, “HS256”);

    // 私钥
    String salt = "pet";

    String token = JWT.create()
            .withHeader(header)
            .withClaim("username", "tom")
            .withExpiresAt(new Date(System.currentTimeMillis() + 1000 \* 60 \* 30)) //
            .sign(Algorithm.HMAC256(salt.getBytes()));
    System.out.println("登陆成功产生token:"+token);

// token = token.replaceAll(“[a-zA-Z]”,“X”);

    String hackToken = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2ODk3Nzk1MjMsInVzZXJuYW1lIjoicGV0In0.laRjGZsM5nq8IYPBMSYCbMdxyFMiXaZXKg9F7WL-n-Q";

    System.out.println("被改过的token:"+hackToken);
    JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256("pet")).build();
    DecodedJWT verify = jwtVerifier.verify(hackToken);
    System.out.println(verify.getClaim("username"));
    System.out.println("签名:"+ verify.getSignature());
}

}


## Jwt的应用:登陆验证的流程


![在这里插入图片描述](https://img-blog.csdnimg.cn/33e4888f638148908f3c10cfd81cd8aa.png)



> 
> 整体的流程:
> 
> 
> 第一步:产生30分钟有效时间的token;
> 
> 
> 第二步:在redis里面存储token,redis里面有效时间为60分钟;
> 
> 
> 第三步:token过期,但是Redis里面的token还没有过期,此时进行续期;
> 
> 
> 第四步:给原有的token续期,需要设置最大续期时间,目前用最大续期次数解决;
> 
> 
> 


### 用户表



> 
> 用户名、密码、电话号码、邮箱
> 
> 
> 


![在这里插入图片描述](https://img-blog.csdnimg.cn/78832746df334aa9a9def01adae7489a.png)


user.java实体类



package com.tianju.redisDemo.entity;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Date;

@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName(“user_owner”)
public class User {
@TableId(value = “id”,type = IdType.AUTO)
private Integer id;
@TableField(“username”)
private String username;

@TableField("realname")
private String realName;

@TableField("password")
private String password;

@TableField("tel")
private String tel;

@TableField("security\_key")
private String securityKey;

@TableField("create\_time")
private Date createTime;

}


### dao数据库


UserMapper.java文件



package com.tianju.redisDemo.dao;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.tianju.redisDemo.entity.User;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface UserMapper extends BaseMapper {

}


### service业务



> 
> 处理登陆业务:抛出异常:
> 
> 
> (1)throw new UsernameIsEmptyException(“输入为空异常”);
> 
> 
> (2)throw new UsernameNotFoundException(“用户名不存在异常”); 后面用布隆过滤器
> 
> 
> (3)throw new UsernameOrPasswordErrorException(“用户名或者密码错误异常”);
> 
> 
> 



package com.tianju.redisDemo.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.tianju.redisDemo.dao.UserMapper;
import com.tianju.redisDemo.entity.User;
import com.tianju.redisDemo.exception.UsernameIsEmptyException;
import com.tianju.redisDemo.exception.UsernameNotFoundException;
import com.tianju.redisDemo.exception.UsernameOrPasswordErrorException;
import com.tianju.redisDemo.service.IUserService;
import com.tianju.redisDemo.vo.UserVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

@Service
@Transactional
@Slf4j
public class UserServiceImpl implements IUserService {

@Autowired
private UserMapper userMapper;

@Autowired
private StringRedisTemplate stringRedisTemplate;

@Override
public List<UserVo> queryAll() {
    List<UserVo> userVolist = new LinkedList<>();
    List<User> userList = userMapper.selectList(null);
    for (User user : userList) {
        userVolist.add(
                new UserVo(user.getUsername(), user.getRealName(), user.getCreateTime())
        );
    }
    return userVolist;

}


/\*\*

* 登陆的业务
* @param username
* @param password
*/
@Override
public User login(String username, String password) {
if (usernamenull || passwordnull||
“”.equals(username)||“”.equals(password)){
// 抛出异常,USERNAME_IS_EMPTY_EXCEPTION
throw new UsernameIsEmptyException(“输入为空异常”);
}
// 判断用户名是否存在
LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.eq(User::getUsername,username);
User user = userMapper.selectOne(lambdaQueryWrapper);

    if (Objects.isNull(user)){
        // 用户名不存在
        // 抛出异常:USERNAME\_NOT\_FOUND\_EXCEPTION
        throw new UsernameNotFoundException("用户名不存在异常");
    }

    if (!user.getPassword().equals(password)){
        // 密码不对
        // 抛出异常:USERNAME\_OR\_PASSWORD\_ERROR\_EXCEPTION
        throw  new UsernameOrPasswordErrorException("用户名或者密码错误异常");
    }

    log.debug("用户名{}登陆系统成功",username);
    return user;
}

}


### 定义异常


![在这里插入图片描述](https://img-blog.csdnimg.cn/6b1cd67203cf405ea0e75a728699474f.png)


![在这里插入图片描述](https://img-blog.csdnimg.cn/4ac2829b35724d13990dd47fe4e8c004.png)



package com.tianju.redisDemo.exception;

/**
* 非法用户
*/
public class IllegalUserException extends RuntimeException{
public IllegalUserException(String message) {
super(message);
}
}



package com.tianju.redisDemo.exception;

/**
* 用户名。或者密码未输入异常
*/
public class UsernameIsEmptyException extends RuntimeException{
public UsernameIsEmptyException(String message) {
super(message);
}
}



package com.tianju.redisDemo.exception;

/**
* 用户未登录
*/
public class UserNotLoginException extends RuntimeException{
public UserNotLoginException(String message) {
super(message);
}
}


#### 异常的拦截处理



> 
> controller层调用service时,会抛出异常,捕获controller层的异常,进行处理,返回响应
> 
> 
> 



package com.tianju.redisDemo.exception.handle;

import com.auth0.jwt.exceptions.TokenExpiredException;
import com.tianju.redisDemo.dto.HttpResp;
import com.tianju.redisDemo.dto.ResultCode;
import com.tianju.redisDemo.exception.*;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import java.util.Date;

/**
* 拦截controller层的异常,捕获异常,进行处理;
* 用户登陆异常
*/
@RestControllerAdvice
public class UserExceptionHandler {

@ExceptionHandler(UsernameIsEmptyException.class)
public HttpResp usernameIsEmptyHandler(UsernameIsEmptyException e){
    return new HttpResp(ResultCode.USER\_LOGIN\_ERROR,new Date(),e.getMessage());
}
@ExceptionHandler(UsernameNotFoundException.class)
public HttpResp usernameNotFoundHandler(UsernameNotFoundException e){
    return new HttpResp(ResultCode.USER\_LOGIN\_ERROR,new Date(),e.getMessage());
}
@ExceptionHandler(UsernameOrPasswordErrorException.class)
public HttpResp usernameOrPasswordErrorHandler(UsernameOrPasswordErrorException e){
    return new HttpResp(ResultCode.USER\_LOGIN\_ERROR,new Date(),e.getMessage());
}

@ExceptionHandler(UserNotLoginException.class)
public HttpResp userNotLoginHandler(UserNotLoginException e){
    return new HttpResp(ResultCode.USER\_LOGIN\_ERROR,new Date(),e.getMessage());
}
@ExceptionHandler(IllegalUserException.class)
public HttpResp illegalUserHandler(IllegalUserException e){
    return new HttpResp(ResultCode.USER\_LOGIN\_ERROR,new Date(),e.getMessage());
}

// @ExceptionHandler(TokenExpiredException.class)
// public HttpResp tokenExpiredHandler(TokenExpiredException e){
// return new HttpResp(ResultCode.USER_LOGIN_ERROR,new Date(),e.getMessage());
// }

@ExceptionHandler(UserNeedRenewTokenException.class)
public HttpResp userNeedRenewTokenHandler(UserNeedRenewTokenException e){
    return new HttpResp(ResultCode.USER\_LOGIN\_ERROR,new Date(),e.getMessage());
}

}


![在这里插入图片描述](https://img-blog.csdnimg.cn/6b1cd67203cf405ea0e75a728699474f.png)


### controller层


UserController.java文件



package com.tianju.redisDemo.controller;

import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.tianju.redisDemo.dto.HttpResp;
import com.tianju.redisDemo.dto.ResultCode;
import com.tianju.redisDemo.entity.User;
import com.tianju.redisDemo.service.IUserService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import io.swagger.models.auth.In;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@Api(tags = “用户Api”)
@RestController
@RequestMapping(“/api/user”)
@Slf4j
public class UserController {

@Autowired
private StringRedisTemplate stringRedisTemplate;

@Autowired
private IUserService userService;

@Value("${lifespanToken}")
private Integer lifespanToken;

@Value("${lifespanRedis}")
private Integer lifespanRedis;

@ApiOperation(value = "login",notes = "用户登陆api")
@ApiImplicitParams({
        @ApiImplicitParam(name = "username",required = true),
        @ApiImplicitParam(name = "password",required = true)
})
@PostMapping("/login")
public HttpResp login(String username, String password, HttpServletResponse response,HttpServletRequest request){
    User user = userService.login(username, password);// 这里有可能出现异常
    // 能走到这一步,说明登陆成功,产生token

    // 第一步:产生30分钟有效时间的token
    String securityKey = user.getSecurityKey();
    String token = createToken(username, securityKey);

    // 存入redis里面,用户名和秘钥,以token为键,所以只要token被改动,就会认为非法用户
    stringRedisTemplate.opsForHash().put(token, "username", username);
    stringRedisTemplate.opsForHash().put(token, "securityKey", securityKey);

    // redis过期晚一点,双token机制
    // 第二步:在redis里面存储token,redis里面有效时间为60分钟;
    stringRedisTemplate.expireAt(token, new Date(System.currentTimeMillis()+lifespanRedis));

    // 3.token要给前端
    response.addHeader("bm\_token",token);

    // 在session里面存续期的次数
    request.getSession().setAttribute("renewTokenTimes", 0);
    return HttpResp.results(ResultCode.USER\_LOGIN\_SUCCESS,new Date(),token);

}

/\*\*

* 产生token对象
* @param username 用户名
* @param securityKey 安全码
* @return
*/
private String createToken(String username,String securityKey){
Map<String,Object> header = new HashMap<>();
header.put(“alg”, “HS256”);
// 链式写法
return JWT.create()
.withHeader(header)
.withClaim(“username”, username)
// .withExpiresAt(new Date(System.currentTimeMillis() + 1000 * 3)) // 60*30
// 第一步:产生30分钟有效时间的token
.withExpiresAt(new Date(System.currentTimeMillis() + lifespanToken)) // 60*30
.sign(Algorithm.HMAC256(securityKey.getBytes()));
}

@ApiOperation(value = "findAll",notes = "用户登陆api")
@GetMapping("/findAll")
public HttpResp findAll(){
    // 使用拦截器处理没有token的情况
    return HttpResp.results(ResultCode.USER\_FIND\_SUCCESS,new Date(),userService.queryAll());
}

}


#### swagger测试


![在这里插入图片描述](https://img-blog.csdnimg.cn/fe7ef12c5c2340a3bafed7e3d143500a.png)  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/ed73fbb5dd9b41e7b0797418565488a9.png)


![在这里插入图片描述](https://img-blog.csdnimg.cn/d541d165409f467b85b7171375616cdb.png)


![在这里插入图片描述](https://img-blog.csdnimg.cn/abefc2658fc6459abeff8d78a8ab9e94.png)


### 使用拦截器统一拦截方式



> 
> AuthInterceptor.java文件  
>  TokenExpiredException异常
> 
> 
> 


![在这里插入图片描述](https://img-blog.csdnimg.cn/bf368ce7a2fe4d1bbb2f560be3392f8f.png)



package com.tianju.redisDemo.interceptor;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.tianju.redisDemo.exception.IllegalUserException;
import com.tianju.redisDemo.exception.UserNeedRenewTokenException;
import com.tianju.redisDemo.exception.UserNotLoginException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@Slf4j
@Component
public class AuthInterceptor implements HandlerInterceptor {

@Autowired
private StringRedisTemplate stringRedisTemplate;

@Value("${lifespanToken}")
private Integer lifespanToken;

@Value("${lifespanRedis}")
private Integer lifespanRedis;

@Value("${MAX\_RENEW\_TOKEN\_TIME}")
private Integer MAX\_RENEW\_TOKEN\_TIME;// 最大续期次数

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    String token = request.getHeader("bm\_token");
    if (token==null || "".equals(token)){
        throw new UserNotLoginException("用户没有登陆");
    }
    // 如何获取token
    String securityKey = (String) stringRedisTemplate.opsForHash().get(token, "securityKey");
    if (securityKey==null){
        throw new IllegalUserException("非法用户");
    }

    // 过期异常,篡改异常

    JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(securityKey)).build();
    try {
        // 如果token过期,会抛出com.auth0.jwt.exceptions.TokenExpiredException
        DecodedJWT decodedJWT = jwtVerifier.verify(token);
    } catch (TokenExpiredException e) { // token过期,redis没有过期
        // 第三步:token过期,但是Redis里面的token还没有过期,此时进行续期
        String username = (String) stringRedisTemplate.opsForHash().get(token, "username");

        // 方案一:产生新的token给前端;

// String newToken = removeTokenMakeNewToken(token, username, securityKey);
// response.addHeader(“bm_token”,token);

        // 方案二:给原有的token续期,需要设置最大续期时间
        Integer renewTokenTimes = (Integer) request.getSession().getAttribute("renewTokenTimes");
        request.getSession().setAttribute("renewTokenTimes",renewTokenTimes+1);

        // 产生一个新的token,更新redis
        log.info( ">>>>>>>>>"+username+"的token过期,即将进行续期,续期次数:"+renewTokenTimes);

        if (renewTokenTimes<MAX\_RENEW\_TOKEN\_TIME){
            System.out.println("我输续期");
            stringRedisTemplate.expireAt(token, new Date(System.currentTimeMillis()+lifespanRedis));
        }
        else {
            System.out.println("不再续期");
            throw new UserNeedRenewTokenException("达到最大续期次数,需要重新登陆");
        }
    }
    return true;
}

private String removeTokenMakeNewToken(String token,String username,String securityKey){
    // 1.产生新的token
    Map<String,Object> header = new HashMap<>();
    header.put("alg", "HS256");
    // 链式写法
    String newToken = JWT.create()
            .withHeader(header)
            .withClaim("username", username)

// .withExpiresAt(new Date(System.currentTimeMillis() + 1000 * 3)) // 60*30
.withExpiresAt(new Date(System.currentTimeMillis() + lifespanToken)) // 60*30
.sign(Algorithm.HMAC256(securityKey.getBytes()));

    // 2.更新redis
    stringRedisTemplate.opsForHash().delete(token,"\*"); // 删除之前的token
    stringRedisTemplate.opsForHash().put(newToken, "username", username);
    stringRedisTemplate.opsForHash().put(newToken, "securityKey", securityKey);
    // redis过期晚一点,双token机制
    stringRedisTemplate.expireAt(newToken, new Date(System.currentTimeMillis()+lifespanRedis));

    // 3.新的token还要给前端
    return newToken;

// response.addHeader(“bm_token”,token);
}
}


![在这里插入图片描述](https://img-blog.csdnimg.cn/368910c6971446018f582426195326a1.png)



> 
> 配置拦截器 @Configuration
> 
> 
> 



package com.tianju.redisDemo.config;

import com.tianju.redisDemo.interceptor.AuthInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import javax.annotation.Resource;

@Configuration
public class BmMvcConfig implements WebMvcConfigurer {

@Resource
private AuthInterceptor authInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(authInterceptor)
            .addPathPatterns("/api/\*\*")
            .excludePathPatterns("/api/user/login");
}

}


### Vo对象给前端



> 
> 只给前端部分数据信息
> 
> 
> 



package com.tianju.redisDemo.vo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Date;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserVo {
private String username;
private String name;
private Date createTime;
}


### DTO对象


#### 日期json问题



> 
> [@JsonFormat]( )(timezone = “GMT+8”)
> 
> 
> 


HttpResp.java文件



package com.tianju.redisDemo.dto;

import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.*;

import java.io.Serializable;
import java.util.Date;

/**
* 返回给前端的响应
* @param
*/
@ApiModel(“DTO返回数据”)
@Data
@NoArgsConstructor
@AllArgsConstructor
public class HttpResp implements Serializable {
private ResultCode resultCode;
@ApiModelProperty(“time”)
@JsonFormat(pattern = “yyyy-MM-dd hh:mm:ss”,timezone = “GMT+8”)
private Date time;
@ApiModelProperty(“results”)
private T result;

public static <T> HttpResp <T> results(
        ResultCode resultCode,
        Date time,
        T results){

    HttpResp httpResp = new HttpResp();
    httpResp.setResultCode(resultCode);
    httpResp.setTime(time);
    httpResp.setResult(results);
    return httpResp;
}

}


#### 枚举类型的json化



> 
> [@JsonFormat]( )(shape = JsonFormat.Shape.OBJECT)
> 
> 
> @Getter
> 
> 
> 


ResultCode.java枚举类



package com.tianju.redisDemo.dto;

import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;

/**
* 枚举类型,http请求的返回值
*/
// 枚举类型的json化,需要有get方法
@JsonFormat(shape = JsonFormat.Shape.OBJECT)
@Getter
public enum ResultCode {
BOOK_RUSH_SUCCESS(20010,“图书抢购成功”),
BOOK_RUSH_ERROR(3001,“图书抢购失败”),
LUA_SCRIPT_ERROR(3002,“Lua脚本操作失败”),
USER_FIND_ERROR(40010,“非法请求,布隆过滤器不通过”),
USER_FIND_SUCCESS(20010,“查询用户名成功”),
USER_LOGIN_ERROR(40030,“用户登陆失败”),
USER_LOGIN_SUCCESS(20020,“用户登陆成功”),
;

@ApiModelProperty("状态码")
private Integer code;

@ApiModelProperty("提示信息")
private String msg;

private ResultCode(Integer code,String msg){
    this.code =code;
    this.msg = msg;
}

}


### application.yml配置文件



server:
port: 9099

token的过期时间是30分钟;redis的过期时间是90分钟

lifespanToken: 6000 # 1000*60*30
lifespanRedis: 20000 # 1000*60*90
MAX_RENEW_TOKEN_TIME: 1 # 最大续期次数

img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上软件测试知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

需要这份系统化的资料的朋友,可以戳这里获取

on化,需要有get方法
@JsonFormat(shape = JsonFormat.Shape.OBJECT)
@Getter
public enum ResultCode {
BOOK_RUSH_SUCCESS(20010,“图书抢购成功”),
BOOK_RUSH_ERROR(3001,“图书抢购失败”),
LUA_SCRIPT_ERROR(3002,“Lua脚本操作失败”),
USER_FIND_ERROR(40010,“非法请求,布隆过滤器不通过”),
USER_FIND_SUCCESS(20010,“查询用户名成功”),
USER_LOGIN_ERROR(40030,“用户登陆失败”),
USER_LOGIN_SUCCESS(20020,“用户登陆成功”),
;

@ApiModelProperty("状态码")
private Integer code;

@ApiModelProperty("提示信息")
private String msg;

private ResultCode(Integer code,String msg){
    this.code =code;
    this.msg = msg;
}

}


### application.yml配置文件



server:
port: 9099

token的过期时间是30分钟;redis的过期时间是90分钟

lifespanToken: 6000 # 1000*60*30
lifespanRedis: 20000 # 1000*60*90
MAX_RENEW_TOKEN_TIME: 1 # 最大续期次数

[外链图片转存中…(img-bk4zQWQh-1715804292204)]
[外链图片转存中…(img-g9cTwfqD-1715804292205)]
[外链图片转存中…(img-Zwbd8e6w-1715804292205)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上软件测试知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

需要这份系统化的资料的朋友,可以戳这里获取

  • 19
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值