Spring Security 全方位教程:从入门到实战
目录
- 引言:为什么需要 Spring Security?
- 核心概念:认证与授权
- 快速上手:第一个 Spring Security 应用
- 深入认证:自定义登录逻辑
- 1. 自定义登录页面
- 2. 使用
UserDetailsService和PasswordEncoder
- 深入授权:基于角色的访问控制
- 1. 方法级安全
- 2. URL/URL 级安全 (Web Security)
- 实现注销功能
- 实战项目:集成 JWT 实现无状态认证
- 1. JWT 简介
- 2. 项目搭建与依赖
- 3. 创建 JWT 工具类
- 4. 配置 Spring Security (无状态)
- 5. 创建认证与授权接口
- 6. 测试接口
- 总结与最佳实践
- 学习资源
引言:为什么需要 Spring Security?
在 Web 应用开发中,安全是至关重要的一环,你需要确保:

- 认证:用户是他们所声称的身份(通过用户名和密码登录)。
- 授权:用户只能访问他们被允许的资源(普通用户不能访问管理员页面)。
手动实现这些逻辑非常繁琐且容易出错,Spring Security 正是为解决这个问题而生的,它是一个功能强大、高度可定制的安全框架,能够轻松为基于 Spring 的项目提供认证和授权功能。
核心优势:
- 全面的安全服务:覆盖认证、授权、CSRF 防护、会话管理等。
- 默认安全:开箱即用,默认提供强大的安全保护。
- 高度可定制:几乎每一个组件都可以被替换或重写,以满足复杂业务需求。
- 与 Spring 生态无缝集成:与 Spring Boot、Spring MVC 等完美配合。
核心概念:认证与授权
在深入代码之前,必须理解这两个核心概念。
1. 认证
认证是 “你是谁?” 的过程,系统通过验证用户的凭证来确认其身份,常见的认证方式有:

- 用户名/密码:最传统的方式。
- 令牌:如 JWT (JSON Web Token)。
- OAuth2:允许用户使用第三方服务(如微信、GitHub)登录。
在 Spring Security 中,认证的核心组件是 Authentication 接口,一个完整的 Authentication 对象包含:
- Principal:主体信息,通常是登录用户的用户名。
- Credentials:凭证信息,通常是密码,在认证成功后,凭证通常会被清空,以防止泄露。
- Authorities:权限列表,表示用户被授予的权限(如
ROLE_USER,ROLE_ADMIN)。
认证过程通常由 AuthenticationManager 来管理,它负责验证 Authentication 对象。
2. 授权
授权是 “你能做什么?” 的过程,在认证之后,系统会根据用户的权限决定其可以访问哪些资源,这通常通过访问控制列表或角色来实现。
在 Spring Security 中,授权的核心是 AccessDecisionVoter(投票器)和 AccessDecisionManager(决策管理器),当用户尝试访问一个受保护的资源时,系统会创建一个 Authentication 对象和一个 RequestAuthorizationContext(包含请求信息),然后由 AccessDecisionManager 协调各个投票器进行投票,最终决定是允许访问(ACCESS_GRANTED)、拒绝访问(ACCESS_DENIED)或弃权(ACCESS_ABSTAIN)。

快速上手:第一个 Spring Security 应用
我们将使用 Spring Boot 来快速创建一个受 Spring Security 保护的应用。
1. 创建项目
使用 Spring Initializr 创建一个新项目,添加以下依赖:
- Spring Web: 用于构建 Web 应用。
- Spring Security: 用于安全控制。
2. 启动并测试
创建一个简单的 Controller:
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello() {
return "Hello, World! This is a protected resource.";
}
@GetMapping("/admin")
public String admin() {
return "Hello, Admin! This is an admin-only resource.";
}
}
启动应用,然后访问 http://localhost:8080/hello。
你会发现,浏览器没有直接显示 "Hello, World!",而是跳转到了一个默认的登录页面,页面标题是 "Spring Security Login"。
为什么?
因为 Spring Security 默认会保护所有 URL,它会自动生成一个用户名为 user,密码为控制台随机生成的一串字符(启动时在日志中可以看到)。
Using generated security password: 1a2b3c4d-5e6f-7890-1234-567890abcdef
你可以使用这个默认的用户名和密码进行登录,登录成功后就能看到 "Hello, World!"。
仅仅添加 spring-boot-starter-security 依赖,你的应用就已经具备了强大的安全防护能力。
深入认证:自定义登录逻辑
默认的登录页面和用户显然不适用于生产环境,下面我们来自定义登录逻辑。
1. 自定义登录页面
创建一个简单的 HTML 登录页面 src/main/resources/templates/login.html。
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>Login Page</title>
<style>
body { font-family: Arial, sans-serif; background-color: #f4f4f4; }
.container { max-width: 400px; margin: 50px auto; padding: 20px; background: #fff; border-radius: 5px; box-shadow: 0 0 10px rgba(0,0,0,0.1); }
input[type="text"], input[type="password"] { width: 100%; padding: 10px; margin: 10px 0; border: 1px solid #ddd; box-sizing: border-box; }
input[type="submit"] { width: 100%; padding: 10px; background-color: #5cb85c; color: white; border: none; border-radius: 4px; cursor: pointer; }
.error { color: red; margin-bottom: 15px; }
</style>
</head>
<body>
<div class="container">
<h2>Login</h2>
<div th:if="${param.error}" class="error">
Invalid username and/or password.
</div>
<form th:action="@{/login}" method="post">
<div>
<label for="username">Username:</label>
<input type="text" id="username" name="username" required autofocus />
</div>
<div>
<label for="password">Password:</label>
<input type="password" id="password" name="password" required />
</div>
<div>
<input type="submit" value="Log In" />
</div>
</form>
</div>
</body>
</html>
2. 使用 UserDetailsService 和 PasswordEncoder
我们需要告诉 Spring Security 如何验证用户,最佳实践是实现 UserDetailsService 接口,并配置密码编码器。
创建 UserDetailsService 实现
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 MyUserDetailsService implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// 这里可以替换为从数据库查询用户
if ("admin".equals(username)) {
return User.withUsername("admin")
.password("{noop}admin123") // {noop} 表示不加密,仅用于演示!生产环境必须加密!
.roles("ADMIN")
.build();
} else if ("user".equals(username)) {
return User.withUsername("user")
.password("{noop}user123")
.roles("USER")
.build();
}
throw new UsernameNotFoundException("User not found with username: " + username);
}
}
User是 Spring Security 提供的一个UserDetails实现类。{noop}是一个密码编码前缀,表示密码是明文。强烈不建议在生产环境中使用!
配置密码编码器 在 Spring Boot 2.x 之后,默认要求密码必须加密,我们需要指定一个密码编码器。
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
public class SecurityConfig {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
我们需要修改 MyUserDetailsService 来使用 BCrypt 加密。
// 修改 MyUserDetailsService
@Service
public class MyUserDetailsService implements UserDetailsService {
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
if ("admin".equals(username)) {
return User.withUsername("admin")
.password(passwordEncoder.encode("admin123")) // 使用编码器
.roles("ADMIN")
.build();
} else if ("user".equals(username)) {
return User.withUsername("user")
.password(passwordEncoder.encode("user123"))
.roles("USER")
.build();
}
throw new UsernameNotFoundException("User not found with username: " + username);
}
}
配置 Spring Security 我们需要编写一个配置类来告诉 Spring Security 使用我们的登录页面和认证逻辑。
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.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
@Configuration
@EnableWebSecurity // 启用 Spring Security 的 Web 安全支持
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/admin/**").hasRole("ADMIN") // /admin/** 路径需要 ADMIN 角色
.antMatchers("/", "/home", "/login").permitAll() // 这些路径允许所有人访问
.anyRequest().authenticated() // 其他所有请求都需要认证
.and()
.formLogin()
.loginPage("/login") // 自定义登录页面
.defaultSuccessUrl("/hello") // 登录成功后跳转的页面
.permitAll()
.and()
.logout()
.permitAll(); // 允许所有人注销
}
// 如果你需要使用基于内存的用户(不推荐用于生产),可以这样配置
/*
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user").password(passwordEncoder().encode("password")).roles("USER")
.and()
.withUser("admin").password(passwordEncoder().encode("admin")).roles("ADMIN");
}
*/
}
解释:
authorizeRequests(): 开始配置 URL 授权规则。antMatchers("/admin/**").hasRole("ADMIN"): 指定匹配/admin/下的所有 URL,访问这些 URL 的用户必须拥有ADMIN角色。permitAll(): 允许任何人访问。anyRequest().authenticated(): 剩下的所有请求都必须经过认证。formLogin(): 启用表单登录。loginPage("/login"): 指定自定义的登录页面 URL。defaultSuccessUrl("/hello"): 登录成功后默认跳转的 URL。logout(): 启用注销功能。
创建登录控制器
为了让 /login 路径能渲染我们的 HTML 页面,需要一个 Controller。
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class LoginController {
@GetMapping("/login")
public String login() {
return "login"; // 返回 Thymeleaf 模板名
}
}
注意:你需要添加 Thymeleaf 依赖才能使用模板。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
启动应用,访问 http://localhost:8080/hello,你会看到自定义的登录页面,使用 user/user123 或 admin/admin123 登录,你会发现 admin 用户可以访问 /admin,而 user 用户会收到 403 Forbidden 错误。
深入授权:基于角色的访问控制
我们已经通过 URL 级别实现了授权,现在来看看方法级别的授权。
1. 方法级安全
Spring Security 允许你在方法上添加注解来控制访问权限。
启用方法安全
在配置类上添加 @EnableGlobalMethodSecurity 注解。
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true) // 启用 @PreAuthorize 等注解
public class SecurityConfig extends WebSecurityConfigurerAdapter {
// ...
}
在 Controller 方法上添加注解
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello() {
return "Hello, World!";
}
@GetMapping("/admin")
@PreAuthorize("hasRole('ADMIN')") // 只有拥有 ADMIN 角色的用户才能调用此方法
public String admin() {
return "Hello, Admin!";
}
@GetMapping("/user")
@PreAuthorize("hasAnyRole('USER', 'ADMIN')") // 拥有 USER 或 ADMIN 角色的用户都可以调用
public String user() {
return "Hello, User!";
}
}
即使一个 URL 路径本身是公开的,但如果被 @PreAuthorize 注解保护,未授权的用户也无法通过 API 调用访问它。
2. URL/URL 级安全 (Web Security)
这部分我们在之前的 configure(HttpSecurity http) 方法中已经详细讲解过了,核心就是使用 antMatchers() 和 access() 或 hasRole() 来定义规则。
实现注销功能
Spring Security 默认提供了 /logout 路径用于注销,我们只需要在前端添加一个注销链接即可。
在 login.html 或任何页面上添加:
<a th:href="@{/logout}">Logout</a>
当用户点击这个链接时,Spring Security 会:
- 使当前用户的 Session 失效。
- 清除 Security Context。
- 重定向到
/login?logout(默认的注销成功页面)。
你可以在 configure(HttpSecurity http) 中自定义注销行为:
.and()
.logout()
.logoutUrl("/perform_logout") // 自定义注销请求的URL
.logoutSuccessUrl("/login?logout") // 注销成功后跳转的页面
.invalidateHttpSession(true) // 使session失效
.deleteCookies("JSESSIONID") // 删除cookies
.permitAll();
实战项目:集成 JWT 实现无状态认证
对于前后端分离的应用(如 React, Vue, Angular),传统的 Session 认证不再适用,JWT (JSON Web Token) 是目前最流行的无状态认证方案。
1. JWT 简介
JWT 是一个开放标准(RFC 7519),它定义了一种紧凑的、自包含的方式,用于在各方之间安全地传输信息,它由三部分组成:Header(头部)、Payload(载荷)、Signature(签名)。
工作流程:
- 登录:用户发送用户名密码到后端。
- 验证:后端验证成功后,生成一个 JWT,并将其返回给前端。
- 携带令牌:前端在后续的每一个请求中,通过
Authorization请求头(Bearer <token>)将 JWT 发送给后端。 - 验证:后端收到请求后,验证 JWT 的有效性,如果有效,则从载荷中获取用户信息,并允许访问资源。
2. 项目搭建与依赖
创建一个新的 Spring Boot 项目,添加以下依赖:
- Spring Web
- Spring Security
- Spring Data JPA (可选,用于数据库操作)
- JWT 相关库:我们使用
io.jsonwebtoken:jjwt - Lombok (可选,简化代码)
<!-- pom.xml -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
3. 创建 JWT 工具类
创建一个工具类来生成和解析 JWT。
import io.jsonwebtoken.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
@Component
public class JwtUtil {
@Value("${jwt.secret:mySecretKey}")
private String secret;
@Value("${jwt.expiration:86400000}") // 24 hours
private long expiration;
public String extractUsername(String token) {
return extractClaim(token, Claims::getSubject);
}
public Date extractExpiration(String token) {
return extractClaim(token, Claims::getExpiration);
}
public <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
final Claims claims = extractAllClaims(token);
return claimsResolver.apply(claims);
}
private Claims extractAllClaims(String token) {
return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
}
private Boolean isTokenExpired(String token) {
return extractExpiration(token).before(new Date());
}
public String generateToken(UserDetails userDetails) {
Map<String, Object> claims = new HashMap<>();
return createToken(claims, userDetails.getUsername());
}
private String createToken(Map<String, Object> claims, String subject) {
return Jwts.builder()
.setClaims(claims)
.setSubject(subject)
.setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + expiration))
.signWith(SignatureAlgorithm.HS256, secret)
.compact();
}
public Boolean validateToken(String token, UserDetails userDetails) {
final String username = extractUsername(token);
return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
}
}
在 application.properties 中配置:
jwt.secret=mySecretKeyForJWT jwt.expiration=86400000
4. 配置 Spring Security (无状态)
这是最关键的一步,我们需要禁用 Session,并配置一个自定义的 UsernamePasswordAuthenticationFilter 来处理 JWT。
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
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.authentication.UsernamePasswordAuthenticationFilter;
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private JwtUtil jwtUtil;
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/api/auth/**").permitAll() // 允许访问认证相关接口
.anyRequest().authenticated() // 其他所有请求都需要认证
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); // 无状态
// 添加 JWT 过滤器
http.addFilterBefore(new JwtRequestFilter(jwtUtil, userDetailsService), UsernamePasswordAuthenticationFilter.class);
}
}
csrf().disable(): 因为是无状态的 REST API,不需要 CSRF 保护。sessionCreationPolicy(SessionCreationPolicy.STATELESS): 明确告诉 Spring Security 不要创建或使用 Session。addFilterBefore(...): 添加我们自定义的 JWT 过滤器到UsernamePasswordAuthenticationFilter之前。
创建 JWT 过滤器
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class JwtRequestFilter extends OncePerRequestFilter {
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private JwtUtil jwtUtil;
@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 = jwtUtil.extractUsername(jwt);
}
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
if (jwtUtil.validateToken(jwt, userDetails)) {
UsernamePasswordAuthenticationToken authenticationToken =
new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
}
}
chain.doFilter(request, response);
}
}
5. 创建认证与授权接口
认证请求 DTO
public class AuthenticationRequest {
private String username;
private String password;
// getters and setters
}
认证响应 DTO
public class AuthenticationResponse {
private String token;
public AuthenticationResponse(String token) {
this.token = token;
}
// getters and setters
}
认证 Controller
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api/auth")
public class AuthenticationController {
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private JwtUtil jwtUtil;
@Autowired
private MyUserDetailsService userDetailsService;
@PostMapping("/authenticate")
public ResponseEntity<?> createAuthenticationToken(@RequestBody AuthenticationRequest authenticationRequest) throws Exception {
try {
authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(authenticationRequest.getUsername(), authenticationRequest.getPassword())
);
} catch (BadCredentialsException e) {
throw new Exception("Incorrect username or password", e);
}
final UserDetails userDetails = userDetailsService.loadUserByUsername(authenticationRequest.getUsername());
final String jwt = jwtUtil.generateToken(userDetails);
return ResponseEntity.ok(new AuthenticationResponse(jwt));
}
}
一个受保护的资源 Controller
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api/resource")
public class ResourceController {
@GetMapping("/user")
@PreAuthorize("hasRole('USER')")
public String userResource() {
return "This is a user resource.";
}
@GetMapping("/admin")
@PreAuthorize("hasRole('ADMIN')")
public String adminResource() {
return "This is an admin resource.";
}
}
6. 测试接口
- 启动应用。
- 获取 Token:使用 Postman 或 curl 调用
/api/auth/authenticate接口。curl -X POST http://localhost:8080/api/auth/authenticate \ -H "Content-Type: application/json" \ -d '{"username":"user", "password":"user123"}'你会得到一个类似这样的 JSON 响应:
{ "token": "eyJhbGciOiJIUzUxMiJ9.eyJzdWIi..." } - 访问受保护资源:复制返回的 token,在请求头中添加
Authorization字段来访问/api/resource/user。curl -X GET http://localhost:8080/api/resource/user \ -H "Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIi..."
token 有效,你会得到 "This is a user resource." 的响应,token 无效或未提供,你会得到 401 Unauthorized 错误。
总结与最佳实践
- 从默认配置开始:Spring Security 的默认配置非常安全,是很好的起点。
- 始终加密密码:生产环境中,绝对不要使用明文密码,使用 BCrypt 等强哈希算法。
- 最小权限原则:只授予用户完成其任务所必需的最小权限。
- 方法级安全:对于复杂的业务逻辑,结合 URL 级和方法级安全可以提供更精细的控制。
- 无状态 API:对于前后端分离的应用,JWT 是一个很好的选择,它使你的服务易于扩展和部署。
- 关注官方文档:Spring Security 的更新很快,官方文档是最好的学习资源。
学习资源
- Spring Security 官方文档:最权威、最全面的学习资料。
- Spring Security 中文文档:Spring 官方网站,提供链接和概述。
- Baeldung - Spring Security Series: https://www.baeldung.com/spring-security-series 非常高质量的教程。
- 视频教程:在 YouTube、Bilibili 等平台搜索 "Spring Security 教程",可以找到大量视频资源,Philipp Lackner 的教程就非常受欢迎。
这份教程涵盖了从基础到高级的 Spring Security 知识,希望对你有所帮助!安全是一个深奥的领域,多实践、多思考是掌握它的关键。
