Spring Security基础:快速搭建安全认证系统(零基础入门实战)
大家好,我是B站的技术UP主小码哥。在大厂做后端开发三年,平时除了写业务代码,也喜欢在B站分享一些实用的开发技巧。最近很多粉丝私信问我:“Spring Security到底怎么入门?网上教程要么太深奥,要么直接甩一堆配置,根本看不懂。”
我当初学的时候也是这样——看到AuthenticationManager、UserDetailsService这些名词就头大,连最基本的“登录”都搞不定。所以今天这篇教程,我就用最简单的语言+完整可运行的代码,手把手带你从零搭建一个带用户名密码登录的安全系统。不讲理论堆砌,只讲你能立刻上手的实战内容。
更重要的是,我会穿插面试题挑战环节,帮你提前应对求职中的高频考点。技术分享不是炫技,而是让你真正掌握、能用、能说清楚。
一、Spring Security 是什么?为什么需要它?
想象一下:你开发了一个后台管理系统,里面有用户数据、订单信息。如果随便谁都能访问 /admin/deleteAllUsers 这个接口,那公司第二天就得关门了。
Spring Security 就是给你的应用装上“门禁系统”:
- 谁可以进来?(认证 Authentication)
- 进来后能去哪些房间?(授权 Authorization)
没有它,你的系统就像一栋没有保安的大楼——危险且不专业。
💡 面试题挑战 #1
Q:Spring Security 和 Shiro 有什么区别?
A:Shiro 更轻量、学习曲线平缓;Spring Security 功能更强大、与 Spring 生态无缝集成(比如 OAuth2、JWT),适合企业级复杂场景。大厂基本都用 Spring Security。
二、环境准备:5分钟搭好开发环境
我们用最主流的组合:Spring Boot + Spring Security + Maven
1. 创建项目
打开 start.spring.io,选择:
| 选项 | 值 |
|---|---|
| Project | Maven |
| Language | Java |
| Spring Boot | 3.2.x (最新稳定版) |
| Dependencies | Spring Web, Spring Security |
点击 “Generate”,下载 ZIP 并解压到本地。
2. 导入 IDE
用 IntelliJ IDEA 打开项目(Eclipse 也可以,但 IDEA 对 Spring 支持更好)。
3. 验证基础结构
确保 pom.xml 中包含以下依赖:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
</dependencies>
✅ 避坑指南:不要手动添加 Spring Security 的版本号!Spring Boot 的 parent POM 已经管理好了兼容版本。
三、核心概念:用生活例子理解专业术语
别被术语吓到,我用“小区门禁”来类比:
| 技术概念 | 小区门禁类比 | 作用 |
|---|---|---|
Authentication |
你的身份证+人脸识别 | 证明“你是谁” |
Authorization |
你的房卡权限(只能进1号楼) | 决定“你能做什么” |
UserDetails |
物业登记的业主信息(姓名、房号、权限) | 存储用户数据 |
PasswordEncoder |
身份证加密存储(不能明文存密码!) | 安全地保存密码 |
关键点:
- 认证(Authentication):登录过程,验证用户名密码是否正确。
- 授权(Authorization):登录后,控制你能访问哪些 URL 或方法。
🌟 我当初学的时候:总混淆这两个词。记住口诀——“先认证(你是谁),再授权(你能干啥)”。
四、实战项目:从零实现用户名密码登录
我们将完成一个最简但完整的系统:
- 访问
/hello→ 需要登录 - 登录页 → 输入用户名密码
- 登录成功 → 显示欢迎信息
步骤 1:启动默认安全(验证环境)
直接运行 Application.java,然后浏览器访问 http://localhost:8080/hello。
你会发现:
- 自动跳转到
/login页面 - 控制台打印了一行密码(形如
Using generated security password: a1b2c3d4...)
✅ 这说明 Spring Security 已生效!但它用的是内存临时用户,我们得改成自己的逻辑。
步骤 2:自定义用户(内存版)
创建配置类 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.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
public class SecurityConfig {
@Bean
public InMemoryUserDetailsManager userDetailsService() {
UserDetails user = User.builder()
.username("admin")
.password("{noop}123456") // {noop} 表示不加密(仅演示用!)
.roles("USER")
.build();
return new InMemoryUserDetailsManager(user);
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/hello").authenticated() // /hello 需要登录
.anyRequest().permitAll() // 其他路径开放
)
.formLogin(form -> form
.loginPage("/login") // 自定义登录页(可选)
.permitAll()
);
return http.build();
}
}
同时创建一个简单 Controller:
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello() {
return "Hello, authenticated user!";
}
}
🔐 重要安全提示:
{noop}表示密码明文存储!生产环境绝对禁止! 下一节我们会改用加密。
现在重启应用,访问 /hello → 跳转登录页 → 输入 admin / 123456 → 成功!
步骤 3:密码加密(必须做!)
修改 SecurityConfig.java,加入 PasswordEncoder:
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(); // Spring Security 推荐的强哈希算法
}
@Bean
public InMemoryUserDetailsManager userDetailsService() {
UserDetails user = User.builder()
.username("admin")
.password(passwordEncoder().encode("123456")) // 加密密码
.roles("USER")
.build();
return new InMemoryUserDetailsManager(user);
}
💡 为什么用 BCrypt?
- 不可逆:无法从密文反推明文
- 加盐:相同密码每次加密结果不同,防彩虹表攻击
- 慢哈希:故意计算慢,防暴力破解
✅ 避坑指南:如果你看到
There is no PasswordEncoder mapped for the id "null"错误,就是因为没配PasswordEncoder!
步骤 4:自定义登录页面(提升体验)
创建 src/main/resources/templates/login.html(需先加 Thymeleaf 依赖):
<!-- pom.xml 新增 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
login.html 内容:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head><title>Login</title></head>
<body>
<h2>用户登录</h2>
<form th:action="@{/login}" method="post">
用户名: <input type="text" name="username" /><br/>
密码: <input type="password" name="password" /><br/>
<input type="submit" value="登录" />
</form>
</body>
</html>
修改 SecurityConfig 中的 formLogin 配置:
.formLogin(form -> form
.loginPage("/login") // 使用自定义登录页
.defaultSuccessUrl("/hello") // 登录成功跳转
.permitAll()
)
再加一个返回登录页的 Controller:
@Controller
public class LoginController {
@GetMapping("/login")
public String login() {
return "login"; // 返回 templates/login.html
}
}
现在登录页就是你自己的页面了!
五、常见问题解答(新手必看)
❓ 问题1:为什么登录后总是跳回首页,而不是我指定的页面?
原因:Spring Security 默认行为是“重定向到之前被拦截的页面”。
解决方案:在 formLogin() 中明确指定:
.defaultSuccessUrl("/dashboard", true) // true = 无论从哪来,都去 /dashboard
❓ 问题2:如何放行静态资源(如 CSS/JS)?
在 authorizeHttpRequests 中添加:
.requestMatchers("/css/**", "/js/**", "/images/**").permitAll()
❓ 问题3:控制台没打印密码,登录不了怎么办?
原因:你已经配置了自定义用户,Spring Security 不会再生成临时密码。
解决方案:确保你的用户账号密码正确,并且密码已加密。
❓ 问题4:如何注销(退出登录)?
Spring Security 默认开启 /logout,访问即可退出。
自定义配置:
.logout(logout -> logout
.logoutUrl("/custom-logout")
.logoutSuccessUrl("/login?logout")
)
六、综合:一个完整的小案例
让我们整合所有知识点,做一个极简后台:
| 路径 | 权限要求 | 说明 |
|---|---|---|
/ |
无需登录 | 首页 |
/admin |
需要 ROLE_ADMIN | 管理员页 |
/user |
需要 ROLE_USER | 普通用户页 |
// SecurityConfig.java 片段
.authorizeHttpRequests(auth -> auth
.requestMatchers("/").permitAll()
.requestMatchers("/admin").hasRole("ADMIN")
.requestMatchers("/user").hasAnyRole("USER", "ADMIN")
.anyRequest().authenticated()
)
创建两个用户:
UserDetails admin = User.builder()
.username("admin")
.password(passwordEncoder().encode("admin123"))
.roles("ADMIN")
.build();
UserDetails user = User.builder()
.username("user")
.password(passwordEncoder().encode("user123"))
.roles("USER")
.build();
return new InMemoryUserDetailsManager(admin, user);
💡 面试题挑战 #2
Q:hasRole("ADMIN")和hasAuthority("ROLE_ADMIN")有什么区别?
A:hasRole会自动加上ROLE_前缀,所以.roles("ADMIN")实际权限是ROLE_ADMIN。两者等价,但建议统一用hasRole。
七、学习建议与下一步
恭喜你!你已经掌握了 Spring Security 的核心流程。但这只是冰山一角。
下一步推荐学习路径:
- 数据库集成:用
JdbcUserDetailsManager或自定义UserDetailsService从 DB 读用户 - JWT 无状态认证:适用于前后端分离、APP 接口
- OAuth2 / 社交登录:微信、GitHub 第三方登录
- 方法级安全:用
@PreAuthorize控制 Service 方法权限
我的学习建议:
- 不要死记配置:理解 Filter Chain 的工作流程(画流程图!)
- 善用调试:在
UserDetailsService.loadUserByUsername打断点,看请求如何流转 - 安全第一:永远不要明文存密码,永远不要信任前端传来的权限信息
🎯 最后的话
我在 B站【小码哥编程】更新了配套视频,包含本教程的完整代码演示和调试过程。搜索“Spring Security 零基础”就能找到。
技术分享的意义,就是让后来者少走弯路。如果你觉得有帮助,不妨点个赞,这对我很重要!
记住:每一个大神,都曾是连登录都配不明白的新手。你现在的困惑,正是成长的开始。

评论 0