Tuesday, September 02, 2008

Spring Security - Using custom Authentication Processing Filter

from Teja Kantamneni

Recently I got a chance working with Spring security, formerly known as Acegi Security for spring. While working with the framework, I heard comments from friends and colleagues saying that spring security lacks proper documentation. So thought of sharing a little knowledge. By the way, this is first ever blog posting and kindly excuse me and let me know any errors and improvements.

Spring security offers a simple configuration based security for your web applications helping you secure your web application with out littering your business logic with any security code. It provides securing URL's based on the Role (Authorities), securing your business methods based on the ACL's.

The first step in hooking up the spring security to your web application is by specifying the DelegatingFilterProxy in your web.xml.

  1. <!--Spring security filter-->

  2. <filter>

  3. <filter-name>springSecurityFilterChain</filter-name>

  4. <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>

  5. </filter

  6. <filter-mapping>

  7. <filter-name>springSecurityFilterChain</filter-name>

  8. <url-pattern>/*</url-pattern>

  9. <dispatcher>REQUEST</dispatcher>

  10. <dispatcher>INCLUDE</dispatcher>

  11. <dispatcher>FORWARD</dispatcher>

  12. </filter-mapping>

  13. <!--End spring security filter-->



If you want to externalize all of your security related configuration into a separate file, you can do so and add that to your context location param.
  1. <context-param>

  2. <param-name>contextConfigLocation</param-name>

  3. <param-value>

  4. /WEB-INF/beans.xml , /WEB-INF/springSecurity.xml </param-value>

  5. </context-param>



Now comes the part of security configuration for your application, Adding the URL security patterns is pretty simple and straight forward. Add all the URL patterns which you want to secure and add the wild card pattern at the end. You need to have some default principal and role even for non logged in users as you need to give access to pages like log in, register and forgot password kind of functionality even to non logged in users.

I tried to add comments to pretty much every element which I am using here.
As an example I added just a wild card intercept url which make every page of my application secure. You need to exclude different urls based on the roles.



  1. <security:http entry-point-ref="myAuthenticationEntryPoint" session-fixation-protection="newSession" >

  2. <!--add any of your cusotom url patterns to protect-->
  3. <security:intercept-url pattern="/login/**" access="ROLE_ANONYMOUS"/>
  4. <security:intercept-url pattern="/register/**" access="ROLE_ANONYMOUS"/>

  5. <security:intercept-url pattern="/**" access="ROLE_USER"/>

  6. <security:logout logout-success-url="/home.htm"/>

  7. <security:anonymous username="guest" granted-authority="ROLE_ANONYMOUS"/>

  8. </security:http>

  9. <!--name of my authenticationManager is authenticationManager-->

  10. <security:authentication-manager alias="authenticationManager"/>

  11. <!--Cutom login filter which replaces the default AUTHENTICATION_PROCESSING_FILTER -->

  12. <bean id="customizedFormLoginFilter" class="org.springframework.security.ui.webapp.AuthenticationProcessingFilter" >

  13. <security:custom-filter position="AUTHENTICATION_PROCESSING_FILTER"/><!--replace the default one-->

  14. <property name="defaultTargetUrl" value="/main.htm"/><!--After a successful login, the user will be taken to this page-->

  15. <property name="authenticationFailureUrl" value="/home.htm?error=true" /><!--Authentication failed? take him to error page-->

  16. <property name="authenticationManager" ref="myAuthenticationManager"/> <!--Here it is the custom authenticationManager, login magic goes here -->

  17. <property name="allowSessionCreation" value="true" /> <!--Allow the application to create sessions-->

  18. </bean>

  19. <!--My custom auth manager-->

  20. <bean id="myAuthenticationManager" class="com.teja.security.CustomAuthunticationManager" />

  21. <!-- Automatically receives AuthenticationEvent messages -->

  22. <bean id="loggerListener" class="org.springframework.security.event.authentication.LoggerListener"/>

  23. <!--My authuntication entry point, can be replaced easily if we are doing custom commence of invalid auths.-->

  24. <bean id="myAuthenticationEntryPoint"

  25. class="com.teja.security.CustomAuthenticationEntryPoint" >

  26. <property name="loginFormUrl" value="/home.htm"/>

  27. </bean>


Following is my custom implementation of AuthenticationEntryPoint, which currently is not doing any thing except leveraging the commence to its super class which is the spring implementation of AuthenticationProcessingFilterEntryPoint. I hooked it to add any custom logic.

  1. public class CustomAuthenticationEntryPoint extends AuthenticationProcessingFilterEntryPoint {

  2. private static final Log logger = LogFactory.getLog(CustomAuthenticationEntryPoint.class);



  3. @Override

  4. public void commence(ServletRequest request, ServletResponse response, AuthenticationException authException) throws IOException, ServletException {

  5. super.commence(request, response, authException);

  6. }

  7. }


This is my custom authentication manager which actually does the custom login of the user. It will throw an BadCredentialsException in case of invalid credentials or thorws a AuthenticationServiceException in case of a service error (Database error, SQL error or any other error).

  1. public class CustomAuthunticationManager implements AuthenticationManager {

  2. @Autowired

  3. UserManagerService userManagerService;

  4. public Authentication authenticate(Authentication authentication) throws AuthenticationException {

  5. if(StringUtils.isBlank((String) authentication.getPrincipal()) || StringUtils.isBlank((String) authentication.getCredentials())){

  6. throw new BadCredentialsException("Invalid username/password");

  7. }

  8. User user = null;

  9. GrantedAuthority[] grantedAuthorities = null;

  10. try{

  11. user = userManagerService.getUser((String) authentication.getPrincipal(), (String) authentication.getCredentials());

  12. }

  13. catch(InvalidCredentialsException ex){

  14. throw new BadCredentialsException(ex.getMessage());

  15. }

  16. catch(Exception e){

  17. throw new AuthenticationServiceException("Currently we are unable to process your request. Kindly try again later.");

  18. }

  19. if (user != null) {

  20. List<Role> roles = user.getAssociatedRoles();

  21. grantedAuthorities = new GrantedAuthority[roles.size()];

  22. for (int i = 0; i &lt; roles.size(); i++) {

  23. Role role = roles.get(i);

  24. GrantedAuthority authority = new GrantedAuthorityImpl(role.getRoleCode());

  25. grantedAuthorities[i] = authority;

  26. }

  27. }

  28. else{

  29. throw new BadCredentialsException("Invalid username/password");

  30. }

  31. return new UsernamePasswordAuthenticationToken(user, authentication.getCredentials(), grantedAuthorities);

  32. }

  33. }



At the client side (jsp), the simple configuration you need to do is post the request to"/j_spring_security_check" with parameters "j_username" and "j_password".

No comments: