Spring Security 从零开始:手把手教你搭建安全认证系统
大家好,我是一个开源项目维护者,也是一名后端讲师。过去几年,我参与和维护了多个基于 Spring Boot 的企业级项目,其中几乎都离不开 Spring Security。我当初学的时候,被它的“自动配置”搞得晕头转向——明明没写代码,为什么一访问就跳登录页?用户名密码又是从哪来的?今天,我就用最简单、最实战的方式,带完全零基础的你,快速搭建一个属于自己的安全认证系统。
为什么需要 Spring Security?
想象一下,你的网站有用户注册、订单查看、后台管理等功能。如果不对访问权限做控制,任何人都能删掉别人的订单,甚至进入管理员后台,那后果不堪设想。Spring Security 就是 Spring 家族中专门用来处理“谁可以访问什么”的安全框架。它能帮你轻松实现:
- 用户登录认证(Authentication)
- 权限控制(Authorization)
- 防止常见攻击(如 CSRF、会话固定等)
而我们今天的目标,就是用最少的代码,跑通一个带登录功能的 Web 应用。
第一步:环境准备
在开始之前,请确保你已经安装了以下工具:
| 工具 | 版本建议 | 说明 |
|---|---|---|
| JDK | 17 或 21 | Spring Boot 3.x 要求 JDK 17+ |
| Maven / Gradle | 最新稳定版 | 项目构建工具,本文使用 Maven |
| IDE | IntelliJ IDEA / VS Code | 推荐 IDEA,对 Spring 支持更好 |
创建 Spring Boot 项目
- 打开 https://start.spring.io
- 选择:
- Project: Maven
- Language: Java
- Spring Boot: 3.3.x(最新稳定版)
- Group:
com.example - Artifact:
security-demo
- 在 Dependencies 中添加:
- Spring Web
- Spring Security
点击“Generate”,下载 ZIP 文件并解压,导入到你的 IDE 中。
💡 小提示:如果你用的是 IDEA,可以直接在新建项目时选择 Spring Initializr,效果一样。
第二步:理解核心概念(用大白话讲)
在写代码前,先搞懂几个关键术语,避免后面“看天书”:
1. 认证(Authentication) vs 授权(Authorization)
- 认证:你是谁?比如输入用户名和密码登录。
- 授权:你能做什么?比如普通用户只能看自己订单,管理员能删所有订单。
2. SecurityFilterChain
这是 Spring Security 的“总开关”。你可以把它想象成一个安检门:
- 所有请求进来,都要经过它检查
- 如果没登录,直接拦下,跳转到登录页
- 如果有权限,放行
3. UserDetailsService
这是一个接口,负责“查用户”。Spring Security 会问它:“这个用户名存在吗?密码是多少?”
你可以从数据库查,也可以像我们下面这样,临时写死几个用户用于测试。
第三步:动手写一个最简单的安全系统
现在,我们一步步来写代码。
1. 创建一个简单的首页
在 src/main/java/com/example/securitydemo/controller 下新建文件:
// HomeController.java
package com.example.securitydemo.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HomeController {
@GetMapping("/hello")
public String hello() {
return "Hello, this is a protected page!";
}
}
启动应用(运行 SecurityDemoApplication.java),访问 http://localhost:8080/hello —— 你会发现页面跳转到了 /login,而且浏览器提示你输入用户名密码!
这就是 Spring Security 的默认行为:所有路径都受保护,必须登录才能访问。
2. 自定义用户和密码
默认情况下,Spring Security 会在启动时生成一个随机密码(打印在控制台)。但我们要自己控制。
创建一个配置类:
// SecurityConfig.java
package com.example.securitydemo.config;
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.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.anyRequest().authenticated() // 所有请求都需要认证
)
.formLogin(form -> form
.loginPage("/login") // 使用默认登录页
.permitAll() // 登录页本身不需要认证
)
.logout(logout -> logout
.permitAll()
);
return http.build();
}
@Bean
public UserDetailsService userDetailsService() {
UserDetails user = User.builder()
.username("user")
.password("{noop}123456") // {noop} 表示不加密
.roles("USER")
.build();
return new InMemoryUserDetailsManager(user);
}
}
⚠️ 注意:
{noop}是告诉 Spring “密码没加密”,仅用于开发!生产环境必须用加密(如 BCrypt)。
重启应用,再次访问 http://localhost:8080/hello,会看到登录页。输入:
- 用户名:
user - 密码:
123456
就能看到 “Hello, this is a protected page!” 了!
3. 添加一个公开页面
有些页面(比如首页、注册页)不需要登录。我们来加一个:
// PublicController.java
@GetMapping("/welcome")
public String welcome() {
return "Welcome to our site! No login required.";
}
然后修改 SecurityConfig 中的 authorizeHttpRequests:
.authorizeHttpRequests(auth -> auth
.requestMatchers("/welcome").permitAll() // 允许所有人访问
.anyRequest().authenticated()
)
现在,访问 http://localhost:8080/welcome 不需要登录,而 /hello 仍需要。
第四步:常见问题与避坑指南
❓ 问题1:为什么我的密码总是错?
- 检查是否用了
{noop}。如果你写的是.password("123456"),Spring 会认为这是加密后的密码,而实际你输入的是明文,所以永远不匹配。 - 正确写法:
.password("{noop}123456")
❓ 问题2:登录页样式太丑,能改吗?
当然可以!Spring Security 默认提供了一个极简登录页。你可以自定义 HTML 页面,只需在 formLogin() 中指定:
.formLogin(form -> form
.loginPage("/my-login") // 指向你的自定义页面
)
然后创建一个 Controller 返回该页面即可。
❓ 问题3:如何从数据库读取用户?
把 InMemoryUserDetailsManager 换成你自己的 UserDetailsService 实现:
@Service
public class MyUserDetailsService implements UserDetailsService {
@Autowired
private UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String username) {
// 从数据库查询用户
UserEntity user = userRepository.findByUsername(username);
if (user == null) throw new UsernameNotFoundException("...");
return User.builder()
.username(user.getUsername())
.password(user.getPassword()) // 注意:这里应是加密后的密码
.roles("USER")
.build();
}
}
并在 SecurityConfig 中注入它:
@Bean
public UserDetailsService userDetailsService() {
return new MyUserDetailsService(); // 或直接 @Autowired 注入
}
第五步:进阶思考:Fine-tuning 与 DeepSeek
你可能注意到文章标题提到了 DeepSeek 和 Fine-tuning。这看起来和 Spring Security 无关?其实不然。
在实际开发中,安全策略往往需要“微调”(Fine-tuning)。比如:
- 不同角色访问不同 API
- JWT 令牌替代 Session
- 集成 OAuth2(如微信登录)
而 DeepSeek 这类 AI 编程助手,正可以帮助你快速生成这些复杂配置的模板。例如,你可以问:
“用 Spring Security 3.3 实现基于 JWT 的无状态认证,返回完整代码”
AI 会给你一个结构清晰的起点,你再根据业务需求 Fine-tune(微调) 细节。这比从零查阅文档快得多。
但记住:AI 是助手,不是替代品。理解底层原理(比如 FilterChain 的执行顺序),才能在出问题时快速定位。
学习建议:下一步该学什么?
恭喜你,已经迈出了安全开发的第一步!接下来,我建议你按这个顺序深入:
- 密码加密:学习
BCryptPasswordEncoder,替换{noop} - 角色与权限:尝试
hasRole('ADMIN')、hasAuthority('DELETE_ORDER') - JWT 集成:实现无状态认证,适合前后端分离项目
- OAuth2 / OIDC:集成第三方登录(微信、GitHub)
- 安全最佳实践:CSRF 防护、CORS 配置、安全头设置
📌 我的建议:不要试图一次学完所有功能。先用内存用户跑通流程,再逐步替换为数据库、加密、JWT 等模块。
结语
我当初学 Spring Security 时,也被它的“魔法”吓到过。但只要你理解了 FilterChain 是入口,UserDetailsService 是用户来源,剩下的就是配置问题。今天这个教程,就是我当年希望有人能给我的“最小可行示例”。
安全不是功能,而是底线。花一点时间掌握 Spring Security,能让你的项目从第一天就远离很多低级漏洞。
现在,去你的项目里加一行 .authorizeHttpRequests(...) 吧!你已经比昨天的自己更安全了。
Happy Coding!

评论 0