我在Struts 2 + Spring IOC项目中使用Spring Security 3。
我在项目中使用了自定义过滤器,身份验证提供程序等。
您可以在此处查看我的security.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.1.xsd">
<global-method-security pre-post-annotations="enabled">
<expression-handler ref="expressionHandler" />
</global-method-security>
<beans:bean id="expressionHandler"
class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler" >
<beans:property name="permissionEvaluator" ref="customPermissionEvaluator" />
</beans:bean>
<beans:bean class="code.permission.MyCustomPermissionEvaluator" id="customPermissionEvaluator" />
<!-- User Login -->
<http auto-config="true" use-expressions="true" pattern="/user/*" >
<intercept-url pattern="/index.jsp" access="permitAll"/>
<intercept-url pattern="/user/showLoginPage.action" access="permitAll"/>
<intercept-url pattern="/user/showFirstPage" access="hasRole('ROLE_USER') or hasRole('ROLE_VISIT')"/>
<intercept-url pattern="/user/showSecondUserPage" access="hasRole('ROLE_USER')"/>
<intercept-url pattern="/user/showThirdUserPage" access="hasRole('ROLE_VISIT')"/>
<intercept-url pattern="/user/showFirstPage" access="hasRole('ROLE_USER') or hasRole('ROLE_VISIT')"/>
<form-login login-page="/user/showLoginPage.action" />
<logout invalidate-session="true"
logout-success-url="/"
logout-url="/user/j_spring_security_logout"/>
<access-denied-handler ref="myAccessDeniedHandler" />
<custom-filter before="FORM_LOGIN_FILTER" ref="myApplicationFilter"/>
</http>
<beans:bean id="myAccessDeniedHandler" class="code.security.MyAccessDeniedHandler" />
<beans:bean id="myApplicationFilter" class="code.security.MyApplicationFilter">
<beans:property name="authenticationManager" ref="authenticationManager"/>
<beans:property name="authenticationFailureHandler" ref="failureHandler"/>
<beans:property name="authenticationSuccessHandler" ref="successHandler"/>
</beans:bean>
<beans:bean id="successHandler"
class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">
<beans:property name="defaultTargetUrl" value="/user/showFirstPage"> </beans:property>
</beans:bean>
<beans:bean id="failureHandler"
class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
<beans:property name="defaultFailureUrl" value="/user/showLoginPage.action?login_error=1"/>
</beans:bean>
<beans:bean id= "myUserDetailServiceImpl" class="code.security.MyUserDetailServiceImpl">
</beans:bean>
<beans:bean id="myAuthenticationProvider" class="code.security.MyAuthenticationProvider">
<beans:property name="userDetailsService" ref="myUserDetailServiceImpl"/>
</beans:bean>
<!-- User Login Ends -->
<!-- Admin Login -->
<http auto-config="true" use-expressions="true" pattern="/admin/*" >
<intercept-url pattern="/index.jsp" access="permitAll"/>
<intercept-url pattern="/admin/showSecondLogin" access="permitAll"/>
<intercept-url pattern="/admin/*" access="hasRole('ROLE_ADMIN')"/>
<form-login login-page="/admin/showSecondLogin"/>
<logout invalidate-session="true"
logout-success-url="/"
logout-url="/admin/j_spring_security_logout"/>
<access-denied-handler ref="myAccessDeniedHandlerForAdmin" />
<custom-filter before="FORM_LOGIN_FILTER" ref="myApplicationFilterForAdmin"/>
</http>
<beans:bean id="myAccessDeniedHandlerForAdmin" class="code.security.admin.MyAccessDeniedHandlerForAdmin" />
<beans:bean id="myApplicationFilterForAdmin" class="code.security.admin.MyApplicationFilterForAdmin">
<beans:property name="authenticationManager" ref="authenticationManager"/>
<beans:property name="authenticationFailureHandler" ref="failureHandlerForAdmin"/>
<beans:property name="authenticationSuccessHandler" ref="successHandlerForAdmin"/>
</beans:bean>
<beans:bean id="successHandlerForAdmin"
class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">
</beans:bean>
<beans:bean id="failureHandlerForAdmin"
class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
<beans:property name="defaultFailureUrl" value="/admin/showSecondLogin?login_error=1"/>
</beans:bean>
<authentication-manager alias="authenticationManager">
<authentication-provider ref="myAuthenticationProviderForAdmin" />
<authentication-provider ref="myAuthenticationProvider" />
</authentication-manager>
<beans:bean id="myAuthenticationProviderForAdmin" class="code.security.admin.MyAuthenticationProviderForAdmin">
<beans:property name="userDetailsService" ref="userDetailsServiceForAdmin"/>
</beans:bean>
<beans:bean id= "userDetailsServiceForAdmin" class="code.security.admin.MyUserDetailsServiceForAdminImpl">
</beans:bean>
<!-- Admin Login Ends -->
<beans:bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource">
<beans:property name="basenames">
<beans:list>
<beans:value>code/security/SecurityMessages</beans:value>
</beans:list>
</beans:property>
</beans:bean>
现在你可以看到uptill,我提到的url-pattern是硬编码的。我想知道是否有办法动态创建新的ROLES和PERMISSIONS,而不是硬编码。
与创建新角色和权限并将其保存到数据库然后从数据库访问一样。我在网上搜索过,但我无法找到如何在代码中添加新条目。
答案 0 :(得分:37)
所以这些至少有两个问题:
我不会详细回答这个问题,因为我认为这个主题经常被讨论过。
最简单的方法是将完整的用户信息(登录名,密码和角色)存储在数据库中(3个表:User,Roles,User2Roles)并使用JdbcDetailService
。您可以在xml配置中非常好地配置两个SQL语句(用于身份验证和授予角色)。
然后用户需要注销并登录才能获得这些新角色。如果这是不可接受的,您还必须操纵当前登录用户的角色。它们存储在用户会话中。我想最简单的方法是在spring安全过滤器链中添加一个过滤器,用于更新每个请求的角色,如果需要更改的话。
这里有两种方式:
FilterSecurityInterceptor
并更新securityMetadataSource
,所需的角色应该存储在那里。至少你必须操纵方法DefaultFilterInvocationSecurityMetadataSource#lookupAttributes(String url, String method)
access
属性的其他表达式而不是access="hasRole('ROLE_USER')"
。示例:access="isAllowdForUserPages1To3"
。当然,您必须创建该方法。这被称为“自定义SpEL表达式处理程序”(如果你在页面210左右有Spring Security 3 Book。希望他们有章节编号!)。所以你现在需要做的是继承WebSecurityExpressionRoot
并引入一个新方法isAllowdForUserPages1To3
。然后,您需要子类DefaultWebSecurityExpressionHandler
并修改createEvaluationContext
方法,以便其第一个请求StandartEvaluationContext
调用super(您需要将结果强制转换为StandartEvaluationContext
)。然后,使用新的rootObject
实施替换StandartEvaluationContext
中的CustomWebSecurityExpressionRoot
。那是困难的部分!然后,您需要使用新的子类expressionHandler
替换xml配置中expressionVoter
(WebExpressionVoter
)的DefaultWebSecurityExpressionHandler
属性。 (这很糟糕,因为您首先需要编写大量安全配置,因为您无法直接从安全命名空间访问它们。)答案 1 :(得分:12)
我想补充Ralph关于创建自定义SpEL表达式的回应。他的解释非常有助于我找到正确的方法来做到这一点,但我认为他们需要扩展。
以下是有关如何创建自定义SpEL表达式的方法:
1)创建 WebSecurityExpressionRoot 类的自定义子类。在此子类中,创建一个将在表达式中使用的新方法。例如:
public class CustomWebSecurityExpressionRoot extends WebSecurityExpressionRoot {
public CustomWebSecurityExpressionRoot(Authentication a, FilterInvocation fi) {
super(a, fi);
}
public boolean yourCustomMethod() {
boolean calculatedValue = ...;
return calculatedValue;
}
}
2)创建 DefaultWebSecurityExpressionHandler 类的自定义子类并覆盖方法 createSecurityExpressionRoot(身份验证身份验证,FilterInvocation fi) (而不是createEvaluationContext(...))在其中返回 CustomWebSecurityExpressionRoot 实例。例如:
@Component(value="customExpressionHandler")
public class CustomWebSecurityExpressionHandler extends DefaultWebSecurityExpressionHandler {
@Override
protected SecurityExpressionRoot createSecurityExpressionRoot(
Authentication authentication, FilterInvocation fi) {
WebSecurityExpressionRoot expressionRoot = new CustomWebSecurityExpressionRoot(authentication, fi);
return expressionRoot;
}}
3)在spring-security.xml中定义对表达式处理程序bean的引用
<security:http access-denied-page="/error403.jsp" use-expressions="true" auto-config="false">
...
<security:expression-handler ref="customExpressionHandler"/>
</security:http>
在此之后,您可以使用自己的自定义表达式而不是标准表达式:
<security:authorize access="yourCustomMethod()">
答案 2 :(得分:2)
您可以使用Voter动态限制访问权限。另请参阅Get Spring Security intercept urls from database or properties
答案 3 :(得分:2)
这个问题有一个非常直截了当的答案。我想知道为什么你还没有得到答案
有两件事至少应该清除:
首先,您应该知道在使用命名空间时,会自动将一些过滤器添加到您编写的每个URL中。
第二,您还应该知道每个过滤器的作用。
回到你的问题:
由于您希望动态配置intercept-url,因此必须删除这些名称空间,并将其替换为以下过滤器:
<bean id="springSecurityFilterChain" class="org.springframework.security.web.FilterChainProxy">
<sec:filter-chain-map path-type="ant">
<sec:filter-chain pattern="/css/**" filters="none" />
<sec:filter-chain pattern="/images/**" filters="none" />
<sec:filter-chain pattern="/login.jsp*" filters="none" />
<sec:filter-chain pattern="/user/showLoginPage.action" filters="none" />
<sec:filter-chain pattern="/**"
filters="
securityContextPersistenceFilter,
logoutFilter,
authenticationProcessingFilter,
exceptionTranslationFilter,
filterSecurityInterceptor" />
</sec:filter-chain-map>
</bean>
然后你必须将自己的SecurityMetadaSource注入FilterSecurityInterceptor。请参阅以下内容:
<bean id="filterSecurityInterceptor"
class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor">
<property name="authenticationManager" ref="authenticationManager" />
<property name="accessDecisionManager" ref="accessDecisionManager" />
<property name="securityMetadataSource" ref="myFilterInvocationSecurityMetadataSource" />
</bean>
<bean id="myFilterInvocationSecurityMetadataSource" class="myPackage.MyFilterSecurityMetadataSource">
</bean>
但在此之前,您必须首先自定义“MyFilterSecurityMetadataSource”
该类必须实现'DefaultFilterInvocationSecurityMetadataSource'
由于您希望在数据库中拥有所有角色和URL,因此您必须自定义其 getAttributes
现在看下面的实现示例:
public class MyFilterSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
public List<ConfigAttribute> getAttributes(Object object) {
FilterInvocation fi = (FilterInvocation) object;
String url = fi.getRequestUrl();
List<ConfigAttribute> attributes = new ArrayList<ConfigAttribute>();
attributes = getAttributesByURL(url); //Here Goes Code
return attributes;
}
public Collection<ConfigAttribute> getAllConfigAttributes() {
return null;
}
public boolean supports(Class<?> clazz) {
return FilterInvocation.class.isAssignableFrom(clazz);
}
}
你看到“这里有你的代码”评论吗?你必须自己实施这个方法
我自己,有一个名为URL_ACCESS的表,其中包含URL及其相应的角色。收到用户的URL后,我会查看该表并返回其相关角色
由于我正在研究这个问题,你可以问任何问题......我会一直回答。
答案 4 :(得分:-1)
该文章中的技巧是将角色分配给用户,但是,要在Authentication.authorities对象中设置这些角色的权限。
为此,我建议您阅读API,看看是否可以扩展一些基本的ProviderManager和AuthenticationProvider而不是实现所有内容。我已经使用LdapAuthenticationProvider设置了一个自定义LdapAuthoritiesPopulator,它将为用户检索正确的角色。