Spring Security 入门:手把手教你搭建安全认证系统
大家好,我是老李,一名从985高校毕业、现在在一线大厂做后端开发的全栈工程师。平时除了写业务代码,我也喜欢在掘金分享一些新手友好的技术教程。今天这篇《Spring Security 入门:手把手教你搭建安全认证系统》就是专门为完全零基础的同学准备的。
我当初学 Spring Security 的时候,被各种 Filter、Provider、AuthenticationManager 搞得晕头转向。网上很多教程一上来就讲 OAuth2、JWT,结果连最基础的“怎么让用户登录”都搞不明白。所以今天,我就用最朴实的语言,带你从零开始,快速搭建一个带用户名密码登录的安全系统。
为什么需要 Spring Security?
想象一下:你写了一个后台管理系统,任何人都能直接访问 /admin/deleteAllUsers 接口——这显然不行!我们需要一套机制来:
- 验证用户身份(你是谁?)
- 控制权限(你能做什么?)
Spring Security 就是 Spring 家族中专门负责安全控制的框架。它能帮你轻松实现登录认证、权限校验、防 CSRF 攻击等功能。
📌 注意:虽然标题里提到了 Java、Python、面试题、算法,但本文核心是 Java + Spring Security。Python 和算法会在文末学习建议中关联,面试题也会穿插讲解。
环境准备:5 分钟搭好开发环境
你需要以下工具:
| 工具 | 版本建议 | 说明 |
|---|---|---|
| JDK | 17 或 21 | 推荐使用 LTS 版本 |
| Maven | 3.8+ | 项目依赖管理 |
| IDE | IntelliJ IDEA | 社区版即可 |
| Spring Boot | 3.x | 本文基于 3.2 |
第一步:创建 Spring Boot 项目
打开 start.spring.io,选择:
- Project: Maven
- Language: Java
- Spring Boot: 3.2.x
- Dependencies: Spring Web, Spring Security
点击 “Generate” 下载 ZIP,解压后用 IDEA 打开。
第二步:验证项目是否跑起来
在 src/main/java/com/example/demo 下新建一个控制器:
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello() {
return "Hello, World!";
}
}
启动应用,访问 http://localhost:8080/hello。
你会发现:页面跳转到了登录页! 而且用户名是 user,密码在控制台输出(类似 Using generated security password: a1b2c3d4-...)。
这就是 Spring Security 的“默认保护”——任何接口都会被拦截,除非你配置放行。
核心概念:3 个关键词搞懂 Spring Security
别被术语吓到,其实就三件事:
1. 认证(Authentication)—— “你是谁?”
比如用户输入用户名 alice 和密码 123456,系统验证是否正确。
2. 授权(Authorization)—— “你能干什么?”
比如只有 ADMIN 角色才能删除用户,普通用户只能查看。
3. 安全上下文(SecurityContext)—— “记住你是谁”
一旦登录成功,Spring 会把你的身份信息存到 SecurityContext 中,后续请求可以直接获取。
💡 我当初学的时候总混淆“认证”和“授权”。记住:先认证,再授权。就像进公司:先刷工卡(认证),再看你能进哪些楼层(授权)。
实战:自定义用户名密码登录
默认的随机密码没法用,我们来改成固定账号。
步骤 1:创建配置类
新建 SecurityConfig.java:
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/public/**").permitAll() // 放行公开接口
.anyRequest().authenticated() // 其他所有请求都要认证
)
.formLogin(form -> form
.loginPage("/login") // 自定义登录页(可选)
.permitAll()
)
.logout(logout -> logout
.permitAll()
);
return http.build();
}
@Bean
public UserDetailsService userDetailsService() {
UserDetails user = User.builder()
.username("alice")
.password("{noop}123456") // {noop} 表示不加密
.roles("USER")
.build();
return new InMemoryUserDetailsManager(user);
}
}
🔍 关键点解释:
{noop}123456:Spring Security 默认要求密码加密,{noop}表示“明文”,仅用于开发!InMemoryUserDetailsManager:把用户存在内存里,适合演示。真实项目要用数据库。
步骤 2:测试登录
重启应用,访问 http://localhost:8080/hello,会被重定向到 /login。
输入:
- 用户名:
alice - 密码:
123456
登录成功后,就能看到 "Hello, World!"。
进阶:从内存用户到数据库用户
实际项目肯定不能写死账号。下面我们改成从数据库读取。
1. 添加依赖
在 pom.xml 中加入:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
2. 创建用户实体
@Entity
@Table(name = "users")
public class AppUser {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String username;
private String password;
private String role; // 简化:只存一个角色
// getter/setter 略
}
3. 实现 UserDetailsService
@Service
public class CustomUserDetailsService implements UserDetailsService {
@Autowired
private UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
AppUser user = userRepository.findByUsername(username);
if (user == null) {
throw new UsernameNotFoundException("用户不存在");
}
return User.builder()
.username(user.getUsername())
.password("{noop}" + user.getPassword()) // 依然用明文,生产务必加密!
.roles(user.getRole())
.build();
}
}
4. 替换配置中的 Bean
在 SecurityConfig 中,把原来的 userDetailsService() 方法删掉,改为:
@Autowired
private CustomUserDetailsService customUserDetailsService;
@Bean
public UserDetailsService userDetailsService() {
return customUserDetailsService;
}
5. 初始化数据
在 application.properties 中加:
spring.sql.init.mode=always
然后在 src/main/resources 下创建 schema.sql 和 data.sql:
-- schema.sql
CREATE TABLE users (id INT PRIMARY KEY, username VARCHAR(50), password VARCHAR(50), role VARCHAR(20));
-- data.sql
INSERT INTO users VALUES (1, 'bob', 'bob123', 'ADMIN');
重启后,用 bob / bob123 登录即可。
常见问题 & 避坑指南
❌ 问题 1:登录总是失败,提示“Bad credentials”
原因:Spring Security 默认要求密码加密(如 BCrypt)。如果你存的是明文,必须加 {noop} 前缀。
✅ 解决方案:开发阶段用 {noop},上线前务必改用 PasswordEncoder。
❌ 问题 2:静态资源(CSS/JS)也被拦截了
原因:Security 配置拦截了所有请求。
✅ 解决方案:在 authorizeHttpRequests 中放行静态资源:
.requestMatchers("/css/**", "/js/**", "/images/**").permitAll()
❌ 问题 3:如何获取当前登录用户?
✅ 解决方案:在 Controller 中注入:
@GetMapping("/me")
public String getCurrentUser(Authentication auth) {
return "当前用户: " + auth.getName();
}
或者用 SecurityContextHolder:
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
学习建议:下一步该学什么?
你现在掌握了 Spring Security 最基础的认证功能。接下来可以:
- 密码加密:学习
BCryptPasswordEncoder,替换{noop}。 - JWT 集成:适用于前后端分离项目(这时候你会用到 Python 写的测试脚本 来模拟请求)。
- 方法级权限:用
@PreAuthorize("hasRole('ADMIN')")控制方法访问。 - OAuth2 / 微信登录:实现第三方登录。
关于面试题 & 算法
Spring Security 是 Java 后端面试高频考点!常见问题包括:
- Spring Security 的过滤器链有哪些?
- 如何自定义登录逻辑?
- CSRF 是什么?如何防御?
而 算法 能力决定了你能否设计高效的安全策略(比如限流、验证码生成)。建议刷 LeetCode 简单/中等题,重点掌握哈希、树、图——这些在权限模型(RBAC)中都会用到。
🌟 我的建议:先搞定基础认证 → 再学 JWT → 最后挑战 OAuth2。每一步都写个小项目,比死记硬背强十倍。
结语
Spring Security 看似复杂,其实核心就两点:认证 + 授权。只要你理解了这个主线,剩下的都是配置细节。
希望这篇教程能帮你少走弯路。如果你觉得有用,欢迎点赞收藏,也欢迎在评论区提问——我当初也是从零开始,深知新手的困惑。
下期预告:《Spring Security + JWT:打造无状态认证系统》,我们不见不散!

评论 0