如何使用SpringSecurityOAuth2和MitredConnect内省保护资源?

如何使用SpringSecurityOAuth2和MitredConnect内省保护资源?,spring,oauth-2.0,bearer-token,spring-oauth2,mitreid-connect,Spring,Oauth 2.0,Bearer Token,Spring Oauth2,Mitreid Connect,我们正在构建一个REST资源服务器(一个Java示例应用程序),我们计划使用MITREID Connect项目提供的RFC7662定义的身份传播机制对其进行保护。我们测试了这两种配置方法、XML设置以及添加到资源服务器类中的基于注释的设置(请参阅下面附带的示例代码) 我们的测试显示Spring安全例程初始化成功,但我们没有成功触发通过授权头的承载令牌通道。请求和资源成功执行,但未进行令牌解析和内省验证。请检查下面所附的配置设置和日志 欢迎支持隔离组件(Spring Security、Spring

我们正在构建一个REST资源服务器(一个Java示例应用程序),我们计划使用MITREID Connect项目提供的RFC7662定义的身份传播机制对其进行保护。我们测试了这两种配置方法、XML设置以及添加到资源服务器类中的基于注释的设置(请参阅下面附带的示例代码)

我们的测试显示Spring安全例程初始化成功,但我们没有成功触发通过授权头的承载令牌通道。请求和资源成功执行,但未进行令牌解析和内省验证。请检查下面所附的配置设置和日志

欢迎支持隔离组件(Spring Security、Spring Oauth2和Mitreid Connect Introspect)之间缺失的电线

设置文件:spring security.xml

<?xml version="1.0" encoding="UTF-8"?>
post.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");
        }
    }

}
    // 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());
    }
}
调试:org.springframework.web.servlet.DispatcherServlet-servlet“appServlet”配置成功 调试:org.springframework.web.context.support.StandardServletEnvironment-添加搜索优先级最低的[servletConfigInitParams]PropertySource 调试:org.springframework.web.context.support.StandardServletEnvironment-添加搜索优先级最低的[servletContextInitParams]PropertySource 调试:org.springframework.web.context.support.StandardServletenEnvironment-添加搜索优先级最低的[jndiProperties]PropertySource 调试:org.springframework.web.context.support.StandardServletenEnvironment-添加搜索优先级最低的[systemProperties]PropertySource 调试:org.springframework.web.context.support.StandardServletenEnvironment-添加搜索优先级最低的[systemEnvironment]PropertySource 调试:org.springframework.web.context.support.StandardServleteEnvironment-使用PropertySources初始化的StandardServleteEnvironment[servletConfigInitParams,servletContextInitParams,JNDIProperty,systemProperties,systemEnvironment] 调试:org.springframework.web.filter.DelegatingFilterProxy-初始化筛选器“springSecurityFilterChain” 调试:org.springframework.beans.factory.support.DefaultListableBeanFactory-返回singleton bean的缓存实例'org.springframework.security.filterChainProxy' 调试:org.springframework.web.filter.DelegatingFilterProxy-已成功配置筛选器“springSecurityFilterChain” 调试:org.springframework.web.servlet.DispatcherServlet-名为“appServlet”的DispatcherServlet正在处理[/rest/service/sample/restService]的POST请求 调试:org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping-查找path/restService的处理程序方法 调试: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)] 调试:org.springframework.beans.factory.support.DefaultListableBeanFactory-返回单例bean“restController”的缓存实例 信息:com.red.sampleoidcservice.RestController-调用rest服务 调试:org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor-使用[org.springframework.http.converter]将[{“status”:“OK”}]写成“text/plain”。StringHttpMessageConverter@6912d551] 调试:org.springframework.web.servlet.DispatcherServlet-返回给名为“appServlet”的DispatcherServlet的Null ModelAndView:假设HandlerAdapter完成了请求处理 调试:org.springframework.web.servlet.DispatcherServlet-已成功完成请求 调试:org.springframework.beans.factory.support.DefaultListableBeanFactory-返回singleton bean“delegatingApplicationListener”的缓存实例

找到解决方案

带注释的配置

    /*******************************************************************************
 * 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");

        }

    }

}

我已经发布了一个使用基于注释的配置的解决方案,它帮助我们使用Java断点调试流细节。使用/introspect的OAuth2承载令牌验证正在工作。感谢MITREID提供的所有调试支持。请参阅报告最后一节中发布的代码解决方案,如上所示

INFO : org.springframework.web.servlet.DispatcherServlet - FrameworkServlet 'appServlet': initialization completed in 5872 ms
    /*******************************************************************************
 * 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");

        }

    }

}