e42.uk Circle Device

 

Quick Reference

Spring Security Upgrade

Spring Security 3 to Spring Security 4

If you are using Spring Web MVG and have implemented a custom AuthenticationProvider for usernames and password authentication (like I did) you may be finding that it is not called when you upgrade to Spring Security 4. After loading the sample and building a minimal project (and spending too long doing so) I found that the issue lay with the form itself. Spring Security from version 2 upwards (maybe earlier too) had used the following form:

<h1>Please Login</h1>
<form method="POST" action="<c:url value='j_spring_security_check' />">
<label for="j_username">Login Name</label>
<input type="text" name="j_username" /><br/>
<label for="j_password">Password</label>
<input type="password" name="j_password" /><br/>
<button type="submit">Login</button>
</form>

Now, I know it is not so attractive but it does the job.

The form in Spring Security 4 has changed, in two ways:

  • It includes a CSRF field to foil cross site request attempts
  • The field names have changed

CSRF Protection

You may receive an error like this:

HTTP Error 403

Expected CSRF token not found. Has your session expired?

The CSRF protection is not new (it was introduced in Spring Security 3.2.0) but it is now enabled by default and so you will have to make some changes to your configuration and your application. For the login form it is fairly easy:

<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>

It is likely you will need to alter some other parts of your application so that CSRF protection works correctly, alternatively you could disable CSRF protection in your Spring Security configuration file. Below is a snippet from my testing XML configuration:

<http auto-config="false" use-expressions="true">
	<intercept-url pattern="/testing" access="isAuthenticated()" />
	<intercept-url pattern="/login" access="permitAll" />
	<form-login login-page="/login" always-use-default-target="false" 
		default-target-url="/home" 
		authentication-failure-url="/login?failed=true" />
	<csrf disabled="true" />
</http>

If you use Java configuration you should be able to do something like this but please read reference 1 in more detail:

@EnableWebSecurity
@Configuration
public class WebSecurityConfig extends
   WebSecurityConfigurerAdapter {

  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http.csrf().disable();
  }
}

Field Names

To make the whole thing work you will also need to change your post destination and field names in your custom login form. Naturally the automatically generated one will work fine. These are explained in section 6 of the migration manual (though it is not too clear, I recommend looking at the JIRA ticket).

<h1>Please Login</h1>
<form method="POST" action="<c:url value='login' />">
<label for="j_username">Login Name</label>
<input type="text" name="username" /><br/>
<label for="j_password">Password</label>
<input type="password" name="password" /><br/>
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
<button type="submit">Login</button>
</form>

And that is it, your login form should work now but as I said CSRF may need more work.

References

Quick Links: Techie Stuff | General | Personal | Quick Reference