Spring Boot中使用thymeleaf-extras-springsecurity的sec标签获取OIDC UserInfo遇问题
在Spring Boot 3.3.5环境下,已引入thymeleaf-extras-springsecurity6依赖并声明xmlns:sec,使用sec:标签展示UserInfo字段时遇到异常:
- 原生Thymeleaf写法可正常运行:
<div th:text="${#authentication.getPrincipal().getUserInfo().getFullName()}"></div> - 但
sec:authentication标签写法执行失败:<div sec:authentication="principal.userinfo.fullname"></div>
堆栈跟踪显示:sec:authentication.principal返回的是OAuth2AuthenticatedPrincipal实例,而#authentication.getPrincipal()返回的是DefaultOidcUser实例——后者自带getUserInfo()方法,前者没有。推测sec:authentication基于访问令牌处理,Thymeleaf原生方式基于ID令牌,想实现让sec:authentication获取ID令牌相关内容。
方法1:SpEL表达式强制类型转换
直接在sec:authentication的表达式里把Principal强转为DefaultOidcUser,就能直接访问userInfo属性:
<div sec:authentication="T(org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser).cast(principal).userInfo.fullName"></div>
方法2:自定义Thymeleaf Spring Security表达式处理器
如果需要频繁使用该逻辑,可以自定义表达式处理器,让sec:标签默认识别DefaultOidcUser:
- 创建自定义解析器类:
import org.springframework.security.core.Authentication; import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser; import org.thymeleaf.extras.springsecurity6.auth.AuthenticationObjectResolver; public class OidcAuthenticationObjectResolver implements AuthenticationObjectResolver { @Override public Object resolvePrincipal(Authentication authentication) { Object principal = authentication.getPrincipal(); if (principal instanceof DefaultOidcUser) { return principal; } return AuthenticationObjectResolver.super.resolvePrincipal(authentication); } }
- 配置Thymeleaf替换默认处理器:
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.thymeleaf.extras.springsecurity6.dialect.SpringSecurityDialect; import org.thymeleaf.extras.springsecurity6.auth.AuthenticationObjectResolver; @Configuration public class ThymeleafSecurityConfig { @Bean public SpringSecurityDialect springSecurityDialect() { SpringSecurityDialect dialect = new SpringSecurityDialect(); dialect.setAuthenticationObjectResolver(new OidcAuthenticationObjectResolver()); return dialect; } }
配置完成后,原来的sec:authentication="principal.userinfo.fullname"写法即可正常运行。
方法3:调整Spring Security OIDC配置
确保Spring Security认证后将DefaultOidcUser作为Authentication的Principal保存,避免被包装为OAuth2AuthenticatedPrincipal。检查OIDC客户端配置,使用默认的OidcUserService即可:
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserService; @Configuration public class OAuth2Config { @Bean public OidcUserService oidcUserService() { return new OidcUserService(); } }
内容的提问来源于stack exchange,提问作者Jeff E Mandel

