Question

I've been trying out 'Method Level' security on this application I've been working on. The idea is to secure a method which gets called from the presentation layer using DWR. Now, I've tried adding the following annotations on my method:

@PreAuthorize("isAuthenticated() and hasRole('ROLE_CUSTOMER')")

And corresponding entry in my security context:

<global-method-security pre-post-annotations="enabled" />

On similar lines, I've tried @Secured annotation:

@Secured({"ROLE_CUSTOMER" })

And corresponding entry in my security context:

<global-method-security secured-annotations="enabled" />

Ideally, I would expect that if a user is not authenticated, they should be redirected to a 'Sign in' page and the 'ROLES' should not be checked. In this case, even for an unauthenticated user, this method call results in 'AccessDeniedException'. I need it to redirect the user to a login page in such a scenario.

To take it forward, I even tried handling the accessdenied exception by creating a custom AccessDenied Handler. Unfortunately, the handler never got called but the exception was thrown.

Here's the configuration:

<access-denied-handler ref="customAccessDeniedHandler"/>

This has a corresponding handler bean defined in the same file.

Still no luck. The accessdeniedhandler never gets called.

Just to summarize the requirement, I need to secure a method. If this method gets called, and the user is unauthenticated the user should get redirected to 'Sign In' page (which as of now is throwing Accessdenied execption).

Appreciate your help folks..

EDIT 1: Here is a snippet from the security context:

<http>
    <intercept-url pattern="/*sign-in.do*" requires-channel="$secure.channel}" />
    .....
    ..... 
    .....
    <intercept-url pattern="/j_acegi_security_check.do" requires-channel="${secure.channel}" />

    <intercept-url pattern="/*.do" requires-channel="http"  />
    <intercept-url pattern="/*.do\?*" requires-channel="http" />
    <form-login login-page="/sign-in.do" authentication-failure-url="/sign-in.do?login_failed=1"
        authentication-success-handler-ref="authenticationSuccessHandler" login-processing-url="/j_acegi_security_check.do"/>

    <logout logout-url="/sign-out.do" logout-success-url="/index.do" />

    <session-management session-authentication-strategy-ref="sessionAuthenticationStrategy" />
        <access-denied-handler ref="customAccessDeniedHandler"/>
  </http>
    <beans:bean id="customAccessDeniedHandler" class="com.mypackage.interceptor.AccessDeniedHandlerApp"/>
Was it helpful?

Solution 3

Okay, I have not had much success finding why I got an AccessDeniedException. Regardless, I've worked my way out of it till the time I find the reason. Here's a couple of approaches I took:

1) As mentioned by Zagyi, I was able to propagate the AccessDenied Exception over to the client side. I could create an exception handler to and redirect the user to the sign in page.

However, I took a different approach (which may not be the most optimal but seems to work as of now. Here's what I've done:

1) Created a DWRAjaxFilter and mapped to only the Remote Objects I am interested in. This would bean only the DWR calls to these remote methods get intercepted by the filter. This is because I do not want all DWR exposed methods to as for a sign in.

   <create creator="spring" javascript="downloadLinksAjaxService">
       <param name="beanName" value="downloadLinksAjaxService" />
       <include method="methodOne" />
       <include method="methodTwo" />  
       <filter class="com.xyz.abc.interceptor.DwrAjaxFilter"></filter>
   </create>

2) Here is the actual filter implementation:

public class DwrSessionFilter implements AjaxFilter {
    public Object doFilter(final Object obj, final Method method,
            final Object[] params, final AjaxFilterChain chain)
            throws Exception {

        SecurityContext context = SecurityContextHolder.getContext();
        Authentication auth = context.getAuthentication();
        if (!auth.isAuthenticated()
                || auth.getAuthorities().contains(
                        new GrantedAuthorityImpl("ROLE_ANONYMOUS"))) {
            throw new LoginRequiredException("Login Required");
        } else {
            return chain.doFilter(obj, method, params);
        }
    }
}

3) Here is the client side handler:

function errorHandler(message, exception){

                if(exception && exception.javaClassName == "org.directwebremoting.extend.LoginRequiredException") {

                  document.location.reload();
                }
            }

4) I also added and exception mapper for DWR so that I could convert JAVA exceptions to JS exceptions:

<convert match="java.lang.Exception" converter="exception">
            <param name='include' value='message'/>
        </convert> 

5) This seems to work well as of now. I'm still testing this to see if this fails somewhere.

Would appreciate any more inputs.

OTHER TIPS

I'm not very familiar with DWR, but I know that it's an RPC mechanism. The problem is that an RPC request sent by the client side javascript is not a regular page request initiated by the user who navigates the browser. The response to such an RPC request cannot possibly make the browser navigate away from the current page, because the response is processed by your javascript code and not by the browser.

What you can do is:

  1. Implement a sort of eager authentication: redirect the user to a simple .jsp login page before allowing it to access the webapp (URL) that makes DWR (or any kind of RPC) requests.
  2. If the AccessDeniedException thrown by the remote procedure gets propagated to the client side in some form, you can try to handle it from javascript code, for example by popping up a login dialog, and submitting user's credentials in an AJAX request. (In this case make sure to save the returned session id, and send it back with each subsequent requests.)

I would be interested in seeing the rest of your security.xml file (specifically, the <http> element), because if every user is being denied, it suggests to me that you have an <intercept-url> property which overrides calls to your controller class(es). If not, the added information might help shed some light on the problem.

Assuming I'm way off so far, another likely error point might be the specified access-denied-page itself -- if its path is a protected path (i.e. by <intercept-url>), or if it isn't specified, you might see the errors of which you speak.

Of course, I, too, am unfamiliar with DWR, so I'm somewhat obviously assuming a standard Spring MVC framework...

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top