Spring security 如何动态决定<;拦截url>;在Spring Security中访问属性值?

Spring security 如何动态决定<;拦截url>;在Spring Security中访问属性值?,spring-security,authorization,security-roles,Spring Security,Authorization,Security Roles,在Spring Security中,我们使用intercept url标记定义对url的访问,如下所示: 这是在applicationContext security.xml中硬编码的。我想从数据库表中读取访问值。我已经定义了自己的UserDetailsService,并从数据库中读取了登录用户的角色。如何在运行时将这些角色分配给URL模式?Spring security中的FilterInvocationSecurityMetadataSourceParser类(使用源代码在STS中尝试C

在Spring Security中,我们使用intercept url标记定义对url的访问,如下所示:



这是在
applicationContext security.xml
中硬编码的。我想从数据库表中读取访问值。我已经定义了自己的
UserDetailsService
,并从数据库中读取了登录用户的角色。如何在运行时将这些角色分配给URL模式?

Spring security中的FilterInvocationSecurityMetadataSourceParser类(使用源代码在STS中尝试Ctrl/Cmd+Shift+T)解析拦截URL标记并创建ExpressionBasedFilterInvocationSecurityMetadataSource的实例,扩展DefaultFilterInvocationSecurityMetadataSource的,实现扩展SecurityMetadataSource的FilterInvocationSecurityMetadataSource

我所做的是创建一个实现FilterInvocationSecurityMetadataSource的自定义类,选项fromDatabaseFilterInvocationSecurityMetaDataSource。我使用DefaultFilterInvocationSecurityMetadataSource作为基础来使用urlMatcher,实现support()方法之类的东西

然后,您必须执行这些方法:

  • 集合getAttributes(对象对象),您可以在其中访问数据库,搜索受保护的“对象”(通常是要访问的URL)以获取允许的ConfigAttribute(通常是角色的)

  • 布尔支持(类clazz)

  • 集合getAllConfigAttributes()

小心使用后者,因为它是在启动时调用的,此时可能配置不好(我的意思是,数据源或持久性上下文是自动连接的,这取决于您使用的是什么)。web环境中的解决方案是将web.xml中的contextConfigLocation配置为在applicationContext-security.xml之前加载applicationContext.xml

最后一步是定制applicationContext-security.xml以加载这个bean

为此,我在该文件中使用了常规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>

我知道这不是一个解释得很好的答案,但它并不像看上去那么难

只要使用spring源代码作为基础,您就会得到您想要的


使用数据库中的数据进行调试将对您有很大帮助。

我也有类似的问题,基本上我希望将截取url列表与其他springsecurity配置部分分开,第一部分属于应用程序配置,第二部分属于产品(核心、插件)配置

<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>
关于这个问题,在《春天的圣母院》中有一篇文章

我不想放弃使用springsecurity名称空间,所以我在考虑一些可能的解决方案来处理这个问题

为了动态创建截取url列表,必须将securitymetadatasource对象注入FilterSecurityInterceptor。 使用springsecurity架构,FilterSecurityInterceptor的实例由HttpBuilder类创建,无法将securitymetadatasource作为架构配置文件中定义的属性传递,更不用说使用某种变通方法,这可能是:

  • 定义一个自定义过滤器,在FilterSecurityInterceptor之前执行,在此过滤器中通过spring上下文检索实例FilterSecurityInterceptor(假设定义了唯一的http节),并将securitymetadatasource实例注入其中
  • 同上,但在HandlerInterceptor中

您认为如何?

这是我应用的解决方案,用于将截取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" />


希望这能有所帮助。

实际上,SpringSecurity3.2不鼓励按照

但是,在命名空间中使用带有自定义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();
}

...
}
公共类CustomAccessDecisionManager扩展了AbstractAccessDecisionManager{
...
public void decise(身份验证、对象筛选器、,
集合(配置属性)
引发AccessDeniedException,InsufficientAuthenticationException{
if((filter==null)| |!this.supports(filter.getClass()){
抛出新的IllegalArgumentException(“对象必须是筛选器职业”);
}
字符串url=((FilterInvocation)filter.getRequestUrl();
字符串contexto=((FilterInvocation)filter.getRequest().getContextPath();
集合角色=service.getConfigAttributesFromSecuredUris(contexto,url);
int=0;
for(accessDecisionVorter:getDecisionVorters()){
int result=voter.vote(身份验证、筛选、角色);
if(logger.isDebugEnabled()){
logger.debug(“投票者:+投票者+”,返回:+结果);
}
开关(结果){
案例访问决策投票人。已授予的访问权:
返回;
案例访问决策投票人。访问被拒绝:
否认++;
打破
违约:
打破
}
}
如果(拒绝>0){
抛出新的AccessDeniedException(messages.getMessage(“AbstractAccessD
<beans:bean id="customAuthenticationProvider"
    class="package.security.CustomAuthenticationProvider" />
public synchronized String getReturnStringMethod()
{
    //get data from database (call your method)

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