我有需要根據調用者的權限排除某些字段。例如,員工的個人資料可能包含他的納稅人ID,這被認爲是敏感信息,並且只有在主叫方是Payrole部門的成員時才應該序列化。由於我使用的是Spring Security,因此我希望將Jackson與當前的安全上下文整合。
public class EmployeeProfile {
private String givenName;
private String surname;
private String emailAddress;
@VisibleWhen("hasRole('PayroleSpecialist')")
private String taxpayerId;
}
最明顯的方法做,這是傑克遜的過濾機制,但它有一些限制:
- 傑克遜不支持嵌套的過濾器,以便將訪問過濾器使用過濾器用於其他任何用途禁止。
- 不能將Jackson註釋添加到現有的第三方類。
- 傑克遜過濾器的設計不是通用的。目的是爲每個想要應用過濾的類編寫一個自定義過濾器。例如,我需要篩選類A和B,然後您必須編寫一個AFilter和一個BFilter。
對於我的用例,解決方案是將自定義註釋introspector與鏈接過濾器結合使用。
public class VisibilityAnnotationIntrospector extends JacksonAnnotationIntrospector {
private static final long serialVersionUID = 1L;
@Override
public Object findFilterId(Annotated a) {
Object result = super.findFilterId(a);
if (null != result) return result;
// By always returning a value, we cause Jackson to query the filter provider.
// A more sophisticated solution will introspect the annotated class and only
// return a value if the class contains annotated properties.
return a instanceof AnnotatedClass ? VisibilityFilterProvider.FILTER_ID : null;
}
}
這基本上是一個副本SimpleBeanProvider替換調用include
通過調用isVisible
。我可能會更新它以使用Java 8 BiPredicate使解決方案更通用但現在可用。 該類還將另一個過濾器作爲參數,並將委託給它以確定是否在字段可見時序列化字段的最終決定。
public class AuthorizationFilter extends SimpleBeanPropertyFilter {
private final PropertyFilter antecedent;
public AuthorizationFilter() {
this(null);
}
public AuthorizationFilter(final PropertyFilter filter) {
this.antecedent = null != filter ? filter : serializeAll();
}
@Deprecated
@Override
public void serializeAsField(Object bean, JsonGenerator jgen, SerializerProvider provider, BeanPropertyWriter writer) throws Exception {
if (isVisible(bean, writer)) {
this.antecedent.serializeAsField(bean, jgen, provider, writer);
} else if (!jgen.canOmitFields()) { // since 2.3
writer.serializeAsOmittedField(bean, jgen, provider);
}
}
@Override
public void serializeAsField(Object pojo, JsonGenerator jgen, SerializerProvider provider, PropertyWriter writer) throws Exception {
if (isVisible(pojo, writer)) {
this.antecedent.serializeAsField(pojo, jgen, provider, writer);
} else if (!jgen.canOmitFields()) { // since 2.3
writer.serializeAsOmittedField(pojo, jgen, provider);
}
}
@Override
public void serializeAsElement(Object elementValue, JsonGenerator jgen, SerializerProvider provider, PropertyWriter writer) throws Exception {
if (isVisible(elementValue, writer)) {
this.antecedent.serializeAsElement(elementValue, jgen, provider, writer);
}
}
private static boolean isVisible(Object pojo, PropertyWriter writer) {
// Code to determine if the field should be serialized.
}
}
然後,我爲每個ObjectMapper實例添加一個自定義過濾器提供程序。
@SuppressWarnings("deprecation")
public class VisibilityFilterProvider extends SimpleFilterProvider {
private static final long serialVersionUID = 1L;
static final String FILTER_ID = "dummy-filter-id";
@Override
public BeanPropertyFilter findFilter(Object filterId) {
return super.findFilter(filterId);
}
@Override
public PropertyFilter findPropertyFilter(Object filterId, Object valueToFilter) {
if (FILTER_ID.equals(filterId)) {
// This implies that the class did not have an explict filter annotation.
return new AuthorizationFilter(null);
}
// The class has an explicit filter annotation so delegate to it.
final PropertyFilter antecedent = super.findPropertyFilter(filterId, valueToFilter);
return new VisibilityPropertyFilter(antecedent);
}
}
最後,我有一個傑克遜模塊,這樣我就不必手動將其添加到每個ObjectMapper實例自動註冊自定義annotaion內省。
public class FieldVisibilityModule extends SimpleModule {
private static final long serialVersionUID = 1L;
public FieldVisibilityModule() {
super(PackageVersion.VERSION);
}
@Override
public void setupModule(Module.SetupContext context) {
super.setupModule(context);
// Append after other introspectors (instead of before) since
// explicit annotations should have precedence
context.appendAnnotationIntrospector(new VisibilityAnnotationIntrospector());
}
}
有更多改進,可以製備和我仍然有更多的單元測試寫入(例如,處理數組和集合),但,這是我所使用的基本策略。
我正在研究使用自定義AnnotationIntrospector來處理此用例的可能性。如果我可以得到它的工作,我會添加另一個答案。 – Faron
你得到它的工作? –
@Vinit Gaikwad他不能這樣做,因爲每個班級都會發生一次內省。您將無法通過AnnotationIntrospector動態確定邏輯 –