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

自定义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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 21:01:36