请写一篇关于【Spring Security基础:快速搭建安全认证系统】的技术文章
去年十月,我坐在光谷软件园D8栋6楼靠窗的工位上,盯着屏幕上一行又一行报错日志,手边的瑞幸已经凉透。老婆刚发来微信:“这个月房贷还差2000,你那项目奖金到底发不发?”
我回了个“快了”,然后默默把手机塞进裤兜——其实我知道,奖金早就泡汤了。因为我们的登录模块,上线三天就被客户投诉“随便输个账号密码都能进”,差点被当成安全漏洞通报。
那一刻,我真的想删库跑路。
从测试转开发,不是换个title那么简单
我是做功能测试出身的,三年前咬牙转开发,月薪从15k涨到22k,房租也从关山大道搬到金融港(3500一个月,带阳台,但楼下天天挖地铁)。我以为只要会写CRUD、能跑通接口就算后端了。直到那个“万能登录”事故把我打回原形。
组长老张拍我肩膀说:“小陈啊,你这权限控制连个门都没有,黑客进来都不用撬锁,直接推门就进。”
我说:“我不是用了Spring Boot的starter-security吗?”
他冷笑:“加个依赖就叫安全?那你家防盗门是不是贴个‘此地有狗’的纸条就算防贼了?”
我哑口无言。
真正的安全,不是“能跑就行”
痛定思痛,我花了整整一周时间,把Spring Security从头啃了一遍。不是看那些“五分钟集成JWT”的水文,而是去翻官方文档、看源码、搭demo、故意搞破坏再修复。以下是我在光谷深夜加班时总结出的一套极简但有效的安全认证搭建流程——专治我们这种“半路出家+急着交活”的开发者。
第一步:别再用默认配置了!
很多人(包括曾经的我)在pom.xml里加上:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
然后启动项目,发现要输用户名密码,一查控制台有个随机生成的密码,觉得“哦,安全了”,完事。
醒醒!这是开发环境的玩具配置,生产环境等于裸奔。
第二步:自定义UserDetailsService,把用户数据接上
我们系统用的是MySQL存用户信息。我建了个User实体,然后实现UserDetailsService:
@Service
public class CustomUserDetailsService implements UserDetailsService {
@Autowired
private UserDao userDao;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userDao.findByUsername(username);
if (user == null) {
throw new UsernameNotFoundException("用户不存在");
}
// 注意:密码必须是BCrypt加密后的
return User.builder()
.username(user.getUsername())
.password(user.getPassword()) // 这里已经是加密后的
.authorities("ROLE_USER")
.build();
}
}
关键点来了:密码必须加密存储!别再用MD5了,Spring Security内置了BCryptPasswordEncoder,直接用:
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
注册时记得加密:
user.setPassword(passwordEncoder.encode(rawPassword));
第三步:配置SecurityFilterChain(Spring Boot 2.7+新写法)
别再用WebSecurityConfigurerAdapter了,它已经被标记为Deprecated。现在推荐用函数式配置:
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf().disable() // 前后端分离可禁用,但要自己处理CSRF(比如用token)
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeHttpRequests(authz -> authz
.requestMatchers("/api/auth/login").permitAll()
.requestMatchers("/swagger-ui/**", "/v3/api-docs/**").permitAll()
.anyRequest().authenticated()
)
.addFilterBefore(new JwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
return http.build();
}
}
这里我用了JWT做无状态认证(前后端分离标配),所以加了个自定义的JwtAuthenticationFilter来解析token并设置上下文。
第四步:登录接口怎么写?
很多人以为登录就是校验账号密码返回token。但Spring Security有自己的认证流程:
@RestController
@RequestMapping("/api/auth")
public class AuthController {
@Autowired
private AuthenticationManager authenticationManager;
@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody LoginRequest request) {
try {
// 触发Spring Security的认证流程
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
request.getUsername(),
request.getPassword()
)
);
// 认证成功后,生成JWT
String token = jwtUtil.generateToken(authentication.getName());
return ResponseEntity.ok(new JwtResponse(token));
} catch (BadCredentialsException e) {
return ResponseEntity.status(401).body("用户名或密码错误");
}
}
}
注意:不要自己写if-else判断密码!交给AuthenticationManager,它会自动调用你的UserDetailsService和PasswordEncoder。
从“能跑”到“靠谱”,简历才敢写“熟悉安全机制”
这套东西搭完,我重新部署到测试环境,请安全组同事来“攻击”。他们试了SQL注入、暴力破解、越权访问……全被拦住了。客户验收时特意夸了一句:“这次权限控制很严谨。”
更重要的是,我在简历上终于能把“Spring Security”从“了解”改成“熟练使用”,并在项目描述里写上:
- 基于Spring Security + JWT实现RBAC权限模型,支持细粒度接口级鉴权
- 防御常见Web攻击(如CSRF、Session Fixation),通过第三方安全审计
上周五晚上,HR约我谈薪资,说有家光谷东的公司看中我的安全模块经验,开到28k。我和老婆在南湖边散步,她笑着说:“看来那几周熬夜没白熬。”
给同样“半路出家”的兄弟几点真心话
- 别怕底层:Security看起来复杂,但核心就三件事——认证(你是谁)、授权(你能干啥)、防护(别乱来)。拆开看,每块都不难。
- 动手胜过千篇教程:光看视频不动手,永远停留在“好像懂了”。自己搭个demo,故意制造漏洞再修复,印象最深。
- 简历不是吹牛,是证据链:你写了“精通Spring Security”,面试官一定会问“你怎么防重放攻击?”、“登出怎么让token失效?”。答不上来,不如写“熟悉基础认证流程”。
最后
从测试转开发这三年,我踩过无数坑,也熬过无数个凌晨三点的光谷。但每次解决一个真实问题,那种“我真能行”的感觉,比涨薪还爽。
技术分享不是为了装大神,而是告诉后来者:这条路有人走过,坑我都踩过了,你绕开就行。
如果你也在武汉,也在光谷软件园,也在为一个登录模块焦头烂额——别慌,兄弟。Spring Security没那么可怕,可怕的是你一直用“能跑就行”麻痹自己。
毕竟,我们写的不是代码,是别人系统的命门。
而你的简历,值得写得更硬气一点。

评论 0