2011-07-31 46 views
20

在Spring Security我們使用攔截的URL標籤來定義的URL訪問如下:如何在Spring Security中動態決定<intercept-url>訪問屬性值?

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

這在applicationContext-security.xml硬編碼。我想讀取數據庫表中的訪問值。我已經定義了自己的UserDetailsService,並從數據庫中讀取登錄用戶的角色。如何在運行時將這些角色分配給URL模式?

+0

它可能會有所幫助:HTTPS://github.com/srinivas1918/spring-security-dynamic -authorization-and-authentication –

回答

18

在彈簧安全的FilterInvocationSecurityMetadataSourceParser類(嘗試按Ctrl /爲Cmd + Shift + T與源代碼STS)解析截距-URL標籤並創建ExpressionBasedFilterInvocationSecurityMetadataSource的實例,其延伸DefaultFilterInvocationSecurityMetadataSource實現FilterInvocationSecurityMetadataSource延伸SecurityMetadataSource。

我所做的是創建一個自定義類,它實現FilterInvocationSecurityMetadataSource,OptionsFromDataBaseFilterInvocationSecurityMetadataSource。我使用DefaultFilterInvocationSecurityMetadataSource作爲基礎來使用urlMatcher,來實現support()方法和類似的東西。

然後您必須實現這些方法:

  • 收集的getAttributes(Object對象),在那裏你可以訪問數據庫,搜索「對象」被固定(通常是URL訪問),以獲取所允許的ConfigAttribute的(通常是角色的)

  • 布爾支撐件(類clazz所)

  • 收集getAllConfigAttributes()

要小心後面的,因爲它在啓動時調用,可能在這個時候配置不好(我的意思是,數據源或持久化上下文自動裝配,取決於你使用的是什麼)。 Web環境中的解決方案是在web.xml中配置contextConfigLocation以在applicationContext-security.xml之前加載applicationContext.xml

最後一步是定製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> 

你必須定義所有相關的豆類。例如:

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

我知道這不是一個很好解釋的答案,但它並不像看起來那麼困難。

只要使用彈簧源作爲基礎,你將獲得你想要的。

使用數據庫中的數據進行調試將對您有所幫助。

+0

您好我試圖達到同樣的事情,但與struts2。通過Spring Security 3.1,但無法獲得足夠的關於與struts2集成的想法。你介意給一些基本的想法與struts2集成嗎? – SunJCarkeY

+0

這個例子是在春天的舊版本下。我如何在spring-security 3.2下做到這一點? – ajaristi

2

我有一種同樣的問題,基本上我想分開攔截url的列表從其他springsecurity配置部分,第一個屬於應用程序配置後者到產品(核心,插件)配置。

春天的JIRA中有一個proposal,關於這個問題。

我不想放棄使用springsecurity命名空間,所以我在想一些可能的解決方案來處理這個問題。

爲了讓動態創建的intercept-url列表您必須將securitymetadatasource對象注入FilterSecurityInterceptor中。 使用springsecurity模式FilterSecurityInterceptor的實例由HttpBuilder類創建的,也沒有辦法通過securitymetadatasource作爲架構配置文件中定義的屬性,儘量少的使用一種解決方法,它可能是:

  • 定義一個自定義過濾器,在FilterSecurityInterceptor之前執行,在此過濾器中,由Spring上下文檢索實例FilterSecurityInterceptor(假設定義了唯一的http部分)並在其中注入securitymetadatasource實例;
  • 與上面相同,但在HandlerInterceptor中。

您怎麼看?

1

這是我已經應用的解決方案,以便從其他spring安全配置中分割攔截URL項目列表。

<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 id="securityMetadataSource" class="mypackage.MyImplementationOfFilterInvocationSecurityMetadataSource" /> 

希望這會有所幫助。

+0

我結束了非常相同的解決方案。遺憾的是,如何分割定義並不容易。 –

4

其實,春季安全3.2不鼓勵了根據使用一個自定義的AccessDecisionManager命名空間http元素http://docs.spring.io/spring-security/site/docs/3.2.x/reference/htmlsingle/faq.html#faq-dynamic-url-metadata

,但是,它是可能的(但不優雅)要做到這一點..

的配置應是:

<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

0

這是怎麼回事可以在Spring安全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 */ 
    } 
} 
1

一個簡單的解決方案,爲我的作品。

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

customAuthenticationProvider是CustomAuthenticationProvider類豆

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