首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計綜合問答

Java Web 中基於過濾器的 JavaBean 屬性動態過濾方案

     編輯:程式設計綜合問答

問題背景

SSH(Struts2 + Spring + Hibernate)架構下,需根據用戶權限、API 版本或客戶端類型,動態決定 JavaBean 序列化 JSON 時輸出哪些屬性。例如:管理員看完整字段,普通用戶隱藏敏感欄位(薪資、身分證號)。

核心思路:Filter + ThreadLocal + Jackson 自定義序列化

1. 定義過濾規則配置

可存放於資料庫、YAML 或註解中。範例採用註解:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface JsonView {
    String[] roles() default {};      // 允許的角色
    String[] profiles() default {};   // 允許的配置檔
}

實體類使用:

public class Employee {
    private Long id;
    private String name;

    @JsonView(roles = {"ADMIN", "HR"})
    private BigDecimal salary;

    @JsonView(roles = {"ADMIN"})
    private String idCard;
    // getter/setter
}

2. 過濾器攔截請求,解析上下文

public class JsonFilterConfigFilter implements Filter {
    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) {
        HttpServletRequest request = (HttpServletRequest) req;
        // 從 Session/Token 獲取當前用戶角色
        Set roles = SecurityContext.getCurrentRoles();
        String profile = request.getHeader("X-Client-Profile"); // 如 "mobile", "admin"

        // 存入 ThreadLocal,供序列化器讀取
        JsonSerializationContext.setContext(new SerializationContext(roles, profile));
        try {
            chain.doFilter(req, res);
        } finally {
            JsonSerializationContext.clear(); // 防洩漏
        }
    }
}

在 web.xml 註冊(或 Spring Boot 中 @WebFilter):

<filter>
    <filter-name>jsonFilterConfig</filter-name>
    <filter-class>com.example.filter.JsonFilterConfigFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>jsonFilterConfig</filter-name>
    <url-pattern>/api/*</url-pattern>
</filter-mapping>

3. 自定義 Jackson BeanSerializerModifier

public class DynamicBeanSerializerModifier extends BeanSerializerModifier {
    @Override
    public List changeProperties(
            SerializationConfig config,
            BeanDescription beanDesc,
            List beanProperties) {

        SerializationContext ctx = JsonSerializationContext.getContext();
        if (ctx == null) return beanProperties; // 無上下文則全輸出

        return beanProperties.stream()
            .filter(writer -> isPropertyVisible(writer, ctx))
            .collect(Collectors.toList());
    }

    private boolean isPropertyVisible(BeanPropertyWriter writer, SerializationContext ctx) {
        JsonView view = writer.getAnnotation(JsonView.class);
        if (view == null) return true; // 無註解預設可見

        // 角色匹配
        if (view.roles().length > 0) {
            boolean match = Arrays.stream(view.roles()).anyMatch(ctx.getRoles()::contains);
            if (!match) return false;
        }
        // 配置檔匹配
        if (view.profiles().length > 0) {
            boolean match = Arrays.stream(view.profiles()).anyMatch(p -> p.equals(ctx.getProfile()));
            if (!match) return false;
        }
        return true;
    }
}

4. 註冊 Module 至 ObjectMapper

@Configuration
public class JacksonConfig {
    @Bean
    public ObjectMapper objectMapper() {
        ObjectMapper mapper = new ObjectMapper();
        SimpleModule module = new SimpleModule();
        module.setSerializerModifier(new DynamicBeanSerializerModifier());
        mapper.registerModule(module);
        return mapper;
    }
}

優勢總結

  • 配置集中:註解貼近領域模型,易維護。
  • 零侵入業務代碼:Controller 照常返回完整對象,過濾在序列化層自動完成。
  • 高效能:僅在序列化時過濾屬性,不創建中間 DTO,避免額外拷貝。
  • 靈活擴展:可輕鬆加入「字段級加密」、「脫敏」等邏輯。

替代方案對比

方案優點缺點
手動構建 DTO/VO完全可控類爆炸、維護成本高
Jackson @JsonView(靜態)原生支援無法根據運行時角色動態決定
本方案(動態過濾器)運行時決策、無 DTO、效能好需理解 Jackson 進階擴展點

結語

結合 Servlet Filter 建立序列化上下文,配合 Jackson 的 BeanSerializerModifier,即可在 SSH/Spring Boot 架構下實現優雅、高效、可維護的 JavaBean 屬性動態過濾。此模式同樣適用於 XML、Protobuf 等其他序列化協議。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved