Pages

February 9, 2011

Integrating Spring Security 3 with Extjs

It has a lot of differences and developments in Spring Security 3 versus 2, for this I thought to add this article..

The following tutorial will talk about how integrating Spring security 3 with extjs login page.

Tools:
   - Netbeans 6.9.1
   - Spring framework 3.0.2
   - Spring security 3.0.4
   - Extjs 3.2.1
   - Tomcat 6.0.24

Step1: 
   Create a java web application with netbeans, and add the spring web MVC framework dependency.

Step2: 
   In the webcontent create a js file named login.js

Ext.onReady(function(){
    Ext.QuickTips.init();
    var login = new Ext.FormPanel({
        labelWidth:80,
        url:'j_spring_security_check',
        frame:true,
        title:'Please Login',
        defaultType:'textfield',
        width:300,
        height:150,
        monitorValid:true,
        items:[{
            fieldLabel:'Username',
            name:'j_username',
            allowBlank:false
        },{
            fieldLabel:'Password',

            name:'j_password',
            inputType:'password',
            allowBlank:false
        }],

        buttons:[{

            text:'Login',
            formBind: true,
            handler:function(){
            login.getForm().submit({

                method:'POST',
                success:function(){
                Ext.Msg.alert('Status', 'Login Successful!', function(btn, text){

                    if (btn == 'ok'){
                        window.location = 'main.htm';
                    }
                });

            },

            failure:function(form, action){
                if(action.failureType == 'server'){
                    obj = Ext.util.JSON.decode(action.response.responseText);

                    Ext.Msg.alert('Login Failed!', obj.errors.reason);
                }else{
                    Ext.Msg.alert('Warning!', 'Authentication server is unreachable : ' + action.response.responseText);

                }
                login.getForm().reset();
            }
            });
        }
        }]
    });
    login.render('login');
});

Step3:
  If you try to execute the application now with a basic applicationContext-security.xml file, the user will be successfully authenticated, but is not going to be redirected. So, we will need to create a filter.

public class RecrutementAuthProcessingFilter extends UsernamePasswordAuthenticationFilter {

    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, Authentication authResult) throws IOException, ServletException {
        SavedRequestAwareAuthenticationSuccessHandler srh = new SavedRequestAwareAuthenticationSuccessHandler();
        this.setAuthenticationSuccessHandler(srh);
        srh.setRedirectStrategy(new RedirectStrategy() {
            @Override
            public void sendRedirect(HttpServletRequest httpServletRequest,
                    HttpServletResponse httpServletResponse, String s) throws IOException {
                    //do nothing, no redirect
            }
        });
        super.successfulAuthentication(request, response, authResult);
        HttpServletResponseWrapper responseWrapper = new HttpServletResponseWrapper(response);
        Writer out = responseWrapper.getWriter();
        out.write("{success:true}");
        out.close();
    }

    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
        super.unsuccessfulAuthentication(request, response, failed);
        HttpServletResponseWrapper responseWrapper = new HttpServletResponseWrapper(response);
        Writer out = responseWrapper.getWriter();
        out.write("{success:false}");
        out.close();
    }
}

UsernamePasswordAuthenticationFilter(AuthenticationProcessingFilter prior to Spring Security 3.0) processes an authentication form submission. Called login forms must present two parameters to this filter: a username (j_username) and password (j_password).
The applicationContext-security will be:

<?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-3.0.xsd
                        http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.0.xsd">


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

    <http auto-config='false' entry-point-ref="authenticationEntryPoint">
        <intercept-url pattern="/index*" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
        <intercept-url pattern="/js/**" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
        <intercept-url pattern="/main*" access="ROLE_USER" />
        <custom-filter position="FORM_LOGIN_FILTER" ref="authenticationFilter" />
    </http>

    <beans:bean id="authenticationFilter" class="com.blog.dev.security.RecrutementAuthProcessingFilter">                
        <beans:property name="authenticationManager" ref="authenticationManager" />
    </beans:bean>

    <beans:bean id="authenticationEntryPoint" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
        <beans:property name="loginFormUrl" value="/index.jsp" />
    </beans:bean>

    <!--
    Usernames/Passwords are
        moez/moez
        test/test        
    -->
    <authentication-manager alias="authenticationManager" >
        <authentication-provider>
            <password-encoder hash="md5"/>
            <user-service>
                <user name="moez" password="f69471ca2c42621b8b8e731b2b4446e4" authorities="ROLE_SUPERVISOR, ROLE_USER, ROLE_TELLER" />
                <user name="test" password="098f6bcd4621d373cade4e832627b4f6" authorities="ROLE_USER" />                
            </user-service>
        </authentication-provider>
    </authentication-manager>

</beans:beans>



Step4: 
  Now you need to create a jsp page 'main.jsp' in order to call it when you are successfully authenticated.
  Since we use Spring MVC, we will need to create a controller.

public class MainController extends MultiActionController{
 public ModelAndView main(HttpServletRequest request,
   HttpServletResponse response) throws Exception {
  return new ModelAndView("main.jsp");
 }
}


Step5: 
   In the dispatcher-servlet.xml add the following line:

<bean name="/*.htm" class="com.blog.dev.controller.MainController"/>


Step6: 
   In the web.xml add the following code:


    <filter>
        <filter-name>springSecurityFilterChain</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>springSecurityFilterChain</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>



Step7: 
   Deploy your application under tomcat, you can test it with test/test.



Of course, you can add a link disconnect in the main page. So you need:
1- add in the configuration spring file and under the tag http the following tag:

<logout/>

2- add in main.jsp the link disconnect:

<a href="j_spring_security_logout">Logout</a>

Source code (Netbeans project) until http://spring-extjs.googlecode.com/svn/trunk/
  

New features for spring 3.0 here

19 comments:

  1. Excellent work!!!

    Can you provide something on Session Timeout using Spring Security 3.0?

    ReplyDelete
  2. Of course, just redirect the user to an appropriate URL by adding into <http> the following tag: <session-management invalid-session-url="/yourSessionTimeoutPage.htm" />

    ReplyDelete
  3. when i use tag in context-security.xml:

    i receive the following error:

    Context initialization failed
    org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: Security namespace does not support decoration of element [http]
    Offending resource: ServletContext resource [/WEB-INF/hrp-applicationContext-security.xml]


    please see my applicationContaxt-security.xml file below:

    ReplyDelete
  4. Ok, try to use the following namespaces:
    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-3.0.xsd
    http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.0.xsd">

    ReplyDelete
  5. getting the following error using above namespaces:


    Nested in org.springframework.beans.factory.xml.XmlBeanDefinitionStoreException: Line 15 in XML document from ServletContext resource [/WEB-INF/hrp-applicationContext-security.xml] is invalid; nested exception is org.xml.sax.SAXParseException: cvc-elt.1: Cannot find the declaration of element 'beans'.:
    org.xml.sax.SAXParseException: cvc-elt.1: Cannot find the declaration of element 'beans'.
    at org.apache.xerces.parsers.DOMParser.parse(DOMParser.java:267)



    unable to copy xml contents here. so, may i have you email id, so i can send you my applicationContaxt-security.xml???

    ReplyDelete
  6. You can use the following link to post your xml content:

    http://www.accessify.com/tools-and-wizards/developer-tools/quick-escape/default.php

    But, you can start from my context-security.xml

    ReplyDelete
  7. <!-- Start of Session Timeout configuration -->



    <bean id="sessionMgmtFilter" class="org.springframework.security.web.session.SessionManagementFilter" >
    <constructor-arg ref="securityContextRepository" />
    <sec:http>
    <sec:session-management invalid-session-url="/actions/login" />
    </sec:http>
    </bean>

    <bean id="concurrencyFilter"
    class="org.springframework.security.web.session.ConcurrentSessionFilter">
    <property name="sessionRegistry" ref="sessionRegistry" />
    <property name="expiredUrl" value="/actions/login" />
    </bean>

    <bean id="sas"
    class="org.springframework.security.web.authentication.session.ConcurrentSessionControlStrategy">
    <constructor-arg name="sessionRegistry" ref="sessionRegistry" />
    <property name="maximumSessions" value="1" />
    </bean>

    <bean id="sessionRegistry" class="org.springframework.security.core.session.SessionRegistryImpl" />



    <!-- End of Session Timeout configuration -->

    ReplyDelete
  8. Not yet... I am using the below namespaces:


    <beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:util="http://www.springframework.org/schema/util"
    xmlns:sec="http://www.springframework.org/schema/security"
    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-3.0.xsd
    http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.0.xsd
    http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">

    ReplyDelete
  9. try this:

    <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-3.0.xsd
    http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.0.xsd">

    <beans:bean id="sessionMgmtFilter" class="org.springframework.security.web.session.SessionManagementFilter" >
    <beans:constructor-arg ref="securityContextRepository" />
    <http>
    <session-management invalid-session-url="/actions/login" />
    </http>
    </beans:bean>

    <beans:bean id="concurrencyFilter"
    class="org.springframework.security.web.session.ConcurrentSessionFilter">
    <beans:property name="sessionRegistry" ref="sessionRegistry" />
    <beans:property name="expiredUrl" value="/actions/login" />
    </beans:bean>

    <beans:bean id="sas"
    class="org.springframework.security.web.authentication.session.ConcurrentSessionControlStrategy">
    <beans:constructor-arg name="sessionRegistry" ref="sessionRegistry" />
    <beans:property name="maximumSessions" value="1" />
    </beans:bean>

    <beans:bean id="sessionRegistry" class="org.springframework.security.core.session.SessionRegistryImpl" />

    ReplyDelete
  10. This comment has been removed by the author.

    ReplyDelete
  11. after removing <http> element from applicationContext-security.xml, session timeout work but does not redirect to invalidSessionUrl.

    Tried overriding SessionManagementFilter also...

    what might be the problem?

    ReplyDelete
  12. Please use my applicationContext-security.xml posted in the blog and add the tag session-managemente. It will be ok.

    ReplyDelete
  13. Hi, Can you post the eclipse project?
    Browsing the will be more understandable.
    Thanks for the article.

    ReplyDelete
  14. Netbeans project until http://spring-extjs.googlecode.com/svn/trunk/

    ReplyDelete
  15. This is a nice article..
    Its very easy to understand ..
    And this article is using to learn something about it..

    c#, dot.net, php tutorial

    Thanks a lot..!

    ReplyDelete
  16. thanks for tutorial can you please show us how we can do it in Grails and what will be the directory structure, i have tried it but it says on login request Failed to load source for: http://localhost:8080/j_spring_security_check.
    https://groups.google.com/forum/#!topic/grails/HzyrRp726DM
    you can take this for reference

    ReplyDelete
  17. great article on spring security

    ReplyDelete
  18. when login failed, failure method of form submit can't pop up the error message even if i changed the code this code in RecrutementAuthProcessingFilter.java;


    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed)
    throws IOException, ServletException {
    super.unsuccessfulAuthentication(request, response, failed);
    HttpServletResponseWrapper responseWrapper = new HttpServletResponseWrapper(response);
    Writer out = responseWrapper.getWriter();
    // out.write("{success:false}");
    out.write("{success:false,errors: { reason: 'Login failed. Try again.' }}");
    out.close();
    }

    ReplyDelete