前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >重学SpringBoot3-集成Spring Security(四)

重学SpringBoot3-集成Spring Security(四)

作者头像
CoderJia
发布2024-10-20 09:22:06
470
发布2024-10-20 09:22:06
举报
文章被收录于专栏:CoderJia的工作笔记

在现代应用开发中,安全性和数据管理是两大重要模块。Spring Security 提供了全面的安全解决方案,而 Spring Data JPA 则简化了与数据库的交互。将两者结合,可以在保护应用的同时,轻松实现基于用户身份的访问控制、权限管理和安全的数据存储操作。

这篇博客将介绍如何在 Spring Boot 3 项目中,整合 Spring SecuritySpring Data JPA,以实现用户认证和基于数据库的授权机制。

1. 创建项目

1.1 项目依赖

在 Spring Boot 3 项目中,首先要添加相关的依赖。你可以在 pom.xml 文件中引入以下 Maven 依赖:

代码语言:javascript
复制
<dependencies>
    <!-- Spring Boot Starter for Web Applications -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!-- Spring Security for Authentication and Authorization -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>

    <!-- Spring Data JPA for Database Access -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>

    <!-- MySQL Database for Testing -->
    <dependency>
        <groupId>com.mysql</groupId>
        <artifactId>mysql-connector-j</artifactId>
        <scope>runtime</scope>
    </dependency>
    
    <!--for bean -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>

这些依赖包括了 Spring Web、Spring Security 和 Spring Data JPA,能够满足创建一个基本的安全和数据库集成的应用。

1.2 创建配置文件

可以参考之前的文章《重学SpringBoot3-Spring Data JPA》,进行Spring Data JPA 的配置:

代码语言:javascript
复制
spring:
  application:
    name: spring-boot3-13-security
  datasource:
    url: jdbc:mysql://localhost:3306/spring_security?useSSL=false&serverTimezone=UTC
    username: root
    password: root123
    driver-class-name: com.mysql.cj.jdbc.Driver
  jpa:
    database-platform: org.hibernate.dialect.MySQL5Dialect  # 指定Hibernate使用的数据库方言为MySQL 5.x。
    generateDdl: true   # 自动更新数据库表结构
    show-sql: true        # 是否显示 SQL 语句

2. 数据库模型

假设我们有一个 User 实体,其中包含用户的登录信息和角色信息。使用 Spring Data JPA 来定义这个实体类:

代码语言:javascript
复制
package com.coderjia.boot313security.bean;

import jakarta.persistence.CascadeType;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.JoinTable;
import jakarta.persistence.ManyToMany;
import jakarta.persistence.Table;
import lombok.Data;

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

/**
 * @author CoderJia
 * @create 2024/10/19 下午 04:12
 * @Description
 **/
@Data
@Entity
@Table(name = "users")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(unique = true)
    private String username;

    private String password;

    @ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    @JoinTable(
            name = "users_roles",
            joinColumns = @JoinColumn(name = "user_id"),
            inverseJoinColumns = @JoinColumn(name = "role_id")
    )
    private Set<Role> roles = new HashSet<>();

}

同时,我们还需要定义一个 Role 实体,来表示用户的权限或角色:

代码语言:javascript
复制
package com.coderjia.boot313security.bean;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.Data;

/**
 * @author CoderJia
 * @create 2024/10/19 下午 04:13
 * @Description
 **/
@Data
@Entity
@Table(name = "roles")
public class Role {

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

    @Column(unique = true)
    private String name;
}

这样,我们就定义好了与数据库交互的基本模型,即用户和角色的关系。


3. 创建用户仓库

为了与数据库交互,我们使用 Spring Data JPA 创建一个 UserRepository,它可以查询用户并获取其角色:

代码语言:javascript
复制
package com.coderjia.boot313security.dao;

import org.springframework.data.jpa.repository.JpaRepository;
import com.coderjia.boot313security.bean.User;

import java.util.Optional;

/**
 * @author CoderJia
 * @create 2024/10/19 下午 04:16
 * @Description
 **/
public interface UserRepository extends JpaRepository<User, Long> {
    Optional<User> findByUsername(String username);
}

这个接口继承了 JpaRepository,并提供了根据用户名查找用户的方法。我们已经完成了 Spring Boot 3 集成 Spring Data JPA 的基本配置和功能实现。运行应用后,Spring Data JPA 会自动创建数据库表,并处理数据库的 CRUD 操作。


4. Spring Security 配置

要使用 Spring Security 进行用户认证,我们需要配置 SecurityConfig,并实现自定义的 UserDetailsService 来与数据库中的用户信息进行集成。

4.1 自定义 UserDetailsService

想从数据库加载用户信息,就需要创建一个自定义的 UserDetailsService 实现类,它的主要作用:

  1. 用户认证:
    • UserDetailsService 负责从数据源(如数据库、LDAP等)中加载用户特定的安全信息,包括用户名、密码和权限(角色)。
    • Spring Security 使用 UserDetailsService 来验证用户提供的凭据是否正确。
  2. 用户授权:
    • 加载用户的权限信息,以便在认证成功后进行授权检查。
    • 权限信息通常包括用户的角色(如 ROLE_ADMIN, ROLE_USER 等),这些角色用于控制用户可以访问的资源和操作。
代码语言:javascript
复制
package com.coderjia.boot313security.config;

import com.coderjia.boot313security.dao.UserRepository;
import com.coderjia.boot313security.bean.User;
import com.coderjia.boot313security.bean.Role;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.core.userdetails.User.UserBuilder;
import org.springframework.stereotype.Service;

import static org.springframework.security.core.userdetails.User.withUsername;


/**
 * @author CoderJia
 * @create 2024/10/19 下午 04:20
 * @Description
 **/
@Service
public class CustomUserDetailsService implements UserDetailsService {

    private final UserRepository userRepository;

    public CustomUserDetailsService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

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

        UserBuilder builder = withUsername(user.getUsername());
        builder.password(user.getPassword());
        builder.roles(user.getRoles().stream().map(Role::getName).toArray(String[]::new));
        return builder.build();
    }
}

这里,我们根据数据库中的用户信息加载用户,并将角色转换为 Spring Security 能识别的格式。

4.2 Spring Security 配置

现在,我们需要配置 HttpSecurity,具体见《重学SpringBoot3-集成Spring Security(一)》,使其使用自定义的 UserDetailsService 来进行用户验证:

代码语言:javascript
复制
package com.coderjia.boot313security.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;

/**
 * @author CoderJia
 * @create 2024/10/13 下午 01:57
 * @Description
 **/
@Configuration
@EnableWebSecurity
public class SecurityConfig {

    // 通过构造函数注入自定义UserDetailsService
    private final CustomUserDetailsService userDetailsService;

    public SecurityConfig(CustomUserDetailsService userDetailsService) {
        this.userDetailsService = userDetailsService;
    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                .authorizeHttpRequests(authz -> authz
                        .requestMatchers("/admin/**").hasRole("ADMIN")
                        .anyRequest().authenticated()
                )
                .userDetailsService(userDetailsService)
                .formLogin(Customizer.withDefaults());
        return http.build();
    }

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

这段配置确保了 /admin/** 路径只能由具有 ADMIN 角色的用户访问,其他路径则需要用户登录后才能访问。


5. 使用 Bcrypt 加密密码

在实际开发中,我们不能将密码以明文形式存储在数据库中。我们可以使用 Spring Security 提供的 BCryptPasswordEncoder 来加密用户密码:

代码语言:javascript
复制
@Bean
public PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
}

在用户注册或更新密码时,使用这个加密器对密码进行加密后再存储:

代码语言:javascript
复制
String encodedPassword = passwordEncoder.encode(plainPassword);
user.setPassword(encodedPassword);
userRepository.save(user);

6. 启动应用与测试

至此,我们已经完成了 Spring Boot 3 项目中 Spring SecuritySpring Data JPA 的集成。

6.1 启动应用

可以启动应用,表结构已自动创建:

表结构自动创建
表结构自动创建

6.2 插入数据

创建一个用户 CoderJia,密码使用 Bcrypt 加密:

Bcrypt 加密
Bcrypt 加密
插入用户
插入用户
插入角色
插入角色
插入关系
插入关系

6.3 登陆测试

访问 /admin 页面,提示登录,输入用户名和密码之后,成功登录:

登录验证
登录验证
登录成功
登录成功
后台校验密码的过程
后台校验密码的过程

7. 总结

在这篇博客中,我们通过使用 Spring Boot 3,将 Spring SecuritySpring Data JPA 整合在一起,实现了数据库驱动的用户认证和基于角色的授权机制。通过定义用户和角色实体、实现自定义的 UserDetailsService,我们轻松实现了用户的身份验证与访问控制。

这种结合方式不仅在安全性上提供了极大的灵活性,也让数据管理变得更加简洁高效。

在后续的博客中,我们可以进一步探讨如何使用 JWT、OAuth2 等机制来强化认证与授权的实现。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-10-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 创建项目
    • 1.1 项目依赖
      • 1.2 创建配置文件
      • 2. 数据库模型
      • 3. 创建用户仓库
      • 4. Spring Security 配置
        • 4.1 自定义 UserDetailsService
          • 4.2 Spring Security 配置
          • 5. 使用 Bcrypt 加密密码
          • 6. 启动应用与测试
            • 6.1 启动应用
              • 6.2 插入数据
                • 6.3 登陆测试
                • 7. 总结
                相关产品与服务
                数据库
                云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档