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

17 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. great article on spring security

    ReplyDelete
  16. 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