目录

1.背景介绍

2.前提工作

3.具体代码

(1)相关依赖

(2)相关配置文件

(3)JwtUtils类

(4)准备好登录逻辑代码(Dao、Service、Controller)

(5)拦截器JwtInterceptor类

(6)注册拦截器到配置类

4. 测试


1.背景介绍

        JWT在之前文章提到过,JWT(JSON Web Token)是一种用于身份验证和授权的开放标准(RFC 7519),它允许在网络中安全地传输声明(claims)作为 JSON 对象。JWT 可以通过数字签名或加密来验证数据的完整性和真实性,从而保证数据在传输过程中不被篡改。

        工作流程

  1. 用户通过用户名和密码等方式进行身份验证。
  2. 服务器验证用户身份,并生成一个 JWT。
  3. 服务器将 JWT 发送给客户端
  4. 客户端将 JWT 存储起来,通常是在本地存储或者内存中。
  5. 客户端将 JWT 添加到每个后续的 HTTP 请求的 Authorization 头部中。
  6. 服务器收到请求后,解析 JWT 并验证签名。
  7. 如果验证通过,则处理请求;如果验证失败,则拒绝请求。

2.前提工作

        1.Redis,用于将生成的token存入其中

        2.用户登录Controller

        3.Jwtutils、相关依赖

        4.拦截器JwtInterceptor

        5.配置类WebMvcConfig,用于注册拦截器

3.具体代码

(1)相关依赖
<dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-api</artifactId>
            <version>0.11.2</version>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-impl</artifactId>
            <version>0.11.2</version>
            <scope>runtime</scope>
        </dependency>

        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-jackson</artifactId>
            <version>0.11.2</version>
            <scope>runtime</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
(2)相关配置文件
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/login?useSSL=false&serverTimezone=UTC
    username: #自己的数据库用户名
    password: #自己的数据库密码

  redis:
    host: localhost
    port: 6379


mybatis:
  mapper-locations: classpath:mapper/*.xml


jwt:
  secret: T7e3t3AhK9kS2DdF6gZr4e7hWmYq3t5vT7e3t3AhK9kS2DdF6gZr4e7hWmYq3t5vT7e3t3AhK9kS2DdF6gZr4e7hWmYq3t5vT7e3t3AhK9kS2DdF6gZr4e7hWmYq3t5v
  expiration: 864000
(3)JwtUtils类
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.concurrent.TimeUnit;

@Component
public class JwtUtils {

    @Autowired
    private RedisTemplate<String, String> redisTemplate;


    @Value("${jwt.secret}")
    private String secret;

    @Value("${jwt.expiration}")
    private  long expiration;

    /**
     * 生成token
     * @param username
     * @return
     */
    public  String generateToken(String username) {
        String token = Jwts.builder()
                .setSubject(username)
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + expiration))
                .signWith(SignatureAlgorithm.HS512, secret)
                .compact();

        // 将 Token 存储到 Redis 中
        redisTemplate.opsForValue().set(username, token, expiration, TimeUnit.MILLISECONDS);

        return token;
    }

    /**
     * 验证token
     * @param token
     * @return
     */
    public boolean validateToken(String token) {
        // 从 Token 中获取用户名
        String username = getUsernameFromToken(token);

        // 从 Redis 中获取存储的 Token
        String storedToken = redisTemplate.opsForValue().get(username);

        // 判断 Redis 中存储的 Token 是否与传入的 Token 相同
        return storedToken != null && storedToken.equals(token);
    }

    /**
     * 删除token
     * @param username
     */
    public void removeToken(String username) {
        // 从 Redis 中删除 Token
        redisTemplate.delete(username);
    }

    /**
     * 根据token获取用户信息
     * @param token
     * @return
     */
    public String getUsernameFromToken(String token) {
        Jws<Claims> claimsJws = Jwts.parser().setSigningKey(secret).parseClaimsJws(token);
        Claims claims = claimsJws.getBody();
        return claims.getSubject();
    }

    /**
     * 判断token是否存在
     * @param username
     * @return
     */
    public String getTokenIfExists(String username) {
        // Check if a valid token exists in Redis for the given username
        String storedToken = redisTemplate.opsForValue().get(username);

        // Validate the stored token
        if (storedToken != null && validateToken(storedToken)) {
            return storedToken;
        } else {
            return null;
        }
    }


}
(4)准备好登录逻辑代码(Dao、Service、Controller)
import com.zhan.zhan215.Entity.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

import java.util.List;

@Mapper
public interface UserMapper{

        User getUserByUsernameAndPassword(@Param("username")String username,@Param("password")String password);


}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zhan.zhan215.Dao.UserMapper">

    <select id="getUserByUsernameAndPassword" parameterType="com.zhan.zhan215.Entity.User" resultType="com.zhan.zhan215.Entity.User">

        select *
        from user
        WHERE username = #{username} AND password = #{password}

    </select>

</mapper>
@RestController
@RequestMapping("/user")
@Api(tags = "User API", description = "Operations for managing users")
public class UserController {

       @Resource
       private UserService userService;

       @Resource
       private JwtUtils jwtUtils;


       @PostMapping("/login")
       @ApiOperation(value = "登录控制器")
       public ResponseBean login(@RequestParam(value = "username") String username, @RequestParam(value = "password") String password) {

           User user1 = userService.getUserByUsernameAndPassword(username, password);

           System.out.println(user1);

           if(user1!=null) {
               // 检查用户是否有token
               String existingToken = jwtUtils.getTokenIfExists(username);

               if(existingToken!=null){

                   return ResponseBean.success("已存在token,无需重复登录",existingToken);

               }else{

                   // 生成token
                   String token = jwtUtils.generateToken(username);
                   System.out.println(token);
                   return ResponseBean.success("登录成功", token);

                   // 将token返回给前端
               }

           }
           return ResponseBean.error("用户名或密码错误");

       }

       @GetMapping("/get")
       public ResponseBean getAll(@RequestHeader("Authorization") String token){

           if(jwtUtils.validateToken(token)){

               return ResponseBean.success(userMapper.getAll());

               }

           else{

               return ResponseBean.error("token无效");

           }
             // 倘若不写拦截器 则每个请求方法都要像getAll这样去做判断,非常麻烦

       }

       @GetMapping("/getAllByPage")
       public ResponseBean getAllByPage(@RequestParam(defaultValue = "1") Integer page, @RequestParam(defaultValue = "2") Integer size) {

          return ResponseBean.success(userService.getAllByPage(page, size));

       }
}
(5)拦截器JwtInterceptor类
import com.zhan.zhan215.Utils.JwtUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


@Component
public class JwtInterceptor implements HandlerInterceptor {


    @Autowired
    private JwtUtils jwtUtils;


    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 从请求头中获取 token
        String token = request.getHeader("Authorization");

        // 验证 token
        if (token != null && jwtUtils.validateToken(token)) {

            return true; // 验证通过,继续处理请求

        } else {
            // 设置响应状态码为 401
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);

            // 设置响应内容为 JSON 格式的错误信息
            String errorMessage = "{\"error\": \"token无效\"}";
            response.setContentType("application/json");
            response.setCharacterEncoding("UTF-8");
            response.getWriter().write(errorMessage);

            return false; // 验证失败,不继续处理请求

        }



    }

}
(6)注册拦截器到配置类
import com.zhan.zhan215.Interceptor.JwtInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;


@Configuration
public class WebMvcConfig  implements WebMvcConfigurer {

    @Autowired
    private JwtInterceptor jwtInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(jwtInterceptor)
                .addPathPatterns("/user/getAllByPage")// 配置拦截路径,这里假设所有 API 都需要验证 token
                .excludePathPatterns("/user/search");// 这是可以排除的路径,
        // 比如登录接口,登录接口不需要 token 验证
        
        // 如果有多个拦截器,可以这样继续添加
        // registry.addInterceptor(jwtInterceptor).addPathPatterns("/user/login");
        // registry.addInterceptor(jwtInterceptor).addPathPatterns("/user/update");
        // registry.addInterceptor(jwtInterceptor).addPathPatterns("/user/delete");
        // registry.addInterceptor(jwtInterceptor).addPathPatterns("/user/getById");

    }

}

4. 测试

先进行登录:

springboot gateway 使用jwt作为token_java

在后端控制台查看token

springboot gateway 使用jwt作为token_spring boot_02

未携带请求头:

springboot gateway 使用jwt作为token_spring boot_03

携带请求头后:

springboot gateway 使用jwt作为token_spring_04

说明拦截器生效