SecurityContextHolder转换Principal为User失败问题求助
解决
java.lang.String cannot be cast to User类型转换错误(Spring Security + MongoDB场景) 最近我在Spring Security结合MongoDB的项目中踩了个坑:尝试通过getAuthentication()获取当前用户时,一直抛出java.lang.String cannot be cast to User的类型转换异常,折腾了好一阵才找到原因。先把我的场景和排查过程分享出来,给遇到同样问题的朋友做个参考。
我的项目场景
项目的核心认证相关代码结构如下:
- 自定义
User领域类:实现了Spring Security的UserDetails接口,用@Id注解标记了username属性(作为MongoDB文档主键) UserDetailsServiceImpl:实现UserDetailsService接口,通过UserRepository从MongoDB加载用户信息IdentityController:接口里通过SecurityContextHolder.getContext().getAuthentication()获取认证信息,然后把Principal强制转换成自定义User对象AuthenticationConfig:配置了认证管理器,还初始化了一个根用户到数据库中
排查思路
遇到这个类型转换错误,我一步步梳理了几个排查方向:
先确认用户是否真的完成了认证
这是最容易忽略的点:如果请求的接口没有被配置为需要认证,Spring Security会默认允许匿名访问,此时Authentication里的Principal就是字符串"anonymousUser",这时候强行转成User肯定会报错。检查WebSecurity的配置细节
- 要确认目标接口(比如我这里的
/identity)是否被添加到了需要认证的规则里,比如.antMatchers(HttpMethod.GET,"/identity").authenticated() - 还要检查是否开启了对应的认证方式,比如
httpBasic(),如果没开启,请求不会触发认证流程,自然拿不到正确的User对象
- 要确认目标接口(比如我这里的
打印Principal的实际类型和值
转换前先把Principal的信息打出来,代码像这样:Authentication auth = SecurityContextHolder.getContext().getAuthentication(); System.out.println("Principal类型:" + auth.getPrincipal().getClass().getName()); System.out.println("Principal内容:" + auth.getPrincipal());这一步能直接帮你定位到当前
Principal到底是字符串还是自定义User对象,快速缩小问题范围
最终解决办法
我最终发现问题就出在WebSecurityConfig的配置缺失上:
- 没有为
/identity接口添加.authenticated()规则,导致该接口允许匿名访问 - 没有开启
.httpBasic()认证,请求进来时完全没触发认证流程
补上这两项配置后,用户访问/identity时会先完成认证,此时SecurityContextHolder中的Principal就是我自定义的User对象,类型转换的问题直接就解决了。
内容的提问来源于stack exchange,提问作者Noki
相关产品推荐
相关产品推荐

