关于spring-security:springsecurity和ObjectMapperjson反序列化的白名单问题记录

这几天在弄一个对立的反对多租户的认证鉴权平台,就想到了应用oauth这种受权协定来进行,通过蕴含open-user信息的jwt来进行散发。应用这个遇到了一些坑,记录如下:

先阐明一下状况,遇到的是fasterxml的json反序列化的白名单问题,因为解决json的框架容易受到攻打,经常出现一些反序列化破绽,新出一个如果采纳黑名单,则这个名单会一直宏大,须要不断更新,故此fasterxml采纳了白名单的机制来实现json反序列化。

这个问题暴发在于应用了OAuth2AuthorizationService这个实现类,外面有new ObjectMapper();咱们不能管制它外面本人new的ObjectMapper,所以咱们必须从新定义一个OAuth2AuthorizationService,

@Bean
    public OAuth2AuthorizationService authorizationService(JdbcTemplate jdbcTemplate,
                                                           RegisteredClientRepository registeredClientRepository,
                                                           ObjectMapper objectMapper) {
        JdbcOAuth2AuthorizationService service = new JdbcOAuth2AuthorizationService(jdbcTemplate, registeredClientRepository);
        JdbcOAuth2AuthorizationService.OAuth2AuthorizationRowMapper authorizationRowMapper = new JdbcOAuth2AuthorizationService.OAuth2AuthorizationRowMapper(registeredClientRepository);
        authorizationRowMapper.setLobHandler(new DefaultLobHandler());
        authorizationRowMapper.setObjectMapper(objectMapper);
        service.setAuthorizationRowMapper(authorizationRowMapper);
        return service;
    }

这里注入ObjectMapper,咱们就能对其进行定制化了。

//@Bean 这里能够看状况全局配置objectMapper,如果用到了springmvc,会烦扰到springmvc的json解析
//能够把这段写到OAuth2AuthorizationService的Bean定义外面去
    public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
        return builder -> {
            //jackson反序列化 平安白名单
            builder.mixIn(OpenAppUser.class, OpenAppUserMixin.class);
            List<Module> securityModules = SecurityJackson2Modules.getModules(LocalDateTimeSerializerConfig.class.getClassLoader());
            securityModules.add(new OAuth2AuthorizationServerJackson2Module());
            builder.modulesToInstall(securityModules.toArray(new Module[securityModules.size()]));
        };
    }

能够去看看这几个Module,外面都是security定义的一些退出平安白名单的类。这里我本人定义了一个用户类,因为登录查问的用户就是应用的这个
这样就能够进行自定义用户对象的json平安反序列化了。

//实际上应用security和objectmapper的时候提供了三种形式来实现退出反序列化的白名单,在下面的那个加载Module代码里,首先都会调用
SecurityJackson2Modules.enableDefaultTyping(mapper);
//办法,而后外面调用
mapper.setDefaultTyping(createAllowlistedDefaultTyping());
//返回的是一个TypeResolverBuilder,这里的实现类是AllowlistTypeResolverBuilder
private static TypeResolverBuilder<? extends TypeResolverBuilder> createAllowlistedDefaultTyping() {
        TypeResolverBuilder<? extends TypeResolverBuilder> result = new AllowlistTypeResolverBuilder(
                ObjectMapper.DefaultTyping.NON_FINAL);
        result = result.init(JsonTypeInfo.Id.CLASS, null);
        result = result.inclusion(JsonTypeInfo.As.PROPERTY);
        return result;
    }
//AllowlistTypeResolverBuilder类外面的idResolver()办法返回的是TypeIdResolver,实现是一个AllowlistTypeIdResolver()
@Override
protected TypeIdResolver idResolver(MapperConfig<?> config, JavaType baseType, PolymorphicTypeValidator subtypeValidator, Collection<NamedType> subtypes, boolean forSer, boolean forDeser) {
    TypeIdResolver result = super.idResolver(config, baseType,subtypeValidator, subtypes, forSer, forDeser);
    return new AllowlistTypeIdResolver(result);
}
//重点就是这个类了,它外面的typeFromId办法
@Override
public JavaType typeFromId(DatabindContext context, String id) throws IOException {
    DeserializationConfig config = (DeserializationConfig) context.getConfig();
    JavaType result = this.delegate.typeFromId(context, id);
    String className = result.getRawClass().getName();
    if (isInAllowlist(className)) {
        return result;
    }
    boolean isExplicitMixin = config.findMixInClassFor(result.getRawClass()) != null;
    if (isExplicitMixin) {
        return result;
    }
    JacksonAnnotation jacksonAnnotation = AnnotationUtils.findAnnotation(result.getRawClass(),
            JacksonAnnotation.class);
    if (jacksonAnnotation != null) {
        eturn result;
    }
    throw new IllegalArgumentException("The class with " + id + " and name of " + className
                    + " is not in the allowlist. "
                    + "If you believe this class is safe to deserialize, please provide an explicit mapping using Jackson annotations or by providing a Mixin. "
                    + "If the serialization is only done by a trusted source, you can also enable default typing. "
                    + "See https://github.com/spring-projects/spring-security/issues/4370 for details");
}

能够看到security提供的这个办法外面的实现,三种形式别离是
1.isInAllowlist()是否在这个白名单汇合里
2.是否有退出到MixIn,这个就是咱们后面应用的形式
3.反序列化类上是否有@JacksonAnnotation注解
对于形式1,是个static汇合不能批改;其余两种都能够

其实objectMapper提供是否平安是通过PolymorphicTypeValidator类来实现的,security下面默认应用的是

AllowlistTypeResolverBuilder(ObjectMapper.DefaultTyping defaultTyping) {
    super(defaultTyping,
        // we do explicit validation in the TypeIdResolver
        BasicPolymorphicTypeValidator.builder().allowIfSubType(Object.class).build());
}

这个意思是只有是object的子类都通过,所以security是在获取JavaType的时候自定义了一下白名单而没有采纳objectMapper的形式。

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理