I just needed something very similar, so I wrote it.
This uses Spring Security 4.2, WebSecurityConfigurationAdapter. There instead of using ...formLogin()...
I wrote an own Configurer that uses JSON when available and defaults to Form if not (because I need both functionalities).
I copied all things that needed to be present (but I didn't care about) from org.springframework.security.config.annotation.web.configurers.FormLoginConfigurer
and org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter
where the source code and documentation greatly helped me.
It is well possible that you will need to copy over other functions as well, but it should do in principle.
The Filter that actually parses the JSON is in the end. The code sample is one class so can be copied over directly.
/** WebSecurityConfig that allows authentication with a JSON Post request */
@Configuration
@EnableWebSecurity(debug = false)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
// resources go here
@Override
protected void configure(HttpSecurity http) throws Exception {
// here you will need to configure paths, authentication provider, etc.
// initially this was http.formLogin().loginPage...
http.apply(new JSONLoginConfigurer<HttpSecurity>()
.loginPage("/authenticate")
.successHandler(new SimpleUrlAuthenticationSuccessHandler("/dashboard"))
.permitAll());
}
/** This is the a configurer that forces the JSONAuthenticationFilter.
* based on org.springframework.security.config.annotation.web.configurers.FormLoginConfigurer
*/
private class JSONLoginConfigurer<H extends HttpSecurityBuilder<H>> extends
AbstractAuthenticationFilterConfigurer<H, JSONLoginConfigurer<H>, UsernamePasswordAuthenticationFilter> {
public JSONLoginConfigurer() {
super(new JSONAuthenticationFilter(), null);
}
@Override
public JSONLoginConfigurer<H> loginPage(String loginPage) {
return super.loginPage(loginPage);
}
@Override
protected RequestMatcher createLoginProcessingUrlMatcher(String loginProcessingUrl) {
return new AntPathRequestMatcher(loginProcessingUrl, "POST");
}
}
/** This is the filter that actually handles the json
*/
private class JSONAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
protected String obtainPassword(JsonObject obj) {
return obj.getString(getPasswordParameter());
}
protected String obtainUsername(JsonObject obj) {
return obj.getString(getUsernameParameter());
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException {
if (!"application/json".equals(request.getContentType())) {
// be aware that objtainPassword and Username in UsernamePasswordAuthenticationFilter
// have a different method signature
return super.attemptAuthentication(request, response);
}
try (BufferedReader reader = request.getReader()) {
//json transformation using javax.json.Json
JsonObject obj = Json.createReader(reader).readObject();
String username = obtainUsername(obj);
String password = obtainPassword(obj);
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
username, password);
return this.getAuthenticationManager().authenticate(authRequest);
} catch (IOException ex) {
throw new AuthenticationServiceException("Parsing Request failed", ex);
}
}
}
}