You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Security授权服务器自定义Principal引发Jackson反序列化失败

解决Spring Boot OAuth2授权信息持久化的Jackson反序列化问题

问题根源

当你用自定义OidcUserService返回MyPrincipal(同时实现UserDetails和OidcUser)时,Spring Security会将OIDC提供商返回的所有ID Token属性(如audience、expiresAt、authenticationContextClass等)存入OAuth2Authorization的attributes集合中。JdbcOAuth2AuthorizationService在持久化时会把整个attributes序列化为JSON存入数据库,后续反序列化时Jackson会尝试将这些额外属性映射到MyPrincipal,但你的类中没有对应的字段和setter,因此抛出反序列化错误。

解决方案

方案1:自定义OAuth2AuthorizationService过滤冗余属性

继承JdbcOAuth2AuthorizationService,重写save方法,在持久化前清理掉不需要的OIDC相关属性,只保留核心用户信息:

@Component
public class CustomJdbcOAuth2AuthorizationService extends JdbcOAuth2AuthorizationService {

    public CustomJdbcOAuth2AuthorizationService(DataSource dataSource, OAuth2AuthorizationRowMapper authorizationRowMapper, OAuth2AuthorizationServiceConfigurer configurer) {
        super(dataSource, authorizationRowMapper, configurer);
    }

    @Override
    public void save(OAuth2Authorization authorization) {
        // 过滤attributes,只保留用户核心信息
        Map<String, Object> filteredAttributes = authorization.getAttributes().entrySet().stream()
                .filter(entry -> entry.getKey().equals("username")
                        || entry.getKey().equals("authorities")
                        || entry.getKey().startsWith("user_")) // 自定义用户属性前缀
                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
        
        // 创建新的授权实例,替换过滤后的属性集合
        OAuth2Authorization filteredAuthorization = OAuth2Authorization.from(authorization)
                .attributes(attrs -> attrs.putAll(filteredAttributes))
                .build();
        
        super.save(filteredAuthorization);
    }
}

方案2:给MyPrincipal添加Jackson忽略未知属性注解

快速临时解决方式:在MyPrincipal类上添加注解,让Jackson反序列化时跳过不存在的属性:

@JsonIgnoreProperties(ignoreUnknown = true)
public class MyPrincipal implements UserDetails, OidcUser {
    // 你的类实现代码
}

注意:这种方式只是绕过错误,数据库仍会存储冗余属性,长期建议结合方案1清理数据。

方案3:调整自定义OidcUserService,控制返回的用户属性

在自定义OidcUserService中,构建MyPrincipal时只传入核心用户字段,避免带入OIDC的全量属性:

@Component
public class CustomOidcUserService extends OidcUserService {

    @Override
    public OidcUser loadUser(OidcUserRequest userRequest) throws OAuth2AuthenticationException {
        OidcUser oidcUser = super.loadUser(userRequest);
        
        // 只提取必要的用户信息
        String userId = oidcUser.getSubject();
        String username = oidcUser.getFullName();
        String email = oidcUser.getEmail();
        Collection<? extends GrantedAuthority> authorities = oidcUser.getAuthorities();
        
        // 仅传递核心信息构建MyPrincipal,不传入OIDC全量属性
        return new MyPrincipal(userId, username, email, authorities);
    }
}

推荐组合

优先使用方案1+方案3:通过CustomOidcUserService控制进入attributes的内容,再通过CustomJdbcOAuth2AuthorizationService做最终过滤,确保数据库只存储必要的用户信息,从根源避免反序列化问题。

内容的提问来源于stack exchange,提问作者NotSure

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 04:36:23