Spring Security基础:快速搭建安全认证系统 —— 一个35岁老码农的血泪经验
上周五晚上十一点,我正瘫在成都温江那套月租3500的老小区沙发上,一边啃着老婆留的冷锅盔,一边改一个烂到家的权限模块。突然微信弹出一条消息,是前同事老张发来的:“哥,你简历上写的‘精通Spring Security’,能帮忙看看我们这个登录死活不跳转的问题吗?”
我苦笑一声,把锅盔放回盘子——哪有什么“精通”,不过是被生活逼出来的熟练工罢了。
一、从“简历写错”到“真香现场”
去年十月,我投了一份成都本地某SaaS公司的Java岗,HR问:“有Spring Security实战经验吗?”
我心一横,打字:“有,主导过RBAC权限系统重构。”
其实呢?我上个项目用的还是Shiro,Security只在B站看过两集视频。但没办法,35岁了,简历不写点“高大上”的,连面试机会都拿不到。月薪15k卡在成都这行里,已经快三年没动过,房贷+娃的奶粉钱压得我喘不过气。
结果面试还真过了。入职第一周,组长丢给我一个任务:“下周上线新后台,用Spring Security搭一套完整的认证授权,支持手机号+验证码+JWT,用户角色要动态加载。”
我当场头皮发麻。回家路上骑共享单车,手都在抖。不是怕技术,是怕露馅。万一搞砸了,35岁被优化,再找工作?简历上“精通”变“造假”,谁还敢要?
二、别信教程,先搭骨架
很多人一上来就看官方文档,或者照着CSDN抄配置。我劝你停一停。Spring Security最坑的地方,不是它难,而是它太灵活,导致你不知道该从哪下手。
我的做法很简单:先搭最小可行骨架,再逐步加功能。
1. 依赖别乱加
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.5</version>
</dependency>
注意:别一上来就加spring-security-oauth2,除非你真要做第三方登录。90%的内部系统,用JWT+自定义过滤器就够了。
2. 关闭默认表单登录(重要!)
Spring Security默认会弹出那个丑到爆的登录页。你肯定不想要:
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf().disable()
.formLogin().disable() // 关掉!
.httpBasic().disable()
.authorizeHttpRequests(authz -> authz
.requestMatchers("/api/auth/**").permitAll()
.anyRequest().authenticated()
)
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
return http.build();
}
}
这段代码我改了七遍。第一次忘了关csrf,前端调接口一直403;第二次没设STATELESS,每次请求都带JSESSIONID,和JWT冲突。这些坑,没人告诉你,只能自己踩。
三、JWT不是万能胶水,别乱贴
很多人以为加个JWT就算“安全”了。错!JWT只是令牌载体,真正的安全逻辑在你的过滤器里。
我写了一个JwtAuthenticationFilter,继承OncePerRequestFilter,核心逻辑就三步:
- 从Header取
Authorization: Bearer <token> - 解析token,校验签名和过期时间
- 构造
UsernamePasswordAuthenticationToken,塞进SecurityContext
但问题来了:怎么动态加载用户权限?
早期我直接在filter里查数据库,结果QPS一高,DB连接池爆了。后来改成缓存用户角色(用Redis,TTL 30分钟),性能立马稳了。
// 伪代码
String token = getTokenFromHeader(request);
Claims claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
String username = claims.getSubject();
List<SimpleGrantedAuthority> authorities = userRoleCache.get(username); // 从缓存拿
SecurityContextHolder.getContext().setAuthentication(
new UsernamePasswordAuthenticationToken(username, null, authorities)
);
记住:认证(Authentication)和授权(Authorization)是两回事。 别把权限判断逻辑全塞进filter,留给@PreAuthorize或hasRole()去处理。
四、Codeium救了我一命
说真的,要不是用了Codeium(免费的AI编程助手),我可能真得熬通宵。
那天晚上,我在写UserDetailsServiceImpl,实现loadUserByUsername方法。脑子里一团浆糊,想不起怎么把用户实体转成UserDetails。手指一敲,Codeium自动补全:
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userMapper.selectByUsername(username);
if (user == null) {
throw new UsernameNotFoundException("用户不存在");
}
return org.springframework.security.core.userdetails.User
.withUsername(user.getUsername())
.password(user.getPassword())
.authorities(user.getRoles().stream()
.map(role -> new SimpleGrantedAuthority("ROLE_" + role.getName()))
.toArray(SimpleGrantedAuthority[]::new))
.build();
}
我愣了三秒,然后默默点了接受。AI不是来取代我们的,是来帮我们填坑的。 尤其当你35岁,脑子不如25岁转得快时,这种工具就是续命丹。
五、技术分享,是我最后的倔强
项目上线后,我主动在公司内部做了次技术分享,主题就是《Spring Security避坑指南》。讲完后,几个年轻同事跑来问:“哥,你咋懂这么多?”
我笑了笑:“不是懂,是摔得够多。”
其实我心里清楚:在成都这种二线城市,技术深度不如一线,但广度必须有。 你不会Security,下个项目可能就轮不到你。而每一次分享,都是在给自己的简历加“真实权重”。
现在我的简历上,“精通Spring Security”后面多了句小字:“基于RBAC模型,支持动态权限+JWT无状态认证,QPS 2000+稳定运行”。虽然HR可能看不懂,但技术面试官一眼就知道——这人真干过。
六、写给和我一样的老码农
35岁还在一线写代码,不是因为热爱,很多时候是没得选。成都工资低,但生活成本也低,房租3500能住三室一厅,老婆做会计,孩子上幼儿园,日子将就过得去。
但将就,不等于躺平。
Spring Security这类框架,学起来枯燥,但它是你对抗年龄焦虑的武器。当别人还在纠结“会不会被AI取代”,你已经用Codeium+扎实的基础,把活干得又快又稳。
技术分享不是为了装逼,是为了让世界知道:我还在线,还能打。
最后一点真心话
如果你也在二线城市,30+,拿着不高不低的工资,担心被淘汰——别慌。深耕一个领域,比如安全认证,把它做到“能讲、能写、能落地”,你就有了护城河。
简历可以包装,但代码骗不了人。Spring Security不难,难的是你愿意花时间去理解它的设计哲学,而不是抄配置。
上周五那个问题,我帮老张解决了。原因很简单:他忘了在HttpSecurity里放行/login接口,导致登录请求被拦截,死循环。
他请我喝了杯瑞幸。我说:“下次别信简历,信实践。”
毕竟,35岁的程序员,剩下的只有真实了。

评论 0