Spring Security基础:快速搭建安全认证系统
开篇:Spring Security是什么?为什么我们需要它?

在我们开发Web应用的时候,安全性是一个极其重要的部分。试想一下:如果你开发了一个用户可以登录的网站,但没有设置任何保护措施,别人是否可以直接访问你的敏感数据?这就像是你家门开着不锁一样危险。
Spring Security 就是这样一个“电子锁”。它是Spring框架中的一个模块,专门用来帮助开发者轻松构建安全的Java Web应用程序。它可以帮助我们实现:
- 用户登录和权限控制
- 密码加密
- 防止常见的攻击(如CSRF)
- 与OAuth2、JWT等现代认证机制整合
今天我们就从零开始,用最简单的方式带你搭建一个带有登录认证功能的安全系统!
环境准备:你需要准备什么?

在正式开始之前,我们需要准备好开发环境。别担心,这一步其实非常简单。
所需工具:
- Java JDK(建议使用JDK 17或更高版本)
- IDE(推荐 IntelliJ IDEA 或 Eclipse)
- Maven(用于项目依赖管理)
- Postman(可选,用于测试API)
创建Spring Boot项目(使用Spring Initializr)
你可以直接通过 https://start.spring.io 快速生成一个Spring Boot项目模板。
- Project: Maven
- Language: Java
- Spring Boot Version: 推荐使用 3.x 版本(例如 3.0.5)
- Dependencies:
- Spring Web
- Spring Security
- Spring Data JPA(可选,方便后面加数据库)
- H2 Database(可选,用于测试数据库连接)
点击“Generate”按钮下载项目压缩包,解压后导入到你的IDE中即可。
核心概念:什么是认证、授权?它们有什么区别?
学习Spring Security之前,先来理解几个核心概念。
1. 认证(Authentication)
就是“你是谁”的问题。
举个例子:你在银行取钱时需要输入密码,这就是银行在验证你的身份。这个过程就叫做认证。
2. 授权(Authorization)
就是“你能做什么”的问题。
假设你是公司的管理员,你有权限删除用户;而普通员工就没有这个权限。这种对不同人的操作权限区分,就叫做授权。
3. 用户信息存储(UserDetailsService)
Spring Security允许我们从数据库、内存甚至LDAP中加载用户信息。最简单的做法是使用InMemoryUserDetailsManager,也就是在内存中定义用户名和密码。
4. 加密(PasswordEncoder)
为了安全起见,我们的密码不能明文保存。Spring Security 提供了多种加密方式,常用的有 BCryptPasswordEncoder。
实战项目:一步步创建你的第一个安全应用
我们来实战一个最简单的认证系统——一个只有注册、登录、访问受保护资源的应用。
第一步:创建一个Controller,返回欢迎页面
打开你刚创建的Spring Boot项目,在src/main/java/xxx/demo/controller下新建一个文件叫HomeController.java(xxx是你项目的包名)。
package com.example.demo.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HomeController {
@GetMapping("/")
public String home() {
return "欢迎来到首页!";
}
@GetMapping("/user")
public String userPage() {
return "这是一个用户才能访问的页面。";
}
@GetMapping("/admin")
public String adminPage() {
return "这是管理员页面。";
}
}
现在启动项目,访问:
http://localhost:8080/→ 会显示欢迎信息。http://localhost:8080/user和/admin也可以正常访问。
但我们现在希望:
- 匿名用户只能访问首页
- 登录后的普通用户可以访问/user页面
- 只有管理员可以访问/admin页面
第二步:配置Spring Security基本安全策略
新建一个类:SecurityConfig.java,放在和HomeController相同的目录下。
package com.example.demo.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
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.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
@EnableWebSecurity
@EnableMethodSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/", "/login").permitAll()
.requestMatchers("/user").hasRole("USER")
.requestMatchers("/admin").hasRole("ADMIN")
.anyRequest().authenticated()
)
.formLogin(login -> login
.loginPage("/login") // 指定登录页面URL(自定义可选)
.defaultSuccessUrl("/") // 登录成功跳转地址
.permitAll()
)
.logout(logout -> logout
.logoutSuccessUrl("/") // 登出成功跳转地址
.permitAll()
);
return http.build();
}
@Bean
public UserDetailsService users(PasswordEncoder encoder) {
UserDetails user = User.builder()
.username("user")
.password(encoder.encode("123456"))
.roles("USER")
.build();
UserDetails admin = User.builder()
.username("admin")
.password(encoder.encode("123456"))
.roles("ADMIN")
.build();
return new InMemoryUserDetailsManager(user, admin);
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
解释关键点:
.authorizeHttpRequests()是设置 URL 的访问权限permitAll()表示所有用户都可以访问(比如首页)hasRole("USER")表示必须拥有 USER 角色才能访问- 使用
.formLogin()启用表单登录 - 使用
BCryptPasswordEncoder对密码进行加密 - 我们在内存中添加了两个用户:user(角色为USER),admin(角色为ADMIN)
第三步:测试访问不同页面
重启你的Spring Boot项目,然后尝试访问以下链接:
http://localhost:8080/→ 不需要登录,直接看得到。http://localhost:8080/user→ 会自动跳转到默认登录页。- 输入用户名
user,密码123456→ 成功进入/user页面。 - 尝试访问
/admin页面 → 会出现拒绝访问提示。 - 再用
admin用户登录 → 可以成功访问/admin
✅ 到这里为止,我们已经完成了最简单的安全认证系统!
常见问题答疑区
1. 登录失败怎么办?
可能原因:
- 用户名或密码错误(确认大小写和空格)
- 密码未正确加密(确认使用的是PasswordEncoder)
- 自定义登录页路径不对(如果自定义了登录页要确保路径正确)
解决方法:
- 在控制台看是否有异常输出
- 使用 Postman 模拟 POST /login 请求查看具体响应内容
2. 为什么我设置了权限还是能访问某些页面?
检查:
- 是否写了
.anyRequest().authenticated()? - 是否使用了正确的
.hasRole()匹配URL路径 - 是否缓存了浏览器的session?试试清除浏览器缓存或换隐身模式
3. 如何退出登录?
访问 /logout 即可退出。如果你看到跳转回主页说明登出成功。
也可以在页面里添加一个退出按钮:
<form action="/logout" method="post">
<input type="submit" value="退出登录">
</form>
⚠️ 如果你使用Thymeleaf或者Vue等前端技术,需要注意添加 CSRF token(后面学到再深入)。
4. 为什么不用ROLE_前缀也能运行?
Spring Security 默认会帮我们在角色名前面加上 ROLE_ 前缀。比如你写的是 hasRole("USER"),实际上对应的角色名是 ROLE_USER。
所以即使你不写ROLE也没关系,但如果想更清晰表达,也可以显式写上。
学习建议:下一步该学什么?
你现在已经在Spring Security的世界里迈出了第一步!接下来你还可以继续学习这些进阶内容:
🔹 进阶学习路径建议:
- 数据库验证用户(取代内存验证)
- 使用JPA + 数据库存储用户信息
- 自定义登录页
- 更友好的用户体验
- 基于注解的方法级别权限控制
- 使用
@PreAuthorize,@Secured
- 使用
- 集成OAuth2
- 让微信/支付宝扫码登录你的系统
- 整合JWT
- 构建无状态的REST API安全系统
- 前后端分离下的安全配置
- 解决跨域(CORS)、CSRF防护等常见问题
总结
在本教程中,你学会了:
- Spring Security 是什么,以及它的主要作用
- 如何搭建Spring Boot + Spring Security开发环境
- 实现了最基本的用户认证和权限控制
- 学到了认证 vs 授权的区别
- 遇到常见问题如何排查
记住一句话:“编程不是看懂了才去写,而是写多了才真正懂”。
所以现在,快动手改一改代码,试着自己加一个新角色,比如VIP,只允许访问/vip页面吧!
🎉 路虽远,行则将至。加油!

评论 0