我在Struts 2 + Spring IOC项目中使用Spring Security 3。
我在项目中使用了自定义过滤器,身份验证提供程序等。
您可以在这里看到我的security.xml
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>
到目前为止,您可以看到,我提到的url-pattern是硬编码的。我想知道是否有一种方法可以动态创建新的ROLES和PERMISSIONS,而不是硬编码。
就像创建新角色和权限并将其保存到数据库,然后从数据库访问一样。我已经在网上搜索过,但是无法找到如何在代码中添加新条目的方法。
因此,这些至少是两个问题:
如何使授予的权限/特权/角色动态化? 如何使URL的访问限制动态化? 1)如何使授予的权限/特权/角色动态化? 我不会详细回答这个问题,因为我相信这个主题经常被讨论。
最简单的方法是将完整的用户信息(登录名,密码和角色)存储在数据库(3个表:User,Roles,User2Roles)中并使用JdbcDetailService。您可以在xml配置中很好地配置两个SQL语句(用于身份验证和授予角色)。
JdbcDetailService
但是随后,用户需要注销并登录以获取这些新角色。如果这是不可接受的,那么您还必须操纵当前登录用户的角色。它们存储在用户会话中。我想最简单的方法是在spring安全过滤器链中添加一个过滤器,以更新每个请求的角色(如果需要更改)。
2)如何使URL的访问限制动态化? 在这里,您最后有两种方法:
入侵FilterSecurityInterceptor和更新securityMetadataSource,所需的角色应存储在此处。至少您必须操纵方法的输出DefaultFilterInvocationSecurityMetadataSource#lookupAttributes(String url, String method)
FilterSecurityInterceptor
securityMetadataSource
DefaultFilterInvocationSecurityMetadataSource#lookupAttributes(String url, String method)
另一种方法是对access属性使用其他表达式,而不是access="hasRole('ROLE_USER')"。范例:access="isAllowdForUserPages1To3"。当然,您必须创建该方法。这被称为“自定义SpEL表达式处理程序”(如果您有Spring Security 3 Book,则在第210页左右。希望他们有章号!)。因此,您现在需要做的是继承WebSecurityExpressionRoot并引入一个新方法isAllowdForUserPages1To3。然后,您需要对方法进行子类化DefaultWebSecurityExpressionHandler和修改,createEvaluationContext以便其第一个请求StandartEvaluationContext调用super(您需要将结果转换为StandartEvaluationContext)。然后,使用您的新实现替换rootObject中的。那是困难的部分!然后,你需要更换的属性(StandartEvaluationContextCustomWebSecurityExpressionRootexpressionHandlerexpressionVoterWebExpressionVoter),并在xml配置中添加新的子类DefaultWebSecurityExpressionHandler。(这很烂,因为您首先需要明确地编写许多安全配置,因为您不能直接从安全名称空间访问它们。)
access
access="hasRole('ROLE_USER')"
access="isAllowdForUserPages1To3"
WebSecurityExpressionRoot
isAllowdForUserPages1To3
DefaultWebSecurityExpressionHandler
createEvaluationContext
StandartEvaluationContext
super
(StandartEvaluationContextCustomWebSecurityExpressionRootexpressionHandlerexpressionVoterWebExpressionVoter)
我想补充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(Authentication authentication,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()">