零基础也能学会:用Spring Security快速搭建Java安全认证系统
大家好,我是掘金上那个总爱写“保姆级教程”的985全栈工程师。今天想和大家聊聊Spring Security——一个让无数Java初学者又爱又怕的框架。
我当初学的时候,也是一头雾水:为什么登录要配置这么多类?为什么前端请求老是403?为什么明明用户名密码对了却进不去?后来我才明白,不是Spring Security难,而是没人用“人话”讲清楚它的核心逻辑。
所以今天这篇教程,不讲理论堆砌,只聚焦一件事:如何用最简单的方式,在你的Java项目里快速加上用户名密码登录功能,并让前端能正常调用。全程手把手,代码可直接运行!
为什么你需要Spring Security?
想象一下:你写了个后台管理系统,任何人都能访问用户列表、删除数据——这显然不行。Spring Security就是帮你做“谁可以访问什么”的守门人。它能自动拦截未登录请求、验证身份、防止CSRF攻击等。
而好消息是:Spring Boot + Spring Security 的组合,已经把90%的复杂配置自动化了。我们只需要几行代码,就能拥有一个带登录页的安全系统。
环境准备:5分钟搭好开发环境
你需要安装:
- JDK 17(推荐,兼容性最好)
- IntelliJ IDEA(社区版免费)
- Maven(IDEA内置,无需单独装)
- 一个现代浏览器(Chrome/Firefox)
💡 避坑提示:别用JDK 8!虽然Spring Security支持,但新版本默认配置更简洁。JDK 17是当前LTS版本,强烈推荐。
创建项目
- 打开 start.spring.io
- 选择:
- Project: Maven
- Language: Java
- Spring Boot: 3.x(最新稳定版)
- Java: 17
- 添加依赖:
- Spring Web
- Spring Security
- 点击“Generate”,下载并解压到本地
- 用IDEA打开项目
✅ 此时你的
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>
核心概念:3个关键词搞懂Spring Security
别被文档吓到,其实就三个核心角色:
| 角色 | 作用 | 类比 |
|---|---|---|
| Authentication(认证) | “你是谁?” | 检查身份证 |
| Authorization(授权) | “你能干什么?” | 查看通行证权限 |
| SecurityFilterChain(过滤链) | 所有请求先过这道安检门 | 机场安检通道 |
📌 重点:Spring Security默认会保护所有路径(
/**),未认证用户访问会被重定向到自动生成的登录页。
实战:5步实现带登录的API接口
我们现在要实现:
- 用户访问
/api/hello必须登录 - 前端通过表单提交用户名密码登录
- 登录成功后返回JSON数据
第1步:创建一个受保护的接口
// src/main/java/com/example/demo/HelloController.java
package com.example.demo;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@GetMapping("/api/hello")
public String hello() {
return "你好!你已成功登录";
}
}
第2步:配置安全规则(关键!)
// src/main/java/com/example/demo/SecurityConfig.java
package com.example.demo;
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
.requestMatchers("/api/**").authenticated() // /api开头的路径需要认证
.anyRequest().permitAll() // 其他路径(如登录页)放行
)
.formLogin(form -> form
.loginPage("/login") // 自定义登录页URL(可选)
.permitAll() // 登录页本身不需要认证
)
.logout(logout -> logout
.permitAll()
);
return http.build();
}
// 内存中存储用户(仅演示用!生产环境要用数据库)
@Bean
public UserDetailsService userDetailsService() {
UserDetails user = User.builder()
.username("admin")
.password("{noop}123456") // {noop}表示不加密(仅测试!)
.roles("USER")
.build();
return new InMemoryUserDetailsManager(user);
}
}
🔑 关键解释:
{noop}123456:{noop}告诉Spring“密码没加密”,实际项目必须用PasswordEncoder加密!.requestMatchers("/api/**").authenticated():只有带/api的路径才需要登录
第3步:启动项目,试试效果
- 运行
DemoApplication.main() - 浏览器访问
http://localhost:8080/api/hello - 自动跳转到登录页!
默认登录页长这样(Spring Security自动生成):
- 用户名输入框
- 密码输入框
- 提交按钮
输入:
- 用户名:
admin - 密码:
123456
登录成功后,自动跳回 /api/hello,显示:“你好!你已成功登录”
第4步:让前端调用更友好(返回JSON而非页面)
问题来了:前端通常是AJAX请求,不需要跳转页面,而是希望返回JSON错误信息。
修改配置,支持JSON响应:
// 在SecurityConfig.java中新增
@Bean
public AuthenticationEntryPoint authenticationEntryPoint() {
return (request, response, authException) -> {
response.setStatus(401);
response.setContentType("application/json");
response.getWriter().write("{\"error\":\"请先登录\"}");
};
}
// 在filterChain方法中加入:
http
.exceptionHandling(e -> e
.authenticationEntryPoint(authenticationEntryPoint()) // 未认证时返回JSON
)
// ... 其他配置保持不变
现在前端用axios请求:
// 前端示例(Vue/React通用)
fetch('/api/hello')
.then(res => {
if (res.status === 401) {
alert('请先登录!');
// 跳转到登录页
} else {
return res.json();
}
});
第5步:处理登录成功/失败的JSON响应(可选进阶)
如果你希望登录也通过AJAX提交(而不是表单跳转),可以这样配置:
.formLogin(form -> form
.successHandler((request, response, authentication) -> {
response.setStatus(200);
response.setContentType("application/json");
response.getWriter().write("{\"status\":\"success\"}");
})
.failureHandler((request, response, exception) -> {
response.setStatus(401);
response.setContentType("application/json");
response.getWriter().write("{\"error\":\"用户名或密码错误\"}");
})
)
此时前端可这样登录:
// 登录请求
fetch('/login', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: 'username=admin&password=123456'
})
.then(res => res.json())
.then(data => {
if (data.status === 'success') {
// 登录成功,跳转页面
}
});
新手常见问题解答
Q1:为什么密码要加 {noop}?
A:Spring Security默认要求密码加密。{noop}是“No Operation”的缩写,表示“不要加密”。生产环境必须移除它,并使用BCrypt加密!
Q2:如何用数据库存用户?
A:把 InMemoryUserDetailsManager 换成自定义的 UserDetailsService,从数据库查用户即可。后续我会单独写一篇《Spring Security + MyBatis实战》。
Q3:前端跨域怎么办?
A:在 SecurityConfig 中加入CORS配置:
http.cors(cors -> cors.configurationSource(request -> {
var corsConfig = new CorsConfiguration();
corsConfig.addAllowedOrigin("http://localhost:3000"); // 前端地址
corsConfig.addAllowedMethod("*");
corsConfig.addAllowedHeader("*");
corsConfig.setAllowCredentials(true);
return corsConfig;
}));
Q4:为什么登出后还能访问接口?
A:检查是否启用了Session。默认基于Session认证,登出即销毁Session。确保前端清除Cookie或Token。
下一步学习建议
- 密码加密:学习
BCryptPasswordEncoder,替换{noop} - JWT认证:适合前后端分离项目,替代Session
- 权限控制:用
@PreAuthorize("hasRole('ADMIN')")控制接口权限 - OAuth2:接入微信/Google第三方登录
💡 我的经验:先跑通一个完整流程,再深入细节。很多新手卡在“完美配置”上,结果连第一个请求都没跑起来。记住:能跑起来的代码,才是好代码。
结语
今天我们用不到100行代码,就搭建了一个带登录认证的Java后端系统,并让前端能友好地交互。Spring Security并没有想象中那么可怕,关键是理解它的“过滤链”思维。
如果你跟着做了一遍,恭喜你!你已经超过了80%只看不练的初学者。接下来,试着把用户数据换成数据库,或者加上验证码功能——实践才是掌握技术的唯一路径。
有问题欢迎在评论区留言,我会一一解答。下期见!

评论 0