SpringSecurity: Authenticating & Authorizing AJAX Requests (Server-Side Implementation)

Umar Ashfaq
Eastros
Published in
4 min readAug 4, 2012

--

In my previous post, we discussed a bit of theory related to authentication and authorization of AJAX requests. We also defined the desired behavior that we would like to achieve as a result of this process. In this post, I will share the server-side code that you will need to achieve that behavior.

ExceptionTranslationFilter

ExceptionTranslationFilter is one of the most important filters in Spring Security filter chain. It catches AccessDeniedException and AuthenticationException thrown by other filters in the chain, and takes actions accordingly. Our target is to intercept control from this filter at different points and tweak it’s behavior if we detect an AJAX request.

Tweaking Authentication Failure Behavior

ExceptionTranslationFilter has a protected method sendStartAuthentication which is invoked whenever an AuthenticationException is caught. This method is responsible for caching current request and redirecting user to a login page. In order to tweak authentication failure behavior, we need to extend ExceptionTranslationFilter with a new class and override sendStartAuthentication. In our implementation of sendStartAuthentication, we will detect if current request is AJAX. If true, we will create a JSON (or XML, if you like) response otherwise we will invoke implementation of super class.

If you have been configuring Spring Security using security namespace heavily, you might find overriding ExceptionTranslationFilter bit tricky, because you cannot override it when using <http> element of the namespace. It implies that you will need to instantiate and wire-up all the plethora of security beans yourself which <http> had been seamlessly doing for you.

Tweaking Authorization Failure Behavior

ExceptionTranslationFilter keeps reference of an instance of AccessDeniedHandler and invokes its handle method whenever an authorization failure occurs. If you do not provide your own implementation to ExceptionTranslationFilter, it uses AccessDeniedHandlerImpl as default implementation of the interface. Once again, we will extend AccessDeniedHandlerImpl and a new class and override its handle method. In our implementation of this method, we will detect if current request is AJAX. If so, we will send a JSON response back to the client, otherwise we will pass control to implementation of super class.

Code & Configuration

Here’s how our extension of ExceptionTranslationFilter would look like:
[sourcecode language=”java”]
public class MyExceptionTranslationFilter extends ExceptionTranslationFilter {
@Override
protected void sendStartAuthentication(HttpServletRequest req,
HttpServletResponse resp, FilterChain chain,
AuthenticationException reason) throws ServletException,
IOException {
boolean isAjax = “XMLHttpRequest”.equals(req.getHeader(“X-Requested-With”));

if (isAjax) {
String jsonObject = “{\”message\”:\”Please login first.\”,”+
“\”access-denied\”:true,\”cause\”:\”AUTHENTICATION_FAILURE\”}”;
String contentType = “application/json”;
resp.setContentType(contentType);
PrintWriter out = resp.getWriter();
out.print(jsonObject);
out.flush();
out.close();
return;
}

super.sendStartAuthentication(req, resp, chain, reason);
}
}
[/sourcecode]
And here is how we would extend AccessDeniedHandlerImpl:
[sourcecode language=”java”]
public class MyAccessDeniedHandlerImpl extends AccessDeniedHandlerImpl {
@Override
public void handle(HttpServletRequest req,
HttpServletResponse resp, AccessDeniedException reason) throws ServletException,
IOException {
boolean isAjax = “XMLHttpRequest”.equals(req.getHeader(“X-Requested-With”));

if (isAjax) {
String jsonObject = “{\”message\”:\”You are not privileged to request this resource.\”,”+
“\”access-denied\”:true,\”cause\”:\”AUTHORIZATION_FAILURE\”}”;
String contentType = “application/json”;
resp.setContentType(contentType);
PrintWriter out = resp.getWriter();
out.print(jsonObject);
out.flush();
out.close();
return;
}

super.handle(req, resp, reason);
}
}
[/sourcecode]
Finally, our configuration would look like this:
[sourcecode language=”xml”]
<! — alias should be same as filter name you have defined in web.xml for Spring Security →
<beans:alias name=”filterChainProxy” alias=”springSecurityFilterChain”/>

<! — filterChainProxy actually creates a security filter chain which had been automatically done for us by &lt;http&gt;. →
<beans:bean id=”filterChainProxy” class=”org.springframework.security.web.FilterChainProxy”>
<filter-chain-map path-type=”ant”>
<! — We don’t need to apply security filters on our css, javascript and image files. →
<filter-chain pattern=”/css/**” filters=”none” />
<filter-chain pattern=”/js/**” filters=”none” />
<filter-chain pattern=”/images/**” filters=”none” />
<! — For rest of the URLs, we specify a filter set. Keep in mind that order of filters is important. You can add more filters or remove any existing from the chain depending upon your requirements. →
<filter-chain pattern=”/**” filters=”securityContextFilter, logoutFilter, formLoginFilter, requestCacheFilter,
servletApiFilter, rememberMeAuthenticationFilter, anonFilter, sessionMgmtFilter, exceptionTranslator, filterSecurityInterceptor” />
</filter-chain-map>
</beans:bean>

<! — This class holds UserDetails object once a user is authenticated. →
<beans:bean id=”securityContextRepository” class=”org.springframework.security.web.context.HttpSessionSecurityContextRepository” />

<! — This filter adds UserDetails object to securityContextRepository once a user is authenticated. →
<beans:bean id=”securityContextFilter” class=”org.springframework.security.web.context.SecurityContextPersistenceFilter” >
<beans:property name=”securityContextRepository” ref=”securityContextRepository” />
</beans:bean>

<! — As name suggests, this filter handles a logout event initiated by user. →
<beans:bean id=”logoutFilter” class=”org.springframework.security.web.authentication.logout.LogoutFilter”>
<! — The page that you would like to redirect after logout is complete. →
<beans:constructor-arg value=”/” />
<! — A list of LogoutHandler(s) that you would like to invoke when logout is triggered. →
<beans:constructor-arg>
<beans:list>
<! — A reference to rememberMeServices (which implements LogoutHandler) is required to clear remember me cookie. →
<beans:ref local=”rememberMeServices”/>
<! — SecurityContextLogoutHandler would clear UserDetails object from securityContextRepository. →
<beans:bean class=”org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler” />
</beans:list>
</beans:constructor-arg>
</beans:bean>

<! — Allows username/password based authentication. →
<beans:bean id=”formLoginFilter” class=”org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter”>
<! — A reference to rememberMeServices is required to add a remember me cookie on login. →
<beans:property name=”rememberMeServices” ref=”rememberMeServices”/>
<! — Authenticates login request. →
<beans:property name=”authenticationManager” ref=”authenticationManager” />
<! — Reference to an implementation of AuthenticationSuccessHandler, whose onAuthenticationSuccess method will be invoked if login attempt was successful. →
<beans:property name=”authenticationSuccessHandler”>
<beans:bean class=”org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler”>
<beans:property name=”defaultTargetUrl” value=”/categories” />
</beans:bean>
</beans:property>
<beans:property name=”sessionAuthenticationStrategy”>
<beans:bean class=”org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy” />
</beans:property>
</beans:bean>

<beans:bean id=”requestCacheFilter” class=”org.springframework.security.web.savedrequest.RequestCacheAwareFilter” />

<beans:bean id=”servletApiFilter” class=”org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter” />

<beans:bean id=”anonFilter” class=”org.springframework.security.web.authentication.AnonymousAuthenticationFilter” >
<beans:property name=”key” value=”SomeUniqueKeyForThisApplication” />
<beans:property name=”userAttribute” value=”anonymousUser,ROLE_ANONYMOUS” />
</beans:bean>

<beans:bean id=”sessionMgmtFilter” class=”org.springframework.security.web.session.SessionManagementFilter” >
<beans:constructor-arg ref=”securityContextRepository” />
</beans:bean>

<beans:bean id=”rememberMeServices” class=”org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices”>
<beans:property name=”key” value=”HEAR_ME_ROAR”/>
<beans:property name=”userDetailsService” ref=”userManager”/>
</beans:bean>

<beans:bean id=”rememberMeAuthenticationFilter” class=”org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter”>
<beans:property name=”rememberMeServices” ref=”rememberMeServices”/>
<beans:property name=”authenticationManager” ref=”authenticationManager”/>
</beans:bean>

<beans:bean id=”rememberMeAuthenticationProvider” class=”org.springframework.security.authentication.RememberMeAuthenticationProvider”>
<beans:property name=”key” value=”HEAR_ME_ROAR”/>
</beans:bean>

<beans:bean id=”filterSecurityInterceptor” class=”org.springframework.security.web.access.intercept.FilterSecurityInterceptor”>
<beans:property name=”securityMetadataSource”>
<filter-security-metadata-source>
<intercept-url pattern=”/” access=”IS_AUTHENTICATED_ANONYMOUSLY” />
<intercept-url pattern=”/transactions**” access=”ROLE_USER”/>
<intercept-url pattern=”/categories**” access=”ROLE_USER”/>
<intercept-url pattern=”/change-password**” access=”ROLE_USER”/>
<intercept-url pattern=”/invite-friend**” access=”ROLE_USER”/>
<intercept-url pattern=”/ajax/json/**” access=”ROLE_USER”/>
</filter-security-metadata-source>
</beans:property>
<beans:property name=”authenticationManager” ref=”authenticationManager” />
<beans:property name=”accessDecisionManager” ref=”accessDecisionManager” />
</beans:bean>

<beans:bean id=”accessDecisionManager” class=”org.springframework.security.access.vote.AffirmativeBased”>
<beans:property name=”decisionVoters”>
<beans:list>
<beans:bean class=”org.springframework.security.access.vote.RoleVoter”/>
<beans:bean class=”org.springframework.security.access.vote.AuthenticatedVoter”/>
</beans:list>
</beans:property>
</beans:bean>

<beans:bean id=”webPrivilegeEvaluator” class=”org.springframework.security.web.access.DefaultWebInvocationPrivilegeEvaluator”>
<beans:constructor-arg ref=”filterSecurityInterceptor” />
</beans:bean>

<authentication-manager alias=”authenticationManager”>
<authentication-provider user-service-ref=”userManager”>
<password-encoder hash=”md5">
<salt-source user-property=”dateCreated”/>
</password-encoder>
</authentication-provider>
<authentication-provider ref=”rememberMeAuthenticationProvider”/>
</authentication-manager>

<beans:bean id=”exceptionTranslator” class=”com.tostring.sample.security.MyExceptionTranslationFilter”>
<beans:property name=”authenticationEntryPoint”>
<beans:bean class=”org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint”>
<beans:property name=”loginFormUrl” value=”/”/>
</beans:bean>
</beans:property>
<beans:property name=”accessDeniedHandler”>
<beans:bean class=”com.tostring.sample.security.MyAccessDeniedHandler”/>
</beans:property>
</beans:bean>
[/sourcecode]
When you have completed server-side implementation, please proceed on to client-side implementation.

--

--