Using Spring Security with ExtJS for Siteminder/LDAP authentication and authorization

Overview

I’ve been merrily creating my ExtJS 4 application when the inevitable came that we must secure the application. Of course it doesn’t stop at authentication, especially in a client-side where nothing should be assumed secure; the users of course want to show/hide, enable/disable things using role-based security.  In a previous life, I used Spring Security to do this sort of thing for Java applications, so I wondered why I couldn’t just do the same for ExtJS.  This article shares a complete example to do exactly that.  Here I will show you how you can use your existing IT infrastructure to secure your application (in my case LDAP and Siteminder) and how to integrate Spring Security 3.0 with ExtJS 4. Lastly, I would like to thank Dmitry and Rajesh for their contributions on this topic.

Authentication

The first step is to step up authentication to your application. The first approach I’ll show is using LDAP authentication (authn) followed by Siteminder authn. To set-up LDAP authn, you have to extend your HTML/JS/CSS3 app to a Java EE Spring application. There are already several articles on the Web on how to do this and most of today’s IDEs can go this for your automatically. Once you have created your Java EE app, you need to configure your web.xml. The basic gist of it is that you will be using a servlet filter to intercept URLs that invoke the application (see DelegatingFilterProxy). Here is an example one:

<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
    <display-name>Your App</display-name>

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>WEB-INF/applicationContext.xml</param-value>
    </context-param>

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <filter>
        <filter-name>charsetFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
        <init-param>
            <param-name>forceEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter> 
    <filter-mapping>
        <filter-name>charsetFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

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

    <servlet>
        <servlet-name>app</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>app</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>
</web-app>

In your Spring applicationContext.xml you’ll want to create and import another spring context configuration that contains security specifics. In the example that follows, I show how you can do LDAP authentication:

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

    <security:http auto-config='true' use-expressions="true">
        <security:intercept-url pattern="/**" access="isAuthenticated()" />
        <!--access="hasRole('ROLE_LDAP_GROUP')"/>-->
        <security:http-basic/>
    </security:http>

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

    <security:ldap-server id="ldapServer" port="636" root="o=company"
                          url="ldaps://myldap.company.com"/>

    <security:authentication-manager alias="company">
        <security:ldap-authentication-provider
                group-search-base="ou=yourgroupou,ou=groups,o=company"
                group-role-attribute="cn"
                user-search-base="ou=people,o=company"
                user-search-filter="uid={0}"
                />

    </security:authentication-manager>
</beans>

Here I do an anonymous bind over the LDAPS port to authenticate based on the users’ credentials they provide (be sure you use SSL and take the time to set up your JVM to support it with our internal certs). The intercept-url includes a pattern (I did everything with “/**”) and the type of access (I did “isAuthenticated()” and defer authorization till later). You’ll see I have commented out access using hasRole(‘ROLE_LDAP_GROUP’) which can be used to limit the users able to open the application using an LDAP group, but I didn’t want to do that. In the next example, I show how you can do the same thing with Siteminder:

<beans xmlns="http://www.springframework.org/schema/beans"
       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.3.xsd">

    <bean id="http403EntryPoint" class="org.springframework.security.web.authentication.Http403ForbiddenEntryPoint">
    </bean>

    <sec:http auto-config="false" entry-point-ref="http403EntryPoint">
        <!--<sec:intercept-url pattern="/**"
                           access="isAuthenticated()"/>-->
        <sec:custom-filter position="PRE_AUTH_FILTER" ref="siteminderFilter" />
    </sec:http>

    <bean id="siteminderFilter" class=
            "org.springframework.security.web.authentication.preauth.RequestHeaderAuthenticationFilter">
        <property name="principalRequestHeader" value="SM_USER"/>
        <property name="authenticationManager" ref="authenticationManager"/>
    </bean>

    <bean id="preauthAuthProvider"
          class="org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider">
        <property name="preAuthenticatedUserDetailsService">
            <bean id="userDetailsServiceWrapper"
                  class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper">
                <property name="userDetailsService" ref="ldapUserDetailsService"/>
            </bean>
        </property>
    </bean>

    <sec:authentication-manager alias="authenticationManager">
        <sec:authentication-provider ref="preauthAuthProvider"/>
    </sec:authentication-manager>

    <!-- Example using LDAP -->
    <sec:ldap-server id="ldapServer" port="636" root="o=company"
                          url="ldaps://dir.company.com"/> 
    <sec:ldap-user-service id="ldapUserDetailsService" server-ref="ldapServer"
                           group-search-base="ou=yourgroupou,ou=groups,o=company"
                           role-prefix="ROLE_" group-role-attribute="cn"
                           user-search-base="ou=people,o=company" user-search-filter="uid={0}"/>
    <security:ldap-server id="ldapServer" port="636" root="o=company"
                          url="ldaps://myldap.company.com"/>
</beans>

While this has been all going on, it is necessary to block the Ext JS app until we have confirmed the identity of the user. In Ext JS we can set up an Ajax request to do that and use the success listener to block the viewport from being created until success is achieved. You’ll notice that my Ajax request is a HTTP POST to user/getUser. This is the mechanism I used to determine the principal from the server-side by parsing the JSON returned into the roles array for the application, which I’ll go into in a little bit.

Ext.application({
    name : 'App',
    controllers : [ 
        'dashboard.TabController' ],
    roles : [ ], 
    version : '1.0',
    onFailure : function(title, response, options) {
        Ext.MessageBox.show({
            title : title,
            msg : response,
            width : 400,
            icon : Ext.Msg.WARNING,
            buttons : Ext.Msg.OK
        });
    },
    hasRole : function(role) {
        return Ext.Array.contains(this.roles, role);
    },
    launch : function() {
        App.app = this;

    
        Ext.Ajax.request({
            url : 'user/getUser',
            method : 'post',
            timeout : '30000',
            defaultHeaders : {
                'Content-Type' : 'application/json; charset=utf-8'
            },
            success : function(response, opts) {
                try {
                    var result = Ext
                            .decode(response.responseText);
                    if (result !== null) {
                        var user = result.userName;
                        var perms = result.permissions;
                        for (var i = 0; i < perms.length; i += 1) {
                            if (perms[i].role
                                    .indexOf("ROLE_ADMIN") === 0) {
                                App.app.roles
                                        .push(perms[i].role);
                            }
                        }
                        if (user !== null) {
                            Ext.QuickTips.init();
                            Ext.create('Ext.container.Viewport',
                            {
                               // Do your normal stuff here
                            });
                        } else {
                            App.app
                                    .onFailure(
                                    'Authorization Failed',
                                    'User does not have the correct permissions',
                                    opts);
                        }
                    } else {
                        App.app
                                .onFailure(
                                'Authorization Failed',
                                'Response returned from service was null.',
                                opts);
                    }
                } catch (e) {
                    App.app.onFailure(
                            'User Serivce Failure', e, opts);
                }
            },
            failure : function(response, opts) {
                App.app.onFailure('User Service Failure',
                        response, opts);
            }
        });

    }
});

Now, let’s take a look at the UserController Java class. This is the class that is responsible for determining the user and retrieving his/her roles. What is really nice about Spring is that I can use their MVC controller mechanism and annotations to intercept URLs and do some sort of logic with a simple POJO as follows:

@Controller
@RequestMapping(value = "/user")
public class UserController {
    private static Logger log = Logger.getLogger(UserController.class);


    @RequestMapping(value = "/getUser", method = RequestMethod.POST)
    @ResponseStatus(HttpStatus.CREATED)
    public @ResponseBody String getUser() throws IllegalStateException {
        try {
            User user = new User(this.getUserName());


            Collection perms = this.getUserPermissions();
            user.setPermissions(perms);

            Gson gson = new Gson();
            String result = gson.toJson(user, User.class);
            log.info("Sending user object: " + result);

            return result;
        } catch (Exception e) {
            log.error(e.getMessage());
            throw new IllegalStateException(e);
        }
    }


    private String getUserName() {
        Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        String username = "";


        if (principal instanceof UserDetails) {
            username = ((UserDetails)principal).getUsername();
        } else {
            username = principal.toString();
        }
        return username;
    }


    private Collection getUserPermissions() {
        Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        Collection perms = null;


        if (principal instanceof UserDetails) {
           perms = ((UserDetails)principal).getAuthorities();
        }


        return perms;
    }
}

Notice I use the Spring’s SecurityContextHolder to retrieve the principal as well as his/her authorities (i.e., roles). I populate those into a User bean, convert that to JSON and return the result to the client. Once this information is provided to the client, we can manage roles effectively on the client side.

Authorization

Now that we’ve authenticated the user, collected their roles and parsed them into the Ext.application’s roles array, how can we use that in the application to protect components. The answer is simple really, you just add some conditional logic for the components config for either “hidden” or “disabled”. For instance, I had a settings Tab on a tab panel that only administrators should see. I was able to accomplish that with this bit of JavaScript code:

{
    xtype : 'settingsTab',
    //Example of role-based hiding
    hidden : !App.app.hasRole('ROLE_UI.ADMIN')
}


// function to support above
hasRole : function(role) {
    return Ext.Array.contains(this.roles, role);
}

As you can see from above, I simply have hasRole return true or false based on whether the user has a given role established at the time we invoked the application and made the AJAX call to getUser() from the previous section. If the user is in the LDAP group “UI.ADMIN” (NOTE: Spring automatically prepends ROLE_), they are granted access to see the tab.

Now you might be saying, well I could write a greasemonkey script and hack the JavaScript so I could see the tab and indeed you can, but the approach I demonstrate is only used to change the behavior of the application, not the functionality. Let’s say you applied this to a “save” button instead, that save button is going to call some server-side functionality to, well, save. You should definitely check on the server-side that the user can execute save functions (in the example below, updateDriver). It would be folly to expect a client-side application to verify this. Again, Spring Security to the rescue with a simple annotation, @PreAuthorize, to the server-side method to check the role of the user as follows:

@RequestMapping(method = RequestMethod.PUT)
    @ResponseStatus(HttpStatus.NO_CONTENT)
    @PreAuthorize("hasAuthority('ROLE_UI.ADMIN')")
    public void updateDriver(@RequestBody String json) throws IllegalStateException {
        try {
            // Using Gson to Serialize/Unserialize as example in case post-processing required
            Gson gson = new Gson();
            HosDriverEvent hde = gson.fromJson(json, HosDriverEvent.class);
            restTemplate.put(VEHICLE_DETAILS_SAVE_URL, gson.toJson(hde));
        } catch (Exception e) {
            log.error(e.getMessage());
            throw new IllegalStateException(e);
        }
    }

Even before this method gets invoked, if the user does not have that role, they will get a 403 exception and thus preventing them from accessing the save operation.

Data Security

Another aspect of security for your application to consider should be the data itself. For instance, you may show a grid of data but one user is authorized to see more information than another. This is controlled with identity propagation to the underlying services. This means that with Siteminder, the users secure, time-based identity will be propagated in the HTTP header downstream to the underlying services. The services can validate the users authorities by parsing the header for the principal and querying data appropriately based on that user’s identity. Thus, the data returned will be unique for that user. It’s important to make sure your underlying services do this sort of checking.

Another approach is to use a secure token service (STS) and SAML assertions. This is especially popular in SOA’s. More information can be found in Thomas Erl’s seminal work on the topic.

Conclusion

Security is essential to all business-based Web applications and so it needs to be carefully thought out. Here I hope I’ve provided useful information on how you can implement your customer’s security requirements for an Ext JS application using Spring Security. You have to assume nothing is ever secure on a client-side application, so you must include a server-side component to ensure your application is secure.

Advertisements

TSSJS Day 3: Spring Framework’s Rod Johnson Keynote

Rod flies in from Paris at 11pm last night.  Let’s see what he has to say being as jet lagged as he must be.  I’m feeling a bit punchy since I lost again at the poker tables last night.

Spring is not prescriptive (i.e., doesn’t tell you how you should code).  The philosophy of Spring is that the developer is always right…I like that!  Spring is putting a lot of emphasis on Grails and SpringRoo.  We then goes on to demo the Spring 3.0 features using Spring Source Tool Suite (STS) doing a SpringRoo project.

SpringRoo comes with a shell built into STS that you can type context-sensitive tab completion for common tasks.  He goes on to set-up context persistence.  He shows how component-scan can rip through your class files and find all the resource annotations continuously. He shows some other mundane things that were basically there from Spring 2 such as autowiring and db connections.

Then he shows how you can create integration tests with SpringRoo automagically…this is nice!  He goes on to talk about how great Spring Integration testing is using SpringJdbcTemplate, which it is if you haven’t checked it out.  He then uses JSR303 validation, which is built-in, to do some basic validation in his entity.  The Roo shell picks up the changes and updates the generated code without running anything.  Roo doesn’t do one-way scaffolding like typical code generation tools but constantly monitors the structure and makes changes to the underlying source better than you!  It’s a code generation tool that is for folks that still like to write code!

He goes on to show how you can use the Roo command line to create more entities and fill them out, including relationships between multiple entities.  It demonstrates how you can do two-way changes from in the code or cmd line…very cool!  Using the shell shows how you can ultimately write a script to build your app in playback mode later.  Additionally, you can use SpringRoo to add dependencies to your Maven POM…what doesn’t it do?

Spring 3.0 adds a RestTemplate to test REST services with JUnit…that should come in handy.  He continued to show how this can be used against the Twitter REST service to pull in tweets related to the conference.

I looked at SpringRoo about a year ago and it seems to have come a long way.  I was especially impressed with the integration in STS.  Something definitely to keep on the radar of things to look at.

TSSJS Day 2: Spring and jBPM

Josh Long gave this topic.  We starts by saying…if you’re writing a huge conditional statement based on states in your application, you can benefit from a Workflow engine.

Process state is described by an UML activity diagram — it’s the voyage not the destination.

BPM is as much a business problem as it is a technical problem.  It’s helpful because it makes reuse from a given way to tackle a particular business problem.  Don’t believe the hype you can take a BP diagram and turn it into the implementation some vendors like you to believe. A techie will be involved and something you should go in knowing.

Workflow engines benefit from multiple steps performed by multiple actors.  State of BPM today focuses on two standards: BPEL 2.0 and BPMN 2.0.  The latter provides an execution behavior to model directly to an implementation…formerly, you could create stuff in BPMN that wasn’t possible BPEL…ouch!

Drools Flow, Grails Flow and jBPM are the relevant open source tools that you should look at.  Note that Drools Flow and jBPM come under jBoss and you probably should use the latter.  Oracle AquaLogic, MS Workflow and TIBCO iProcess are commercial alternatives.

jBPM 3.x is the most widely supported and works well with existing ESBs.  Spring Modules used to provide jBPMTemplate for Spring users but doesn’t work, so avoid it.  jBPM 4.x has integrated Spring support.  It’s also embeddable and integrates with many db’s and architectures.  Great support for human tasks as well as automated one asynchronously. jPDL is a much simpler alternative to BPEL  BPEL 2.0 support is limited in jBPM 4.x.

His demo was very clean but requires XML for configuration.  If you done that sort of stuff in Spring, you’ll have no problem here.  Borrows a lot from how you configure Hibernate in Spring.