Saturday, August 23, 2008

Spring Security - Using custom Authentication Processing Filter

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".

That's pretty much all you need to do for enabling spring security to your existing web application. I will try to explain about doing the method security using ACL's and configuring the view using spring security tags in another post.

32 comments:

Anonymous said...

Nice. Very helpful. Thanks for posting this!

Anonymous said...

Thanks for a great example of a custom authentication manager!

Jason said...

It's really helpful but I have an understanding problem. If I try to setup a solution like yours I will get the error that no authentication-provider is defined in my xml-file. Did you leave the part out? Because that would be interesting for me.

Teja said...

Nothing was missed. If you can give me the exact error you are facing, I will try to help you solve the problem

Taras Matyashovsky said...

Hello Teja,
Thanks for good article.
I'm trying to implement authentication without form login. I tried to implement my own EntryPoint, AuthenticationProvider, etc., but it is not working. Have You ever tried to implement something similar? Will be very grateful for any useful links or examples. Thanks once more.

Anonymous said...

Teja,
I am getting error for UserManagerService and Role can you tell me the class implementation or it is a spring security class

Teja said...

UserManagerService is a custom service which will fetch the user and roles by username and password. Role is my cusom object which finally endedup being a GrantedAutority which is a spring impl.

Rishabh said...

http://static.springframework.org/spring-security/site/apidocs/org/springframework/security/ui/AbstractProcessingFilter.html is also helpful in understanding the http security.

Anonymous said...

hello teja,

i try to implement my own custom authentication Manager based on your example code, but faceing this problem:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name '_authenticationManager': Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: No authentication providers were found in the application context

??? i have listed the authentication Manager the way you shown. Or is it a namespace problem and my bean-tag is not recognized by the security app? thanks for your help and time...

Anonymous said...

hi teja,

i am getting springcontextholder object null and authentication null. can you help me out!

Taras Petrytsyn said...

Hi Teja, I use your example but have loop redirect starting my application, have you any idea what the problem is?

sid said...

Nice straightforward example and very helpful. Thanks!

manojsaxena said...

While I have been able to customize the JasperServer to use existing iBatis/Struts infrastructure and integrate authentication using existing app, there is one thorn.

How can I change the login page to accept another field? Say I want user to enter Domain in addition to username and password. And use the three to authenticate and eventually show reports. I have been able to write my custom Dao that validates jasper user from my DB, but how do I get new attribute - domain to reach my Dao, so that it can be used to authenticate the user?
While customizing the login page to accept additional fields is straightforward - you just need to modify login.jsp and make it your login page - making the new values reach \'some Java handler\' is an issue.

Appreciate inputs on this.

Login filter said...

Nicely covered all the details

Thanks
SN

Anonymous said...

Hi all,

I wonder what is the implementation for org.springframework.security.ui.webapp.AuthenticationProcessingFilter?

I tried this example in my project using Spring security 3 and the server complains on during start up as the class cannot be found? Does anyone know what I am missing?

Thanks

raju said...

Hi Teja,

This example is very nice.Could you please post rest of the implementaion calless also.Thanks in Advance.

Regards,
Raju k

raju said...

I am very new to this kind of stuff.Please help to post all the classes you implemented.

Thanks,
Raju k

Anonymous said...

I tried this code but spring is complaining about list of providers.
Property 'providers' threw exception; nested exception is java.lang.IllegalArgumentException: A list of AuthenticationProviders is required

how can i fix this..tnx

shanthi said...

Hi Teja can we make the custom authentication to use user id and password instead of user name... we have that requirement in our applciation please help me

Edwin Dhondt said...

Hi,

Is it possible to show an example using a pre-authentication scenario, where a user is already authenticated by another system (e.g. SiteMinder) and we only want to use Spring security to get the authentication details from the http request header ?

Thanks,
EDH

Anonymous said...

The problem with this post is the same problem with a lot of documentation and probably the issue your colleagues were referring to when they complained about the spring documentation. You don't explain how all these pieces work in unison to achieve the goal. In fact, you don't even explain what the specific goal of the code is. You just show us each individual piece and it's up to us to infer the rest. It's like looking at a picture with a magnifying glass. You can see each little piece clearly, but you can't see the big picture without lots of mental gymnastics. I appreciate the post and I'm not trying to be harsh. It's just hard and time consuming to properly relay complex information.

aperez said...
This comment has been removed by the author.
aperez said...
This comment has been removed by the author.
aperez said...

To solution the error:
java.lang.IllegalArgumentException: No authentication providers were found in the application context.

You should add the tag &ltcustom-authentication-provider&gt in the bean definition myAuthenticationManager.

Anonymous said...

Could your page be any slimmer?

99198782672 said...

Hi

Can somebody tell me how to use spring security for any OS(Linux/Windows)authentication? Please reply asap.

Thanks,
AP

Innocent said...

Very very nice article on spring security.
I would be very grateful if you can provide Tag definitions as well.

like -

what do u mean by "access" attribute in above tab etc.

Else every thing is great

sandeep said...

very nice article. To read more on spring security, you can refer to these link
Spring Security 3 – Form Login and Logout Tutorial
Spring Security 3 – MVC integration Tutorial

Sandeep Kumar said...

To read in detail, browse this link Spring Security login example using database

Jonathan Bispo said...

How can I include another field for authentication, for example j_some_id?

Thanks.

Ankur said...

Hi Tejas,

Thanks for sharing this helpful example.
I am beginner in Spring Security.

Here my question is if we want to use different parameter than "j_username" and "j_password". How can we do that?

I need to implement security for Spring REST web service.
Please advise.

Ankur said...

Hi Tejas ,I am using cleartrust for pre authentication and is overriding getPreAuthenticatedCredentials method.If the headers are null as is the case for the first time then user is redirected to login page where he can enter the credentials. however the problem I have is it loops thru this method atleast 10 times before actually continuing with the chain.This actually slows down the performance.

Can you suggest what needs to be done to fix this.