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

  • Post author:
  • Post category:其他



授权 – 如何在 Spring 安全性中动态决定<拦截 url> 访问属性值?- 堆栈溢出 (stackoverflow.com)



在 Spring 安全性中,我们使用拦截 url 标签来定义 URL 的访问权限,如下所示:

<span style="color:#232629"><span style="background-color:#ffffff"><span style="background-color:var(--highlight-bg)"><span style="color:var(--highlight-color)"><code><intercept-url pattern="/**" access="ROLE_ADMIN" />
<intercept-url pattern="/student" access="ROLE_STUDENT" />
</code></span></span></span></span>



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

applicationContext-security.xml


UserDetailsService





丹妮





3,676

44枚金质徽章2626 个银色徽章3535枚铜牌



提问 Jul 31, 2011 在 23:36




饼干





1,760

44枚金质徽章2424枚银牌3434枚铜牌








6 回答




排序方式:



最高分(默认)                     趋势(最近的投票计数更多)                     修改日期(最新的在前)                     创建日期(最旧的在前)








21



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



我所做的是创建一个实现 FilterInvocationSecurityMetadataSource、

OptionsFromDataBaseFilterInvocationSecurityMetadataSource

的自定义类。我使用 DefaultFilterInvocationSecurityMetadataSource 作为使用 urlMatcher 的基础,来实现 support() 方法和类似的东西。



然后,您必须实现以下方法:



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



  • 布尔支撑(类 clazz)



  • Collection getAllConfigAttributes()



请注意后者,因为它是在启动时调用的,并且此时可能没有很好地配置(我的意思是,数据源或持久性上下文自动连接,具体取决于您使用的内容)。Web 环境中的解决方案是在 Web 中配置 contextConfigLocation.xml以加载 applicationContext.xml 在 applicationContext-security 之前.xml



最后一步是定制应用程序上下文安全性.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>



我知道这不是一个很好解释的答案,但它并不像看起来那么困难。



只需使用弹簧源作为基础,您就会得到您想要的。



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



answered Aug 1, 2011 at 13:06




jbbarquero





2,822

22 gold badges1818 silver badges1717 bronze badges



  • hi I’m trying to achieve same thing but with struts2. Went through Spring Security 3.1 but couldn’t get enough ideas about integrating with struts2. Would you mind giving some basic ideas to integrate with struts2?



    Jul 11, 2014 at 8:47



  • This example is under an old version of spring. How I can do it under spring-security 3.2?



    Mar 3, 2015 at 14:50








4



Actually, spring security 3.2 do not encourage to do this according to

http://docs.spring.io/spring-security/site/docs/3.2.x/reference/htmlsingle/faq.html#faq-dynamic-url-metadata




but, it is possible (but not elegant) using http element in namespace with a custom accessDecisionManager..



The config should be:

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



The CustomAccessDecisionManager should be…

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



回答 Jan 15, 2014 在 2:40




雨果·罗巴约





1,090

11枚金牌77 个银色徽章77枚铜牌








2



我有同样的问题,基本上我想将拦截网址列表与其他 springsecurity 配置部分分开,第一个属于应用程序配置,后者属于产品(核心、插件)配置。



春天的JIRA中有一个关于这个问题的

提案





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



为了动态创建拦截 url 列表,您必须在 FilterSecurityInterceptor 中注入 securitymetadatasource 对象。使用 springsecurity 模式,FilterSecurityInterceptor 的实例是由 HttpBuilder 类创建的,并且无法将 securitymetadatasource 作为模式配置文件中定义的属性传递,就像使用某种解决方法一样,这可能是:



  • 定义一个自定义过滤器,在 FilterSecurityInterceptor 之前执行,在此过滤器中通过 spring 上下文检索实例 FilterSecurityInterceptor(假设定义了唯一的 http 部分),并在那里注入安全元数据源实例;



  • 与上面相同,但在处理程序拦截器中。



你觉得怎么样?



回答 五月 29, 2012 在 12:03




恩里科·朱林





1,995

2929 个银色徽章2727枚铜牌








1



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



希望这有帮助。



回答 Jun 13, 2012 在 6:11




恩里科·朱林





1,995

2929 个银色徽章2727枚铜牌



  • 我最终得到了相同的解决方案。遗憾的是,没有简单的方法来拆分定义。



    4月 27, 2016 在 7:27








1



这是在 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 */
    }
}




丹妮





3,676

44枚金质徽章2626 个银色徽章3535枚铜牌



回答 七月 12, 2014 在 0:19




里特什





7,372

22枚金质徽章3838 个银色徽章4242枚铜牌








1



一个对我有用的简单解决方案。

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



customAuthenticationProvider 是一个 bean

<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";
}



版权声明:本文为allway2原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。