Вопрос по spring, spring-security – PreAuthorize не работает

6

Я пишу приложение для сокет-сервера (без веб-приложения!) И хочу использовать защиту на основе методов для удовлетворения моих потребностей в ACL. я последовал небольшой учебник я нашелвесенняя безопасность на примере

пока я настроил:

<code><security:global-method-security pre-post-annotations="enabled">
    <security:expression-handler ref="expressionHandler" />
</security:global-method-security>
<bean id="expressionHandler" class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler">
    <property name="permissionEvaluator">
        <bean id="permissionEvaluator" class="myPermissionEvaluator" />
    </property>
</bean>

<security:authentication-manager id="authenticationmanager">
    <security:authentication-provider ref="authenticationprovider" />
</security:authentication-manager>
<bean id="authenticationprovider" class="myAuthenticationProvider" />
</code>

С компонентом службы:

<code>@Named
public class ChannelService {
    @PreAuthorize("isAuthenticated() and hasPermission(#channel, 'CHANNEL_WRITE')")
    public void writeMessage(Channel channel, String message) { ... }
}
</code>

Все компилируется и приложение запускается и работает нормально, но без контроля доступа. Мой журнал отладки показывает, что мой Evaluator никогда не вызывается.

Когда я попробовал нечто подобное с@Secured аннотация аннотация была оценена, и в доступе было отказано. но простой безопасности на основе ролей недостаточно для моих требований.

EDIT сделал еще несколько тестов: когда я настраивал только безопасные аннотации = & quot; включен & quot; Ролевая безопасность работает. когда настраивать предварительные пост-аннотации = & quot; включено & quot; К тому же ни охраняемые, ни предварительно авторизованные работы. когда я настраиваю только предварительные аннотации, это все равно не работает.

EDIT2

еще несколько тестов: только с secured_annotations = & quot; включено & quot; вызов к моей службе канала проходит через Cglib2AopProxy как только я активирую предварительные аннотации, звонок попадает прямо в сервис канала. нет перехватчика, нет прокси, ничего.

Я в отчаянии ...

EDIT3

Я выполнил отладку своих тестов

только с защищенными аннотациями = & quot; включено & quot;

<code>2012-04-12 13:36:46,171 INFO  [main] o.s.s.c.SpringSecurityCoreVersion - You are running with Spring Security Core 3.1.0.RELEASE
2012-04-12 13:36:46,174 INFO  [main] o.s.s.c.SecurityNamespaceHandler - Spring Security 'config' module version is 3.1.0.RELEASE
2012-04-12 13:36:49,042 DEBUG [main] o.s.s.a.m.DelegatingMethodSecurityMetadataSource - Caching method [CacheKey[mystuff.UserService; public void mystuff.UserService.serverBan(java.lang.String,mystuff.models.User,org.joda.time.DateTime)]] with attributes [user]
2012-04-12 13:36:49,138 DEBUG [main] o.s.s.a.i.a.MethodSecurityInterceptor - Validated configuration attributes
2012-04-12 13:36:49,221 DEBUG [main] o.s.s.a.m.DelegatingMethodSecurityMetadataSource - Caching method [CacheKey[mystuff.ChannelService; public void mystuff.ChannelService.writeMessage(mystuff.models.Channel,java.lang.String)]] with attributes [blubb]
2012-04-12 13:36:51,159 DEBUG [main] o.s.s.a.ProviderManager - Authentication attempt using mystuff.GlobalchatAuthenticationProvider
2012-04-12 13:36:56,166 DEBUG [Timer-1] o.s.s.a.ProviderManager - Authentication attempt using mystuff.GlobalchatAuthenticationProvider
2012-04-12 13:36:56,183 DEBUG [Timer-1] o.s.s.a.i.a.MethodSecurityInterceptor - Secure object: ReflectiveMethodInvocation: public void mystuff.ChannelService.writeMessage(mystuff.models.Channel,java.lang.String); target is of class [mystuff.ChannelService]; Attributes: [blubb]
2012-04-12 13:36:56,184 DEBUG [Timer-1] o.s.s.a.i.a.MethodSecurityInterceptor - Previously Authenticated: org.springframew[email protected]312e8aef: Principal: [email protected]; Credentials: [PROTECTED]; Authenticated: true; Details: null; Not granted any authorities
Exception in thread "Timer-1" org.springframework.security.access.AccessDeniedException: Access is denied
    at org.springframework.security.access.vote.AbstractAccessDecisionManager.checkAllowIfAllAbstainDecisions(AbstractAccessDecisionManager.java:70)
    at org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.java:88)
    at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:205)
    at org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor.invoke(MethodSecurityInterceptor.java:59)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:622)
    at mystuff.ChannelService$$EnhancerByCGLIB$$3ad5e57f.writeMessage(<generated>)
    at mystuff.run(DataGenerator.java:109)
    at java.util.TimerThread.mainLoop(Timer.java:512)
    at java.util.TimerThread.run(Timer.java:462)
2012-04-12 13:36:56,185 DEBUG [Timer-1] o.s.s.access.vote.AffirmativeBased - Voter: [email protected], returned: 0
2012-04-12 13:36:56,185 DEBUG [Timer-1] o.s.s.access.vote.AffirmativeBased - Voter: [email protected]a7, returned: 0
</code>

с пред-пост-аннотациями = & quot; включено & quot;

<code>2012-04-12 13:39:54,926 INFO  [main] o.s.s.c.SpringSecurityCoreVersion - You are running with Spring Security Core 3.1.0.RELEASE
2012-04-12 13:39:54,929 INFO  [main] o.s.s.c.SecurityNamespaceHandler - Spring Security 'config' module version is 3.1.0.RELEASE
2012-04-12 13:39:54,989 INFO  [main] o.s.s.c.m.GlobalMethodSecurityBeanDefinitionParser - Using bean 'expressionHandler' as method ExpressionHandler implementation
2012-04-12 13:39:59,812 DEBUG [main] o.s.s.a.ProviderManager - Authentication attempt mystuff.GlobalchatAuthenticationProvider
2012-04-12 13:39:59,850 DEBUG [main] o.s.s.a.i.a.MethodSecurityInterceptor - Validated configuration attributes
</code>

Насколько я понимаю, эта пружина вывода журнала не понимает, что мои bean-компоненты должны быть проксированы, поэтому они не защищены, и поэтому я не получаю защиту.

EDIT4

Я отладил и запустил полный запуск спринта ... (вот один большой журнал), и там я нахожу:

<code>2012-04-12 14:40:41,385 INFO [main] o.s.c.s.ClassPathXmlApplicationContext - Bean 'channelService' of type [class mystuff.ChannelService] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
</code>

Есть ли способ выяснить, почему? потому что, насколько я понимаю. из-за @preauthorize бин должен иметь право. только с защищенными аннотациями = & quot; включено & quot; я получаю журнал постобработки.

когда я удалил @inject ChannelService из моего PermissionEvaluator, был создан прокси. Laures
@Roadrunner весна 3.1.0. ВЫПУСКАЙТЕ на все. Laures
Какую версию Spring и Spring Security вы используете? Roadrunner
@ Лучано, я могу, и это не так. Похоже, что поскольку моему PermissionEvaluator нужна канальная служба, эта служба создается до источника данных (создание службы происходит между строк журнала 215 (начало) -702 (конец); источник данных впервые упоминается в строке 870 с & quot; не подходит для получения ....) Laures
Можете ли вы выполнить отладку, если & quot; PrePostAnnotationSecurityMetadataSource # getAttributes () & quot; вызывается для вашего метода при инициализации Spring? Luciano

Ваш Ответ

2   ответа
5

<bean id="securityExpressionHandler"
    class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler" /> 

<bean id="preInvocationAdvice"
    class="org.springframework.security.access.expression.method.ExpressionBasedPreInvocationAdvice"
    p:expressionHandler-ref="securityExpressionHandler" />

<util:list id="decisionVoters">
    <bean class="org.springframework.security.access.vote.AuthenticatedVoter" />
    <bean class="org.springframework.security.access.vote.RoleVoter" />
    <bean class="org.springframework.security.access.prepost.PreInvocationAuthorizationAdviceVoter"
        c:pre-ref="preInvocationAdvice" />
</util:list>

<bean id="accessDecisionManager"
    class="org.springframework.security.access.vote.UnanimousBased"
    c:decisionVoters-ref="decisionVoters" />

<sec:global-method-security
    authentication-manager-ref="authenticationManager"
    access-decision-manager-ref="accessDecisionManager"
    pre-post-annotations="enabled" />

Я получил сообщение журнала:

WARN  org.springframework.security.access.expression.DenyAllPermissionEvaluator - 
    Denying user jack permission 'CHANNEL_WRITE' on object Channel[ name=null ]

И исключение:

org.springframework.security.access.AccessDeniedException: Access is denied

Из простого теста:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:META-INF/spring/application-context.xml")
public class SpringSecurityPrePostTest {

    @Autowired
    ChannelService channelService;

    @Test
    public void shouldSecureService() throws Exception {
        Authentication authentication = new UsernamePasswordAuthenticationToken("jack", "sparrow");
        SecurityContext securityContext = SecurityContextHolder.getContext();
        securityContext.setAuthentication(authentication);

        channelService.writeMessage(new Channel(), "test");
    }
}

Одна вещь, которую я сделал diffrent, состояла в том, чтобы использовать интерфейс на сервисе и прокси JDK вместо cglib:

public interface ChannelService {

    void writeMessage(Channel channel, String message);
}

а также:

@Component
public class ChannelServiceImpl implements ChannelService {

    private static final Logger LOG = LoggerFactory.getLogger(ChannelServiceImpl.class);

    @Override
    @PreAuthorize("isAuthenticated() and hasPermission(#channel, 'CHANNEL_WRITE')")
    public void writeMessage(Channel channel, String message) {
        LOG.info("Writing message {} to: {}" , message, channel);
    }

}

UPDATE1:

С этим упрощенным конфигом я получаю тот же результат:

<bean id="securityExpressionHandler"
    class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler" /> 

<sec:global-method-security
    authentication-manager-ref="authenticationManager"
    pre-post-annotations="enabled">
    <sec:expression-handler ref="securityExpressionHandler" />
</sec:global-method-security>

UPDATE2:

Отладочное сообщение от Edit4 указывает, чтоchannelService возможно, бин вообще не был прокси, так как он был классифицирован какnot eligible for auto-proxying. Этот вопрос отвечает на аналогичную проблему - старайтесь не использовать@Autowired или любой другой механизм, основанный наBeanPostProcessors настроить компоненты, участвующие в проверках безопасности (т.е.myPermissionEvaluator).

UPDATE3:

Выcannot использовать защищенные ресурсы (то есть сервисы) внутри компонентов, отвечающих за проверки безопасности! Это создает цикл зависимости и является ошибкой в вашей конфигурации. Выmust использовать доступ на уровне любовника (т.е. DAO) для проверки разрешений, всего, чтоnot secured! Реализация проверок безопасности с использованием защищенных ресурсовnot what You want to do.

Если несмотря на использование незащищенных ресурсов с@Autowired все работает не так, как ожидалось, попробуйте использовать стиль старой школы XML-конфигурации для всех компонентов, участвующих в проверках безопасности. Также помните, что<context:component-scan /> на самом делеBeanDefinitionRegistryPostProcessor и вводит отсканированные бобы вBeanFactory после того, как все, объявленные в XML, уже есть.

@Laures см. Мой расширенный ответ для получения дополнительных советов.
методы, которые я использую для проверки разрешений, не защищены (и не будут), но они являются частью службы, которая имеет защищенные методы. Я решил проблему, посмотрев объект службы из контекста приложения, когда мне это понадобится. возможно не самое чистое решение, но в моем случае нет более низкого уровня доступа. Laures
в моем PermissionEvaluator используется служба Channel and User, потому что разрешения пользователей на эти объекты хранятся в этих объектах. Но & # xB4; @ inject & # xB4; из этих компонентов вызывает создание этих двух служб перед PrePostAnnotationSecurityMetadataSource. когда я их удаляю, мой оценщик вызывается правильно. теперь мне просто нужно выяснить, как внедрить мои прокси сервисы в оценщик, не вызывая этого. Laures
Третье обновление спасло мне жизнь. Вы НЕ МОЖЕТЕ вызывать службы в вашем customPermissionEvaluator, которые имеют какой-либо вид Pre / PostAuthorization. Это просто молча терпит неудачу, и авторизация никогда не выполняется. Я решил это, добавив @Lazy к вышеупомянутым сервисам, когда проходил через мой конструктор PermissionEvaluator
@Laures Вы должны разделить эту службу на 2 отдельных класса - один, содержащий только незащищенные методы для использования в проверках безопасности, и другой, содержащий защищенные методы для случаев использования в бизнесе. Тогда оба@Inject небезопасного обслуживания ВашегоpermissionEvaluator и авто-проксирование защищенного будет работать правильно. Даже если вы не используете защищенные методы, вы все еще используете класс, который, как вы ожидаете, будут проксированы с помощью проверок безопасности - это цикл зависимостей, который должен быть удален предлагаемым делением.
-1

<sec:global-method-security pre-post-annotations="enabled"/> в вашем весеннем сервлете (то есть где вы можете иметь свой<mvc:annotation-driven/>)

& Quot; сек & Quot; изxmlns:sec="http://www.springframework.org/schema/security"

@Laures Какую аннотацию Named вы используете? это не весенняя аннотация, может, вам стоит попробовать аннотацию компонента
Как вы можете видеть, у меня уже настроен тег global-method-security, и это не веб-приложение, поэтому нет сервлета или чего-либо еще. Laures
named является аннотацией jsr330, которая в основном означает то же самое, что и компонент, но стандартизирована и не зависит от пружины. JSR 330 реализован весной. Laures

Похожие вопросы