在 Spring Boot 中使用 Spring Security + JWT + MySQL 实现基于 Token 的身份认证

文章目录

  • 在 Spring Boot 中使用 Spring Security + JWT + MySQL 实现基于 Token 的身份认证
    • 一、引言
    • 二、环境搭建
      • 1、第一步:引入依赖
      • 2、第二步:配置MySQL数据库
    • 三、实现身份认证
    • 三、实现身份认证
      • 1、定义实体和数据访问层
        • 1.1、实体类定义
        • 1.2、数据访问层
      • 2、JWT工具类
        • 2.1、JwtTokenProvider类
      • 3、Spring Security配置
        • 3.1、配置WebSecurityConfigurerAdapter
        • 3.2、用户详情服务
      • 4、登录和认证接口
        • 4.1、登录接口
        • 4.2、AuthService类
        • 4.3、过滤器
    • 四、总结

在 Spring Boot 中使用 Spring Security + JWT + MySQL 实现基于 Token 的身份认证

一、引言

在现代Web应用中,安全是一个核心考虑因素。随着微服务架构和前后端分离模式的流行,传统的会话管理(基于Cookie和Session)已不再满足需求。基于Token的身份认证机制因其无状态、可扩展性高和适用于分布式系统等优点,逐渐成为主流。本文将介绍如何在Spring Boot应用中整合Spring Security、JWT和MySQL,实现基于Token的身份认证机制。

二、环境搭建

1、第一步:引入依赖

在Spring Boot项目中,首先需要引入必要的依赖。以下是pom.xml中需要添加的依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
</dependency>

2、第二步:配置MySQL数据库

创建一个名为login_system的数据库,并在application.properties中配置数据库连接信息:

spring.datasource.url=jdbc:mysql://localhost:3306/login_system
spring.datasource.username=root
spring.datasource.password=root
spring.jpa.hibernate.ddl-auto=update

三、实现身份认证

三、实现身份认证

1、定义实体和数据访问层

1.1、实体类定义

首先,定义UserRole实体类,并在它们之间建立多对多关系。使用JPA注解来标注这些类和字段。

User实体:

import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;

import java.util.Set;

@Entity
@Table(name = "users")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    @Column(nullable = false, unique = true)
    private String username;
    @Column(nullable = false, unique = true)
    private String email;
    @Column(nullable = false)
    private String password;

    @ManyToMany(fetch = FetchType.EAGER)
    @JoinTable(name = "users_roles",
               joinColumns = @JoinColumn(name = "user_id", referencedColumnName = "id"),
               inverseJoinColumns = @JoinColumn(name = "role_id", referencedColumnName = "id"))
    private Set<Role> roles;
}

Role实体:

import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;

@Entity
@Table(name = "roles")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class Role {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
}
1.2、数据访问层

UserRole创建相应的JPA仓库接口。

UserRepository接口:

import org.springframework.data.jpa.repository.JpaRepository;

import java.util.Optional;

public interface UserRepository extends JpaRepository<User, Long> {
    Optional<User> findByUsername(String username);
    Optional<User> findByEmail(String email);
    Boolean existsByEmail(String email);
    Boolean existsByUsername(String username);
}

RoleRepository接口:

import org.springframework.data.jpa.repository.JpaRepository;

import java.util.Optional;

public interface RoleRepository extends JpaRepository<Role, Long> {
    Optional<Role> findByName(String name);
}

2、JWT工具类

2.1、JwtTokenProvider类

创建JwtTokenProvider类,用于生成和解析JWT Token。使用java-jwt库来实现JWT的创建和验证。

import io.jsonwebtoken.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.function.Function;

@Component
public class JwtTokenProvider {

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

    @Value("${app.jwt-expiration-milliseconds}")
    private Long jwtExpirationInMs;

    public String generateToken(String username) {
        return generateToken(username, null);
    }

    public String generateToken(String username, Date expiration) {
        return Jwts.builder()
                .setSubject(username)
                .setIssuedAt(new Date())
                .setExpiration(expiration != null ? expiration : expirationDate())
                .signWith(SignatureAlgorithm.HS512, jwtSecret)
                .compact();
    }

    public String extractUsername(String token) {
        return extractClaim(token, Claims::getSubject);
    }

    public <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
        final Claims claims = extractAllClaims(token).getBody();
        return claimsResolver.apply(claims);
    }

    private Claims extractAllClaims(String token) {
        try {
            return Jwts.parser()
                    .setSigningKey(jwtSecret)
                    .parseClaimsJws(token)
                    .getBody();
        } catch (JwtException | IllegalArgumentException e) {
            throw new IllegalArgumentException("Expired or invalid JWT token");
        }
    }

    public Boolean validateToken(String token) {
        try {
            extractAllClaims(token);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    private Date expirationDate() {
        return new Date(System.currentTimeMillis() + jwtExpirationInMs);
    }
}

3、Spring Security配置

3.1、配置WebSecurityConfigurerAdapter

创建一个配置类,继承WebSecurityConfigurerAdapter,重写方法以配置Spring Security。

import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
            .authorizeRequests()
            .antMatchers("/api/auth/**").permitAll()
            .anyRequest().authenticated()
            .and()
            .addFilter(new JwtAuthenticationFilter(authenticationManager()))
            .addFilter(new JwtAuthorizationFilter(authenticationManager()))
            .exceptionHandling().authenticationEntryPoint(new JwtAuthenticationEntryPoint());
    }
}
3.2、用户详情服务

实现UserDetailsService接口,用于从数据库加载用户信息。

import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.stream.Collectors;

@Service
public class CustomUserDetailsService implements UserDetailsService {

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userRepository.findByUsername(username)
                .orElseThrow(() -> new UsernameNotFoundException("User not found with username: " + username));

        return new org.springframework.security.core.userdetails.User(
                user.getUsername(),
                user.getPassword(),
                user.getRoles().stream().map(role -> new SimpleGrantedAuthority(role.getName())).collect(Collectors.toList())
        );
    }
}

4、登录和认证接口

4.1、登录接口

创建一个登录接口,用户可以通过提供用户名和密码来获取JWT Token。

@RestController
@RequestMapping("/api/auth")
public class AuthController {

    @Autowired
    private AuthService authService;

    @PostMapping("/login")
    public ResponseEntity<?> createAuthenticationToken(@RequestBody AuthRequest authenticationRequest) {
        final String jwt = authService.login(authenticationRequest.getUsername(), authenticationRequest.getPassword());

        return ResponseEntity.ok(new AuthResponse(jwt));
    }
}
4.2、AuthService类
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Service;

@Service
public class AuthService {

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private JwtTokenProvider jwtTokenProvider;

    public String login(String username, String password) {
        Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
        
        return jwtTokenProvider.generateToken(authentication.getName());
    }
}
4.3、过滤器

实现两个过滤器,JwtAuthenticationFilter用于处理登录请求,生成JWT;JwtAuthorizationFilter用于验证后续请求中的JWT。

JwtAuthenticationFilter:

import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.OncePerRequestFilter;

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

public class JwtAuthenticationFilter extends OncePerRequestFilter {

    @Autowired
    private JwtTokenProvider tokenProvider;

    @Autowired
    private CustomUserDetailsService userDetailsService;

    @Autowired
    private AuthenticationManager authenticationManager;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
        final String authorizationHeader = request.getHeader("Authorization");

        String username = null;
        String jwt = null;

        if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
            jwt = authorizationHeader.substring(7);
            username = tokenProvider.extractUsername(jwt);
        }

        if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
            UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
            if (tokenProvider.validateToken(jwt, userDetails)) {
                UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
                SecurityContextHolder.getContext().setAuthentication(authToken);
            }
        }

        chain.doFilter(request, response);
    }
}

JwtAuthorizationFilter:

import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.OncePerRequestFilter;

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

public class JwtAuthorizationFilter extends OncePerRequestFilter {

    @Autowired
    private JwtTokenProvider tokenProvider;

    @Autowired
    private CustomUserDetailsService userDetailsService;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
        final String authorizationHeader = request.getHeader("Authorization");

        String username = null;
        String jwt = null;

        if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
            jwt = authorizationHeader.substring(7);
            username = tokenProvider.extractUsername(jwt);
        }

        if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
            UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
            if (token
Provider.validateToken(jwt, userDetails)) {
                UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
                SecurityContextHolder.getContext().setAuthentication(authToken);
            }
        }

        chain.doFilter(request, response);
    }
}

这样,我们就完成了基于Token的身份认证机制的实现。用户登录时,系统会生成一个JWT,用户需在随后的请求中携带此JWT进行身份验证。通过这种方式,我们可以确保应用的安全性和可扩展性。

四、总结

通过上述步骤,我们成功在Spring Boot应用中整合了Spring Security、JWT和MySQL,实现了基于Token的身份认证机制。这种机制不仅提高了应用的安全性,还增强了其可扩展性和维护性。在微服务和分布式系统中,基于Token的身份认证是推荐的做法。


版权声明:本博客内容为原创,转载请保留原文链接及作者信息。

参考文章

  • Springdoc - Spring Boot, Spring Security, JWT and MySQL
  • CSDN - 【全网最细致】SpringBoot整合Spring Security + JWT实现用户认证

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

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

相关文章

36.贪心算法3

1.坏了的计算器&#xff08;medium&#xff09; . - 力扣&#xff08;LeetCode&#xff09; 题目解析 算法原理 代码 class Solution {public int brokenCalc(int startValue, int target) {// 正难则反 贪⼼int ret 0;while (target > startValue) {if (target % 2 0…

深入理解中比较两个字符串差异的方法”或“高效比对字符串:diff-match-patch:c++实战指南

diff-match-patch 是一个强大的开源 JavaScript 库&#xff0c;由 Google 开发并维护&#xff0c;用于计算两个字符串之间的差异&#xff0c;并进行高效的匹配和补丁应用。这个库广泛应用于版本控制系统、协同编辑系统以及任何需要处理文本变化的场景。 GitHub地址&#xff1a;…

继承1 2024_9_18

1.继承的基本用法 当需要继承的时候,我们就在派生类的后面加上一个权限父类,这个权限可以是公有,保护和私有,后面就是继承的父类.此时,下面的stu这个派生类,也就可以使用Person里面的方法了. 2.继承基类成员访问方式的变化 当父类被继承到派生类的时候,此时会根据继承方式的不…

k8s的NodeIP、PodIP、ClusterIP、ExternalIP

1.NodeIP K8s集群由Master Node与Worker Node组成。 Node&#xff1a;组成k8s集群的机器&#xff0c;可以是物理机或虚拟机。 Master Node &#xff1a;管理节点也叫控制平面主要负责管理控制方面。 Worker Node&#xff1a;&#xff1a;工作节点用于部署处理业务的工作负载或p…

spring springboot 日志框架

一、常见的日志框架 JUL、JCL、Jboss-logging、logback、log4j、log4j2、slf4j.... 注意&#xff1a;SLF4j 类似于接口 Log4j &#xff0c;Logback 都是出自同一作者之手 JUL 为apache 公司产品 Spring&#xff08;commons-logging&#xff09;、Hibernate&#xff08;jboss…

万兆时代 TCP/IP如何赋能以太网飞跃

科技飞速发展&#xff0c;数据传输的需求日益增长&#xff0c;尤其是在物理、科研等领域&#xff0c;对数据传输的速度、稳定性和效率提出了更高的要求。在这样的背景下&#xff0c;万兆以太网&#xff08;10Gbit Ethernet&#xff09;以其高带宽、低延迟和强大的传输能力成为众…

视频监控摄像头国标GB28181配置参数逐条解析

转载&#xff1a;视频监控摄像头国标GB28181配置参数逐条解析 现在的很多信息化项目&#xff0c;都会涉及到国标GB28181的视频监控产品&#xff0c;当我们配置这些国标平台&#xff0c;录像机&#xff0c;摄像头时&#xff0c;如果对相关参数的定义不清楚的话&#xff0c;会给我…

Vulnhub:BlueSky

靶机下载地址 信息收集 主机发现 nmap扫描攻击机同网段存活主机。 nmap 192.168.31.0/24 -Pn -T4 靶机ip&#xff1a;192.168.31.171。 端口扫描 nmap 192.168.31.171 -A -p- -T4 开放端口22,8080。 目录扫描 访问8080端口&#xff0c;如图&#xff0c;是tomcat管理页面…

硬件工程师笔试面试——变压器

目录 9、变压器 9.1 基础 变压器原理图 变压器实物图 9.1.1 概念 9.1.2 变压器组成结构 9.1.3 变压器原理 9.1.4 变压器的类型 9.1.5 应用领域 9.2 相关问题 9.2.1 变压器的工作原理是什么? 9.2.2 如何选择合适的变压器类型? 9.2.3 变压器在实际应用中,如何进行…

安卓BLE蓝牙通讯

蓝牙测试demo 简介   Android手机间通过蓝牙方式进行通信&#xff0c;有两种常见的方式&#xff0c;一种是socket方式&#xff08;传统蓝牙&#xff09;&#xff0c;另一种是通过GATT&#xff08;BLE蓝牙&#xff09;。与传统蓝牙相比&#xff0c;BLE 旨在大幅降低功耗。这样…

iKuai使用及设置流程

iKuai使用及设置流程 iKuai安装步骤 一、配置主机 1.电脑连接ETH0网口 2.ETH1网口连接猫上面的千兆口 3.手动配置pc的IP地址和192.168.1.1./24在同一网段 3.浏览器输入192.168.1.1 admin admin 二、外网设置 1.直接联通电信网络设置 2.点击 网络设置-内外网设置-点击接…

Python “字符串操作” ——Python面试100道实战题目练习,巩固知识、检查技术、成功就业

本文主要是作为Python中列表的一些题目&#xff0c;方便学习完Python的元组之后进行一些知识检验&#xff0c;感兴趣的小伙伴可以试一试&#xff0c;含选择题、判断题、实战题、填空题&#xff0c;答案在第五章。 在做题之前可以先学习或者温习一下Python的列表&#xff0c;推荐…

食品检测与分类系统源码分享

食品检测与分类检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Computer V…

推荐10款最佳的电脑监控软件,知名电脑监控软件推荐

随着互联网和科技的飞速发展&#xff0c;电脑监控软件成为企业和个人用户管理和保护信息安全的必备工具。这些软件可以帮助你实时了解电脑的使用情况、保护隐私、优化工作效率&#xff0c;甚至防止潜在的安全威胁。在这篇文章中&#xff0c;我们将为你推荐10款最佳的电脑监控软…

iPhone 16系列:摄影艺术的全新演绎,探索影像新境界

在科技的浪潮中&#xff0c;智能手机摄影功能的进化从未停歇。 苹果公司即将推出的iPhone 16系列&#xff0c;以其卓越的相机升级和创新特性&#xff0c;再次站在了手机摄影的前沿。 从硬件到软件&#xff0c;从拍照体验到图像处理&#xff0c;iPhone 16系列都展现了其在移动…

1×4矩阵键盘详解(STM32)

目录 一、介绍 二、传感器原理 工作原理介绍 三、程序设计 main.c文件 1x4key.h文件 1x4key.c文件 四、实验效果 五、资料获取 项目分享 一、介绍 矩阵键盘是单片机外部设备中所使用排布类似于矩阵键盘组&#xff0c;矩阵式结构的键盘会比独立键盘复杂一点&#xff…

国内外ChatGPT网站集合,无限制使用【2024-09最新】~

经过我一年多以来&#xff0c;使用各种AI工具的体验&#xff0c;我收集了一批AI工具和站点 这些工具都是使用的最强最主流的模型&#xff0c;也都在各个领域里都独领风骚的产品。 而且&#xff0c;这些工具你都可以无限制地使用。 无论你是打工人、科研工作者、学生、文案写…

Python 数学建模——傅里叶变换时间序列分析

文章目录 前言原理Python 库函数实现单周期函数多周期函数真实数据挑战 前言 在数学建模过程中&#xff0c;得到一个序列 x 1 , ⋯ , x n x_1,\cdots,x_n x1​,⋯,xn​&#xff0c;我们首先要进行数据分析&#xff0c;其中就包括分析数据的周期性。这里的周期性不是数学上严格…

逆向学习系列(三)adb的使用

由于是记录学习&#xff0c;我就用结合自己的理解&#xff0c;用最通俗的语言进行讲解。 adb是android debug bridge的简写&#xff0c;其作用就是将电脑和手机相连接&#xff0c;用电脑控制手机。 一、adb哪里来 我使用的adb一般都是安装模拟器的时候&#xff0c;模拟器自带…

深入探索Android开发之Java核心技术学习大全

Android作为全球最流行的移动操作系统之一&#xff0c;其开发技能的需求日益增长。本文将为您介绍一套专为Android开发者设计的Java核心技术学习资料&#xff0c;包括详细的学习大纲、PDF文档、源代码以及配套视频教程&#xff0c;帮助您从Java基础到高级特性&#xff0c;再到A…