web-dev-qa-db-ja.com

Spring Securityで<intercept-url>アクセス属性値を動的に決定する方法は?

Spring Securityでは、以下のようにintercept-urlタグを使用して、URLへのアクセスを定義します。

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

これはapplicationContext-security.xmlにハードコードされています。代わりに、データベーステーブルからアクセス値を読み取ります。自分のUserDetailsServiceを定義し、ログインしたユーザーのロールをデータベースから読み取りました。実行時にこれらのロールをURLパターンに割り当てるにはどうすればよいですか?

26
Cracker

Spring-securityのFilterInvocationSecurityMetadataSourceParserクラス(ソースコードを使用してSTSでCtrl/Cmd + Shift + Tを試行)は、intercept-urlタグを解析し、S​​ecurityMetadataSourceを拡張するFilterInvocationSecurityMetadataSourceを実装するDefaultFilterInvocationSecurityMetadataSourceを拡張するExpressionBasedFilterInvocationSecurityMetadataSourceのインスタンスを作成します。

私が行ったことは、FilterInvocationSecurityMetadataSource OptionsFromDataBaseFilterInvocationSecurityMetadataSourceを実装するカスタムクラスを作成することです。 DefaultFilterInvocationSecurityMetadataSourceをベースとして使用して、urlMatcherを使用し、support()メソッドなどを実装しました。

次に、これらのメソッドを実装する必要があります。

  • コレクションgetAttributes(Objectオブジェクト)。データベースにアクセスし、保護されている「オブジェクト」(通常はアクセスするURL)を検索して、許可されたConfigAttribute(通常はROLE)を取得します。

  • ブール値のサポート(クラスclazz)

  • コレクションgetAllConfigAttributes()

後者は起動時に呼び出され、現時点では適切に構成されていない可能性があるため、注意してください(つまり、使用しているものに応じて、データソースまたは永続コンテキストが自動接続されているため)。 Web環境での解決策は、applicationContext-security.xmlの前にapplicationContext.xmlをロードするようにweb.xmlのcontextConfigLocationを構成することです。

最後のステップは、このBeanをロードするようにapplicationContext-security.xmlをカスタマイズすることです。

そのために、セキュリティ名前空間ではなく、このファイルで通常のBeanを使用しました。

    <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>

関連するすべてのBeanを定義する必要があります。例えば:

    <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>

私はそれが十分に説明された答えではないことを知っていますが、見た目ほど難しくはありません。

ばね源をベースとして使用するだけで、必要なものが得られます。

データベースのデータを使用してデバッグすることは、非常に役立ちます。

21
jbbarquero

実際、Spring Security 3.2は http://docs.spring.io/spring-security/site/docs/3.2.x/reference/htmlsingle/faq.html#faq-dynamicに従ってこれを行うことを推奨していません-url-metadata

しかし、カスタムaccessDecisionManagerで名前空間のhttp要素を使用することは可能です(ただしエレガントではありません)。

構成は次のようになります。

<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が特定のURLのフォームDB deロールを取得する場所

4
Hugo Robayo

私は同じ問題を抱えていますが、基本的には、intercept-urlのリストを他のspringsecurity構成セクションから分離しておきます。最初はアプリケーション構成に属し、後者は製品(コア、プラグイン)構成に属します。

この問題に関して、春のJIRAには proposal があります。

Springsecurity名前空間の使用をあきらめたくないので、これに対処するためにいくつかの可能な解決策を考えていました。

Intercept-urlのリストを動的に作成するには、filterSecurityInterceptorにsecuritymetadatasourceオブジェクトを挿入する必要があります。 SpringSecurityスキーマを使用すると、FilterSecurityInterceptorのインスタンスがHttpBuilderクラスによって作成されます。次のような回避策を使用する場合ほど、スキーマ構成ファイルで定義されたプロパティとしてsecuritymetadatasourceを渡す方法はありません。

  • このフィルターでカスタムフィルターを定義し、FilterSecurityInterceptorの前に実行して、SpringコンテキストによってインスタンスFilterSecurityInterceptor(一意のhttpセクションが定義されていると想定)を取得し、そこにsecuritymetadatasourceインスタンスを挿入します。
  • 上記と同じですが、HandlerInterceptorにあります。

どう思いますか?

2
Enrico Giurin

私のために働く簡単な解決策。

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

customAuthenticationProviderはBeanです

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

customAuthenticationProviderクラスのcreateメソッド:

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

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

これは、intercept-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> 

もちろん、インターフェースFilterInvocationSecurityMetadataSourceを実装することにより、独自のsecurityMetadataSource Beanを実装することを決定できます。このようなもの:

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

お役に立てれば。

1
Enrico Giurin

これは、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 */
    }
}
0
Ritesh