Spring security OAuth2 accept JSON

前端 未结 6 1843
深忆病人
深忆病人 2020-12-09 00:56

I am starting with Spring OAuth2. I would like to send the username and password to /oauth/token endpoint in POST body in application/json format.

curl -X PO         


        
6条回答
  •  萌比男神i
    2020-12-09 01:14

    Hello based on @Jakub Kopřiva answer I have made improvements in order to create working integration tests. Just so you know, Catalina RequestFacade throws an error in Junit and MockHttpServletRequest, used by mockmvc, does not contain a field "request" as I expect in the filter (therefore throwning NoSuchFieldException when using getDeclaredField()): Field f = request.getClass().getDeclaredField("request");
    This is why I used "Rest Assured". However at this point I ran into another issue which is that for whatever reason the content-type from 'application/json' is overwritten into 'application/json; charset=utf8' even though I use MediaType.APPLICATION_JSON_VALUE. However, the condition looks for something like 'application/json;charset=UTF-8' which lies behind MediaType.APPLICATION_JSON_UTF8_VALUE, and in conclusion this will always be false.
    Therefore I behaved as I used to do when I coded in PHP and I have normalized the strings (all characters are lowercase, no spaces). After this the integration test finally passes.

    ---- JsonToUrlEncodedAuthenticationFilter.java

    package com.example.springdemo.configs;
    
    import com.fasterxml.jackson.databind.ObjectMapper;
    import lombok.SneakyThrows;
    import org.apache.catalina.connector.Request;
    import org.springframework.core.annotation.Order;
    import org.springframework.http.MediaType;
    import org.springframework.security.web.savedrequest.Enumerator;
    import org.springframework.stereotype.Component;
    
    import javax.servlet.*;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletRequestWrapper;
    import java.io.BufferedReader;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    import java.lang.reflect.Field;
    import java.util.*;
    import java.util.stream.Collectors;
    
    @Component
    @Order(value = Integer.MIN_VALUE)
    
    public class JsonToUrlEncodedAuthenticationFilter implements Filter {
    
        private final ObjectMapper mapper;
    
        public JsonToUrlEncodedAuthenticationFilter(ObjectMapper mapper) {
            this.mapper = mapper;
        }
    
        @Override
        public void init(FilterConfig filterConfig) {
        }
    
        @Override
        @SneakyThrows
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
            Field f = request.getClass().getDeclaredField("request");
            f.setAccessible(true);
            Request realRequest = (Request) f.get(request);
    
           //Request content type without spaces (inner spaces matter)
           //trim deletes spaces only at the beginning and at the end of the string
            String contentType = realRequest.getContentType().toLowerCase().chars()
                    .mapToObj(c -> String.valueOf((char) c))
                    .filter(x->!x.equals(" "))
                    .collect(Collectors.joining());
    
            if ((contentType.equals(MediaType.APPLICATION_JSON_UTF8_VALUE.toLowerCase())||
                    contentType.equals(MediaType.APPLICATION_JSON_VALUE.toLowerCase()))
                            && Objects.equals((realRequest).getServletPath(), "/oauth/token")) {
    
                InputStream is = realRequest.getInputStream();
                try (BufferedReader br = new BufferedReader(new InputStreamReader(is), 16384)) {
                    String json = br.lines()
                            .collect(Collectors.joining(System.lineSeparator()));
                    HashMap result = mapper.readValue(json, HashMap.class);
                    HashMap r = new HashMap<>();
    
                    for (String key : result.keySet()) {
                        String[] val = new String[1];
                        val[0] = result.get(key);
                        r.put(key, val);
                    }
                    String[] val = new String[1];
                    val[0] = (realRequest).getMethod();
                    r.put("_method", val);
    
                    HttpServletRequest s = new MyServletRequestWrapper(((HttpServletRequest) request), r);
                    chain.doFilter(s, response);
                }
    
            } else {
                chain.doFilter(request, response);
            }
        }
    
        @Override
        public void destroy() {
        }
    
        class MyServletRequestWrapper extends HttpServletRequestWrapper {
            private final HashMap params;
    
            MyServletRequestWrapper(HttpServletRequest request, HashMap params) {
                super(request);
                this.params = params;
            }
    
            @Override
            public String getParameter(String name) {
                if (this.params.containsKey(name)) {
                    return this.params.get(name)[0];
                }
                return "";
            }
    
            @Override
            public Map getParameterMap() {
                return this.params;
            }
    
            @Override
            public Enumeration getParameterNames() {
                return new Enumerator<>(params.keySet());
            }
    
            @Override
            public String[] getParameterValues(String name) {
                return params.get(name);
            }
        }
    

    Here is the repo with the integration test

提交回复
热议问题