序列化时未使用 Jackson PropertyFilter XML
Jackson PropertyFilter not used when serializing XML
我已经创建了一个 Jackson PropertyFilter 并将其注册到 XmlMapper,但它不用于过滤从 Spring @RestController 返回的属性。
我创建并使用了 Jackson PropertyFilter 来过滤 JSON ObjectMapper 为 Spring @RestController 生成的结果。我正在尝试为 XML 启用相同的功能,但无法正常工作。
我试过直接在 XmlMapper 实例上并通过 Jackson2ObjectMapperBuilder 注册过滤器。在这两种情况下都不会调用它。
我已逐步完成代码,XmlBeanSerializer 似乎引用了过滤器,但过滤器从未被调用。
我创建了一个 LogAllPropertyFilter class 来记录过滤器是否被调用并且没有生成任何日志消息。
public class LogAllPropertyFilter extends SimpleBeanPropertyFilter implements PropertyFilter {
private Logger logger = LoggerFactory.getLogger(getClass());
@Override
public void serializeAsField(Object pojo, JsonGenerator gen, SerializerProvider prov, PropertyWriter writer)
throws Exception {
logger.info(" *** *** serializeAsField {}.{}",
pojo.getClass().getSimpleName(),
writer.getName());
super.serializeAsField(pojo, gen, prov, writer);
}
@Override
public void serializeAsElement(Object elementValue, JsonGenerator gen, SerializerProvider prov,
PropertyWriter writer) throws Exception {
logger.info(" *** *** serializeAsElement {}.{}",
elementValue.getClass().getSimpleName(),
writer.getName());
super.serializeAsElement(elementValue, gen, prov, writer);
}
@SuppressWarnings("deprecation")
@Override
public void depositSchemaProperty(PropertyWriter writer, ObjectNode propertiesNode, SerializerProvider provider)
throws JsonMappingException {
logger.info(" *** *** depositSchemaProperty {} (deprecated)",
writer.getName());
super.depositSchemaProperty(writer, propertiesNode, provider);
}
@Override
public void depositSchemaProperty(PropertyWriter writer, JsonObjectFormatVisitor objectVisitor,
SerializerProvider provider) throws JsonMappingException {
logger.info(" *** *** depositSchemaProperty {} (deprecated)",
writer.getName());
super.depositSchemaProperty(writer, objectVisitor, provider);
}
}
我正在这样创建和注册 PropertyFilter:
<bean id="logAllFilter" class="calpers.eai.config.auth.jacksonpropertyfilter.LogAllPropertyFilter" />
<bean id="logAllFilterProvider"
class="com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider">
<constructor-arg>
<map>
<entry key="logAllFilter"
value-ref="logAllFilter" />
</map>
</constructor-arg>
</bean>
<bean id="xmlObjectMapper"
class="com.fasterxml.jackson.dataformat.xml.XmlMapper" />
<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetObject" ref="xmlObjectMapper" />
<property name="targetMethod" value="setFilterProvider" />
<property name="arguments" ref="logAllFilterProvider" />
</bean>
<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetObject" ref="xmlObjectMapper" />
<property name="targetMethod" value="disable" />
<property name="arguments" value="WRITE_DATES_AS_TIMESTAMPS" />
</bean>
<!-- indent json - disable this in prod -->
<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetObject" ref="xmlObjectMapper" />
<property name="targetMethod" value="enable" />
<property name="arguments" value="INDENT_OUTPUT" />
</bean>
<bean id="xmlConverter" class="org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter">
<constructor-arg ref="xmlObjectMapper" />
</bean>
<mvc:annotation-driven>
<mvc:message-converters>
<!-- json works -->
<ref bean="jsonConverter" />
<!-- xml doesn't work -->
<ref bean="xmlConverter" />
</mvc:message-converters>
</mvc:annotation-driven>
XML 输出 是 缩进的,所以我知道 XmlMapper 实例正在被拾取。但是,从不调用 PropertyFilter 方法。我被难住了。
除非 class 以某种方式链接到过滤器,否则不会应用过滤器。通常使用注释,但在这种情况下,我需要过滤所有对象的属性,而不管它们的出处,因此我们将在所有 [=20= 的公共 base-class 上使用 mix-in ] 对象:
<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetObject" ref="xmlObjectMapper" />
<property name="targetMethod" value="addMixIn" />
<property name="arguments">
<list>
<value type="java.lang.Class">java.lang.Object</value>
<value type="java.lang.Class">eai.config.auth.jacksonpropertyfilter.SecurityRoleAwareJacksonMixIn</value>
</list>
</property>
</bean>
将此添加到配置后,我的过滤器在每个 XML 对象上 运行 从我的 Spring MVC @RestController 提供。
这是一个方便的过滤器,可根据 Spring 安全性中的安全角色控制对 class 属性的访问。享受吧!
package eai.config.auth.jacksonpropertyfilter;
import java.io.IOException;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.ldap.userdetails.LdapAuthority;
import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
import com.fasterxml.jackson.databind.ser.PropertyWriter;
import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter;
import eai.config.auth.jacksonpropertyfilter.xml.SecurityRole;
import eai.config.refreshable.Refreshable;
/**
* Filters based on the union of properties a principal can view. In JsonViewConfiguration a user
* with multiple views will be assigned the highest ranked view and only see the properties that are
* included in that view. With SecurityRoleAwareJacksonFilterImpl, the user will see any property they
* have access to based on ALL the groups they are members of. Therefore, it is the union of
* all @JsonView's.
*
* This class should be instantiated as a Spring Bean, probably in the XML config to maximize
* configuration options that avoid a re-compile.
*
* @author TPerry2
*/
public class SecurityRoleAwareJacksonFilterImpl extends SimpleBeanPropertyFilter
implements SecurityRoleAwareJacksonFilter, Refreshable {
private final Logger logger = LoggerFactory.getLogger(
SecurityRoleAwareJacksonFilterImpl.class);
Map<Class<?>, Map<String, Collection<SecurityRole>>> classPropertyRoles =
new HashMap<>();
List<SecurityRoleToClassPropertyReader> securityRoleToClassPropertyReaders =
new ArrayList<>();
private ConcurrentHashMap<String, String> knownUserNoRole =
new ConcurrentHashMap<>();
private ConcurrentHashMap<Class<?>, Set<String>> classPropsWithNoAccess =
new ConcurrentHashMap<>();
/**
* Add mapping for what class properties a LDAP role can view.
*
* @param securityRoleToClassPropertyXmlReaders to obtain mapping data from.
* @throws ClassNotFoundException if the java class can not be found.
* @throws IOException when security role to class property XML files can't be read.
*/
@Override
@Autowired
public void setSecurityRoleToClassPropertyReaders(
List<SecurityRoleToClassPropertyReader> securityRoleToClassPropertyReaders)
throws ClassNotFoundException, IOException {
this.securityRoleToClassPropertyReaders = securityRoleToClassPropertyReaders;
loadClassPropertyRoles();
}
/**
* Method called to determine whether property will be included
* (if 'true' returned) or filtered out (if 'false' returned)
*/
protected boolean include(BeanPropertyWriter writer) {
AnnotatedMember memberToSerialize = writer.getMember();
if (memberToSerialize == null) {
logger.warn("Could not get member to serialize for writer {}",
writer.getClass().getName());
return false;
}
final Class<?> clazz = memberToSerialize.getDeclaringClass();
return include(clazz, writer.getName());
}
/**
* Method called to determine whether property will be included
* (if 'true' returned) or filtered out (if 'false' returned)
*/
protected boolean include(PropertyWriter writer) {
AnnotatedMember memberToSerialize = writer.getMember();
if (memberToSerialize == null) {
logger.warn("Could not get member to serialize for writer {}",
writer.getClass().getName());
return false;
}
final Class<?> clazz = memberToSerialize.getDeclaringClass();
return include(clazz, writer.getName());
}
protected boolean include(
Class<?> clazz,
String propertyName) {
logger.info("Checking {}.{}", clazz.getSimpleName(), propertyName);
final Map<String, Collection<SecurityRole>> propertyLdapRoleMap =
classPropertyRoles.get(clazz);
if (propertyLdapRoleMap != null) {
final Collection<SecurityRole> securityRoles =
propertyLdapRoleMap.get(propertyName);
if (securityRoles != null && securityRoles.size() > 0) {
Authentication auth = getAuthentication();
if (isAuthorized(getGrantedAuthorities(auth), securityRoles)) {
logger.info("allowing {}.{}", clazz.getSimpleName(), propertyName);
return true;
} else {
logUserNoRole(clazz, propertyName, securityRoles, auth);
}
} else {
logPropertyWithNoAccess(clazz, propertyName);
}
} else {
logPropertyWithNoAccess(clazz, "-- all properties --");
}
return false;
}
private void logUserNoRole(
Class<?> clazz,
String propertyName,
Collection<SecurityRole> allowedRoles,
Authentication auth) {
if (!logger.isDebugEnabled()) {
return;
}
String username = (auth == null ? "anonymous" : auth.getName());
final String knownUserNoRoleString = ""
+ clazz.getName() + "." + propertyName + "."
+ username;
boolean known = knownUserNoRole.containsKey(knownUserNoRoleString);
if (!known) {
knownUserNoRole.put(knownUserNoRoleString, "");
logger.debug("User {} does not have valid role for {}.{}. "
+ "Requires one of {}", username, clazz.getName(),
propertyName, allowedRoles);
}
}
private void logPropertyWithNoAccess(Class<?> clazz, String propertyName) {
Set<String> knownPropsWithNoAccess = classPropsWithNoAccess.get(clazz);
if (knownPropsWithNoAccess == null) {
logger.warn("No roles enable access to {}.{}",
clazz.getSimpleName(), propertyName);
knownPropsWithNoAccess = new HashSet<>();
classPropsWithNoAccess.put(clazz, knownPropsWithNoAccess);
}
boolean wasAdded = false;
synchronized (knownPropsWithNoAccess) {
wasAdded = knownPropsWithNoAccess.add(propertyName);
}
if (wasAdded) {
logger.warn("No roles enable access to {}.{}",
clazz.getSimpleName(), propertyName);
}
}
private boolean isAuthorized(
Collection<? extends GrantedAuthority> grantedAuths,
Collection<SecurityRole> securityRoles) {
try {
if (grantedAuths == null) {
return false;
}
for (GrantedAuthority grantedAuth : grantedAuths) {
if (grantedAuth instanceof LdapAuthority) {
LdapAuthority ldapAuth = (LdapAuthority) grantedAuth;
for (SecurityRole secRole : securityRoles) {
if (secRole.distinguishedNameIsAuthorized(
ldapAuth.getDn())) {
return true;
}
if (secRole.displayNameIsAuthorized(
ldapAuth.getAuthority())) {
return true;
}
}
} else {
for (SecurityRole secRole : securityRoles) {
if (secRole.displayNameIsAuthorized(
grantedAuth.getAuthority())) {
return true;
}
}
}
}
return false;
} catch (NullPointerException npe) {
logger.error("FIXME", npe);
return false;
}
}
private Collection<? extends GrantedAuthority> getGrantedAuthorities(
Authentication auth) {
if (auth == null) {
return Collections.emptyList();
}
try {
return auth.getAuthorities();
}
catch (Exception e) {
logger.error("Could not retrieve authorities", e);
return Collections.emptyList();
}
}
private Authentication getAuthentication() {
try {
SecurityContext secCtxt = SecurityContextHolder.getContext();
if (secCtxt == null) {
logger.warn("SecurityContextHolder.getContext() returned null, " +
+ "no authorities present");
return null;
}
Authentication auth = secCtxt.getAuthentication();
if (auth == null) {
logger.warn("SecurityContextHolder.getContext().getAuthentication() "
+ "returned null, no authorities present");
}
return auth;
} catch (Exception e) {
logger.error("Could not retrieve Authentication", e);
return null;
}
}
private void loadClassPropertyRoles() {
Map<Class<?>, Map<String, Collection<SecurityRole>>> newClassPropertyRoles =
new HashMap<>();
for (SecurityRoleToClassPropertyReader reader : securityRoleToClassPropertyReaders) {
Map<Class<?>, Map<String, Collection<SecurityRole>>> readerClassPropertyRoles =
reader.loadClassPropertyRoles();
for (Class<?> clazz : readerClassPropertyRoles.keySet()) {
Map<String, Collection<SecurityRole>> propertyRoles =
newClassPropertyRoles.get(clazz);
if (propertyRoles == null) {
propertyRoles = new HashMap<>();
newClassPropertyRoles.put(clazz, propertyRoles);
}
for (String propertyName : readerClassPropertyRoles.get(clazz).keySet()) {
Collection<SecurityRole> allowedRolesForProp =
propertyRoles.get(propertyName);
if (allowedRolesForProp == null) {
allowedRolesForProp = new ArrayList<>();
propertyRoles.put(propertyName, allowedRolesForProp);
}
Collection<SecurityRole> newLdapRoles =
readerClassPropertyRoles.get(clazz).get(propertyName);
for (SecurityRole securityRole : newLdapRoles) {
if (!allowedRolesForProp.contains(securityRole)) {
allowedRolesForProp.add(securityRole);
}
}
}
}
}
this.classPropertyRoles = newClassPropertyRoles;
}
}
我已经创建了一个 Jackson PropertyFilter 并将其注册到 XmlMapper,但它不用于过滤从 Spring @RestController 返回的属性。
我创建并使用了 Jackson PropertyFilter 来过滤 JSON ObjectMapper 为 Spring @RestController 生成的结果。我正在尝试为 XML 启用相同的功能,但无法正常工作。
我试过直接在 XmlMapper 实例上并通过 Jackson2ObjectMapperBuilder 注册过滤器。在这两种情况下都不会调用它。
我已逐步完成代码,XmlBeanSerializer 似乎引用了过滤器,但过滤器从未被调用。
我创建了一个 LogAllPropertyFilter class 来记录过滤器是否被调用并且没有生成任何日志消息。
public class LogAllPropertyFilter extends SimpleBeanPropertyFilter implements PropertyFilter {
private Logger logger = LoggerFactory.getLogger(getClass());
@Override
public void serializeAsField(Object pojo, JsonGenerator gen, SerializerProvider prov, PropertyWriter writer)
throws Exception {
logger.info(" *** *** serializeAsField {}.{}",
pojo.getClass().getSimpleName(),
writer.getName());
super.serializeAsField(pojo, gen, prov, writer);
}
@Override
public void serializeAsElement(Object elementValue, JsonGenerator gen, SerializerProvider prov,
PropertyWriter writer) throws Exception {
logger.info(" *** *** serializeAsElement {}.{}",
elementValue.getClass().getSimpleName(),
writer.getName());
super.serializeAsElement(elementValue, gen, prov, writer);
}
@SuppressWarnings("deprecation")
@Override
public void depositSchemaProperty(PropertyWriter writer, ObjectNode propertiesNode, SerializerProvider provider)
throws JsonMappingException {
logger.info(" *** *** depositSchemaProperty {} (deprecated)",
writer.getName());
super.depositSchemaProperty(writer, propertiesNode, provider);
}
@Override
public void depositSchemaProperty(PropertyWriter writer, JsonObjectFormatVisitor objectVisitor,
SerializerProvider provider) throws JsonMappingException {
logger.info(" *** *** depositSchemaProperty {} (deprecated)",
writer.getName());
super.depositSchemaProperty(writer, objectVisitor, provider);
}
}
我正在这样创建和注册 PropertyFilter:
<bean id="logAllFilter" class="calpers.eai.config.auth.jacksonpropertyfilter.LogAllPropertyFilter" />
<bean id="logAllFilterProvider"
class="com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider">
<constructor-arg>
<map>
<entry key="logAllFilter"
value-ref="logAllFilter" />
</map>
</constructor-arg>
</bean>
<bean id="xmlObjectMapper"
class="com.fasterxml.jackson.dataformat.xml.XmlMapper" />
<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetObject" ref="xmlObjectMapper" />
<property name="targetMethod" value="setFilterProvider" />
<property name="arguments" ref="logAllFilterProvider" />
</bean>
<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetObject" ref="xmlObjectMapper" />
<property name="targetMethod" value="disable" />
<property name="arguments" value="WRITE_DATES_AS_TIMESTAMPS" />
</bean>
<!-- indent json - disable this in prod -->
<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetObject" ref="xmlObjectMapper" />
<property name="targetMethod" value="enable" />
<property name="arguments" value="INDENT_OUTPUT" />
</bean>
<bean id="xmlConverter" class="org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter">
<constructor-arg ref="xmlObjectMapper" />
</bean>
<mvc:annotation-driven>
<mvc:message-converters>
<!-- json works -->
<ref bean="jsonConverter" />
<!-- xml doesn't work -->
<ref bean="xmlConverter" />
</mvc:message-converters>
</mvc:annotation-driven>
XML 输出 是 缩进的,所以我知道 XmlMapper 实例正在被拾取。但是,从不调用 PropertyFilter 方法。我被难住了。
除非 class 以某种方式链接到过滤器,否则不会应用过滤器。通常使用注释,但在这种情况下,我需要过滤所有对象的属性,而不管它们的出处,因此我们将在所有 [=20= 的公共 base-class 上使用 mix-in ] 对象:
<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetObject" ref="xmlObjectMapper" />
<property name="targetMethod" value="addMixIn" />
<property name="arguments">
<list>
<value type="java.lang.Class">java.lang.Object</value>
<value type="java.lang.Class">eai.config.auth.jacksonpropertyfilter.SecurityRoleAwareJacksonMixIn</value>
</list>
</property>
</bean>
将此添加到配置后,我的过滤器在每个 XML 对象上 运行 从我的 Spring MVC @RestController 提供。
这是一个方便的过滤器,可根据 Spring 安全性中的安全角色控制对 class 属性的访问。享受吧!
package eai.config.auth.jacksonpropertyfilter;
import java.io.IOException;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.ldap.userdetails.LdapAuthority;
import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
import com.fasterxml.jackson.databind.ser.PropertyWriter;
import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter;
import eai.config.auth.jacksonpropertyfilter.xml.SecurityRole;
import eai.config.refreshable.Refreshable;
/**
* Filters based on the union of properties a principal can view. In JsonViewConfiguration a user
* with multiple views will be assigned the highest ranked view and only see the properties that are
* included in that view. With SecurityRoleAwareJacksonFilterImpl, the user will see any property they
* have access to based on ALL the groups they are members of. Therefore, it is the union of
* all @JsonView's.
*
* This class should be instantiated as a Spring Bean, probably in the XML config to maximize
* configuration options that avoid a re-compile.
*
* @author TPerry2
*/
public class SecurityRoleAwareJacksonFilterImpl extends SimpleBeanPropertyFilter
implements SecurityRoleAwareJacksonFilter, Refreshable {
private final Logger logger = LoggerFactory.getLogger(
SecurityRoleAwareJacksonFilterImpl.class);
Map<Class<?>, Map<String, Collection<SecurityRole>>> classPropertyRoles =
new HashMap<>();
List<SecurityRoleToClassPropertyReader> securityRoleToClassPropertyReaders =
new ArrayList<>();
private ConcurrentHashMap<String, String> knownUserNoRole =
new ConcurrentHashMap<>();
private ConcurrentHashMap<Class<?>, Set<String>> classPropsWithNoAccess =
new ConcurrentHashMap<>();
/**
* Add mapping for what class properties a LDAP role can view.
*
* @param securityRoleToClassPropertyXmlReaders to obtain mapping data from.
* @throws ClassNotFoundException if the java class can not be found.
* @throws IOException when security role to class property XML files can't be read.
*/
@Override
@Autowired
public void setSecurityRoleToClassPropertyReaders(
List<SecurityRoleToClassPropertyReader> securityRoleToClassPropertyReaders)
throws ClassNotFoundException, IOException {
this.securityRoleToClassPropertyReaders = securityRoleToClassPropertyReaders;
loadClassPropertyRoles();
}
/**
* Method called to determine whether property will be included
* (if 'true' returned) or filtered out (if 'false' returned)
*/
protected boolean include(BeanPropertyWriter writer) {
AnnotatedMember memberToSerialize = writer.getMember();
if (memberToSerialize == null) {
logger.warn("Could not get member to serialize for writer {}",
writer.getClass().getName());
return false;
}
final Class<?> clazz = memberToSerialize.getDeclaringClass();
return include(clazz, writer.getName());
}
/**
* Method called to determine whether property will be included
* (if 'true' returned) or filtered out (if 'false' returned)
*/
protected boolean include(PropertyWriter writer) {
AnnotatedMember memberToSerialize = writer.getMember();
if (memberToSerialize == null) {
logger.warn("Could not get member to serialize for writer {}",
writer.getClass().getName());
return false;
}
final Class<?> clazz = memberToSerialize.getDeclaringClass();
return include(clazz, writer.getName());
}
protected boolean include(
Class<?> clazz,
String propertyName) {
logger.info("Checking {}.{}", clazz.getSimpleName(), propertyName);
final Map<String, Collection<SecurityRole>> propertyLdapRoleMap =
classPropertyRoles.get(clazz);
if (propertyLdapRoleMap != null) {
final Collection<SecurityRole> securityRoles =
propertyLdapRoleMap.get(propertyName);
if (securityRoles != null && securityRoles.size() > 0) {
Authentication auth = getAuthentication();
if (isAuthorized(getGrantedAuthorities(auth), securityRoles)) {
logger.info("allowing {}.{}", clazz.getSimpleName(), propertyName);
return true;
} else {
logUserNoRole(clazz, propertyName, securityRoles, auth);
}
} else {
logPropertyWithNoAccess(clazz, propertyName);
}
} else {
logPropertyWithNoAccess(clazz, "-- all properties --");
}
return false;
}
private void logUserNoRole(
Class<?> clazz,
String propertyName,
Collection<SecurityRole> allowedRoles,
Authentication auth) {
if (!logger.isDebugEnabled()) {
return;
}
String username = (auth == null ? "anonymous" : auth.getName());
final String knownUserNoRoleString = ""
+ clazz.getName() + "." + propertyName + "."
+ username;
boolean known = knownUserNoRole.containsKey(knownUserNoRoleString);
if (!known) {
knownUserNoRole.put(knownUserNoRoleString, "");
logger.debug("User {} does not have valid role for {}.{}. "
+ "Requires one of {}", username, clazz.getName(),
propertyName, allowedRoles);
}
}
private void logPropertyWithNoAccess(Class<?> clazz, String propertyName) {
Set<String> knownPropsWithNoAccess = classPropsWithNoAccess.get(clazz);
if (knownPropsWithNoAccess == null) {
logger.warn("No roles enable access to {}.{}",
clazz.getSimpleName(), propertyName);
knownPropsWithNoAccess = new HashSet<>();
classPropsWithNoAccess.put(clazz, knownPropsWithNoAccess);
}
boolean wasAdded = false;
synchronized (knownPropsWithNoAccess) {
wasAdded = knownPropsWithNoAccess.add(propertyName);
}
if (wasAdded) {
logger.warn("No roles enable access to {}.{}",
clazz.getSimpleName(), propertyName);
}
}
private boolean isAuthorized(
Collection<? extends GrantedAuthority> grantedAuths,
Collection<SecurityRole> securityRoles) {
try {
if (grantedAuths == null) {
return false;
}
for (GrantedAuthority grantedAuth : grantedAuths) {
if (grantedAuth instanceof LdapAuthority) {
LdapAuthority ldapAuth = (LdapAuthority) grantedAuth;
for (SecurityRole secRole : securityRoles) {
if (secRole.distinguishedNameIsAuthorized(
ldapAuth.getDn())) {
return true;
}
if (secRole.displayNameIsAuthorized(
ldapAuth.getAuthority())) {
return true;
}
}
} else {
for (SecurityRole secRole : securityRoles) {
if (secRole.displayNameIsAuthorized(
grantedAuth.getAuthority())) {
return true;
}
}
}
}
return false;
} catch (NullPointerException npe) {
logger.error("FIXME", npe);
return false;
}
}
private Collection<? extends GrantedAuthority> getGrantedAuthorities(
Authentication auth) {
if (auth == null) {
return Collections.emptyList();
}
try {
return auth.getAuthorities();
}
catch (Exception e) {
logger.error("Could not retrieve authorities", e);
return Collections.emptyList();
}
}
private Authentication getAuthentication() {
try {
SecurityContext secCtxt = SecurityContextHolder.getContext();
if (secCtxt == null) {
logger.warn("SecurityContextHolder.getContext() returned null, " +
+ "no authorities present");
return null;
}
Authentication auth = secCtxt.getAuthentication();
if (auth == null) {
logger.warn("SecurityContextHolder.getContext().getAuthentication() "
+ "returned null, no authorities present");
}
return auth;
} catch (Exception e) {
logger.error("Could not retrieve Authentication", e);
return null;
}
}
private void loadClassPropertyRoles() {
Map<Class<?>, Map<String, Collection<SecurityRole>>> newClassPropertyRoles =
new HashMap<>();
for (SecurityRoleToClassPropertyReader reader : securityRoleToClassPropertyReaders) {
Map<Class<?>, Map<String, Collection<SecurityRole>>> readerClassPropertyRoles =
reader.loadClassPropertyRoles();
for (Class<?> clazz : readerClassPropertyRoles.keySet()) {
Map<String, Collection<SecurityRole>> propertyRoles =
newClassPropertyRoles.get(clazz);
if (propertyRoles == null) {
propertyRoles = new HashMap<>();
newClassPropertyRoles.put(clazz, propertyRoles);
}
for (String propertyName : readerClassPropertyRoles.get(clazz).keySet()) {
Collection<SecurityRole> allowedRolesForProp =
propertyRoles.get(propertyName);
if (allowedRolesForProp == null) {
allowedRolesForProp = new ArrayList<>();
propertyRoles.put(propertyName, allowedRolesForProp);
}
Collection<SecurityRole> newLdapRoles =
readerClassPropertyRoles.get(clazz).get(propertyName);
for (SecurityRole securityRole : newLdapRoles) {
if (!allowedRolesForProp.contains(securityRole)) {
allowedRolesForProp.add(securityRole);
}
}
}
}
}
this.classPropertyRoles = newClassPropertyRoles;
}
}