51工具盒子

依楼听风雨
笑看云卷云舒,淡观潮起潮落

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

本文将会带你了解在 Spring Boot 中如何使用 Spring Security、JWT 和 MySQL 数据库实现基于 Token 的身份认证。

JWT (JSON Web Token)概览 {#jwt-json-web-token概览}

JWT 是 JSON Web Token 的缩写,是一种安全地在各方之间传输信息的开放标准。它是一种紧凑、自包含的数据传输方法,通常用于客户端和服务器之间的数据传输。

JWT 通常用于认证和授权,服务器通过验证 JWT 中包含的数字签名来验证用户。

JWT 由三部分组成:Header、Payload 和 Signature(签名)。

  • Header 包含 Token 类型和 Token 签名算法的元数据。
  • Payload 包含关于被验证用户或实体的声明(Claim)或陈述。这些声明可包括用户 ID、用户名或电子邮件地址等信息。
  • Signature(签名)使用秘钥和 Header 及 Payload 生成,以确保 JWT 的完整性。

使用 JWT 的一个好处是它们是无状态的,这意味着服务器无需跟踪用户的身份认证状态。这可以提高可扩展性和性能。此外,JWT 可以在不同的域和服务中使用,只要它们共享相同的秘钥来验证签名即可。

Spring Security 概览 {#spring-security-概览}

Spring Security 是一个提供身份认证、授权和防护常见攻击的框架。它为确保 Web 和响应式应用程序的安全提供一流的支持,是保护基于 Spring 的应用程序的事实标准。

Spring Security 用于保护 Web 应用程序、REST API 和微服务的安全,为身份认证和授权提供内置支持。

数据库表结构 {#数据库表结构}

数据库表关系图

添加 Maven 依赖 {#添加-maven-依赖}

添加如下依赖到 Spring Boot 项目:

<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.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-impl --> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-impl</artifactId> <version>0.11.5</version> <scope>runtime</scope> </dependency> <!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-api --> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-api</artifactId> <version>0.11.5</version> </dependency> <!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-jackson --> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-jackson</artifactId> <version>0.11.5</version> <scope>runtime</scope> </dependency>

配置 MySQL 数据库 {#配置-mysql-数据库}

通过如下命令在 MySQL 中创建一个数据库,名为 login_system

create database login_system

使用 MySQL数据库,需要配置数据库 URL、用户名和密码,以便 Spring 能在启动时与数据库建立连接。编辑 src/main/resources/application.properties 文件,添加以下属性:

spring.datasource.url = jdbc:mysql://localhost:3306/login_system
spring.datasource.username = root
spring.datasource.password = root

Hibernate ddl auto (create, create-drop, validate, update)

spring.jpa.hibernate.ddl-auto = update

logging.level.org.springframework.security=DEBUG

Model 层 - 创建 JPA 实体 {#model-层---创建-jpa-实体}

创建 UserRole JPA 实体,并在它们之间建立 多对多(MANY-to-MANY) 关系。

使用 JPA 注解在 UserRole 实体之间建立 多对多 关系。

User {#user}

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

import java.util.Set;

@Setter @Getter @NoArgsConstructor @AllArgsConstructor @Entity @Table(name = "users") 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, cascade = CascadeType.ALL)
@JoinTable(name = &quot;users_roles&quot;,
    joinColumns = @JoinColumn(name = &quot;user_id&quot;, referencedColumnName = &quot;id&quot;),
        inverseJoinColumns = @JoinColumn(name = &quot;role_id&quot;, referencedColumnName = &quot;id&quot;)
)
private Set&lt;Role&gt; roles;

}

Role {#role}

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

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

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;

}

Repository 层 {#repository-层}

UserRepository {#userrepository}

import net.javaguides.todo.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.Optional;

public interface UserRepository extends JpaRepository<User, Long> {

Optional&lt;User&gt; findByUsername(String username);

Boolean existsByEmail(String email);

Optional&lt;User&gt; findByUsernameOrEmail(String username, String email);

boolean existsByUsername(String username);

}

RoleRepository {#rolerepository}

import net.javaguides.todo.entity.Role;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.Map; import java.util.Optional;

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

JWT 实现 {#jwt-实现}

在 Spring boot 项目中创建一个 security 包,并添加以下与 JWT 相关的类。

JwtAuthenticationEntryPoint {#jwtauthenticationentrypoint}

创建 JwtAuthenticationEntryPoint 类,如下:

import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;

import java.io.IOException;

@Component public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {

@Override
public void commence(HttpServletRequest request,
                     HttpServletResponse response,
                     AuthenticationException authException) throws IOException, ServletException {

    response.sendError(HttpServletResponse.SC_UNAUTHORIZED, authException.getMessage());
}

}

AuthenticationEntryPointExceptionTranslationFilter 用来启动身份认证方案。它是一个入口点,用于检查用户是否已通过身份认证,如果用户已经认证,则登录该用户,否则抛出异常(unauthorized)。通常情况下,在简单的应用程序中可以直接使用该类,但当在 REST、JWT 等中使用 Spring Security 时,就必须对其进行继承,以提供更好的 Spring Security 过滤器链(filter chain)管理。

JWT - 修改 application.properties {#jwt---修改-applicationproperties}

application.properties 文件中添加以下两个与 JWT 相关的属性:

app.jwt-secret=daf66e01593f61a15b857cf433aae03a005812b31234e149036bcc8dee755dbb
app-jwt-expiration-milliseconds=604800000

JTW 工具类 - JwtTokenProvider {#jtw-工具类---jwttokenprovider}

创建一个名为 JwtTokenProvider 工具类,用于生成、验证 JWT 以及从 JWT 中提取信息。

import io.jsonwebtoken.*;
import io.jsonwebtoken.io.Decoders;
import io.jsonwebtoken.security.Keys;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;

import java.security.Key; import java.util.Date;

@Component public class JwtTokenProvider {

private static final Logger logger = LoggerFactory.getLogger(JwtTokenProvider.class);

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

@Value(&quot;${app-jwt-expiration-milliseconds}&quot;)
private long jwtExpirationDate;

// 生成 JWT token
public String generateToken(Authentication authentication){
    String username = authentication.getName();

    Date currentDate = new Date();

    Date expireDate = new Date(currentDate.getTime() + jwtExpirationDate);

    String token = Jwts.builder()
            .setSubject(username)
            .setIssuedAt(new Date())
            .setExpiration(expireDate)
            .signWith(key())
            .compact();
    return token;
}

private Key key(){
    return Keys.hmacShaKeyFor(
            Decoders.BASE64.decode(jwtSecret)
    );
}

// 从 Jwt token 获取用户名
public String getUsername(String token){
    Claims claims = Jwts.parserBuilder()
            .setSigningKey(key())
            .build()
            .parseClaimsJws(token)
            .getBody();
    String username = claims.getSubject();
    return username;
}

// 验证 Jwt token
public boolean validateToken(String token){
    try{
        Jwts.parserBuilder()
                .setSigningKey(key())
                .build()
                .parse(token);
        return true;
    } catch (MalformedJwtException e) {
        logger.error(&quot;Invalid JWT token: {}&quot;, e.getMessage());
    } catch (ExpiredJwtException e) {
        logger.error(&quot;JWT token is expired: {}&quot;, e.getMessage());
    } catch (UnsupportedJwtException e) {
        logger.error(&quot;JWT token is unsupported: {}&quot;, e.getMessage());
    } catch (IllegalArgumentException e) {
        logger.error(&quot;JWT claims string is empty: {}&quot;, e.getMessage());
    }
    return false;
}

}

generateToken 方法 {#generatetoken-方法}

public String generateToken(Authentication authentication){
    String username = authentication.getName();
Date currentDate = new Date();

Date expireDate = new Date(currentDate.getTime() + jwtExpirationDate);

String token = Jwts.builder()
        .setSubject(username)
        .setIssuedAt(new Date())
        .setExpiration(expireDate)
        .signWith(key())
        .compact();
return token;

}

generateToken(Authentication authentication) 方法根据提供的 Authentication 对象生成一个新的 JWT,该对象包含被验证用户的信息。它使用 Jwts.builder() 方法创建一个新的 JwtBuilder 对象,设置 JWT 的 subject(即用户名)、发布日期(issue date)和到期日期(expiration date),并使用 key() 方法对 JWT 进行签名。最后,它会以字符串形式返回 JWT。

getUsername(String token) {#getusernamestring-token}

// 从 Jwt token 获取用户名
public String getUsername(String token){
    Claims claims = Jwts.parserBuilder()
            .setSigningKey(key())
            .build()
            .parseClaimsJws(token)
            .getBody();
    String username = claims.getSubject();
    return username;
}

getUsername(String token) 方法从提供的 JWT 中提取 username。该方法使用 Jwts.parserBuilder() 方法创建一个新的 JwtParserBuilder 对象,使用 key() 方法设置签名密钥(Signing Key),并使用 parseClaimsJws() 方法解析 JWT。然后,它会从 JWT 的 Claims 对象中获取 subject(即用户名),并以字符串形式返回。

validateToken(String token) {#validatetokenstring-token}

// 校验 Jwt token
public boolean validateToken(String token){
    try{
        Jwts.parserBuilder()
                .setSigningKey(key())
                .build()
                .parse(token);
        return true;
    } catch (MalformedJwtException e) {
        logger.error("Invalid JWT token: {}", e.getMessage());
    } catch (ExpiredJwtException e) {
        logger.error("JWT token is expired: {}", e.getMessage());
    } catch (UnsupportedJwtException e) {
        logger.error("JWT token is unsupported: {}", e.getMessage());
    } catch (IllegalArgumentException e) {
        logger.error("JWT claims string is empty: {}", e.getMessage());
    }
    return false;
}

validateToken(String token) 方法会验证所提供的 JWT。该方法使用 Jwts.parserBuilder() 方法创建一个新的 JwtParserBuilder 对象,使用 key() 方法设置签名密钥,并使用 parse() 方法解析 JWT。如果 JWT 有效,该方法会返回 true。如果 JWT 无效或已过期,该方法会使用 logger 对象输出错误信息并返回 false

JwtAuthenticationFilter {#jwtauthenticationfilter}

在 Spring Boot 应用程序中创建 JwtAuthenticationFilter 类,该类可拦截传入的 HTTP 请求并验证包含在 Authorization 头中的 JWT Token。如果 Token 有效,Filter 就会在 SecurityContext 中设置当前用户的 Authentication

import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;

@Component public class JwtAuthenticationFilter extends OncePerRequestFilter {

private JwtTokenProvider jwtTokenProvider;

private UserDetailsService userDetailsService;

public JwtAuthenticationFilter(JwtTokenProvider jwtTokenProvider, UserDetailsService userDetailsService) {
    this.jwtTokenProvider = jwtTokenProvider;
    this.userDetailsService = userDetailsService;
}

@Override
protected void doFilterInternal(HttpServletRequest request,
                                HttpServletResponse response,
                                FilterChain filterChain) throws ServletException, IOException {

    // 从 request 获取 JWT token
    String token = getTokenFromRequest(request);

    // 校验 token
    if(StringUtils.hasText(token) &amp;&amp; jwtTokenProvider.validateToken(token)){

        // 从 token 获取 username
        String username = jwtTokenProvider.getUsername(token);

        // 加载与令 token 关联的用户
        UserDetails userDetails = userDetailsService.loadUserByUsername(username);

        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(
            userDetails,
            null,
            userDetails.getAuthorities()
        );

        authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));

        SecurityContextHolder.getContext().setAuthentication(authenticationToken);

    }

    filterChain.doFilter(request, response);
}

private String getTokenFromRequest(HttpServletRequest request){

    String bearerToken = request.getHeader(&quot;Authorization&quot;);

    if(StringUtils.hasText(bearerToken) &amp;&amp; bearerToken.startsWith(&quot;Bearer &quot;)){
        return bearerToken.substring(7, bearerToken.length());
    }

    return null;
}

}

上述代码的关键点如下:

  • 该类继承了 Spring 的 OncePerRequestFilter,可确保每个请求只执行一次过滤器。
  • 构造函数需要两个依赖:JwtTokenProviderUserDetailsService,它们是通过 Spring 的构造函数依赖注入机制注入的。
  • doFilterInternal 方法是 Filter 的主要逻辑。它使用 getTokenFromRequest 方法从 Authorization Header 中提取 JWT Token,使用 JwtTokenProvider 类验证 Token,并在 SecurityContextHolder 中设置 Authentication 信息。
  • getTokenFromRequest 方法会解析 Authorization Header,并返回 Token 部分。
  • SecurityContextHolder 用于存储当前 request 的 Authentication 信息。在这种情况下,Filter 会将 UsernamePasswordAuthenticationToken 与该 Token 关联的 UserDetailsauthorities(授权)设置在一起。

CustomUserDetailsService {#customuserdetailsservice}

创建一个 Service,根据 nameemail 从数据库中加载用户详细信息。

创建一个 CustomUserDetailsService,它实现了 UserDetailsService 接口(Spring Security 内置接口),并提供了 loadUserByUername() 方法的实现:

import lombok.AllArgsConstructor;
import net.javaguides.todo.entity.User;
import net.javaguides.todo.repository.UserRepository;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import java.util.Set; import java.util.stream.Collectors;

@Service @AllArgsConstructor public class CustomUserDetailsService implements UserDetailsService {

private UserRepository userRepository;

@Override
public UserDetails loadUserByUsername(String usernameOrEmail) throws UsernameNotFoundException {

    User user = userRepository.findByUsernameOrEmail(usernameOrEmail, usernameOrEmail)
            .orElseThrow(() -&gt; new UsernameNotFoundException(&quot;User not exists by Username or Email&quot;));

    Set&lt;GrantedAuthority&gt; authorities = user.getRoles().stream()
            .map((role) -&gt; new SimpleGrantedAuthority(role.getName()))
            .collect(Collectors.toSet());

    return new org.springframework.security.core.userdetails.User(
            usernameOrEmail,
            user.getPassword(),
            authorities
    );
}

}

Spring Security 使用 UserDetailsService 接口,该接口包含 loadUserByUsername(String username) 方法,用于查找给定 usernameUserDetails

UserDetails 接口代表一个经过认证的用户对象,Spring Security 提供了 org.springframework.security.core.userdetails.User 的开箱即用实现。

Spring Security 配置 {#spring-security-配置}

创建 SpringSecurityConfig 类,并添加以下配置:

import lombok.AllArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;

@Configuration @AllArgsConstructor public class SpringSecurityConfig {

private UserDetailsService userDetailsService;

@Bean
public static PasswordEncoder passwordEncoder(){
    return new BCryptPasswordEncoder();
}

@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {

    http.csrf().disable()
            .authorizeHttpRequests((authorize) -&gt; {
                  authorize.requestMatchers(&quot;/api/auth/**&quot;).permitAll();
                authorize.anyRequest().authenticated();
            });
    return http.build();
}

@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration) throws Exception {
    return configuration.getAuthenticationManager();
}

}

@Configuration 注解表示该类定义了 Spring Application Context 的配置。

@AllArgsConstructor 注解来自 Lombok 库,它会生成一个包含所有用 @NonNull 注解的字段的构造函数。

passwordEncoder() 方法是一个 Bean,用于创建 BCryptPasswordEncoder 实例,对密码进行编码。

securityFilterChain() 方法是一个定义安全过滤器链(Security Filter Chain)的 Bean。HttpSecurity 参数用于配置应用程序的安全设置。在本例中,该方法禁用 CSRF 保护,并根据 HTTP 方法和 URL 授权请求。

authenticationManager() 方法是一个提供 AuthenticationManager 的 Bean。它从 AuthenticationConfiguration 实例中检索 Authentication Manager。

Service 层 {#service-层}

创建一个 service 包,并添加以下与 service 层相关的 AuthService 接口和 AuthServiceImpl 类。

AuthService 接口 {#authservice-接口}

import net.javaguides.todo.dto.LoginDto;

public interface AuthService { String login(LoginDto loginDto); }

AuthServiceImpl 类 {#authserviceimpl-类}

import net.javaguides.todo.dto.LoginDto;
import net.javaguides.todo.repository.RoleRepository;
import net.javaguides.todo.repository.UserRepository;
import net.javaguides.todo.security.JwtTokenProvider;
import net.javaguides.todo.service.AuthService;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import java.util.HashSet; import java.util.Set;

@Service public class AuthServiceImpl implements AuthService {

private AuthenticationManager authenticationManager;
private UserRepository userRepository;
private PasswordEncoder passwordEncoder;
private JwtTokenProvider jwtTokenProvider;


public AuthServiceImpl(
        JwtTokenProvider jwtTokenProvider,
        UserRepository userRepository,
        PasswordEncoder passwordEncoder,
        AuthenticationManager authenticationManager) {
    this.authenticationManager = authenticationManager;
    this.userRepository = userRepository;
    this.passwordEncoder = passwordEncoder;
    this.jwtTokenProvider = jwtTokenProvider;
}

@Override
public String login(LoginDto loginDto) {

    Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(
            loginDto.getUsernameOrEmail(), loginDto.getPassword()));

    SecurityContextHolder.getContext().setAuthentication(authentication);

    String token = jwtTokenProvider.generateToken(authentication);

    return token;
}

}

这是 AuthService 接口的实现。它包含一个方法 login(),用于处理应用程序的登录功能。loginDto 对象包含用户输入的用户名(username)和密码(password)。

该类的构造函数需要四个参数:JwtTokenProviderUserRepositoryPasswordEncoderAuthenticationManager

login() 方法中,authenticationManager 会尝试将用户的 loginDto 凭证传递给 UsernamePasswordAuthenticationToken,从而对用户进行身份认证。如果认证成功,将使用 jwtTokenProvider 对象生成一个 Token 并返回给调用者。

该 service 类使用 @Service 注解,表明它是 Spring 服务组件,可由 Spring Context 自动发现。

Controller 层 - 返回 JWT Token 的 REST API(登录) {#controller-层---返回-jwt-token-的-rest-api登录}

创建 AuthController 类,并添加以下代码:

import lombok.AllArgsConstructor;
import net.javaguides.todo.dto.JWTAuthResponse;
import net.javaguides.todo.dto.LoginDto;
import net.javaguides.todo.service.AuthService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

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

private AuthService authService;

// Login REST API
@PostMapping(&quot;/login&quot;)
public ResponseEntity&lt;JWTAuthResponse&gt; authenticate(@RequestBody LoginDto loginDto){
    String token = authService.login(loginDto);

    JWTAuthResponse jwtAuthResponse = new JWTAuthResponse();
    jwtAuthResponse.setAccessToken(token);

    return ResponseEntity.ok(jwtAuthResponse);
}

}

这段代码定义了一个用于用户身份认证的 REST API 端点。它通过 /api/auth/login URL 接收 POST 请求,请求体中的登录凭证是一个 JSON 对象。LoginDto 对象用于将 JSON 对象映射到 Java 对象。

AuthController 类有一个构造函数,用于接收 AuthService 的实例,AuthService 提供了身份认证逻辑。

authenticate 方法接收 LoginDto 对象作为参数,并调用 AuthServicelogin 方法来执行身份认证。如果验证成功,login 方法会返回一个 JWT Token。然后,该 Token 被封装在一个 JWTAuthResponse 对象中,并作为响应返回。

@PostMapping 注解将方法映射为 HTTP POST 方法。@RequestBody 注解表示请求体应映射到 LoginDto 对象。

SQL 脚本 {#sql-脚本}

在测试 Spring Security 和 JWT 之前,请确保使用以下 SQL 脚本将数据插入相应的表中:

INSERT INTO `users` VALUES
(1,'ramesh@gmail.com','ramesh','$2a$10$5PiyN0MsG0y886d8xWXtwuLXK0Y7zZwcN5xm82b4oDSVr7yF0O6em','ramesh'),
(2,'admin@gmail.com','admin','$2a$10$gqHrslMttQWSsDSVRTK1OehkkBiXsJ/a4z2OURU./dizwOQu5Lovu','admin');

INSERT INTO roles VALUES (1,'ROLE_ADMIN'),(2,'ROLE_USER');

INSERT INTO users_roles VALUES (2,1),(1,2);

Hibernate 会自动创建数据库表,因此无需手动创建。

使用 Postman 测试 {#使用-postman-测试}

测试返回 JWT Token 的登录 REST API,如下:

在 Postman 中测试登录 REST API

一切OK!


参考:https://www.javaguides.net/2023/05/spring-boot-spring-security-jwt-mysql.html

赞(6)
未经允许不得转载:工具盒子 » 在 Spring Boot 中使用 Spring Security + JWT + MySQL 实现基于 Token 的身份认证