Springboot + Angular: attemptAuthentication request parameters null

Viewed 38

I'm making an Angular application as the frontend with a Springboot backend. I have set up SpringSecurity to handle logins and if I try it using Postman everything works, but when I try it using Angulars login, the "request.getParameter" calls always return me "null". I have tried changing it in several ways but the result is always the same.

The successful login return information about the user and a token.

Here is the Java part:

@Configuration
@EnableWebSecurity
@ComponentScan
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Lazy
    @Autowired
    CurrentUserDetailsServiceImpl userDetailsService;
    @Autowired
    TokenAuthenticationService tokenAuthService;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .headers().cacheControl().disable();
        http
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        http
                .headers().xssProtection();
        http
                .exceptionHandling().and()
                .anonymous().and()
                .servletApi().and()
                .csrf().disable()
                .authorizeRequests()
                .antMatchers(HttpMethod.POST, "/auth/**").permitAll()
                .anyRequest().authenticated().and()
                .addFilterBefore(
                        new LoginFilter("/auth/login", authenticationManager(), tokenAuthService),
                        UsernamePasswordAuthenticationFilter.class)
                // Custom Token based authentication based on the header previously given to the
                // client
                .addFilterBefore(new JWTFilter(tokenAuthService), UsernamePasswordAuthenticationFilter.class);
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth
                .userDetailsService(userDetailsService)
                .passwordEncoder(new BCryptPasswordEncoder());
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        web
                .ignoring()
                // Allow anonymous resource requests
                .antMatchers("/favicon.ico");
    }
}

The LoginFilter does quite some things, but this is the minimum:

public class LoginFilter extends AbstractAuthenticationProcessingFilter {
    private TokenAuthenticationService tokenAuthenticationService;

    public LoginFilter(String urlMapping, AuthenticationManager authenticationManager, TokenAuthenticationService tokenAuthenticationService) {
        super(new AntPathRequestMatcher(urlMapping));
        setAuthenticationManager(authenticationManager);
        this.tokenAuthenticationService = tokenAuthenticationService;
    }

    @Override
    @Transactional
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
            throws AuthenticationException, IOException, ServletException {
        String username = request.getParameter("username");
        String password = request.getParameter("password");

        System.out.println("USERNAME: " + username + " - PASSWORD: " + password);
    }

    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
            Authentication authentication) throws IOException, ServletException {
        CurrentUser loggedUser = (CurrentUser) authentication.getPrincipal();
        ...
    }
}

Finally, the Angular form does some validations and calls to a service:

@Injectable({
    providedIn: 'root'
})
export class LoginService {
    apiUrl: string = environment.API_URL;
    constructor(private http: HttpClient) {}
    login(data: LoginInterface): Observable<LoginResult> {
        const formData: string =
            'username=' + data.username + '&password=' + data.password;

        const httpOptions = {
            headers: new HttpHeaders({
                'Content-Type': 'application/x-www-form-urlencoded'
            })
        };

        return this.http.post<LoginResult>(
            this.apiUrl + '/auth/login',
            formData,
            httpOptions
        );
    }
}

What am I doing wrong? Why is it working with Postman?

Thanks!

Edit: As requested, here are the console details for the call in Chrome

Headers:

Chrome headers

Body:

Chrome body data

Postman headers:

Postman headers

Postman data:

Postman data

And here is Eclipse showing the null value that arrived:

Eclipse showing null value

Thanks!

1 Answers

You can probably do it by using HttpParams as the request body:

login(data: LoginInterface): Observable<LoginResult> {
  const params = new HttpParams({
    fromObject: {
      username: data.username,
      password: data.password,
    },
  });
  return this.http.post<LoginResult>(this.apiUrl + '/auth/login', params, {
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded',
    },
  });
}
Related