Spring Security从零开始:手把手搭建你的第一个安全认证系统
大家好,我是一名开源项目维护者,也经常给团队新人做后端技术培训。在过去的几年里,我发现很多刚接触Java Web开发的同学,在面对Spring Security时总是感到“高不可攀”——文档复杂、概念抽象、配置繁琐。其实,它并没有那么难。
我当初学的时候,也踩过不少坑:明明只是想加个登录功能,结果被一堆AuthenticationManager、UserDetailsService、PasswordEncoder搞得晕头转向。后来我才明白,Spring Security 的核心思想其实非常清晰:保护资源、验证身份、授权访问。
今天这篇教程,就是为完全零基础的你量身打造。我们将用最简单的语言、最实用的代码,一步步搭建一个具备基本用户名密码登录功能的安全系统。你不需要懂加密算法原理,也不需要精通Spring Boot——只要你会写“Hello World”,就能跟上。
为什么我们需要 Spring Security?
想象一下:你开发了一个后台管理系统,任何人都可以直接访问 /admin/users 页面删除用户数据——这显然不行。安全认证(Authentication)和权限控制(Authorization) 是任何真实项目不可或缺的部分。
Spring Security 就是 Spring 生态中专门解决这类问题的框架。它能帮你:
- 防止未登录用户访问受保护页面
- 验证用户名和密码是否正确
- 控制不同角色能访问哪些接口
- 抵御常见的网络攻击(如 CSRF、会话固定等)
更重要的是,它与 Spring Boot 无缝集成,几行配置就能启动基础保护。
环境准备:5分钟搭好开发环境
在开始编码前,请确保你的电脑已安装以下工具:
| 工具 | 版本建议 | 作用 |
|---|---|---|
| JDK | 17 或 21 | Java 运行环境 |
| Maven / Gradle | 最新版 | 项目依赖管理 |
| IDE | IntelliJ IDEA / VS Code | 代码编辑器 |
第一步:创建 Spring Boot 项目
打开 start.spring.io,选择:
- Project: Maven / Gradle(按你习惯)
- Language: Java
- Spring Boot: 3.x(推荐 3.2+)
- Dependencies: 勾选
Spring Web和Spring Security
点击 “Generate” 下载 ZIP 包,解压后导入你的 IDE。
💡 提示:如果你使用命令行,也可以通过 Spring Initializr CLI 创建:
curl https://start.spring.io/starter.zip -d dependencies=web,security -d javaVersion=17 -o demo.zip
第二步:验证项目是否运行
在 src/main/java 下找到主类(如 DemoApplication.java),直接运行。
默认情况下,Spring Security 会自动启用基本认证(Basic Auth)。启动后访问 http://localhost:8080,浏览器会弹出登录框!
用户名是 user,密码会在控制台打印出来,类似:
Using generated security password: a1b2c3d4-e5f6-7890-g1h2-i3j4k5l6m7n8
这就是 Spring Security 的“开箱即用”能力——即使你没写一行安全代码,它也已经保护了你的整个应用。
核心概念:三个关键词搞懂 Spring Security
要掌握 Spring Security,只需理解三个核心组件:
1. Authentication(认证)
“你是谁?”
当用户提交用户名和密码,系统需要验证其真实性。Spring Security 通过 AuthenticationManager 来处理这个过程。
2. Authorization(授权)
“你能做什么?”
即使用户身份合法,也要检查他是否有权限访问某个资源。比如普通用户不能删除管理员账户。
3. Security Filter Chain(安全过滤链)
“请求如何被保护?”
所有 HTTP 请求都会经过一系列过滤器(Filter)。Spring Security 在这里拦截请求,决定是否放行。
🧠 类比理解:
把你的应用想象成一栋大楼。
- 认证 = 门卫查身份证
- 授权 = 查看你是否有某楼层的门禁卡
- 过滤链 = 大楼入口的安检通道
实战项目:实现自定义用户名密码登录
现在,我们来替换默认的随机密码,实现自己的用户体系。
步骤一:创建用户实体类
// src/main/java/com/example/demo/User.java
public class User {
private String username;
private String password;
private String role;
// 构造函数、getter/setter 省略(可用 Lombok 简化)
public User(String username, String password, String role) {
this.username = username;
this.password = password;
this..role = role;
}
// getter 方法...
}
步骤二:配置内存中的用户(仅用于演示)
⚠️ 注意:真实项目应从数据库读取用户,但为简化入门,我们先用内存存储。
// src/main/java/com/example/demo/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.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
public class SecurityConfig {
@Bean
public UserDetailsService userDetailsService() {
// 创建两个测试用户
UserDetails admin = org.springframework.security.core.userdetails.User.builder()
.username("admin")
.password(passwordEncoder().encode("123456"))
.roles("ADMIN")
.build();
UserDetails user = org.springframework.security.core.userdetails.User.builder()
.username("user")
.password(passwordEncoder().encode("123456"))
.roles("USER")
.build();
return new InMemoryUserDetailsManager(admin, user);
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authz -> authz
.requestMatchers("/public/**").permitAll() // 公开路径
.anyRequest().authenticated() // 其他路径需登录
)
.formLogin(form -> form // 启用表单登录
.loginPage("/login") // 自定义登录页(可选)
.permitAll()
)
.logout(logout -> logout.permitAll()); // 允许登出
return http.build();
}
}
关键点解析:
BCryptPasswordEncoder
Spring Security 不会明文存储密码。BCrypt是一种安全的哈希算法,每次加密结果都不同(因为加了 salt),但能正确验证原密码。这是现代 Web 应用的标准做法。roles("ADMIN")vsauthorities("ROLE_ADMIN")
Spring Security 内部会自动在角色名前加ROLE_前缀。所以.roles("ADMIN")等价于.authorities("ROLE_ADMIN")。formLogin()
默认会提供一个朴素的登录页面(路径/login)。你可以自定义 HTML,但初学者建议先用默认的。
步骤三:创建一个受保护的控制器
// src/main/java/com/example/demo/HelloController.java
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, authenticated user!";
}
@GetMapping("/admin")
public String admin() {
return "Welcome, admin!";
}
}
步骤四:启动并测试
- 启动应用
- 访问
http://localhost:8080/hello - 跳转到登录页(由 Spring Security 自动生成)
- 输入:
- 用户名:
user,密码:123456→ 可访问/hello - 用户名:
admin,密码:123456→ 可访问/hello和/admin
- 用户名:
✅ 恭喜!你已经实现了基于角色的访问控制。
新手常见问题解答
Q1:为什么密码要用 BCrypt?不能直接存明文吗?
绝对不行! 如果数据库泄露,明文密码会让用户所有账户(很多人多个网站用同一密码)面临风险。BCrypt 是单向哈希算法,无法逆向解密,且计算慢(防暴力破解)。
Q2:登录页太丑了,怎么自定义?
创建 src/main/resources/templates/login.html(需引入 Thymeleaf):
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head><title>Login</title></head>
<body>
<form th:action="@{/login}" method="post">
<input type="text" name="username" placeholder="Username" required />
<input type="password" name="password" placeholder="Password" required />
<button type="submit">Login</button>
</form>
</body>
</html>
然后在 SecurityConfig 中指定:
.formLogin(form -> form.loginPage("/login").permitAll())
Q3:如何从数据库读取用户?
你需要实现自己的 UserDetailsService:
@Service
public class CustomUserDetailsService implements UserDetailsService {
@Autowired
private UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUsername(username);
if (user == null) throw new UsernameNotFoundException("User not found");
return org.springframework.security.core.userdetails.User.builder()
.username(user.getUsername())
.password(user.getPassword()) // 已加密
.roles(user.getRole())
.build();
}
}
然后在 SecurityConfig 中注入这个 Bean 即可。
Q4:登录后跳转到哪里?
默认登录成功后跳回最初请求的页面。可通过 .defaultSuccessUrl("/dashboard", true) 强制跳转。
学习建议与下一步
你现在掌握了 Spring Security 的基础骨架。但真实项目远不止于此。我建议你按以下路径深入:
理解过滤器链
使用@EnableWebSecurity(debug = true)查看请求经过哪些 Filter。学习 JWT 无状态认证
适用于 API 服务(如 Vue/React 前端 + Spring Boot 后端)。集成 OAuth2 / 社交登录
支持微信、GitHub 等第三方登录。细粒度权限控制
使用@PreAuthorize("hasRole('ADMIN')")注解控制方法级权限。安全加固
配置 HTTPS、CSP、XSS 防护等。
📌 避坑指南:
- 不要为了“快速上线”而关闭 CSRF(
csrf().disable()),除非你明确知道自己在做什么。- 密码永远不要硬编码在代码中,使用配置文件或密钥管理服务。
- 测试时用内存用户没问题,但上线前务必切换到数据库。
结语
Spring Security 初看复杂,但它的设计哲学非常优雅:通过组合小而专注的组件,构建强大的安全体系。你今天迈出的第一步,正是通往专业后端开发的关键一环。
记住,每一个复杂的系统,都是从一行 HttpSecurity 配置开始的。希望这篇教程能成为你安全开发之路的起点。
如果你觉得有帮助,欢迎在 GitHub 上给我的开源项目点个 Star —— 那里有更多实战案例和最佳实践。Happy coding!

评论 0