Spring Boot安全starter如何配置

wen 网络安全 2

本文目录导读:

Spring Boot安全starter如何配置

  1. 第一阶段:快速入门与默认行为
  2. 第二阶段:最常用的配置(内存用户 + 表单登录)
  3. 第三阶段:自定义 Security 配置类(最主流方式)
  4. 第四阶段:使用数据库进行用户认证(生产环境)
  5. 第五阶段:保护 REST API(JWT / 无状态)
  6. 常见问题与技巧
  7. 总结配置步骤

Spring Boot 的安全(Security)配置主要通过 Spring Security 来实现,官方提供了一个非常方便的 starter 包:spring-boot-starter-security

以下是针对不同场景的详细配置指南,从入门到常用自定义设置。

第一阶段:快速入门与默认行为

pom.xmlbuild.gradle 中添加依赖后,无需任何额外代码,应用即被保护。

添加依赖(Maven)

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

Gradle

implementation 'org.springframework.boot:spring-boot-starter-security'

默认生效规则

  • 所有端点都被保护:除了 /actuator/health 等极少数端点外,所有HTTP接口都需要认证。
  • 自动生成密码:启动日志中会打印一行:Using generated security password: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
  • 默认用户名user
  • 登录页面:Spring Security 会提供一个默认的 HTML 登录页面,访问 /login 即可。
  • 登出:访问 /logout

第二阶段:最常用的配置(内存用户 + 表单登录)

通常你不会想用启动时随机生成的密码,而是在 application.ymlapplication.properties 中配置固定的用户名和密码。

配置 application.yml

spring:
  security:
    user:
      name: admin          # 自定义用户名
      password: 123456     # 自定义密码
      roles: ADMIN         # 用户角色(可选)

这种方式适合开发环境或极简应用,对于生产环境,建议使用数据库或 OAuth2。

第三阶段:自定义 Security 配置类(最主流方式)

为了自定义安全过滤规则、资源放行、加密方式等,你需要创建一个配置类。

创建安全配置类 SecurityConfig.java

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
@EnableWebSecurity  // 启用 Spring Security 的 Web 安全功能
public class SecurityConfig {
    // 核心:定义安全过滤链(替换旧的 WebSecurityConfigurerAdapter)
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            // 1. 授权规则
            .authorizeHttpRequests(authorize -> authorize
                .requestMatchers("/", "/home", "/public/**").permitAll()  // 放行公开路径
                .requestMatchers("/admin/**").hasRole("ADMIN")           // 需要 ADMIN 角色
                .requestMatchers("/user/**").hasAnyRole("USER", "ADMIN") // 需要 USER 或 ADMIN 角色
                .anyRequest().authenticated()                             // 其余所有请求都需要登录
            )
            // 2. 登录配置(使用表单登录)
            .formLogin(form -> form
                .loginPage("/login")           // 自定义登录页(需自己写Controller)
                .permitAll()                   // 允许所有人访问登录页
                .defaultSuccessUrl("/dashboard", true) // 登录成功后跳转
                .failureUrl("/login?error=true")
            )
            // 3. 注销配置
            .logout(logout -> logout
                .logoutSuccessUrl("/login?logout")
                .permitAll()
            )
            // 4. 其他功能(此处禁用CSRF便于Postman测试,生产环境建议开启)
            .csrf(csrf -> csrf.disable()); 
        return http.build();
    }
    // 配置内存用户(测试用,生产环境建议用数据库)
    @Bean
    public UserDetailsService users() {
        // InMemoryUserDetailsManager 是官方提供的简易实现
        return new InMemoryUserDetailsManager(
            User.withUsername("admin")       // 用户名
                .password("{noop}admin123")  // {noop} 表示密码不加密(明文)
                .roles("ADMIN")
                .build(),
            User.withUsername("user")
                .password("{noop}user123")
                .roles("USER")
                .build()
        );
    }
    // (可选)配置密码编码器,这样就不需要 {noop} 了
    // @Bean
    // public PasswordEncoder passwordEncoder() {
    //     return new BCryptPasswordEncoder();  // 推荐使用 BCrypt
    // }
}

常见注解说明

  • @EnableWebSecurity:标志这是一个 Spring Security 配置类。
  • SecurityFilterChain:代替了过时的 WebSecurityConfigurerAdapter(Spring Boot 2.7+ 推荐方式)。
  • UserDetailsService:负责从内存、数据库、LDAP等地方加载用户信息。

第四阶段:使用数据库进行用户认证(生产环境)

内存用户只适用于测试,生产环境中,用户信息通常存储在数据库中。

配置密码编码器

在配置类中加入:

@Bean
public PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder(); // 推荐使用 BCrypt 哈希
}

实现 UserDetailsService 接口

你需要创建一个类从数据库查询用户,并返回 UserDetails 对象。

import org.springframework.security.core.userdetails.User;
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;
@Service
public class DatabaseUserDetailsService implements UserDetailsService {
    // 假设这是你从数据库查询用户的服务
    private final UserRepository userRepository; 
    // 替换为自己的数据库访问层
    public DatabaseUserDetailsService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
    @Override
    public UserDetails loadUserByUsername(String username) 
            throws UsernameNotFoundException {
        // 1. 从数据库查询用户(可自行扩展)
        MyUser myUser = userRepository.findByUsername(username)
                .orElseThrow(() -> new UsernameNotFoundException("User not found"));
        // 2. 将查询结果转换为 Spring Security 的 UserDetails
        return User.builder()
                .username(myUser.getUsername())
                .password(myUser.getPassword()) // 注意:这里必须是 BCrypt 加密后的密码
                .roles(myUser.getRole())        // 假设数据库中的 role 字段是 "ADMIN" 或 "USER"
                .build();
    }
}

配置类中使用自定义的 UserDetailsService

@Configuration
@EnableWebSecurity
public class SecurityConfig {
    private final DatabaseUserDetailsService userDetailsService;
    public SecurityConfig(DatabaseUserDetailsService userDetailsService) {
        this.userDetailsService = userDetailsService;
    }
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        // ... 同上,省略路径配置
        return http.build();
    }
    // 注册自定义 UserDetailsService 和密码编码器
    @Bean
    public AuthenticationManager authenticationManager(
            AuthenticationConfiguration authenticationConfiguration) 
            throws Exception {
        return authenticationConfiguration.getAuthenticationManager();
    }
}

(注意:如果使用了自定义 UserDetailsService,需要再注入一个 PasswordEncoder,并在 AuthenticationProvider 中设置它们,实际 Spring Boot 会自动装配,只需在配置类中声明 DaoAuthenticationProvider 或利用自动配置即可。)

简化做法: 直接在 SecurityConfig 中声明 DaoAuthenticationProvider

@Bean
public AuthenticationProvider authenticationProvider() {
    DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
    provider.setUserDetailsService(userDetailsService);
    provider.setPasswordEncoder(passwordEncoder());
    return provider;
}

第五阶段:保护 REST API(JWT / 无状态)

如果你的应用是前后端分离的 REST API,不应使用表单登录,而是使用 JWTBasic Auth

使用 HTTP Basic 认证(简单,但不推荐生产)

http.httpBasic(Customizer.withDefaults());  // 替代 formLogin

使用 JWT(标准做法)

需要额外步骤:

  • 不启用 formLoginsession
  • 创建一个自定义的 OncePerRequestFilter 来解析 JWT Token。
  • SecurityContextHolder 设置认证信息。
  • SecurityFilterChain 中把该过滤器添加到 UsernamePasswordAuthenticationFilter 之前。

常见问题与技巧

  1. 密码格式:如果使用 {noop} 前缀,密码不加密,正式环境必须使用 BCryptPasswordEncoder,数据库中存储加密后的哈希值。
  2. 跨域(CORS):如果是前后端分离,需要配置:
    http.cors(Customizer.withDefaults());

    并配合 @CrossOrigin 注解或 CorsConfigurationSource Bean。

  3. 禁用 CSRF:仅当你的应用是无状态 REST API 时可禁用;如果是传统的服务端模板渲染网站,建议开启(默认开启)。
  4. 自定义权限:除了角色,还可以使用 @PreAuthorize("hasAuthority('PRIVILEGE_VIEW')") 实现细粒度权限控制(需要先添加 @EnableMethodSecurity 注解)。
  5. 静态资源放行:如果使用了前端打包工具,记得放行 /static/**, /css/**, /js/**, /images/**

总结配置步骤

  1. 添加 spring-boot-starter-security 依赖。
  2. (可选)在 application.yml 中配置临时账号密码。
  3. 创建配置类,定义 SecurityFilterChain Bean,配置放行路径、登录方式、CSRF、CORS。
  4. 定义用户来源:在配置类中定义 UserDetailsService(内存或数据库)。
  5. 定义密码编码器BCryptPasswordEncoder
  6. (可选)实现自定义认证过滤器(如 JWT)。

通过以上步骤,你可以快速搭建一个安全的 Spring Boot 应用,如果遇到问题,先确认版本是否一致(Spring Boot 3.x 和 2.x 在 Java 17+ 的一些 API 上略有不同,但以上代码主要兼容 3.x)。

抱歉,评论功能暂时关闭!