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

授权 - 如何在 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.xmlUserDetailsService

丹妮
3,67644枚金质徽章2626 个银色徽章3535枚铜牌
提问 Jul 31, 2011 在 23:36
饼干
1,76044枚金质徽章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,82222 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? 
    – SunJCarkeY
     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? 
    – ajaristi
     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,09011枚金牌77 个银色徽章77枚铜牌
2

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

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

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

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

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

你觉得怎么样?

回答 五月 29, 2012 在 12:03
恩里科·朱林
1,9952929 个银色徽章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,9952929 个银色徽章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,67644枚金质徽章2626 个银色徽章3535枚铜牌
回答 七月 12, 2014 在 0:19
里特什
7,37222枚金质徽章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";
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值