SpringBoot配置JWT拦截器

目录

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. 测试

先进行登录:

在后端控制台查看token

未携带请求头:

携带请求头后:

说明拦截器生效

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/484898.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

MySQL B树 和B+数据的区别

MySQL B树 和B数据的区别 树分类&#xff1a;二叉树完全二叉树满二叉树堆红黑树 B树B树 首先我们搞清楚数据结构中的 树这个概念 树 分类&#xff1a; 二叉树 限定每个节点的子节点最多只有2个子节点&#xff0c;且有左右之分。完全二叉树 满二叉树 堆 红黑树 B树 B树 …

初识 Redis 浅谈分布式

目 录 一.认识 Redis二.浅谈分布式单机架构分布式是什么数据库分离和负载均衡理解负载均衡数据库读写分离引入缓存数据库分库分表引入微服务 三.概念补充四.分布式小结 一.认识 Redis 在 Redis 官网我们可以看到介绍 翻译过来就是&#xff1a;数以百万计的开发人员用作缓存、…

图解Kafka架构学习笔记(三)

准备Kafka环境 这里推荐使用Docker Compose快速搭建一套本地开发环境。 以下docker-compose.yml文件用来搭建一套单节点zookeeper和单节点kafka环境&#xff0c;并且在8080端口提供kafka-ui管理界面。 version: 2.1services:zoo1:image: confluentinc/cp-zookeeper:7.3.2hos…

【Flask】Flask数据模型关系

数据模型关系 一对多 如上所示&#xff0c;一个作者关联多个文章&#xff0c;暂时认定&#xff0c;一篇文章只能有一个作者。 作者以及文章的类定义如下所示&#xff1a; class Author(db.Model):id db.Column(db.Integer, primary_keyTrue)name db.Column(db.String(128)…

实现登录拦截功能

1.4、实现登录拦截功能 温馨小贴士&#xff1a;tomcat的运行原理 当用户发起请求时&#xff0c;会访问我们像tomcat注册的端口&#xff0c;任何程序想要运行&#xff0c;都需要有一个线程对当前端口号进行监听&#xff0c;tomcat也不例外&#xff0c;当监听线程知道用户想要和…

Django之Celery篇(三)

一、任务交给Celery Django任务交给Celery的方法和普通使用Celery任务的调用基本无区别,只是将执行代码的放到到View视图中 而获取结果,往往并不能把结果和第1次请求一起响应,若想获取结果是通过第2次请求获取结果 代码如下: from django.http import HttpResponsefrom …

蓝桥杯2019年第十届省赛真题-组队

一、题目 组队 题目描述 作为篮球队教练&#xff0c;你需要从以下名单中选出 1 号位至 5 号位各一名球员&#xff0c; 组成球队的首发阵容。每位球员担任 1 号位至 5 号位时的评分如下表所示。请你计算首发阵容 1 号位至 5 号位的评分之和最大可能是多少&#xff1f; &#xff…

nodejs+vue高校社团管理小程序的设计与实现python-flask-django-php

相比于以前的传统手工管理方式&#xff0c;智能化的管理方式可以大幅降低学校的运营人员成本&#xff0c;实现了高校社团管理的标准化、制度化、程序化的管理&#xff0c;有效地防止了高校社团管理的随意管理&#xff0c;提高了信息的处理速度和精确度&#xff0c;能够及时、准…

【LVGL-使用GUI Guider 拖拽式设计LVGL】

LVGL-使用GUI Guider 拖拽式设计LVGL ■ SDL2-简介■ PC模拟器&#xff1a;SDL2■ 编译工具链&#xff08;MinGW CMake&#xff09;■ 一个IDE&#xff0c;SDL可以配合以下IDE使用 ■ GUI Guider-简介■ GUI Guider-下载安装■ GUI Guider-使用■ 创建工程■ 切换成中文显示■…

阿里云2核4G服务器优惠价格,轻量165元1年,ECS 199元一年

阿里云2核4G服务器优惠价格&#xff0c;轻量165元1年&#xff0c;ECS 199元一年。2024年腾讯云服务器优惠价格表&#xff0c;一张表整理阿里云服务器最新报价&#xff0c;阿里云服务器网整理云服务器ECS和轻量应用服务器详细CPU内存、公网带宽和系统盘详细配置报价单&#xff0…

Redis技术学习|实战项目记录|商户缓存

学习资料声明 黑马程序员的Redis学习视频&#xff1a;黑马程序员Redis入门到实战教程 需要用到的知识&#xff1a;linux&#xff08;推荐韩顺平老师的教程&#xff0c;学到p30&#xff0c;创建好虚拟机和简单的几个命令就好。&#xff09;SSM。SpringBoot。 还用到了MybatisPl…

SpringBoot+Mysql实现的旅游景点酒店平台系统源码+运行教程+开发文档(参考论文)【猿来入此】优秀学员作品

今天发布的是由【猿来入此】的优秀学员独立做的一个基于springboot脚手架的旅游景点酒店预约管理系统&#xff0c;主要实现了 除脚手架功能以外下面是系统的功能&#xff1a; 前台普通用户&#xff1a;注册、登录、首页、景点列表&#xff08;预约&#xff09;、酒店列表&#…

MySQL-4.数据类型

数据库与编程不太一样&#xff0c;它会更加的珍惜字节空间&#xff0c;需考虑所定义字段的大小和所定义字段的实际使用&#xff08;有无符号&#xff09;。 4.1 数值类型 4.1.1 整型 数据类型字节数无符号数的取值范围有符号数的取值范围TINYINT10~255-128~127SMALLINT20~655…

数学建模体育建模和经济建模国防科大版

目录 6.体育中的数学建模 7.经济学问题中的数学建模 7.1.实物交换模型 7.2.边际效应 7.3.最佳消费选择模型 6.体育中的数学建模 体育科学的研究中&#xff0c;也有大量的数学建模问题&#xff0c;例如&#xff1a;棒球的最佳击球点问题、滑板滑雪赛道的设计、越野自行车比…

基于Google云原生工程师的kubernetes最佳实践(三)

目录 三、集群管理 利用node affinity、taint等机制管理node 通过pod affinity/anti-affinity机制将pod分配到合适的node Node分级管理 从Qos角度将Pod分级 用namespace隔离不同的环境和用户 配置RBAC权限控制 1. 遵循最小权限原则 2. 使用 Role 和 ClusterRole 分离权…

STM32使用滴答定时器实现delayms

在STM32上使用SysTick实现jiffies&#xff08;时间戳&#xff09;并且实现delay_ms 代码实现&#xff1a; volatile uint32_t jiffies 0; // 用于记录系统运行的jiffies数 void SysTick_Handler(void) {/* 每次SysTick中断&#xff0c;jiffies增加 */jiffies; }uint32_t tick…

matlab 智能电器的状态监测故障模拟

1、内容简介 略 83-可以交流、咨询、答疑 2、内容说明 略 U120√2sin(2) Ii1i2 逻辑关系&#xff1a; 在0-0.1&#xff0c;正弦电给并联的电容和电阻/电感供电&#xff0c;电压的有效值为120V,通过RMS模块检查电压的变化&#xff0c;在0.1时通过斩波器把电源2端与大地连接…

苹果CMS影视APP源码,二开版本带视频教程

编译app教程 工具下载&#xff1a;Android Studio 官网地址&#xff1a;https://developer.android.google.cn/studio/ 环境设置&#xff1a; 设置中文&#xff1a;https://blog.csdn.net/qq_37131111/article/details/131492844 汉化包找最新的下载就行了&#xff0c;随便下载…

SpringJPA 做分页条件查询

前言: 相信小伙伴们的项目很多都用到SpringJPA框架的吧,对于单表的增删改查利用jpa是很方便的,但是对于条件查询并且分页 是不是很多小伙伴不经常写到. 今天我整理了一下在这里分享一下. 话不多说直接上代码: Controller: RestController public class ProductInstanceContr…

idea maven配置

修改maven的路径&#xff08;使用本地的Maven&#xff09;&#xff0c;以及修改settings文件的位置和本地仓库的位置。 -DarchetypeCataloginternal 配置阿里云镜像&#xff08;在setting.xml文件中配置&#xff09; <!-- 配置阿里云 --> <mirror> <id>…