Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

I'm trying to change JHipster so it uses a JSON object for authentication instead of form parameters. I've managed to make this work for its JWT authentication mechanism. Now I'd like to do it for other authentication options.

Is there an easy way to change Spring Security's default security configuration to allow this? Here's what 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'd like to send the following as JSON instead of form parameters:

{username: "admin", password: "admin", rememberMe: true}
like image 909
Matt Raible Avatar asked Feb 28 '16 19:02

Matt Raible


People also ask

How do you pass a JSON object as a parameter in Java?

Either use an export mapping to create a JSON string that you can pass to the Java action and then create a JSON object again from that string or just pass a root object to the Java and then in Java retrieve all the attached objects over the references to that root object.

Can Spring be configured with a JSON file?

Our Spring application will support both JSON as well as XML. It will even support XML request with JSON response and vice versa.

How pass JSON object in post request in Spring boot?

Send JSON Data in POST Spring provides a straightforward way to send JSON data via POST requests. The built-in @RequestBody annotation can automatically deserialize the JSON data encapsulated in the request body into a particular model object. In general, we don't have to parse the request body ourselves.


1 Answers

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);
            }
        }
    }
}
like image 70
Angelo Fuchs Avatar answered Sep 21 '22 19:09

Angelo Fuchs