Отказ
Как упоминает Spring Security FAQ , первое, что вы должны сделать, это спросить, должен ли я действительно это сделать? Безопасность сложна, и конфигурация должна быть тщательно протестирована. Разрешение динамического изменения конфигурации только еще больше усложняет ситуацию, делая приложение гораздо более уязвимым. Если вы действительно хотите это сделать, в разделе «Часто задаваемые вопросы» описан основной метод для достижения этой цели. Я расширил ответ на часто задаваемые вопросы ниже.
Реализация пользовательского FilterInvocationSecurityMetadataSource
Для динамического получения сопоставлений URL безопасности вы можете реализовать свой собственный FilterInvocationSecurityMetadataSource. Пример реализации приведен ниже.
ПРИМЕЧАНИЕ: Имейте в виду, что getAttributes будет вызываться для каждого запроса, который перехватывает Spring Security, поэтому вам, скорее всего, понадобится какое-то кэширование.
public class JdbcFilterInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
FilterInvocation fi = (FilterInvocation) object;
String url = fi.getRequestUrl();
HttpServletRequest request = fi.getHttpRequest();
// Instead of hard coding the roles lookup the roles from the database using the url and/or HttpServletRequest
// Do not forget to add caching of the lookup
String[] roles = new String[] { "ROLE_ADMIN", "ROLE_USER" };
return SecurityConfig.createList(roles);
}
public Collection<ConfigAttribute> getAllConfigAttributes() {
return null;
}
public boolean supports(Class<?> clazz) {
return FilterInvocation.class.isAssignableFrom(clazz);
}
}
Создание BeanPostProcessor
Вы не можете использовать пространство имен для подключения, поэтому, воспользовавшись подсказкой из FAQ , вы можете использовать BeanPostProcessor, который может выглядеть следующим образом:
public class FilterInvocationSecurityMetadataSourcePostProcessor implements BeanPostProcessor, InitializingBean {
private FilterInvocationSecurityMetadataSource securityMetadataSource;
public Object postProcessAfterInitialization(Object bean, String name) {
if (bean instanceof FilterSecurityInterceptor) {
((FilterSecurityInterceptor)bean).setSecurityMetadataSource(securityMetadataSource);
}
return bean;
}
public Object postProcessBeforeInitialization(Object bean, String name) {
return bean;
}
public void setSecurityMetadataSource(FilterInvocationSecurityMetadataSource securityMetadataSource) {
this.securityMetadataSource = securityMetadataSource;
}
public void afterPropertiesSet() throws Exception {
Assert.notNull(securityMetadataSource,"securityMetadataSource cannot be null");
}
}
Конфигурация XML
Затем, предполагая, что оба приведенных выше bean-компонента находятся в образце пакета, вы добавили бы следующую конфигурацию
<bean class="sample.FilterInvocationSecurityMetadataSourcePostProcessor">
<property name="securityMetadataSource">
<bean class="sample.JdbcFilterInvocationSecurityMetadataSource"/>
</property>
</bean>
Возможные проблемы
Если в итоге вы получите ClassCastException, вы, скорее всего, столкнетесь с SEC-1957 , который был исправлен в Spring Security 3.1.1+. Попробуйте обновить его до последней версии, чтобы устранить эту проблему.