How to make Spring Security accept JSON instead of form parameters?

I am trying to modify JHipster, so it uses a JSON object for authentication instead of form parameters. I was able to complete this work for the JWT authentication mechanism. Now I would like to do this for other authentication options.

Is there an easy way to change Spring's default security configuration for security? Here JHipster uses now:

.and() .rememberMe() .rememberMeServices(rememberMeServices) .rememberMeParameter("remember-me") .key(env.getProperty("jhipster.security.rememberme.key")) .and() .formLogin() .loginProcessingUrl("/api/authentication") .successHandler(ajaxAuthenticationSuccessHandler) .failureHandler(ajaxAuthenticationFailureHandler) .usernameParameter("j_username") .passwordParameter("j_password") .permitAll() 

I would like to send the following as JSON instead of form parameters:

 {username: "admin", password: "admin", rememberMe: true} 
+6
source share
2 answers

I have done this. The solution is not complicated, but I did the trick by creating a custom security filter, mainly based on UserNamePasswordAuthenticationFilter.

In fact, you should override the tryAuthentication method. Just overriding getPassword and getUsername may not be enaugh, since you want to read the request body, and you must do this for both parameters at once (unless you are creating a multi-page HttpServletRequest shell)

The solution should be like this:

  public class JsonUserNameAuthenticationFilter extends UsernamePasswordAuthenticationFilter{ //[...] public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { if (postOnly && !request.getMethod().equals("POST")) { throw new AuthenticationServiceException( "Authentication method not supported: " + request.getMethod()); } UsernamePasswordAuthenticationToken authRequest = this.getUserNamePasswordAuthenticationToken(request); // Allow subclasses to set the "details" property setDetails(request, authRequest); return this.getAuthenticationManager().authenticate(authRequest); } //[...] protected UserNamePasswordAuthenticationToken(HttpServletRequest request){ // here read the request body and retrieve the params to create a UserNamePasswordAuthenticationToken. You may use jackson of whatever you like most } //[...] } 

Then you must configure it. I always use xml based configuration for such complex configurations,

  <beans:bean id="jsonUserNamePasswordAuthenticationFilter" class="xxx.yyy.JsonUserNamePasswordAuthenticationFilter"> <beans:property name="authenticationFailureHandler> <beans:bean class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler"> <!-- set the failure url to a controller request mapping returning failure response body. it must be NOT secured --> </beans:bean> </beans:property> <beans:property name="authenticationManager" ref="mainAuthenticationManager" /> <beans:property name="authenticationSuccessHandler" > <beans:bean class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler"> <!-- set the success url to a controller request mapping returning success response body. it must be secured --> </beans:bean> </beans:property> </beans:bean> <security:authentication-manager id="mainAuthenticationManager"> <security:authentication-provider ref="yourProvider" /> </security:authentication-manager> <security:http pattern="/login-error" security="none"/> <security:http pattern="/logout" security="none"/> <security:http pattern="/secured-pattern/**" auto-config='false' use-expressions="false" authentication-manager-ref="mainAuthenticationManager" create-session="never" entry-point-ref="serviceAccessDeniedHandler"> <security:intercept-url pattern="/secured-pattern/**" access="ROLE_REQUIRED" /> <security:custom-filter ref="jsonUserNamePasswordAuthenticationFilter" position="FORM_LOGIN_FILTER" /> <security:access-denied-handler ref="serviceAccessDeniedHandler"/> <security:csrf disabled="true"/> </security:http> 

You can create some additional objects as an access denial handler, but this is the easiest part of the thing

0
source

I just need something very similar, so I wrote this.

It uses Spring Security 4.2, WebSecurityConfigurationAdapter. There, instead of using ...formLogin()... I wrote my own Configurer, which uses JSON when it is available, and the default is Form if not (because I need both functions).

I copied all the things that should have been present (but that didn’t bother me) from org.springframework.security.config.annotation.web.configurers.FormLoginConfigurer and org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter where the source code is located. and the documentation really helped me.

It is possible that you will have to copy other functions, but in principle this should be done.

A filter that actually parses JSON, after all. The sample code is one class, so you can copy it 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); } } } } 
0
source

All Articles