这几天在弄一个对立的反对多租户的认证鉴权平台,就想到了应用 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 的形式。