乐趣区

关于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 的形式。

退出移动版