Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating New Roles and Permissions Dynamically in Spring Security 3

I am using Spring Security 3 in Struts 2 + Spring IOC project.

I have used Custom Filter, Authentication Provider etc. in my Project.

You can see my security.xml here

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

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.

like image 506
Dark Drake Avatar asked Nov 30 '11 05:11

Dark Drake


People also ask

What is hasRole and hasAnyRole?

hasRole, hasAnyRole. These expressions are responsible for defining the access control or authorization to specific URLs and methods in our application: @Override protected void configure(final HttpSecurity http) throws Exception { ... . antMatchers("/auth/admin/*").

What are dynamic permissions?

Domain-based Dynamic Access Control enables administrators to apply access-control permissions and restrictions based on well-defined rules that can include the sensitivity of the resources, the job or role of the user, and the configuration of the device that is used to access these resources.


2 Answers

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?

1) How to make the granted authorities/privileges/Roles dynamic?

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.

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.

2) How to make the access restriction for the URLs dynamic?

Here you have at last two ways:

  • Hacking into the FilterSecurityInterceptor and updating the securityMetadataSource, the needed Roles should be stored there. At least you must manipulate the output of the method DefaultFilterInvocationSecurityMetadataSource#lookupAttributes(String url, String method)
  • The other way would be using other expressions for the access attribute instead of access="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 Book it's around page 210. Wish they had chapter numbers!). So what you need to do now is to subclass WebSecurityExpressionRoot and introduce a new method isAllowdForUserPages1To3. Then you need to subclass DefaultWebSecurityExpressionHandler and modify the createEvaluationContext method so that its first request StandartEvaluationContext calls super (you need to cast the result to StandartEvaluationContext). Then, replace the rootObject in the StandartEvaluationContext using your new CustomWebSecurityExpressionRoot implementation. That's the hard part! Then, you need to replace the expressionHandler attribute of the expressionVoter (WebExpressionVoter) in the xml configuration with your new subclassed DefaultWebSecurityExpressionHandler. (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.)
like image 59
Ralph Avatar answered Sep 23 '22 05:09

Ralph


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.

Here is a way on how to create custom SpEL expression:

1) Create custom subclass of WebSecurityExpressionRoot class. In this subclass create a new method which you will use in expression. For example:

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 DefaultWebSecurityExpressionHandler class and override method createSecurityExpressionRoot(Authentication authentication, FilterInvocation fi) (not createEvaluationContext(...)) in it to return your CustomWebSecurityExpressionRoot instance. For example:

@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

<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()"> 
like image 42
dimas Avatar answered Sep 22 '22 05:09

dimas