Thursday, June 05, 2008

Spring Security 2.0 : Different target urls for different user roles

I'm evaluating Spring Security 2.0. The nice thing - it's really easier to have deal with it, rather than Acegi Security. I had an xml config file with 250+ LOC with Acegi ! Something like Rocket Science... but it is very flexible and expendable, it is very big plus.

After all, it is still not so trivial to implement some things which I found very commonly used. But... Spring rocks ! And Spring community rocks even more. I've posted message to Spring forum here.
Thanks Luke Taylor for pointing me to the right direction.

Lets imagine we have the same login form for common user and admin and we want admin to be redirected to some kind admin control panel and common user to /home /profile or something like that. In my case customized AuthenticationProcessingFilter will do the trick. The code looks like this :

package com.mycoolcompany.App.CustomSecurity;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.springframework.security.Authentication;
import org.springframework.security.AuthenticationException;
import org.springframework.security.GrantedAuthority;
import org.springframework.security.providers.
UsernamePasswordAuthenticationToken;
import org.springframework.security.ui.webapp.
AuthenticationProcessingFilter;

public class CustomAuthenticationProcessingFilter
extends AuthenticationProcessingFilter
{
@Override
public Authentication attemptAuthentication(HttpServletRequest request)
throws AuthenticationException {
String username = obtainUsername(request);
String password = obtainPassword(request);

if (username == null) {
username = "";
}

if (password == null) {
password = "";
}

username = username.trim();

UsernamePasswordAuthenticationToken authRequest =
new UsernamePasswordAuthenticationToken(username, password);

// Place the last username attempted into HttpSession for views
HttpSession session = request.getSession(false);

if (session != null || getAllowSessionCreation()) {
request.getSession()
.setAttribute(SPRING_SECURITY_LAST_USERNAME_KEY,
escapeEntities(username));
}

// Allow subclasses to set the "details" property
setDetails(request, authRequest);

//role&URLs stuff
final Authentication auth =
this
.getAuthenticationManager().authenticate(authRequest);
final GrantedAuthority[] grantedAuthorities = auth.getAuthorities();
boolean isAdmin = false;
for(GrantedAuthority grantedAuthority : grantedAuthorities)
{
if("ROLE_SUPERVISOR".equals(grantedAuthority.toString()))
{
isAdmin = true;
break;
}
}
String outcome = null;
if(isAdmin)
{
outcome = "/adminArea";
}
else
{
outcome = "/someOtherUserArea";
}
//actual change of default url for user
this.setDefaultTargetUrl(outcome);

return auth;
}
public static String escapeEntities(String s) {
StringBuffer sb = new StringBuffer();
for (int i=0; i < s.length(); i++) {
char c = s.charAt(i);

if(c == '<') {
sb.append("&lt;");
} else if (c == '>') {
sb.append("&gt;");
} else if (c == '"') {
sb.append("&#034;");
} else if (c == '\'') {
sb.append("&#039;");
} else {
sb.append(c);
}
}

return sb.toString();
}
}
And XML config :

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

<http auto-config="false"
entry-point-ref
="authenticationProcessingFilterEntryPoint">
<intercept-url
pattern
="/secure/extreme/**"
access="ROLE_SUPERVISOR"/>
<intercept-url
pattern
="/someDefaultUrl/**"
access
="ROLE_SUPERVISOR"/>
<intercept-url
pattern="/secure/**"
access
="IS_AUTHENTICATED_REMEMBERED" />
<intercept-url
pattern="/**"
access
="IS_AUTHENTICATED_ANONYMOUSLY" />
<anonymous />
</http>

<!--
Usernames/Passwords are
rod/koala
dianne/emu
scott/wombat
peter/opal
-->
<authentication-provider>
<password-encoder hash="md5"/>
<user-service>
<user name="rod"
password
="a564de63c2d0da68cf47586ee05984d7"
authorities
="ROLE_SUPERVISOR, ROLE_USER, ROLE_TELLER" />
<user name="dianne"
password
="65d15fe9156f9c4bbffd98085992a44e"
authorities
="ROLE_USER,ROLE_TELLER" />
<user name="scott"
password
="2b58af6dddbd072ed27ffc86725d7d3a"
authorities="ROLE_USER" />
<user name="peter"
password
="22b5c9accc6e1ba628cedc63a72d57f8"
authorities
="ROLE_USER" />
</user-service>
</authentication-provider>

<authentication-manager alias='authenticationManagerAlias'/>
<beans:bean id="authenticationProcessingFilterEntryPoint"
class="org.springframework.security.ui.webapp.
AuthenticationProcessingFilterEntryPoint
">
<beans:property
name
="loginFormUrl"
value="/login.jsp"/>
<beans:property
name="forceHttps"
value
="false" />
</beans:bean>
<beans:bean id="myAuthenticationProcessingFilter"
class="com.mycoolcompany.App.CustomSecurity.
CustomAuthenticationProcessingFilter
">
<beans:property
name
="filterProcessesUrl"
value
="/j_spring_security_check" />
<beans:property
name
="defaultTargetUrl"
value="/someDefaultUrl/index.jsp"/>
<beans:property
name="authenticationManager"
ref="authenticationManagerAlias"/>
<custom-filter position="AUTHENTICATION_PROCESSING_FILTER"/>
</beans:bean>
</beans:beans>

The end.