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.