I have a Spring web application that's currently using HTTP Basic authentication, using a username and password, passed in the header. I want to add additional fields to be passed in with the authentication request, including an (optional) security answer and a device id. I want to switch to passing this information inside of the request body, such that I can easily extend UsernamePasswordAuthenticationFilter.
I have this in WebSecurityConfig:
@Override
protected void configure(HttpSecurity http) throws Exception
{
http
.authorizeRequests()
.antMatchers(permitAllUrls)
.permitAll();
http
.addFilterAt(new MobileUserAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
http.logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout")).logoutSuccessUrl("/");
http
.authorizeRequests()
.antMatchers("/**")
.hasRole("USER")
.and()
.csrf()
.requireCsrfProtectionMatcher(new NoAntPathRequestMatcher(nonCsrfUrls))
.and()
.addFilterAfter(new CsrfGrantingFilter(), SessionManagementFilter.class);
}
MobileUserAuthenticationFilter
is (currently) an empty class that extends UsernamePasswordAuthenticationFilter
. Sending a request to the login endpoint successfully calls the attemptAuthentication
method of UsernamePasswordAuthenticationFilter
. However, the request body being received appears to be empty.
This is the internals of UsernamePasswordAuthenticationFilter
(unnecessary code omitted for clarity):
public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "username";
public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "password";
private String usernameParameter = SPRING_SECURITY_FORM_USERNAME_KEY;
private String passwordParameter = SPRING_SECURITY_FORM_PASSWORD_KEY;
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response) throws AuthenticationException {
if (postOnly && !request.getMethod().equals("POST")) {
throw new AuthenticationServiceException(
"Authentication method not supported: " + request.getMethod());
}
String username = obtainUsername(request);
String password = obtainPassword(request);
if (username == null) {
username = "";
}
if (password == null) {
password = "";
}
username = username.trim();
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
username, password);
// Allow subclasses to set the "details" property
setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
}
protected String obtainUsername(HttpServletRequest request) {
return request.getParameter(usernameParameter);
}
protected void setDetails(HttpServletRequest request,
UsernamePasswordAuthenticationToken authRequest) {
authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
}
Stepping through this reveals that the request
argument of attemptAuthentication
contains no parameters (it's calling request.getParameter("username")
). Adding the debug expression request.getParameterNames()
returns an empty enumeration.
My outgoing request is:
{
debugId: 1,
uri: 'http: //localhost:8080/login',
method: 'POST',
headers: {
host: 'localhost: 8080',
accept: 'application/json',
'content-type': 'application/json',
'content-length': 49
},
body: '"{\\"username\\":\\"3\\",\\"password\\":\\"password1\\"}"'
}
But username and password are both null when reaching the filter. What am I missing, is there something I need to add to allow the filter to accept a request body?