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.
Note: These adjustments need to be made to a Spring MVC based application.
<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>
<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
<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>
<authentication-manager alias="authenticationManager">
<authentication-provider>
<user-service>
<user name="frank" password="1234" authorities="ROLE_USER" />
</user-service>
</authentication-provider>
</authentication-manager>
<import resource="security.xml" />
@ImportResource("security.xml")
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.
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
Notes:
@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\"}";
}
}
}
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).
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.
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.
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.
Write the Javascript code to process the user's login attempt.
This process is further broken down into these steps:
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
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.
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.
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.
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.