Performing an AJAX login with Spring Security 3.0
search cancel

Performing an AJAX login with Spring Security 3.0

book

Article ID: 341485

calendar_today

Updated On:

Products

VMware Spring Runtime

Issue/Introduction

Many applications are using Javascript and AJAX calls to provide a more interactive experience to their users. This article explains how an application can be configured to make use of those technologies in combination with Spring Security to authenticate its users.

Environment

Spring Framework 3.0
Spring Security 2.0
Spring Security 3.0
Spring Framework 2.5

Resolution

To provide users an enhanced login experience through Spring Security, Javascript, and AJAX, the configuration on both the server and the client needs to be adjusted. These adjustments are performed as enhancements to the normal Spring Security login mechanisms, so normal form-based login should continue to work side-by-side with the enhanced login process.

Modifications on the Server Side

Note: These adjustments need to be made to a Spring MVC based application.

Install and set up Spring Security
To install and set up Spring Security:
  1. Add the Spring Security dependencies.

    If using Maven or Ant for dependency management, add these dependencies. The first two dependencies, spring-security-web and spring-security-config, are required. The last dependencies, spring-security-taglibs, is optional but recommended when using JSP pages:

    <dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-web</artifactId>
    <version>3.0.5.RELEASE</version>
    <type>jar</type>
    <scope>compile</scope>
    </dependency>
    <dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-config</artifactId>
    <version>3.0.5.RELEASE</version>
    <type>jar</type>
    <scope>compile</scope>
    </dependency>
    <dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-taglibs</artifactId>
    <version>3.0.5.RELEASE</version>
    <type>jar</type>
    <scope>compile</scope>
    </dependency>

  2. Edit the web.xml file.

    Add the Spring Security filters by adding this filter definition. Generally, this results in the addition of the filter to the beginning of the list of filter chains (add it first in web.xml). It is recommended that Spring Security be added to the beginning of the filter chain so that subsequent filters are protected by Spring Security.

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

Configure Spring Security

To configure Spring Security:
  1. Add a Spring bean configuration file to your project. This contains the Spring Security configuration.
  2. At the top of the newly added Spring bean configuration file, add:

    <http>
    <form-login>
    <logout />
    <remember-me />
    <intercept-url pattern="/resources/**" filters="none" />
    <intercept-url pattern="/" access="IS_AUTHENTICATED_ANONYMOUSLY" />
    <intercept-url pattern="/secure" access="ROLE_USER" />
    <intercept-url pattern="/login" access="IS_AUTHENTICATED_ANONYMOUSLY" />
    </http>

    </form-login>
    This instructs Spring Security to configure itself with the standard form-based login and support for “Remember Me”. In addition, the configuration adds a few <intercept-url/> tags. Each of these tags are used to define the access required for the given URL. The configuration above defines these URLs:

    • /resources/** contains static files such as images, CSS and Javascript. This is not processed by Spring Security.
    • / and /login are the landing page and the login page. Both of these pages are processed by Spring Security, but do not require any access privileges so that unauthenticated users can access these resources.
    • /secure is the "protected" resource. In this Sample Application there is only one "protected" resource. However, in a more full featured application there are many resources, likely specified with wild cards. This URL is also processed by Spring Security and requires the ROLE_USER privilege.

      Note: It is necessary to customize the above <INTERCEPT-URL></INTERCEPT-URL>tags to fit the requirements of each individual application. The ones listed above are basic examples specific to the Sample Application.

  3. In the Spring Security configuration file after the <http/> tag, add:

    <authentication-manager alias="authenticationManager">
    <authentication-provider>
    <user-service>
    <user name="frank" password="1234" authorities="ROLE_USER" />
    </user-service>
    </authentication-provider>
    </authentication-manager>


    This instructs Spring Security to create a simple in-memory authentication manager that is used to authenticate users. The in-memory service contains one user, frank, with a password of 1234.

    Note: While this in-memory solution is sufficient for the attached example, it is not a recommended approach for a production deployment. In a production scenario, it is better to substitute a authentication manager that is backed by JDBC or LDAP.
Active Spring Security Configuration and Validation
To configure and validate the active Spring Security configuration:
  1. Locate the configuration file that is loaded as the root application context for your application. This is the Spring bean configuration file or Spring bean Java class that is loaded by the ContextLoaderListener (configured in web.xml).

  2. Edit the root application context so that it imports the previously created Spring Security configuration file.

    If the root application context is defined in XML, the import looks like this:

    <import resource="security.xml" />

    If the root application context is defined in a Java class, the import looks like this:

    @ImportResource("security.xml")

  3. At this point, Spring Security should now be completely configured with standard form-based authentication. Before continuing with the rest of the installation, it is critical to test that everything has been correctly configured to this point.

    This can be done by deploying the application and attempting to access a secured resource. This should result in the web browser being redirected to a simple login form. Enter the login frank and the password 1234. If the login completes successfully and the browser is redirected to the secured resource, then the application is functioning properly.

    If there are an exceptions, revisit the previous sections of this article and check for mistakes.

Creating and validating a Security Controller
To create and validate a Security Controller:
  1. Create a new controller called AjaxLoginController. This controller handles displaying the login form as an HTML fragment and processing the AJAX POST request when the form is submitted.

    The class should look like:

    @Controller
    @RequestMapping("/login")
    public class AjaxLoginController {
    @Autowired
    @Qualifier("authenticationManager")
    AuthenticationManager authenticationManager;

    @Autowired
    SecurityContextRepository repository;

    @Autowired
    RememberMeServices rememberMeServices;

    @RequestMapping(method=RequestMethod.GET)
    public void login() {}

    @RequestMapping(method=RequestMethod.POST)
    @ResponseBody
    public String performLogin(
    @RequestParam("j_username") String username,
    @RequestParam("j_password") String password,
    HttpServletRequest request, HttpServletResponse response)
    {
    UsernamePasswordAuthenticationToken token =
    new UsernamePasswordAuthenticationToken(username, password);
    try {
    Authentication auth = authenticationManager.authenticate(token);
    SecurityContextHolder.getContext().setAuthentication(auth);
    repository.saveContext(SecurityContextHolder.getContext(), request, response);
    rememberMeServices.loginSuccess(request, response, auth);
    return "{\"status\": true}";
    } catch (BadCredentialsException ex) {
    return "{\"status\": false, \"error\": \"Bad Credentials\"}";
    }
    }
    }

    Notes:

    • The class is annotated with @RequestMapping(“/login”). This informs Spring that you want requests /login to be routed to this controller. It is important that this URL is listed in a <intercept-url /> <INTERCEPT-URL></INTERCEPT-URL>tag in the Spring Security configuration and that the <INTERCEPT-URL></INTERCEPT-URL>tag is configured to allow anonymous access to the URL. Otherwise unauthenticated users cannot view the login form.
    • The Spring Security resources are autowired into the class AuthenticationManager, SecurityContextRepository, and RememberMeServices. These resources are used by the AjaxLoginController to perform the actual login process.
    • The login function is called in response to a GET request (that is, a user requests the login form). This does nothing and proceeds to display the view. The view then returns the login form as an HTML fragment (not a complete HTML page) so that the HTML fragment can be received by Javascript and inserted into an already existing HTML document, perhaps with a Javascript dialog box.
    • The performLogin function is called in response to a POST request (that is, a user login attempt). This function uses the injected services to authenticate the user. If the user authenticates successfully then the authentication object is saved and passed to the remember-me service. A simple JSON string is returned as the response.

  2. Add the controller to the application's Spring MVC configuration. Typically this means ensuring that the controller is located in a package that will be searched by component scanning (that is, <context:component-scan />). However, depending on the application's configuration, it may be necessary to manually define a bean for the controller (for example, when an application does not use component scanning).

  3. Test the availability of the AjaxLoginController, both GET and POST.

    To validate a GET request, simply open a web browser and navigate to http://localhost:8080/login. This should present the login form HTML fragment (it may look a strange as this is not a complete HTML page).

    If the HTML fragment is not displayed and instead the default Spring Security login form is displayed, check the Spring Security configuration to ensure that access to the /login URL is available to unauthenticated users (that is, access is set to IS_AUTHENTICATED_ANONYMOUSLY).

    To validate a POST request, use a tool like cURL to POST a request to http://localhost:8080/login. This command should execute a POST request and return the JSON response. Validate that the JSON response is successful.

    curl -d "j_username=frank" -d "j_password=1234" http://localhost:8080/appName/login

    When you validate the controller, proceed to configuring the client.


Client Configuration

The configuration required on the client side is performed in HTML and Javascript. The HTML portion consists of some basic standard HTML forms. The Javascript code is dependent on the specific Javascript library (or lack of a library) that an application chooses to employ. As such, this section focuses on the tasks that are required to configure the client side, rather than the actual implementation. For more concrete details, see the Sample Application, which uses HTML & jQuery.
  1. Define a landing page. This is where the unauthenticated user first navigates and where he or she will click on the login link. In the Sample Application, this is the / url.

  2. Define a login form. This is displayed when the user clicks on the login link. This should be just the HTML fragment for the form. In most cases the login form would be displayed in a “dialog” box. The exact method used to display this would depend on the application and the Javascript library used by it.

    In the Sample Application, this is the /login url and it is displayed in a jQuery dialog box.

  3. Write the Javascript code to process the user's login attempt.

    This process is further broken down into these steps:

    1. When a user clicks on the login link, use Javascript to send a GET request to the AjaxLoginController's login method. This should return the login form as an HTML fragment, which can easily be placed in a

      tag or inside a “dialog” box.

    2. When the user enters their credentials into the form and clicks the login button, gather the form data and use Javascript to send a POST request to the AjaxLoginController's performLogin method. The response from this method is a JSON message that indicates if the user has authenticated successfully.

    3. Parse the JSON response to determine if the login attempt was successful. If the login failed, display the reason to the user. The reason is included in the JSON response as the error property. If the login is successful, close the dialog and alert the user that they are now logged into the application.

      Note: As long as both sides agree on the format, it is entirely possible to customize the JSON data that is exchanged between the AjaxLoginController's performLogin method and the Javascript login process.

  4. Logout can be achieved by directing the user towards the /j_spring_security_logout URL. This can be done with an tag or by using Javascript to send a GET request to the same URL.

Sample Application

A sample application is attached to this article (2002229_SpringSecutiryAjaxDemo.zip). The application can be built with Maven and can be executed with the command mvn tomcat:run.

This deploys the sample application to the URL http://localhost:8080/ajax-login/. Accessing this URL displays the landing page of the application. Clicking the Standard Login link follows the workflow of a standard Spring Security login. Clicking the Ajax Login link follows the workflow of the enhanced Spring Security login.

The sample application is developed using Spring MVC, Spring Security and jQuery. For more information on the process documented above, see the attached sample application.


Additional Information

Note: The Spring Framework and Spring Security are flexible and allow you to configure tasks to meet the your unique needs. The solution presented in this article is one possible solution, which meets the needs described in the Purpose section. As your needs may differ, you may need to adjust the configuration and/or code presented in this article to fit your needs exactly.

If you need additional assistance with the configuration in this article or adjusting it to fit your needs, start a thread on the Spring Security Forum.



Spring Security 3.0 を使用した AJAX ログインを実行する

Attachments

2002229_SpringSecurityAjaxDemo.zip get_app