Подтвердить что ты не робот

Как динамически выбрать значение атрибута доступа <intercept-url> в Spring Безопасность?

В Spring безопасности мы используем тег intercept-url для определения доступа для URL-адресов, как показано ниже:

<intercept-url pattern="/**" access="ROLE_ADMIN" />
<intercept-url pattern="/student" access="ROLE_STUDENT" />

Это жестко закодировано в applicationContext-security.xml. Вместо этого я хочу прочитать значения доступа из таблицы базы данных. Я определил свой собственный UserDetailsService, и я прочитал роли для зарегистрированного пользователя из базы данных. Как назначить эти роли шаблонам URL во время выполнения?

4b9b3361

Ответ 1

Класс FilterInvocationSecurityMetadataSourceParser в Spring -security (попробуйте Ctrl/Cmd + Shift + T в STS с исходным кодом) анализирует теги intercept-url и создает экземпляры ExpressionBasedFilterInvocationSecurityMetadataSource, который расширяет DefaultFilterInvocationSecurityMetadataSource, который реализует FilterInvocationSecurityMetadataSource, который расширяет SecurityMetadataSource.

Я создал класс, который реализует FilterInvocationSecurityMetadataSource, OptionsFromDataBaseFilterInvocationSecurityMetadataSource. Я использовал DefaultFilterInvocationSecurityMetadataSource как базу для использования urlMatcher, чтобы реализовать метод support() и что-то в этом роде.

Затем вы должны реализовать эти методы:

  • Коллекция getAttributes (объект объекта), где вы можете получить доступ к базе данных, ища защищенный объект (обычно URL-адрес для доступа), чтобы получить разрешенный ConfigAttribute (обычно ROLE)

  • boolean поддерживает (Class clazz)

  • Коллекция getAllConfigAttributes()

Будьте осторожны с более поздним, потому что он вызвал при запуске и, возможно, не настроен в данный момент (я имею в виду, что с источниками данных или контекстом постоянства автоматически, в зависимости от того, что вы используете). Решение в веб-среде состоит в том, чтобы настроить contextConfigLocation в web.xml для загрузки applicationContext.xml перед applicationContext-security.xml

Последний шаг - настроить applicationContext-security.xml для загрузки этого bean.

Для этого я использовал обычный beans в этом файле вместо пространства имен безопасности:

    <beans:bean id="springSecurityFilterChain" class="org.springframework.security.web.FilterChainProxy">
    <filter-chain-map path-type="ant">
        <filter-chain pattern="/images/*" filters="none" />
        <filter-chain pattern="/resources/**" filters="none" />
        <filter-chain pattern="/**" filters="
        securityContextPersistenceFilter,
        logoutFilter,
        basicAuthenticationFilter,
        exceptionTranslationFilter,
        filterSecurityInterceptor" 
    />
    </filter-chain-map>
</beans:bean>

Вы должны определить все связанные beans. Например:

    <beans:bean id="filterSecurityInterceptor" class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor">
    <beans:property name="authenticationManager" ref="authenticationManager"></beans:property>
    <beans:property name="accessDecisionManager" ref="affirmativeBased"></beans:property>
    <beans:property name="securityMetadataSource" ref="optionsFromDataBaseFilterInvocationSecurityMetadataSource"></beans:property>
    <beans:property name="validateConfigAttributes" value="true"/></beans:bean>

Я знаю, что это не хорошо объясненный ответ, но это не так сложно, как кажется.

Просто используйте источник spring как базу, и вы получите то, что хотите.

Отладка с данными в вашей базе данных поможет вам многое.

Ответ 2

На самом деле, spring security 3.2 не рекомендуется делать это в соответствии с http://docs.spring.io/spring-security/site/docs/3.2.x/reference/htmlsingle/faq.html#faq-dynamic-url-metadata

но возможно (но не изящно) с помощью http-элемента в пространстве имен с пользовательским accessDecisionManager..

Конфигурация должна быть:

<http pattern="/login.action" security="none"/>
<http pattern="/media/**" security="none"/>

<http access-decision-manager-ref="accessDecisionManager" >
    <intercept-url pattern="/**" access="ROLE_USER"/>
    <form-login login-page="/login.action"
                authentication-failure-url="/login?error=1"
                default-target-url="/console.action"/>
    <logout invalidate-session="true" delete-cookies="JSESIONID"/>
    <session-management session-fixation-protection="migrateSession">
        <concurrency-control max-sessions="1" error-if-maximum-exceeded="true" expired-url="/login.action"/>
    </session-management>

    <!-- NO ESTA FUNCIONANDO, los tokens no se ponen en el request!
    <csrf />
     -->

</http>
<authentication-manager>
    <authentication-provider>
        <user-service>
            <user name="test" password="test" authorities="ROLE_USER" />
        </user-service>
    </authentication-provider>
</authentication-manager>

<beans:bean id="accessDecisionManager" class="openjsoft.core.services.security.auth.CustomAccessDecisionManager">
    <beans:property name="allowIfAllAbstainDecisions" value="false"/>
    <beans:property name="decisionVoters">
    <beans:list>
        <beans:bean class="org.springframework.security.access.vote.RoleVoter"/>
    </beans:list>
    </beans:property>
</beans:bean>

CustomAccessDecisionManager должен быть...

public class CustomAccessDecisionManager extends AbstractAccessDecisionManager  {
...

public void decide(Authentication authentication, Object filter,
        Collection<ConfigAttribute> configAttributes)
        throws AccessDeniedException, InsufficientAuthenticationException {

  if ((filter == null) || !this.supports(filter.getClass())) {
        throw new IllegalArgumentException("Object must be a FilterInvocation");
    }

    String url = ((FilterInvocation) filter).getRequestUrl();
    String contexto = ((FilterInvocation) filter).getRequest().getContextPath();

    Collection<ConfigAttribute> roles = service.getConfigAttributesFromSecuredUris(contexto, url);



    int deny = 0;

    for (AccessDecisionVoter voter : getDecisionVoters()) {
        int result = voter.vote(authentication, filter, roles);

        if (logger.isDebugEnabled()) {
            logger.debug("Voter: " + voter + ", returned: " + result);
        }

        switch (result) {
        case AccessDecisionVoter.ACCESS_GRANTED:
            return;

        case AccessDecisionVoter.ACCESS_DENIED:

            deny++;

            break;

        default:
            break;
        }
    }

    if (deny > 0) {
        throw new AccessDeniedException(messages.getMessage("AbstractAccessDecisionManager.accessDenied",
                "Access is denied"));
    }

    // To get this far, every AccessDecisionVoter abstained
    checkAllowIfAllAbstainDecisions();
}

...
}

Где getConfigAttributesFromSecuredUris извлекает форму DB для ролей для определенного URL

Ответ 3

У меня есть одна и та же проблема, в основном я бы хотел разделить список перехвата-url из другого раздела конфигурации springsecurity, первый из которых относится к конфигурации приложения последнему к продукту (ядро, плагин) конфигурации.

В JIRA spring есть предложение в отношении этой проблемы.

Я не хочу отказаться от использования пространства имен springsecurity, поэтому я подумал о некоторых возможных решениях, чтобы справиться с этим.

Чтобы список динамического создания перехвата-url, вам нужно ввести объект securitymetadatasource в FilterSecurityInterceptor. Используя springsecurity schema, экземпляр FilterSecurityInterceptor создается классом HttpBuilder, и нет способа передать свойство securitymetadatasource как свойство, определенное в файле конфигурации схемы, меньше, чем использование метода обхода, которое может быть:

  • Определите настраиваемый фильтр, который должен быть выполнен до FilterSecurityInterceptor, в этом фильтре, который получает экземпляр FilterSecurityInterceptor (при условии, что определен уникальный раздел http определен) в контексте spring и вставляет туда экземпляр securitymetadatasource;
  • То же, что и выше, но в HandlerInterceptor.

Как вы думаете?

Ответ 4

Это решение, которое я применил, чтобы разбить список записей перехвата-url из другой конфигурации безопасности spring.

<security:custom-filter ref="parancoeFilterSecurityInterceptor"
        before="FILTER_SECURITY_INTERCEPTOR" />
........

<bean id="parancoeFilterSecurityInterceptor" class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor" >  
    <property name="authenticationManager" ref="authenticationManager"/>
    <property name="accessDecisionManager" ref="accessDecisionManager"/>
    <property name="securityMetadataSource" ref="securityMetadataSource"/>
</bean>

bean securityMetadataSource может быть помещен либо в тот же файл конфигурации, либо в другой файл конфигурации.

<security:filter-security-metadata-source
    id="securityMetadataSource" use-expressions="true">
    <security:intercept-url pattern="/admin/**"
        access="hasRole('ROLE_ADMIN')" />
</security:filter-security-metadata-source> 

Конечно, вы можете решить реализовать свой собственный securityMetadataSource bean, реализовав интерфейс FilterInvocationSecurityMetadataSource. Что-то вроде этого:

<bean id="securityMetadataSource" class="mypackage.MyImplementationOfFilterInvocationSecurityMetadataSource" />

Надеюсь, что это поможет.

Ответ 5

Простое решение, которое работает для меня.

<intercept-url pattern="/**/**" access="#{@customAuthenticationProvider.returnStringMethod}" />
<intercept-url pattern="/**" access="#{@customAuthenticationProvider.returnStringMethod}" />

customAuthenticationProvider - это bean

<beans:bean id="customAuthenticationProvider"
    class="package.security.CustomAuthenticationProvider" />

в методе создания класса CustomAuthenticationProvider:

public synchronized String getReturnStringMethod()
{
    //get data from database (call your method)

    if(condition){
        return "IS_AUTHENTICATED_ANONYMOUSLY";
    }
    return "ROLE_ADMIN,ROLE_USER";
}

Ответ 6

Вот как это можно сделать в Spring Security 3.2:

@Configuration
@EnableWebMvcSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Bean
    public SecurityConfigDao securityConfigDao() {
        SecurityConfigDaoImpl impl = new SecurityConfigDaoImpl() ;
        return impl ;
    }



    @Override
    protected void configure(HttpSecurity http) throws Exception {
        /* get a map of patterns and authorities */
        Map<String,String> viewPermissions = securityConfigDao().viewPermissions() ;

         ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry interceptUrlRegistry = http
        .authorizeRequests().antMatchers("/publicAccess/**")
        .permitAll(); 

        for (Map.Entry<String, String> entry: viewPermissions.entrySet()) {
            interceptUrlRegistry.antMatchers(entry.getKey()).hasAuthority(entry.getValue());
        }

        interceptUrlRegistry.anyRequest().authenticated()
        .and()
        ...
        /* rest of the configuration */
    }
}