2017-03-10 100 views
1

我們正在構建一個REST資源服務器(一個Java示例應用程序),我們計劃使用由MITREID Connect項目提供的RFC7662定義的身份傳播機制來保護它。我們測試了配置方法,XML設置以及添加到資源服務器類的基於註釋的設置(請參閱下面的示例代碼)。如何使用Spring Security OAuth2和MITREID Connect Introspect保護資源?

我們的測試顯示Spring Security例程成功初始化,但我們沒有成功通過授權頭觸發持票人令牌傳遞。請求和資源成功執行,但沒有令牌解析和自檢驗證發生。請檢查下面的配置設置和日誌。

歡迎支持隔離組件之間的缺失線(Spring Security,Spring Oauth2和Mitreid Connect Introspect)。

安裝文件:彈簧security.xml文件

<?xml version="1.0" encoding="UTF-8"?> 

http://www.springframework.org/schema/beans/spring-beans-4.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-4.0.xsd http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2.xsd http://www.springframework.org/schema/utilhttp://www.springframework.org/schema/util/spring-util-4.1.xsd「>

<sec:http pattern="/css/**" security="none" /> 
<sec:http pattern="/js/**" security="none" /> 

<sec:http auto-config="true" use-expressions="true" 
    disable-url-rewriting="true" entry-point-ref="oauthAuthenticationEntryPoint" 
    pattern="/rest/service/sample/restService"> 

    <sec:custom-filter before="PRE_AUTH_FILTER" ref="resourceServerFilter" /> 
</sec:http> 

<sec:authentication-manager alias="authenticationManager"> 
</sec:authentication-manager> 

<!-- Begin OAuth2 Introspect configuration --> 


<bean id="oauthAuthenticationEntryPoint" 
    class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint"> 
    <property name="realmName" value="W3IDRealm" /> 
</bean> 

<oauth:resource-server id="resourceServerFilter" 
    token-services-ref="introspectingService" /> 

<bean id="introspectingService" 
    class="org.mitre.oauth2.introspectingfilter.IntrospectingTokenService"> 
    <property name="introspectionConfigurationService" ref="staticIntrospectionConfigurationService"> 
    </property> 
</bean> 

    <!-- <oauth:resource 
    id="protectedResource" 
    access-token-uri="${oidc.tokenEndpointUri}" 
    client-secret="${oidc.clientSecret}" 
    client-id="${oidc.clientId}"></oauth:resource> --> 

<bean 
    class="org.mitre.oauth2.introspectingfilter.service.impl.StaticIntrospectionConfigurationService" 
    id="staticIntrospectionConfigurationService"> 
    <property name="introspectionUrl" value="${oidc.introspectEndpointUri}" /> 
    <property name="clientConfiguration"> 
     <bean class="org.mitre.oauth2.model.RegisteredClient"> 
      <property name="clientId" value="${oidc.clientId}" /> 
      <property name="clientSecret" value="${oidc.clientSecret}" /> 
     </bean> 
    </property> 
    <!-- <property name="introspectionAuthorityGranter"> 
     <bean class="org.mitre.oauth2.introspectingfilter.SimpleIntrospectionAuthorityGranter"> 
      <property name="authorities"> 
       <value>ROLE_API</value> 
      </property> 
     </bean> 
    </property> --> 
</bean> 

resource.java

package com.red.sampleoidcservice; 

import java.util.Map; 

import javax.servlet.http.HttpServletRequest; 
import javax.servlet.http.HttpServletResponse; 

import org.slf4j.Logger; 
import org.slf4j.LoggerFactory; 
import org.springframework.context.annotation.Configuration; 
import org.springframework.security.config.annotation.web.builders.HttpSecurity; 
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; 
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; 
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter; 
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer; 
import org.springframework.stereotype.Controller; 
import org.springframework.web.bind.annotation.RequestMapping; 
import org.springframework.web.bind.annotation.RequestMethod; 
import org.springframework.web.bind.annotation.ResponseBody; 

@Controller 
@EnableWebSecurity 
@Configuration 
@EnableResourceServer 
public class RestController { 

    private static final Logger logger = LoggerFactory.getLogger(RestController.class); 

    @RequestMapping(value = "/restService", method = RequestMethod.POST) 
    public @ResponseBody String restService(HttpServletRequest request, HttpServletResponse respose) { 

     logger.info("Calling rest service"); 

     String requestToString = request.toString(); 

     String headerType = request.getHeader("Content-Type"); 
     String headerAuth = request.getHeader("Authorization"); 

     Map map = request.getParameterMap(); 

     String attributes = request.getAttributeNames().toString(); 

     // String someParam = request.getParameter("someParam"); 

     return "{\"status\":\"OK\"}"; 
    } 

    protected static class ResourceServer extends ResourceServerConfigurerAdapter { 

     @Override 
     public void configure(HttpSecurity http) throws Exception { 
      http.requestMatchers().antMatchers("/rest/service/sample/restService").and().authorizeRequests() 
        .anyRequest().access("#oauth2.hasScope('read')"); 
     } 

     @Override 
     public void configure(ResourceServerSecurityConfigurer resources) throws Exception { 
      resources.resourceId("W3IDRealm"); 
     } 
    } 

} 

post.java

// HTTP POST request 
private void sendPost(String token) throws Exception { 

    try { 

     token = "blablabla"; 

     TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() { 
      public java.security.cert.X509Certificate[] getAcceptedIssuers() { 
       return new X509Certificate[0]; 
      } 

      public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) { 
      } 

      public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) { 
      } 
     } }; 

     SSLContext sc = SSLContext.getInstance("SSL"); 
     sc.init(null, trustAllCerts, new java.security.SecureRandom()); 
     HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); 

     CloseableHttpClient httpClient = HttpClientBuilder.create().build(); 

     SSLConnectionSocketFactory f = new SSLConnectionSocketFactory(sc, new String[] { "TLSv1.2" }, null, 
       org.apache.http.conn.ssl.SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER); 

     httpClient = HttpClients.custom().setSSLSocketFactory(f).build(); 

     HttpPost postRequest = new HttpPost("https://localhost:9444/rest/service/sample/restService"); 

     postRequest.addHeader("Content-Type", "application/x-www-form-urlencoded"); 

     List<NameValuePair> formparams = new ArrayList<NameValuePair>(); 
     formparams.add(new BasicNameValuePair("client_id", clientId)); 
     formparams.add(new BasicNameValuePair("client_secret", clientSecret)); 
     formparams.add(new BasicNameValuePair("token", token)); 

     UrlEncodedFormEntity entity = new UrlEncodedFormEntity(formparams, "utf-8"); 
     postRequest.setEntity(entity); 

     postRequest.setHeader("Authorization", "Bearer " + token + ""); 

     HttpResponse response = httpClient.execute(postRequest, new BasicHttpContext()); 

     int statusCode = response.getStatusLine().getStatusCode(); 

     logger.info("HTTP status code : " + statusCode); 

    } catch (Exception e) { 
     logger.error(e.getMessage()); 
    } 
} 

跟蹤:

INFO : org.springframework.web.servlet.DispatcherServlet - FrameworkServlet 'appServlet': initialization completed in 5872 ms 

DEBUG:org.springframework.web.servlet.DispatcherServlet - 的Servlet 'appServlet' 配置成功 DEBUG:org.springframework.web.context.support.StandardServletEnvironment - 具有最低優先級的搜索添加 [servletConfigInitParams] PropertySource DEBUG:org.springframework.web.context.support.StandardServletEnvironment - 添加[servletContextInitParams]具有最低搜索優先級的PropertySource DEBUG:org.springframework.web.context.support.StandardServletEnvironment - 以最低搜索優先級添加[jndiProperties] PropertySource DEBUG: org.springframework.web.context.support.StandardServletEnvironment - 使用lowe添加[systemProperties] PropertySource st搜索優先級 DEBUG:org.springframework.web.context.support.StandardServletEnvironment - 以最低搜索優先級添加[systemEnvironment] PropertySource DEBUG:org.springframework.web.context.support.StandardServletEnvironment - 使用PropertySources初始化StandardServletEnvironment [servletConfigInitParams,servletContextInitParams ,jndiProperties,systemProperties,systemEnvironment] DEBUG:org.springframework.web.filter.DelegatingFilterProxy - 初始化過濾器'springSecurityFilterChain' DEBUG:org.springframework.beans.factory.support。DefaultListableBeanFactory - 返回單豆 'org.springframework.security.filterChainProxy' DEBUG的緩存實例:org.springframework.web.filter.DelegatingFilterProxy - 過濾器 'springSecurityFilterChain' 配置成功 DEBUG:org.springframework.web.servlet.DispatcherServlet - DispatcherServlet的名稱爲'appServlet'處理[/ rest/service/sample/restService]的POST請求 DEBUG:org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping - 查找path/restService的處理程序方法 DEBUG:org .springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping - 返回處理程序方法[public java.lang.String com.red.sampleoidcservice.RestController.restService(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse )] DEBUG:org.springfram ework.beans.factory.support.DefaultListableBeanFactory - 返回單例bean'restController'的緩存實例 INFO:com.red.sampleoidcservice.RestController - 調用rest服務 DEBUG:org.springframework.web.servlet.mvc.method.annotation。使用[[email protected]12d551] DEBUG:org.springframework.web.servlet.DispatcherServlet - 返回Null ModelAndView,將RequestResponseBodyMethodProcessor - 寫入[{「status」:「OK」}]作爲「text/plain」到名爲'appServlet'的DispatcherServlet:假設HandlerAdapter完成請求處理 DEBUG:org.springframework.web.servlet.DispatcherServlet - 成功完成請求 DEBUG:org.springframework.beans.factory.support.DefaultListableBeanFactory - 返回單例bean的緩存實例'委託應用程序監聽器'

發現的解決方案

配置與註釋

/******************************************************************************* 
* Copyright 2014 The MITRE Corporation 
* and the MIT Kerberos and Internet Trust Consortium 
* 
* Licensed under the Apache License, Version 2.0 (the "License"); 
* you may not use this file except in compliance with the License. 
* You may obtain a copy of the License at 
* 
* http://www.apache.org/licenses/LICENSE-2.0 
* 
* Unless required by applicable law or agreed to in writing, software 
* distributed under the License is distributed on an "AS IS" BASIS, 
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
* See the License for the specific language governing permissions and 
* limitations under the License. 
*******************************************************************************/ 
package com.RED.sampleoidcservice; 

import java.io.IOException; 
import java.security.Principal; 
import java.util.Locale; 
import java.util.Map; 

import javax.servlet.Filter; 
import javax.servlet.FilterChain; 
import javax.servlet.ServletException; 
import javax.servlet.http.HttpServletRequest; 
import javax.servlet.http.HttpServletResponse; 

import org.mitre.oauth2.introspectingfilter.IntrospectingTokenService; 
import org.mitre.oauth2.introspectingfilter.service.impl.StaticIntrospectionConfigurationService; 
import org.mitre.oauth2.model.ClientDetailsEntity.AuthMethod; 
import org.mitre.oauth2.model.RegisteredClient; 
import org.slf4j.Logger; 
import org.slf4j.LoggerFactory; 
import org.springframework.beans.factory.InitializingBean; 
import org.springframework.beans.factory.annotation.Value; 
import org.springframework.context.annotation.ComponentScan; 
import org.springframework.context.annotation.Configuration; 
import org.springframework.http.HttpMethod; 
import org.springframework.security.config.annotation.web.builders.HttpSecurity; 
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; 
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; 
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter; 
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer; 
import org.springframework.security.oauth2.provider.authentication.BearerTokenExtractor; 
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; 
import org.springframework.security.web.util.matcher.RequestMatcher; 
import org.springframework.stereotype.Controller; 
import org.springframework.web.bind.annotation.RequestMapping; 
import org.springframework.web.bind.annotation.RequestMethod; 
import org.springframework.web.bind.annotation.ResponseBody; 
import org.springframework.web.filter.OncePerRequestFilter; 
import org.springframework.web.servlet.ModelAndView; 

@Controller 
@EnableWebSecurity 
@Configuration 
@EnableResourceServer // [2] 
@ComponentScan({ "com.RED.sampleoidcservice" }) 
public class ResourceServer extends ResourceServerConfigurerAdapter { 

    private static final Logger logger = LoggerFactory.getLogger(ResourceServer.class); 

    @Value("${oidc.jwks.keys}") 
    private String jwksString; 

    @Value("${oidc.introspectEndpointUri}") 
    private String introspectURL; 

    @Value("${oidc.clientId}") 
    private String clientId; 

    @Value("${oidc.clientSecret}") 
    private String clientSecret; 

    IntrospectingTokenService introspectTokenService = new IntrospectingTokenService(); 

    @RequestMapping(value = "/", method = RequestMethod.GET) 
    public ModelAndView modelHome(Locale locale, Principal p) { 

     logger.info("Initializing service resource"); 

     ModelAndView model = new ModelAndView("/home.tiles"); 
     return model; 
    } 

    @RequestMapping(value = "/jwk", method = RequestMethod.GET, produces = "application/json") 
    public @ResponseBody String jwk() { 
     return jwksString; 
    } 

    @RequestMapping(value = "/restService", method = RequestMethod.POST) 
    public @ResponseBody String restService(HttpServletRequest request, HttpServletResponse respose) { 

     logger.info("Calling rest service"); 

     String requestToString = request.toString(); 

     String headerType = request.getHeader("Content-Type"); 
     String headerAuth = request.getHeader("Authorization"); 

     String token = headerAuth.split(" ")[1]; 

     // introspectTokenService.readAccessToken(token); 

     Map map = request.getParameterMap(); 

     String attributes = request.getAttributeNames().toString(); 

     // String someParam = request.getParameter("someParam"); 

     return "{\"status\":\"OK\"}"; 
    } 

    @Override 
    public void configure(HttpSecurity http) throws Exception { 
     http.requestMatcher(new OAuthRequestedMatcher()) 
     .authorizeRequests() 
      .antMatchers(HttpMethod.OPTIONS).permitAll() 
      .anyRequest().authenticated(); 
//  http.addFilterBefore(new TokenExtractorFilter(), BasicAuthenticationFilter.class).requestMatchers() 
//    .antMatchers("/rest/service/sample/restService").and().authorizeRequests().anyRequest() 
//    .access("ROLE_API"); 
    } 

    @Override 
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception { 
     resources.resourceId("W3IDRealm"); 
     resources.tokenExtractor(new BearerTokenExtractor()); 

     StaticIntrospectionConfigurationService introspectConfig = new StaticIntrospectionConfigurationService(); 

     introspectConfig.setIntrospectionUrl(introspectURL); 

     RegisteredClient client = new RegisteredClient(); 
     client.setClientId(clientId); 
     client.setClientSecret(clientSecret); 
     client.setTokenEndpointAuthMethod(AuthMethod.NONE); 

     introspectConfig.setClientConfiguration(client); 

     introspectTokenService.setIntrospectionConfigurationService(introspectConfig); 

     resources.tokenServices(introspectTokenService); 
    } 

    private static class OAuthRequestedMatcher implements RequestMatcher { 

     public boolean matches(HttpServletRequest request) { 

      String auth = request.getHeader("Authorization"); 
      // Determine if the client request contained an OAuth Authorization 
      boolean haveOauth2Token = (auth != null) && auth.startsWith("Bearer"); 
      boolean haveAccessToken = request.getParameter("access_token")!=null; 
      return haveOauth2Token || haveAccessToken; 
     } 

    } 

    class TokenExtractorFilter extends OncePerRequestFilter implements Filter, InitializingBean { 

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

      //UserDetails details = (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); 

      BearerTokenExtractor bte = new BearerTokenExtractor(); 

      String mytoken = bte.extract(request).toString(); 

      logger.info("Filter activated"); 

     } 

    } 

} 

回答

1

我發表使用基於註解的配置,這幫助我們調試使用Java斷點流程細節的解決方案。使用/ introspect的OAuth2持證人標記驗證正在工作。感謝MITREID提供的所有調試支持。請參閱上面顯示的報告最後部分發布的代碼解決方案。

相關問題