1

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?

0 Answers0