2016-07-02 111 views
3

我從Spring OAuth2開始。我想以application/json格式將用戶名和密碼發送到POST主體中的/ oauth/token端點。Spring安全OAuth2接受JSON

curl -X POST -H "Authorization: Basic YWNtZTphY21lc2VjcmV0" -H "Content-Type: application/json" -d '{ 
"username": "user", 
"password": "password", 
"grant_type": "password" 
}' "http://localhost:9999/api/oauth/token" 

這可能嗎?

你能給我一個建議嗎?

+0

你爲什麼要使用JSON HTTP內容類型在您的要求? OAuth規範(https://tools.ietf.org/html/rfc6749)非常清楚應該在請求中使用哪種內容類型。如果你遵循你的JSON路徑,你的客戶端將被綁定到一個定製的實現,只與你定製的服務器端代碼兼容。您也無法與多個OAuth提供商集成,併爲最終用戶選擇登錄方法,例如:Google,Facebook,Amazon等。 – RZet

回答

5

解決方案(如果正確的話不知道,但它縫它工作):

資源服務器配置:

@Configuration 
public class ServerEndpointsConfiguration extends ResourceServerConfigurerAdapter { 

    @Autowired 
    JsonToUrlEncodedAuthenticationFilter jsonFilter; 

    @Override 
    public void configure(HttpSecurity http) throws Exception { 
     http 
      .addFilterBefore(jsonFilter, ChannelProcessingFilter.class) 
      .csrf().and().httpBasic().disable() 
      .authorizeRequests() 
      .antMatchers("/test").permitAll() 
      .antMatchers("/secured").authenticated(); 
    } 
} 

篩選:

@Component 
@Order(value = Integer.MIN_VALUE) 
public class JsonToUrlEncodedAuthenticationFilter implements Filter { 

    @Override 
    public void init(FilterConfig filterConfig) throws ServletException { 
    } 

    @Override 
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, 
      ServletException { 
     if (Objects.equals(request.getContentType(), "application/json") && Objects.equals(((RequestFacade) request).getServletPath(), "/oauth/token")) { 
      InputStream is = request.getInputStream(); 
      ByteArrayOutputStream buffer = new ByteArrayOutputStream(); 

      int nRead; 
      byte[] data = new byte[16384]; 

      while ((nRead = is.read(data, 0, data.length)) != -1) { 
       buffer.write(data, 0, nRead); 
      } 
      buffer.flush(); 
      byte[] json = buffer.toByteArray(); 

      HashMap<String, String> result = new ObjectMapper().readValue(json, HashMap.class); 
      HashMap<String, String[]> 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] = ((RequestFacade) request).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() { 
    } 
} 

請求包裝:

public class MyServletRequestWrapper extends HttpServletRequestWrapper { 
    private final HashMap<String, String[]> params; 

    public MyServletRequestWrapper(HttpServletRequest request, HashMap<String, String[]> 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<String, String[]> getParameterMap() { 
     return this.params; 
    } 

    @Override 
    public Enumeration<String> getParameterNames() { 
     return new Enumerator<>(params.keySet()); 
    } 

    @Override 
    public String[] getParameterValues(String name) { 
     return params.get(name); 
    } 
} 

授權服務器配置(禁用基本驗證了/的OAuth /令牌端點:

​​
+0

從哪裏可以獲得RequestFacade? –

2

OAuth 2 specification

的客戶端向所述令牌端點的請求通過使用「應用程序/ x WWW的形式進行了urlencoded」

訪問發送
以下參數令牌請求應使用application/x-www-form-urlencoded

在Spring中的安全性,資源所有者密碼憑據格蘭特流量由ResourceOwnerPasswordTokenGranter#getOAuth2Authentication Spring Security的處理:

protected OAuth2Authentication getOAuth2Authentication(AuthorizationRequest clientToken) { 
    Map parameters = clientToken.getAuthorizationParameters(); 
    String username = (String)parameters.get("username"); 
    String password = (String)parameters.get("password"); 
    UsernamePasswordAuthenticationToken userAuth = new UsernamePasswordAuthenticationToken(username, password); 

您可以發送usernamepassword請求參數。

如果您確實需要使用JSON,則有一種解決方法。如您所見,usernamepassword從請求參數中檢索。因此,如果您將它們從JSON主體傳遞到請求參數,它將起作用。

的想法是像如下:

  1. 創建自定義春季安全過濾器。
  2. 在您的自定義過濾器中,創建一個子類HttpRequestWrapper。該類允許您包裝原始請求並從JSON獲取參數。
  3. 在你的HttpRequestWrapper子類,解析您的JSON請求中的身體得到usernamepasswordgrant_type,並把它們與原來的請求參數到一個新的HashMap。然後,覆蓋方法getParameterValues,getParameter,getParameterNamesgetParameterMap以返回來自該新的值HashMap
  4. 將您的包裹請求傳遞到過濾器鏈中。
  5. 在您的Spring Security配置中配置您的自定義過濾器。

希望這可以幫助

0

您也可以修改@的Jakub-kopřiva解決方案來支持HTTP基本身份驗證是OAuth。

資源服務器配置:

@Configuration 
public class ServerEndpointsConfiguration extends ResourceServerConfigurerAdapter { 

    @Autowired 
    JsonToUrlEncodedAuthenticationFilter jsonFilter; 

    @Override 
    public void configure(HttpSecurity http) throws Exception { 
     http 
      .addFilterAfter(jsonFilter, BasicAuthenticationFilter.class) 
      .csrf().disable() 
      .authorizeRequests() 
      .antMatchers("/test").permitAll() 
      .antMatchers("/secured").authenticated(); 
    } 
} 

內部RequestWrapper此類過濾

@Component 
public class JsonToUrlEncodedAuthenticationFilter extends OncePerRequestFilter { 

    @Override 
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { 

     if (Objects.equals(request.getServletPath(), "/oauth/token") && Objects.equals(request.getContentType(), "application/json")) { 

      byte[] json = ByteStreams.toByteArray(request.getInputStream()); 

      Map<String, String> jsonMap = new ObjectMapper().readValue(json, Map.class);; 
      Map<String, String[]> parameters = 
        jsonMap.entrySet().stream() 
          .collect(Collectors.toMap(
            Map.Entry::getKey, 
            e -> new String[]{e.getValue()}) 
          ); 
      HttpServletRequest requestWrapper = new RequestWrapper(request, parameters); 
      filterChain.doFilter(requestWrapper, response); 
     } else { 
      filterChain.doFilter(request, response); 
     } 
    } 


    private class RequestWrapper extends HttpServletRequestWrapper { 

     private final Map<String, String[]> params; 

     RequestWrapper(HttpServletRequest request, Map<String, String[]> 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<String, String[]> getParameterMap() { 
      return this.params; 
     } 

     @Override 
     public Enumeration<String> getParameterNames() { 
      return new Enumerator<>(params.keySet()); 
     } 

     @Override 
     public String[] getParameterValues(String name) { 
      return params.get(name); 
     } 
    } 
} 

,你也需要允許X WWW的形式,進行了urlencoded認證

@Configuration 
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter { 

    ... 

    @Override 
    public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception { 
     oauthServer.allowFormAuthenticationForClients(); 
    } 

    ... 

} 

用這種方法你仍然可以使用基本身份驗證的oauth標記和請求標記與json像這樣:

頁眉:

Authorization: Basic bG9yaXpvbfgzaWNwYQ== 

身體:

{ 
    "grant_type": "password", 
    "username": "admin", 
    "password": "1234" 
} 
相關問題