Wir erstellen einen REST-Ressourcenserver (eine Java-Beispielanwendung), den wir mithilfe eines Mechanismus zur Identitätsweitergabe schützen möchten, der von RFC7662 definiert wird und im MITREID Connect-Projekt verfügbar ist. Wir haben beide Konfigurationsmethoden, das XML-Setup, sowie das Annotations-basierte Setup getestet, das der Ressourcen-Server-Klasse hinzugefügt wurde (siehe unten angehängter Beispielcode).So schützen Sie eine Ressource mit Spring Security OAuth2 und MITREID Connect Introspect?
Unser Test zeigt die erfolgreiche Initialisierung der Spring Security-Routine, aber es ist uns nicht gelungen, die Bearer-Token-Passage durch den Autorisierungsheader auszulösen. Die Anfrage und die Ressource werden erfolgreich ausgeführt, es wurde jedoch keine Token-Analyse und Introspect-Validierung durchgeführt. Bitte überprüfen Sie die unten aufgeführten Konfigurationseinstellungen und Protokolle.
Unterstützung ist willkommen, das fehlende Kabel zwischen den Komponenten zu isolieren (Spring Security, Spring Oauth2 und Mitreid Connect Introspect).
Setup-Datei: spring-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());
}
}
Trace:
INFO : org.springframework.web.servlet.DispatcherServlet - FrameworkServlet 'appServlet': initialization completed in 5872 ms
DEBUG: org.springframework.web.servlet.DispatcherServlet - Servlets 'appServlet' konfiguriert erfolgreich DEBUG: org.springframework.web.context.support.StandardServletEnvironment - Hinzufügen von [servletConfigInitParams] PropertySource mit niedrigstem Suche Vorrang DEBUG: org.springframework.web.context.support.StandardServletEnvironment - Hinzufügen von [servletContextInitParams] PropertySource mit der niedrigsten Suchpräzedenz DEBUG: org.springframework.web.context.support.StandardServletEnvironment - Hinzufügen von [jndiProperties] PropertySource mit der niedrigsten Suchpriorität DEBUG: org.springframework.web.context.support.StandardServletEnvironment - Hinzufügen von [systemProperties] PropertySource mit lowe st Suche Vorrang DEBUG: org.springframework.web.context.support.StandardServletEnvironment - Hinzufügen von [systemEnvironment] PropertySource mit niedrigstem Suche Vorrang DEBUG: org.springframework.web.context.support.StandardServletEnvironment - Initialized StandardServletEnvironment mit PropertySources [servletConfigInitParams, servletContextInitParams , jndiProperties, systemProperties, systemEnvironment] DEBUG: org.springframework.web.filter.DelegatingFilterProxy - Initialisierungsfilter 'springSecurityFilterChain' DEBUG: org.springframework.beans.factory.support.DefaultListableBeanFactory - Zurückgeben gecachten Instanz von Singletons bean 'org.springframework.security.filterChainProxy' DEBUG: org.springframework.web.filter.DelegatingFilterProxy - Filter 'springSecurityFilterChain' konfiguriert erfolgreich DEBUG: org.springframework.web.servlet.DispatcherServlet - DispatcherServlet mit Name 'appServlet' Verarbeitung POST-Anfrage für [/ Rest/Service/Probe/RestService] DEBUG: org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping - Nachschlagen Handler-Methode für Pfad/RestService DEBUG: org .springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping - Zurückgegebene Prozedur [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 - Rückgabe der zwischengespeicherten Instanz der Singleton-Bean 'restController' INFO: com.red.sampleoidcservice.RestController - Aufruf des Restdienstes DEBUG: org.springframework.web.servlet.mvc.method.annotation. RequestResponseBodyMethodProcessor - Geschrieben [{"status": "OK"}] als "text/plain" mit [[email protected]12d551] DEBUG: org.springframework.web.servlet.DispatcherServlet - Null ModelAndView zurückgegeben an DispatcherServlet mit dem Namen 'appServlet': Annahme, dass HandlerAdapter die Bearbeitung der Anfrage abgeschlossen hat DEBUG: org.springframework.web.servlet.DispatcherServlet - Anfrage erfolgreich abgeschlossen DEBUG: org.springframework.beans.factory.support.DefaultListableBeanFactory - Zurückgegebene zwischengespeicherte Instanz der Singleton-Bean 'delegatingApplicationListener'
Lösung gefunden
Konfiguration mit Annotation
/*******************************************************************************
* 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");
}
}
}