Spring Boot + Spring OAuth Java配置

标签 spring oauth spring-security spring-boot

我试图在一个简单的 Spring Boot + Spring OAuth 应用程序上获得 OAuth 1(3 条腿),仅作为消费者。

我一直在尝试将 tonr 示例移植到 spring-security-oauth 存储库 ( https://github.com/spring-projects/spring-security-oauth ) 以使用 Java 配置而不是 XML。

但是,我得到:

java.lang.NullPointerException: null
at org.springframework.security.oauth.consumer.filter.OAuthConsumerProcessingFilter.doFilter(OAuthConsumerProcessingFilter.java:87)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.springframework.boot.actuate.trace.WebRequestTraceFilter.doFilterInternal(WebRequestTraceFilter.java:102)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:88)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.springframework.boot.actuate.autoconfigure.MetricFilterAutoConfiguration$MetricsFilter.doFilterInternal(MetricFilterAutoConfiguration.java:90)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:219)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:501)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:142)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:516)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1086)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:659)
at org.apache.coyote.http11.Http11NioProtocol$Http11ConnectionHandler.process(Http11NioProtocol.java:223)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1558)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1515)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:745)

...可能是因为未正确设置 OAuthConsumerContextFilter。

我尝试配置 <oauth:consumer>部分如下:

@Bean
public OAuthConsumerProcessingFilter oAuthConsumerProcessingFilter()
{
    OAuthConsumerProcessingFilter result = new OAuthConsumerProcessingFilter();
    result.setProtectedResourceDetailsService(protectedResourceDetailsService());

    final LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>> map = new LinkedHashMap<>();
    map.put(new RegexRequestMatcher("/sparklr/*", null), Collections.singletonList(ConsumerSecurityConfig.PERMIT_ALL_ATTRIBUTE));
    result.setObjectDefinitionSource(new DefaultFilterInvocationSecurityMetadataSource(map));
    return result;
}

@Bean
public ProtectedResourceDetailsService protectedResourceDetailsService()
{
    return (String id) -> {
        switch (id) {
            case "sparklrPhotos":
                sparklrProtectedResourceDetails();
                break;
        }
        throw new RuntimeException("Error");
    };
}

@Bean
public OAuthConsumerContextFilter oAuthConsumerContextFilter() {
    final CoreOAuthConsumerSupport consumerSupport = new CoreOAuthConsumerSupport();
    consumerSupport.setProtectedResourceDetailsService(protectedResourceDetailsService());

    final OAuthConsumerContextFilter filter = new OAuthConsumerContextFilter();
    filter.setConsumerSupport(consumerSupport);
    return filter;
}

...但显然缺少了一些东西。我什至删除了 switch并始终返回相同的 protected 资源详细信息,但这并没有改变我没有上下文的事实。

我应该怎么做才能使这项工作正常进行?让我知道是否需要显示代码的任何其他部分。

更新:我已经添加了 Consumer Context 过滤器,但我认为它没有被应用,因为我遇到了同样的错误

最佳答案

我发现这个问题和其他答案中的几个部分的代码是不完整的,并且当作为一个整体时由于各种原因无法正常工作。这是我发现使用 Java 配置让 Spring Security OAuth 1 与 Spring Boot 一起工作的完整解决方案。

你需要一个像这样的配置类:

@Configuration
@EnableWebSecurity
public class OAuthConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.addFilterAfter(
            this.oauthConsumerContextFilter(),
            SwitchUserFilter.class
        );
        http.addFilterAfter(
            this.oauthConsumerProcessingFilter(),
            OAuthConsumerContextFilter.class
        );
    }

    // IMPORTANT: this must not be a Bean
    OAuthConsumerContextFilter oauthConsumerContextFilter() {
        OAuthConsumerContextFilter filter = new OAuthConsumerContextFilter();
        filter.setConsumerSupport(this.consumerSupport());
        return filter;
    }

    // IMPORTANT: this must not be a Bean
    OAuthConsumerProcessingFilter oauthConsumerProcessingFilter() {
        OAuthConsumerProcessingFilter filter = new OAuthConsumerProcessingFilter();
        filter.setProtectedResourceDetailsService(this.prds());

        LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>> map =
            new LinkedHashMap<>();

        // one entry per oauth:url element in xml
        map.put(
            // 1st arg is equivalent of url:pattern in xml
            // 2nd arg is equivalent of url:httpMethod in xml
            new AntPathRequestMatcher("/oauth/**", null),
            // arg is equivalent of url:resources in xml
            // IMPORTANT: this must match the ids in prds() and prd() below
            Collections.singletonList(new SecurityConfig("myResource"))
        );

        filter.setObjectDefinitionSource(
            new DefaultFilterInvocationSecurityMetadataSource(map)
        );

        return filter;
    }

    @Bean // optional, I re-use it elsewhere, hence the Bean
    OAuthConsumerSupport consumerSupport() {
        CoreOAuthConsumerSupport consumerSupport = new CoreOAuthConsumerSupport();
        consumerSupport.setProtectedResourceDetailsService(prds());
        return consumerSupport;
    }

    @Bean // optional, I re-use it elsewhere, hence the Bean
    ProtectedResourceDetailsService prds() {
        return (String id) -> {
            switch (id) {
            // this must match the id in prd() below
            case "myResource":
                return prd();
            }
            throw new RuntimeException("Invalid id: " + id);
        };
    }

    ProtectedResourceDetails prd() {
        BaseProtectedResourceDetails details = new BaseProtectedResourceDetails();

        // this must be present and match the id in prds() and prd() above
        details.setId("myResource");

        details.setConsumerKey("OAuth_Key");
        details.setSharedSecret("OAuth_Secret");

        details.setRequestTokenURL("...");
        details.setUserAuthorizationURL("...");
        details.setAccessTokenURL("...");

        // any other service-specific settings

        return details;
    }
}

需要了解的一些关键点,这样您就可以避免我遇到的问题:

首先,原始问题使用 ConsumerSecurityConfig.PERMIT_ALL_ATTRIBUTE 配置处理过滤器,但这不起作用。映射中的值必须是包含资源 ID 的 SecurityConfig,该资源是这些路径的 OAuth 所有者。

此 ID 还必须与 ProtectedResourceDetailsS​​erviceProtectedResourceDetails 中的 ID 匹配,并且两个 ID 都必须存在。尽管它们有些多余,但在 ProtectedResourceDetails 中遗漏 id 会破坏设置。

其次,原始问题将两个过滤器都配置为 Beans。但是,Spring Boot 会自动在 main 应用程序过滤器链中注册任何实现 Filter 的 Bean,在这种情况下,这将导致它们运行两次并且每次都使 OAuth 访问 token 过程失败,因为它看起来像是重放攻击.此问题的更多详细信息:Oauth 1.0a consumer code equesting an access token twice

有两种方法可以解决这个问题。我在上面使用了较短的一个(只是不要让它们成为 Beans),但您也可以创建一个 FilterRegistrationBean 来禁用自动注册行为。

通过这些更正,上面的代码经过测试并确认支持在 Spring Boot 容器中使用纯 Java 配置进行有效的 OAuth 1 协商。

引用资料:

Spring OAuth 1 文档有助于理解所涉及的类及其用途:https://projects.spring.io/spring-security-oauth/docs/oauth1.html

Bean 定义解析器是将 XML 转换为 Java 的规范实现,因此对于我没有提到的任何边缘情况,请参阅:https://github.com/codehaus/spring-security-oauth/blob/master/spring-security-oauth/src/main/java/org/springframework/security/oauth/config/OAuthConsumerBeanDefinitionParser.java

关于Spring Boot + Spring OAuth Java配置,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29001343/

相关文章:

elasticsearch - 将Kibana连接到Keycloak时出现许可证错误

ruby-on-rails - OAuth 安装 - 如何创建跨请求持续存在的 session ?

react-native - 登录 Instagram 时出错(禁用 Cookie)-React Native WebView

java - 如何使用 Spring Security 修复对资源的访问?

java - 从 Spring 应用程序中删除 JSESSIONID cookie

java - 无法使用 Spring 创建 java.util.concurrent.ThreadPoolExecutor 的 bean

spring - 如何使用 RestTemplate 进行 PUT

java - Spring @ConfigurationProperties 不映射对象列表

spring - header 在 REST 服务的集成测试中消失

javascript - Spring SseEmitter 的 Spring Security Authentication 对象为 null