Spring Boot: Spring Security 基础
最后更新:2026-08-26
Spring Security 是 Java 安全的行业标准——过滤器链、认证、授权三件套,守护 API 安全。
1. 你将学到
- Spring Security 过滤器链架构与
SecurityFilterChain配置 HttpSecurity配置:路径授权、CSRF、CORS- 基于内存的用户认证
UserDetailsService与PasswordEncoder @PreAuthorize/@Secured方法级授权- Alice 限制订单管理接口仅 ADMIN 角色可访问
2. 一个产品经理的真实故事
(1) 痛点:接口裸奔
Charlie 在产品评审会上发现 OrderFlow 的所有 API 完全没有认证——任何人都能调用创建订单、删除商品的接口。更糟糕的是,Bob 在公网暴露了 Actuator 端点,导致生产环境数据库信息被扫描器获取。Charlie 要求立即加安全控制,Alice 需要最快的方案。
(2) Spring Security 的解法
Spring Security 几行配置就能保护 API:
JAVA
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/v1/admin/**").hasRole("ADMIN")
.requestMatchers("/api/v1/orders/**").authenticated()
.anyRequest().permitAll()
)
.httpBasic(Customizer.withDefaults());
return http.build();
}
}
(3) 收益
Alice 用 Spring Security 为 OrderFlow 加上认证授权后,管理接口只有 ADMIN 角色可访问,普通接口需要登录,Actuator 端点限制内网访问。Charlie 对安全合规表示满意。
3. Spring Security 架构
(1) 过滤器链
sequenceDiagram
participant Client
participant Chain as SecurityFilterChain
participant Auth as Authentication Filter
participant Authz as Authorization Filter
participant Controller
Client->>Chain: HTTP Request
Chain->>Auth: 1. Authentication
alt Credentials Invalid
Auth-->>Client: 401 Unauthorized
end
Auth->>Authz: 2. Authorization
alt Access Denied
Authz-->>Client: 403 Forbidden
end
Authz->>Controller: 3. Forward to Controller
Controller-->>Client: 200 OK
| 核心概念 | 说明 |
|---|---|
SecurityFilterChain |
一组过滤器的有序链,每个请求依次经过 |
Authentication |
认证:确认"你是谁" |
Authorization |
授权:确认"你能做什么" |
SecurityContext |
安全上下文,持有当前用户信息 |
GrantedAuthority |
权限/角色信息 |
(2) 核心组件关系
| 组件 | 职责 | 接口 |
|---|---|---|
AuthenticationManager |
认证管理器 | authenticate() |
ProviderManager |
AuthenticationManager 默认实现 | 委托给 AuthenticationProvider |
UserDetailsService |
加载用户信息 | loadUserByUsername() |
PasswordEncoder |
密码编码 | encode() / matches() |
4. SecurityFilterChain 配置
▶ 示例: 基础安全配置
JAVA
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable())
.cors(cors -> cors.configurationSource(corsConfigurationSource()))
.sessionManagement(session ->
session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/v1/products/**").permitAll()
.requestMatchers("/api/v1/orders/**").authenticated()
.requestMatchers("/api/v1/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
)
.httpBasic(Customizer.withDefaults());
return http.build();
}
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration config = new CorsConfiguration();
config.setAllowedOrigins(List.of("https://orderflow.example.com"));
config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE"));
config.setAllowedHeaders(List.of("*"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/api/**", config);
return source;
}
}
输出:
TEXT
📖 仅展示
// 执行成功
| 配置项 | 说明 | REST API 推荐 |
|---|---|---|
| CSRF | 跨站请求伪造防护 | 禁用(无状态 API 不需要) |
| CORS | 跨域资源共享 | 配置允许的域名 |
| Session | 会话管理 | STATELESS(无状态) |
| httpBasic | HTTP 基本认证 | 开发/测试用 |
| formLogin | 表单登录 | 传统 Web 应用用 |
5. 基于内存的用户认证
▶ 示例: InMemoryUserDetailsManager
JAVA
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public UserDetailsService userDetailsService(PasswordEncoder encoder) {
UserDetails admin = User.builder()
.username("alice")
.password(encoder.encode("admin123"))
.roles("ADMIN", "CUSTOMER")
.build();
UserDetails customer = User.builder()
.username("bob")
.password(encoder.encode("customer123"))
.roles("CUSTOMER")
.build();
return new InMemoryUserDetailsManager(admin, customer);
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
输出:
TEXT
📖 仅展示
// 执行成功
| PasswordEncoder | 安全性 | 适用场景 |
|---|---|---|
BCryptPasswordEncoder |
高(自带盐值) | 生产环境推荐 |
Argon2PasswordEncoder |
最高(GPU 抵抗) | 高安全要求 |
NoOpPasswordEncoder |
无(明文) | 仅测试用 |
🔒 安全: 永远不要在生产环境使用
NoOpPasswordEncoder。BCrypt 是最低标准,Argon2 是更高标准。
▶ 示例: 测试认证
BASH
# Access without credentials -> 401
curl http://localhost:8080/api/v1/orders
# Access with customer credentials -> 200
curl -u bob:customer123 http://localhost:8080/api/v1/orders
# Access admin endpoint with customer -> 403
curl -u bob:customer123 http://localhost:8080/api/v1/admin/dashboard
# Access admin endpoint with admin -> 200
curl -u alice:admin123 http://localhost:8080/api/v1/admin/dashboard
输出:
TEXT
📖 仅展示
{"status":"ok","data":{}}
6. 方法级授权
▶ 示例: @PreAuthorize 方法授权
JAVA
@Service
public class OrderService {
@Transactional(readOnly = true)
@PreAuthorize("hasAnyRole('ADMIN', 'CUSTOMER')")
public Order getOrder(Long orderId) {
return orderRepository.findById(orderId).orElseThrow();
}
@Transactional
@PreAuthorize("hasRole('ADMIN')")
public void deleteOrder(Long orderId) {
orderRepository.deleteById(orderId);
}
@Transactional
@PreAuthorize("hasRole('ADMIN') or #customerId == authentication.principal.id")
public List<Order> getCustomerOrders(Long customerId) {
return orderRepository.findByCustomerId(customerId);
}
}
输出:
TEXT
📖 仅展示
// 执行成功
| 注解 | 特点 | 适用场景 |
|---|---|---|
@PreAuthorize |
SpEL 表达式,访问方法前检查 | 最灵活,推荐 |
@PostAuthorize |
SpEL 表达式,方法执行后检查 | 需要根据返回值判断权限 |
@Secured |
角色列表,不支持 SpEL | 简单角色检查 |
@RolesAllowed |
JSR-250 标准注解 | 跨框架兼容 |
📌 重点: 使用方法级授权需要在配置类上加
@EnableMethodSecurity。
▶ 示例: @EnableMethodSecurity 配置
JAVA
@Configuration
@EnableWebSecurity
@EnableMethodSecurity
public class SecurityConfig {
// ... SecurityFilterChain and UserDetailsService beans
}
输出:
TEXT
📖 仅展示
// 执行成功
7. 路径授权规则速查
| 匹配方法 | 说明 | 示例 |
|---|---|---|
requestMatchers(String) |
Ant 风格路径匹配 | /api/v1/orders/** |
requestMatchers(HttpMethod, String) |
限定 HTTP 方法 | POST /api/v1/orders |
anyRequest() |
匹配所有请求 | 放在最后作为兜底 |
| 授权方法 | 说明 |
|---|---|
permitAll() |
允许所有人访问 |
authenticated() |
需要认证 |
hasRole("ADMIN") |
需要 ADMIN 角色 |
hasAnyRole("A", "B") |
需要任一角色 |
denyAll() |
拒绝所有访问 |
▶ 示例: 按角色限制接口
JAVA
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(auth -> auth
.requestMatchers(HttpMethod.GET, "/api/v1/products/**").permitAll()
.requestMatchers(HttpMethod.POST, "/api/v1/products").hasRole("ADMIN")
.requestMatchers(HttpMethod.PUT, "/api/v1/products/**").hasRole("ADMIN")
.requestMatchers(HttpMethod.DELETE, "/api/v1/products/**").hasRole("ADMIN")
.requestMatchers("/api/v1/orders/**").hasAnyRole("ADMIN", "CUSTOMER")
.requestMatchers("/actuator/**").hasRole("ADMIN")
.anyRequest().authenticated()
)
.httpBasic(Customizer.withDefaults());
return http.build();
}
输出:
TEXT
📖 仅展示
// 执行成功
8. 综合示例:OrderFlow 安全配置
JAVA
// SecurityConfig.java
package com.orderflow.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.User;
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.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
@EnableWebSecurity
@EnableMethodSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable())
.sessionManagement(s -> s.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(auth -> auth
.requestMatchers(HttpMethod.GET, "/api/v1/products/**").permitAll()
.requestMatchers("/api/v1/orders/**").hasAnyRole("CUSTOMER", "ADMIN")
.requestMatchers("/api/v1/admin/**").hasRole("ADMIN")
.requestMatchers("/actuator/health").permitAll()
.requestMatchers("/actuator/**").hasRole("ADMIN")
.anyRequest().authenticated()
)
.httpBasic(Customizer.withDefaults());
return http.build();
}
@Bean
public UserDetailsService userDetailsService(PasswordEncoder encoder) {
var admin = User.builder()
.username("alice").password(encoder.encode("admin123"))
.roles("ADMIN", "CUSTOMER").build();
var customer = User.builder()
.username("bob").password(encoder.encode("pass123"))
.roles("CUSTOMER").build();
return new InMemoryUserDetailsManager(admin, customer);
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
// OrderController with method-level security
@RestController
@RequestMapping("/api/v1/orders")
public class OrderController {
@GetMapping
@PreAuthorize("hasAnyRole('CUSTOMER', 'ADMIN')")
public List<Order> listOrders() { /* ... */ }
@GetMapping("/{id}")
@PreAuthorize("hasRole('ADMIN') or @orderSecurity.isOwner(#id, authentication)")
public Order getOrder(@PathVariable Long id) { /* ... */ }
@DeleteMapping("/{id}")
@PreAuthorize("hasRole('ADMIN')")
public void deleteOrder(@PathVariable Long id) { /* ... */ }
}
❓ 常见问题
Q 为什么 REST API 要禁用 CSRF?
A CSRF 保护依赖 Cookie + Session 机制。REST API 通常是无状态的(Token 认证),不使用 Cookie,CSRF 攻击无法成功,所以可以禁用。
Q hasRole 和 hasAuthority 有什么区别?
A hasRole("ADMIN") 会自动加 "ROLE_" 前缀,检查 ROLE_ADMIN 权限。hasAuthority("ADMIN") 不加前缀,直接检查 ADMIN 权限。推荐统一使用 hasRole。
Q InMemoryUserDetailsManager 能用于生产吗?
A 不能。内存用户管理仅用于开发和测试。生产环境需要自定义 UserDetailsService 从数据库加载用户信息。
Q 如何获取当前登录用户信息?
A 三种方式:1)
SecurityContextHolder.getContext().getAuthentication();2)Controller 方法参数注入 Principal principal;3)@AuthenticationPrincipal UserDetails user。推荐第三种。Q Spring Security 的过滤器顺序可以自定义吗?
A 可以,通过 @Order 控制 SecurityFilterChain 的顺序。多个 SecurityFilterChain 可以匹配不同的请求路径。
Q httpBasic 和 formLogin 该选哪个?
A REST API 用 httpBasic 或 Bearer Token;传统 Web 应用用 formLogin。本课使用 httpBasic 作为入门,后续课程切换到 JWT。
📖 小节
- Spring Security 核心架构:SecurityFilterChain → Authentication → Authorization
SecurityFilterChain配置路径授权规则:permitAll / authenticated / hasRole- REST API 推荐:禁用 CSRF、STATELESS 会话、HTTP Basic 或 Bearer Token
@PreAuthorize+ SpEL 表达式实现方法级精细授权BCryptPasswordEncoder是密码编码的最低安全标准- InMemoryUserDetailsManager 仅用于开发,生产环境需自定义 UserDetailsService
📝 作业
-
基础题(难度⭐):为 OrderFlow 配置 Spring Security,实现商品查询 permitAll、订单操作 authenticated、管理接口 hasRole("ADMIN"),使用 httpBasic 和内存用户测试。
-
进阶题(难度⭐⭐):实现
@PreAuthorize方法级授权——用户只能查看自己的订单,ADMIN 可以查看所有订单。提示:创建 OrderSecurity 辅助 Bean 在 SpEL 中引用。 -
挑战题(难度⭐⭐⭐):实现自定义 UserDetailsService 从数据库(JPA)加载用户和角色信息,替换 InMemoryUserDetailsManager,思考密码存储和用户注册流程的安全设计。