在 Spring Security 3 中动态创建新角色和权限
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/8321696/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Creating New Roles and Permissions Dynamically in Spring Security 3
提问by Dark Drake
I am using Spring Security 3 in Struts 2 + Spring IOC project.
我在 Struts 2 + Spring IOC 项目中使用 Spring Security 3。
I have used Custom Filter, Authentication Provider etc. in my Project.
我在我的项目中使用了自定义过滤器、身份验证提供程序等。
You can see my security.xml here
你可以在这里看到我的 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 now you can see, url-pattern I have mentioned is hard coded. I wanted to know if there is a way to create new ROLES and PERMISSIONS dynamically, not hard coded.
现在你可以看到,我提到的 url-pattern 是硬编码的。我想知道是否有办法动态创建新角色和权限,而不是硬编码。
Like creating new roles and permissions and saving them to database and then accessing from database. I have searched on net, but I am not able to find out how to add new entries to code.
就像创建新角色和权限并将它们保存到数据库然后从数据库访问一样。我在网上搜索过,但我无法找到如何向代码添加新条目。
回答by Ralph
So these are at least two questions:
所以这些至少是两个问题:
- How to make the granted authorities/privileges/Roles dynamic?
- How to make the access restriction for the URLs dynamic?
- 如何使授予的权限/特权/角色具有动态性?
- 如何使 URL 的访问限制动态化?
1) How to make the granted authorities/privileges/Roles dynamic?
1) 如何使授予的权限/特权/角色具有动态性?
I will not answer this in great detail, because I believe this theme was discussed often enough.
我不会详细回答这个问题,因为我相信这个主题已经讨论得够多了。
The easiest way would be to store the complete user information (login, password and roles) in a database (3 Tables: User, Roles, User2Roles) and use the JdbcDetailService. You can configure the two SQL Statements (for authentication and for granting the roles) very nicely in your xml configuration.
最简单的方法是将完整的用户信息(登录名、密码和角色)存储在数据库中(3 个表:User、Roles、User2Roles)并使用JdbcDetailService. 您可以在 xml 配置中很好地配置两个 SQL 语句(用于身份验证和授予角色)。
But then the user needs to logout and login to get these new Roles. If this is not acceptable, you must also manipulate the Roles of the current logged in user. They are stored in the users session. I guess the easiest way to do that is to add a filter in the spring security filter chain that updates the Roles for every request, if they need to be changed.
但是随后用户需要注销并登录才能获得这些新角色。如果这是不可接受的,您还必须操作当前登录用户的角色。它们存储在用户会话中。我想最简单的方法是在 spring 安全过滤器链中添加一个过滤器,如果需要更改,它会更新每个请求的角色。
2) How to make the access restriction for the URLs dynamic?
2) 如何使 URL 的访问限制动态化?
Here you have at last two ways:
在这里你有最后两种方法:
- Hacking into the
FilterSecurityInterceptorand updating thesecurityMetadataSource, the needed Roles should be stored there. At least you must manipulate the output of the methodDefaultFilterInvocationSecurityMetadataSource#lookupAttributes(String url, String method) - The other way would be using other expressions for the
accessattribute instead ofaccess="hasRole('ROLE_USER')". Example:access="isAllowdForUserPages1To3". Of course you must create that method. This is called a "custom SpEL expression handler" (If you have the Spring Security 3 Bookit's around page 210. Wish they had chapter numbers!). So what you need to do now is to subclassWebSecurityExpressionRootand introduce a new methodisAllowdForUserPages1To3. Then you need to subclassDefaultWebSecurityExpressionHandlerand modify thecreateEvaluationContextmethod so that its first requestStandartEvaluationContextcalls super (you need to cast the result toStandartEvaluationContext). Then, replace therootObjectin theStandartEvaluationContextusing your newCustomWebSecurityExpressionRootimplementation. That's the hard part! Then, you need to replace theexpressionHandlerattribute of theexpressionVoter(WebExpressionVoter) in the xml configuration with your new subclassedDefaultWebSecurityExpressionHandler. (This sucks because you first need to write a lot of security configuration explicity as you can't access them directly from the security namespace.)
- 入侵
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. (这很糟糕,因为您首先需要明确地编写大量安全配置,因为您无法直接从安全命名空间访问它们。)
回答by dimas
I would like to supplement Ralph's response about creating custom SpEL expression. His explanations helped very much on my attempt to find the right way to do this, but i think that they need to be extended.
我想补充 Ralph 关于创建自定义 SpEL 表达式的回复。他的解释对我尝试找到正确的方法有很大帮助,但我认为它们需要扩展。
Here is a way on how to create custom SpEL expression:
以下是如何创建自定义 SpEL 表达式的方法:
1) Create custom subclass of WebSecurityExpressionRootclass. In this subclass create a new method which you will use in expression. For example:
1) 创建WebSecurityExpressionRoot类的自定义子类。在这个子类中创建一个你将在表达式中使用的新方法。例如:
public class CustomWebSecurityExpressionRoot extends WebSecurityExpressionRoot {
public CustomWebSecurityExpressionRoot(Authentication a, FilterInvocation fi) {
super(a, fi);
}
public boolean yourCustomMethod() {
boolean calculatedValue = ...;
return calculatedValue;
}
}
2) Create custom subclass of DefaultWebSecurityExpressionHandlerclass and override method createSecurityExpressionRoot(Authentication authentication, FilterInvocation fi)(not createEvaluationContext(...)) in it to return your CustomWebSecurityExpressionRootinstance. For example:
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) Define in your spring-security.xml the reference to your expression handler bean
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>
After this, you can use your own custom expression instead of the standard one:
在此之后,您可以使用自己的自定义表达式而不是标准表达式:
<security:authorize access="yourCustomMethod()">
回答by Matin Kh
This question has a very straightforward answer. I wonder why you haven't got your answer yet.
There are two things that should be cleared at least:
First, you should know that when you are using namespace, automatically some filters will be added to each URL you have written.
Second, you should also know that what each filter does.
Back to your question:
As you want to have intercept-url to be dynamically configured, you have to remove those namespaces, and replace them with these filters:
这个问题有一个非常直接的答案。我想知道为什么你还没有得到你的答案。
至少有两件事应该清除:
首先,您应该知道,当您使用命名空间时,会自动向您编写的每个 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>
Then you have to inject your own SecurityMetadaSource into FilterSecurityInterceptor. See the following:
然后你必须将你自己的 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>
But before that, you have to customize 'MyFilterSecurityMetadataSource' first.
This class has to implement the 'DefaultFilterInvocationSecurityMetadataSource'.
As you want to have all roles and URLs in your DB, you have to customize its getAttributes
Now see the following example of its implementation:
但在此之前,您必须先自定义“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);
}
}
do you see the "Here goes your code" comment? You have to implement that method yourself.
I myself, have a table named URL_ACCESS which contains both URLs and their corresponding roles. After receiving URL from user, I look up into that table and return its related role.
As I'm working exactly on this subject, you may ask any questions... I will always answer.
你看到“这是你的代码”评论吗?您必须自己实现该方法。
我自己有一个名为 URL_ACCESS 的表,其中包含 URL 及其相应的角色。从用户收到 URL 后,我查找该表并返回其相关角色。
由于我正在研究这个主题,因此您可以提出任何问题......我会一直回答。
回答by Ritesh
You can use Voterto dynamically restrict access. Also see Get Spring Security intercept urls from database or properties
您可以使用Voter来动态限制访问。另请参阅从数据库或属性获取 Spring Security 拦截 url
回答by Himanshu Shukla
- Create your model (user, role, permissions) and a way to retrieve permissions for a given user;
- Define your own org.springframework.security.authentication.ProviderManager and configure is (set its providers) to a custom org.springframework.security.authentication.AuthenticationProvider; this last one should return on its authenticate method a Authentication, which should be setted with the GrantedAuthority, in your case, all the permissions for the given user.
- 创建您的模型(用户、角色、权限)以及检索给定用户权限的方法;
- 定义您自己的 org.springframework.security.authentication.ProviderManager 并将 is(设置其提供者)配置为自定义 org.springframework.security.authentication.AuthenticationProvider; 最后一个应该在其身份验证方法上返回一个身份验证,该身份验证应该使用 GrantedAuthority 进行设置,在您的情况下,是给定用户的所有权限。
The trick in that article is to have roles assigned to users, but, to set the permissions for those roles in the Authentication.authorities object.
该文章中的技巧是将角色分配给用户,但是,在 Authentication.authorities 对象中设置这些角色的权限。
For that I advise you to read the API, and see if you can extend some basic ProviderManager and AuthenticationProvider instead of implementing everything. I've done that with LdapAuthenticationProvider setting a custom LdapAuthoritiesPopulator, that would retrieve the correct roles for the user.
为此,我建议您阅读 API,看看您是否可以扩展一些基本的 ProviderManager 和 AuthenticationProvider 而不是实现所有内容。我已经通过 LdapAuthenticationProvider 设置了一个自定义的 LdapAuthoritiesPopulator,这将为用户检索正确的角色。

