自定义Jackson字段权限注解仅对首个登录用户生效问题求助
问题原因
Jackson的ObjectMapper默认是单例Bean,初始化时会绑定你传入的AnnotationIntrospector实例并缓存。虽然你的JsonSerializerRestrictToIntrospector标记为request作用域,但由于Jackson2ObjectMapperBuilderCustomizer是单例的,它会在启动时就获取到该实例的代理,而Jackson内部缓存的是这个代理的目标实例,导致后续所有请求都复用同一个实例,无法获取当前请求的用户信息。
解决方案
方案一:使用Provider延迟获取Request作用域实例
通过Spring的ObjectProvider延迟获取request作用域的JsonSerializerRestrictToIntrospector,避免单例提前绑定实例。
1. 新增代理注解解析器
@Component public class ProxyJsonSerializerIntrospector extends NopAnnotationIntrospector { private final ObjectProvider<JsonSerializerRestrictToIntrospector> introspectorProvider; public ProxyJsonSerializerIntrospector(ObjectProvider<JsonSerializerRestrictToIntrospector> introspectorProvider) { this.introspectorProvider = introspectorProvider; } @Override public Version version() { return PackageVersion.VERSION; } @Override public boolean hasIgnoreMarker(AnnotatedMember m) { // 每次调用时动态获取当前请求的实例 return introspectorProvider.getIfAvailable().hasIgnoreMarker(m); } }
2. 保持原注解解析器的Request作用域
原JsonSerializerRestrictToIntrospector代码不变,确保@Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)注解保留。
3. 修改配置类注入代理解析器
@Configuration @RequiredArgsConstructor class JacksonConfig { private final ProxyJsonSerializerIntrospector proxyIntrospector; @Bean public Jackson2ObjectMapperBuilderCustomizer jsonCustomizer() { return builder -> { builder.annotationIntrospector(proxyIntrospector); }; } }
方案二:使用ContextualSerializer动态判断
如果方案一仍有问题,可以改用Jackson的ContextualSerializer接口,在序列化字段时动态获取当前用户角色,彻底避免单例缓存问题。
1. 自定义序列化器
public class RestrictForSerializer extends JsonSerializer<Object> implements ContextualSerializer { private boolean shouldSerialize; // 无参构造,用于初始创建 public RestrictForSerializer() {} // 带参构造,用于上下文初始化 public RestrictForSerializer(boolean shouldSerialize) { this.shouldSerialize = shouldSerialize; } @Override public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException { if (shouldSerialize) { gen.writeObject(value); } } @Override public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException { RestrictFor annotation = property.getAnnotation(RestrictFor.class); if (annotation == null) { // 无注解时使用默认序列化器 return prov.findValueSerializer(property.getType(), property); } // 获取当前登录用户的角色 LoggedUserProvider loggedUserProvider = prov.getApplicationContext().getBean(LoggedUserProvider.class); Set<String> allowedRoles = new HashSet<>(Arrays.asList(annotation.roles())); allowedRoles.retainAll(loggedUserProvider.getRoles()); // 返回带判断结果的序列化器 return new RestrictForSerializer(!allowedRoles.isEmpty()); } }
2. 修改自定义注解绑定序列化器
@Documented @Retention(RUNTIME) @JsonSerialize(using = RestrictForSerializer.class) public @interface RestrictFor { String[] roles() default {}; }
3. 移除原AnnotationIntrospector相关配置
不需要再配置JsonSerializerRestrictToIntrospector和对应的Jackson自定义器,直接使用注解即可。
内容的提问来源于stack exchange,提问作者Denis Kisina
相关产品推荐
相关产品推荐

