Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Handling AccessDenied with Method Level Security

i have a method secured with spring security as follows:

@PreAuthorize("hasRole('add_user')")
public void addUser(User user) ;

and if a user with no enoguh permissions is trying to invoke it , an accessDenied exception is thrown:

org.springframework.security.access.AccessDeniedException: Access is denied

this is what's expected, but the question is, why the defined access-denied-handler

in security.xml configuration file is not working:

<access-denied-handler error-page="accessDenied"/>

I mean by not working that when user with not enough permission when trying to press the button addUser which will invoke the service addUser (that's only accessible by user has this permission) an AccessDenied Exception is thrown and that's the desired behavior, but the user isn't redirected to the access denied exception as configured in xml.

shouldn't the user gets redirected automatically to access denied page when this exception is thrown, or i have to define such behavior explicitly in code ?

please advise.

I am using Spring Security 3.0.5 with JSF 2.1 and ICEFaces 2

UPDATE: applicationSecurity.xml:

<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:util="http://www.springframework.org/schema/util"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
          http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
          http://www.springframework.org/schema/security
          http://www.springframework.org/schema/security/spring-security-3.0.4.xsd
          http://www.springframework.org/schema/util
          http://www.springframework.org/schema/util/spring-util-3.1.xsd">


        <!-- Enable @pre, @post spring security method level annotations -->
        <global-method-security pre-post-annotations="enabled" />   


        <http use-expressions="true"  auto-config="true" access-denied-page="/accessDenied">

     <session-management session-fixation-protection="none"/>

        <remember-me  token-validity-seconds="1209600"/>


        <intercept-url pattern="/accessDenied" access="permitAll"/>        
        <intercept-url pattern="/login" access="permitAll"/>
        <intercept-url pattern="/j_spring_security_check" access="permitAll" />


        <intercept-url pattern="/faces/javax.faces.resource/**" access="permitAll" />
        <intercept-url pattern="/xmlhttp/**" access="permitAll" />
        <intercept-url pattern="/resources/**" access="permitAll" />        
        <intercept-url pattern="/scripts/**" access="permitAll" />
        <intercept-url pattern="/images/**" access="permitAll" />
        <intercept-url pattern="/css/**" access="permitAll" />


        <!-- All pages requires authentication (not anonymous user) -->

        <intercept-url pattern="/**" access="isAuthenticated()" />
        <intercept-url pattern="/faces/**" access="isAuthenticated()" />


        <form-login default-target-url="/"   
        always-use-default-target="true"            
            login-processing-url="/j_spring_security_check"         
            login-page="/login"
            authentication-failure-url="/login?login_error=1"                                                               
        />

        <logout logout-url="/logout" logout-success-url="/login" />     
    </http>

    <authentication-manager alias="authenticationManager">          
      <authentication-provider user-service-ref="userDetailsServiceImpl"/>    
    </authentication-manager>


    </beans:beans>

UPDATE 2 : debugs before exception:

DEBUG [http-bio-8080-exec-1] (PrePostAnnotationSecurityMetadataSource.java:93) - @org.springframework.security.access.prepost.PreAuthorize(value=hasRole('add_user')) found on specific method: public void com.myapp.service.impl.UserServiceImpl.addUser(com.myapp.domain.User) throws java.lang.Exception,org.springframework.security.access.AccessDeniedException
DEBUG [http-bio-8080-exec-1] (DelegatingMethodSecurityMetadataSource.java:66) - Adding security method [CacheKey[com.myapp.service.impl.UserServiceImpl; public abstract void com.myapp.service.UserService.addUser(com.myapp.domain.User) throws java.lang.Exception,org.springframework.security.access.AccessDeniedException]] with attributes [[authorize: 'hasRole('add_user')', filter: 'null', filterTarget: 'null']]
DEBUG [http-bio-8080-exec-1] (AbstractSecurityInterceptor.java:191) - Secure object: ReflectiveMethodInvocation: public abstract void com.myapp.service.UserService.addUser(com.myapp.domain.User) throws java.lang.Exception,org.springframework.security.access.AccessDeniedException; target is of class [com.myapp.service.impl.UserServiceImpl]; Attributes: [[authorize: 'hasRole('add_user')', filter: 'null', filterTarget: 'null']]
DEBUG [http-bio-8080-exec-1] (AbstractSecurityInterceptor.java:292) - Previously Authenticated: org.springframework.security.authentication.UsernamePasswordAuthenticationToken@c650d918: Principal: org.springframework.security.core.userdetails.User@db344023: Username: [email protected]; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: access_viewUsers; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@fffde5d4: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: E6BBAC0CD4499B1455227DC6035CC882; Granted Authorities: access_viewUsers
DEBUG [http-bio-8080-exec-1] (AffirmativeBased.java:53) - Voter: org.springframework.security.access.prepost.PreInvocationAuthorizationAdviceVoter@1d1e082e, returned: -1
DEBUG [http-bio-8080-exec-1] (AffirmativeBased.java:53) - Voter: org.springframework.security.access.vote.RoleVoter@1eab12f1, returned: 0
DEBUG [http-bio-8080-exec-1] (AffirmativeBased.java:53) - Voter: org.springframework.security.access.vote.AuthenticatedVoter@71689bf1, returned: 0
like image 936
fresh_dev Avatar asked Oct 27 '11 13:10

fresh_dev


1 Answers

According to the spring Security documentation the user of the access-denied-page attribute of the element has been deprecated in Spring 3.0 and above.

We do the following in our app:

  1. Create a custom access denied handler by extending the Spring Security framework's AccessDeniedHandlerImpl.
  2. Call the setErrorPage method, passing in the name of the controller that will display your access denied page
  3. In our case we lock the user's account in the custom handler - there's no good reason for any user to ever get an access denied exception unless they're doing something that they should't. We also log what they were trying to access, etc.
  4. Call super.handle(_request, _response, _exception); at the end of the handler. Spring will forward control to the controller listed in #2 above.

    public class AccessDeniedHandlerApp extends AccessDeniedHandlerImpl {
        private static Logger logger = Logger.getLogger(AccessDeniedHandlerApp.class);
    
        private static final String LOG_TEMPLATE = "AccessDeniedHandlerApp:  User attempted to access a resource for which they do not have permission.  User %s attempted to access %s";
    
         @Override
         public void handle(HttpServletRequest _request, HttpServletResponse _response, AccessDeniedException _exception) throws IOException, ServletException {
             setErrorPage("/securityAccessDenied");  // this is a standard Spring MVC Controller
    
             // any time a user tries to access a part of the application that they do not have rights to lock their account
             <custom code to lock the account>
             super.handle(_request, _response, _exception);
    }
    

    }

Here's my XML: AccessDeniedHandlerApp extends 'AccessDeniedHandlerImpl`

<http auto-config='true'>
    <intercept-url pattern="/views/**" access="ROLE_USER" />
    <form-login login-page="/Login.jsp" authentication-success-handler-ref="loginSuccessFilter"
                authentication-failure-handler-ref="loginFailureFilter" />
    <logout logout-success-url="/home" />
    <access-denied-handler ref="customAccessDeniedHandler"/>
</http>

<beans:bean id="customAccessDeniedHandler" class="org.demo.security.AccessDeniedHandlerApp"/>

Here's my Access Denied Controller - I should have posted this earlier - sorry about that. In order to get the access denied page to come up I had to use a redirect:

@Controller
public class AccessDeniedController {
    private static Logger logger = Logger.getLogger(AccessDeniedController.class);

    @RequestMapping(value = "/securityAccessDenied")
    public String processAccessDeniedException(){
        logger.info("Access Denied Handler");
        return "redirect:/securityAccessDeniedView";
    }

    @RequestMapping(value = "/securityAccessDeniedView")
    public String displayAccessDeniedView(){
        logger.info("Access Denied View");
        return "/SecurityAccessDenied";
    }

Please let me know if this doesn't resolve it and I'll keep digging - I just tested it again locally here and this should do the trick. }

like image 164
jcurtin Avatar answered Nov 13 '22 22:11

jcurtin