Spring 启动 + Spring OAuth Java 配置
Spring Boot + Spring OAuth Java configuration
我正在尝试通过简单的 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
并始终返回相同的受保护资源详细信息,但这并没有改变我没有上下文的事实。
我应该怎么做才能完成这项工作?如果我需要显示代码的任何其他部分,请告诉我。
更新:我已经添加了消费者上下文过滤器,但我认为它没有被应用,因为我得到了同样的错误
为了在 Java 配置中使用 Spring 安全性,您必须拥有包含类似内容的 SecurityConfig 文件(取自 http://projects.spring.io/spring-security-oauth/docs/oauth2.html)
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests().antMatchers("/login").permitAll().and()
// default protection for all resources (including /oauth/authorize)
.authorizeRequests()
.anyRequest().hasRole("USER")
// ... more configuration, e.g. for form login
}
这也是您可以使用 http.addFilterAfter(oAuthConsumerContextFilter(), AnonymousAuthenticationFilter.class);
按特定顺序添加过滤器的地方
您的代码存在问题,您的过滤器在创建身份验证之前正在执行。
所以我想你的两个过滤器至少应该在 AnonymousAuthenticationFilter.class
之后
您可以在此处找到过滤器列表:http://docs.spring.io/spring-security/site/docs/3.1.x/reference/springsecurity-single.html#filter-stack
这对我有用:
http
.addFilterAfter(oAuthConsumerContextFilter(), SwitchUserFilter.class)
.addFilterAfter(oAuthConsumerProcessingFilter(), OAuthConsumerContextFilter.class)
我发现这个问题和其他答案中的代码有几个部分是不完整的,并且从整体上看由于各种原因无法正常工作。这是我发现 Spring Security OAuth 1 working with Spring Boot using Java config.
的完整解决方案
您需要这样的配置 class:
@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 还必须与 ProtectedResourceDetailsService
和 ProtectedResourceDetails
中的 ID 匹配,并且两个 ID 都必须存在。尽管它们有些多余,但在 ProtectedResourceDetails
中遗漏 id 会破坏设置。
其次,原题将两个过滤器都配置为Bean。但是,Spring Boot 会自动注册任何在 main 应用程序过滤器链中实现 Filter 的 Bean,在这种情况下,这将导致它们 运行 两次并使 OAuth 访问失败每次都进行令牌处理,因为它看起来像重放攻击。此问题的更多详细信息:Oauth 1.0a consumer code equesting an access token twice
有两种方法可以解决这个问题。我在上面使用了较短的一个(只是不要让它们成为 Beans),但你也可以创建一个 FilterRegistrationBean
来禁用 auto-registration 行为。
通过这些更正,上面的代码经过测试并确认支持在 Spring 引导容器中使用纯 Java 配置的有效 OAuth 1 协商。
参考文献:
Spring OAuth 1 文档有助于理解所涉及的 classes 及其用途: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 应用程序获取 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
并始终返回相同的受保护资源详细信息,但这并没有改变我没有上下文的事实。
我应该怎么做才能完成这项工作?如果我需要显示代码的任何其他部分,请告诉我。
更新:我已经添加了消费者上下文过滤器,但我认为它没有被应用,因为我得到了同样的错误
为了在 Java 配置中使用 Spring 安全性,您必须拥有包含类似内容的 SecurityConfig 文件(取自 http://projects.spring.io/spring-security-oauth/docs/oauth2.html)
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests().antMatchers("/login").permitAll().and()
// default protection for all resources (including /oauth/authorize)
.authorizeRequests()
.anyRequest().hasRole("USER")
// ... more configuration, e.g. for form login
}
这也是您可以使用 http.addFilterAfter(oAuthConsumerContextFilter(), AnonymousAuthenticationFilter.class);
您的代码存在问题,您的过滤器在创建身份验证之前正在执行。
所以我想你的两个过滤器至少应该在 AnonymousAuthenticationFilter.class
之后您可以在此处找到过滤器列表:http://docs.spring.io/spring-security/site/docs/3.1.x/reference/springsecurity-single.html#filter-stack
这对我有用:
http
.addFilterAfter(oAuthConsumerContextFilter(), SwitchUserFilter.class)
.addFilterAfter(oAuthConsumerProcessingFilter(), OAuthConsumerContextFilter.class)
我发现这个问题和其他答案中的代码有几个部分是不完整的,并且从整体上看由于各种原因无法正常工作。这是我发现 Spring Security OAuth 1 working with Spring Boot using Java config.
的完整解决方案您需要这样的配置 class:
@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 还必须与 ProtectedResourceDetailsService
和 ProtectedResourceDetails
中的 ID 匹配,并且两个 ID 都必须存在。尽管它们有些多余,但在 ProtectedResourceDetails
中遗漏 id 会破坏设置。
其次,原题将两个过滤器都配置为Bean。但是,Spring Boot 会自动注册任何在 main 应用程序过滤器链中实现 Filter 的 Bean,在这种情况下,这将导致它们 运行 两次并使 OAuth 访问失败每次都进行令牌处理,因为它看起来像重放攻击。此问题的更多详细信息:Oauth 1.0a consumer code equesting an access token twice
有两种方法可以解决这个问题。我在上面使用了较短的一个(只是不要让它们成为 Beans),但你也可以创建一个 FilterRegistrationBean
来禁用 auto-registration 行为。
通过这些更正,上面的代码经过测试并确认支持在 Spring 引导容器中使用纯 Java 配置的有效 OAuth 1 协商。
参考文献:
Spring OAuth 1 文档有助于理解所涉及的 classes 及其用途: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