Spring Boot @EnableResourceServer意外401未经授权的错误

时间:2017-11-20 15:29:45

标签: spring-boot spring-security oauth-2.0

我已经使用Spring Boot oAuth2支持设置了独立的OAuth2客户端和授权服务器提供程序。我现在正在尝试创建受保护的API资源服务器,如文档here所示:

@SpringBootApplication
@EnableResourceServer
public class HelloService extends ResourceServerConfigurerAdapter {
    public static void main(String[] args) {
        SpringApplication.run(HelloService.class, args);
    }
}

我的Hello servlet控制器同样微不足道:

@RestController
public class HelloController {
   @CrossOrigin
   @RequestMapping("/hello")
   public String index() {
      return "Greeting from Hello Service!";
   }
}

我在application.properties中设置了security.oauth2.resource.user-info-uri:http://localhost:9999/user。从我阅读所有需要的Spring Boot文档。 Spring引导应该从类路径中选择spring-boot-starter-securityspring-security-oauth2 - 自动要求对/hello端点进行身份验证。 oAuth2过滤器链应从Authorization标头中获取承载访问令牌,并调用配置的Authorization-Server端点以检查访问令牌。但是在浏览器中我看到以下响应:

WWW-Authenticate:Bearer realm="oauth2-resource", error="unauthorized", 
error_description="Full authentication is required to access this resource"

这似乎意味着至少要识别标题。然后我打开了对/hello端点的访问权限:

@SpringBootApplication
@EnableResourceServer
public class HelloService extends ResourceServerConfigurerAdapter {
    public static void main(String[] args) {
        SpringApplication.run(HelloService.class, args);
    }

   @Override
   public void configure(HttpSecurity http) throws Exception {
       http.authorizeRequests().anyRequest().permitAll();
   }
}

然后我可以访问/hello端点。我还可以看到授权服务器端点http://localhost:9999/user被命中,因为如果我将端口号更改为9998,那么资源服务日志中会出现以下错误"http://localhost:9998/user": Connection refused

Connection refused端点受保护时没有/hello错误。所以问题是当/hello端点受到保护时,oAuth2过滤器链没有调用远程Authorization-Server端点。我不明白的是为什么?

禁用身份验证permitAll()我添加了自己的TokenExtractor并将日志级别设置为debug:

hello.MyTokenExtractor: **** Calling MyTokenExtractor.extract(): org.springframework.security.web.firewall.RequestWrapper
hello.MyTokenExtractor: Header token value: 1611e720-9213-4e0e-bfb3-a6b926335ab7
o.s.b.a.s.o.r.UserInfoTokenServices: Getting user info from: http://localhost:9999/user
o.s.s.oauth2.client.OAuth2RestTemplate: Created GET request for "http://localhost:9999/user"
o.s.s.oauth2.client.OAuth2RestTemplate   : Setting request Accept header to [application/json, application/*+json]

您可以看到标头令牌 - 对身份验证服务器的请求,一切都很好。但是当我呼叫authenticated()时,授权令牌已经消失。

hello.MyTokenExtractor: **** Calling MyTokenExtractor.extract(): org.springframework.security.web.firewall.RequestWrapper
hello.MyTokenExtractor: Header token value: null
hello.MyTokenExtractor: Token not found in headers. Trying request parameters.
hello.MyTokenExtractor: Token not found in request parameters.  Not an OAuth2 request. 

完整跟踪如下:

org.eclipse.jetty.server.Server         : REQUEST OPTIONS /hello on HttpChannelOverHttp@7f06f300{r=1,c=false,a=DISPATCHED,uri=//localhost:8000/hello}
o.e.jetty.server.handler.ContextHandler  : scope null||/hello @ o.s.b.c.e.j.JettyEmbeddedWebAppContext@7ce6a65d{/,[file:///private/var/folders/z5/wh0gsly536zbv68jbny_jzmswqk01z/T/jetty-docbase.5203414264432039078.8000/],AVAILABLE}
o.e.jetty.server.handler.ContextHandler  : context=||/hello @ o.s.b.c.e.j.JettyEmbeddedWebAppContext@7ce6a65d{/,[file:///private/var/folders/z5/wh0gsly536zbv68jbny_jzmswqk01z/T/jetty-docbase.5203414264432039078.8000/],AVAILABLE}
org.eclipse.jetty.server.session         : sessionHandler=org.eclipse.jetty.server.session.SessionHandler352359770==dftMaxIdleSec=1800
org.eclipse.jetty.server.session         : session=null
o.eclipse.jetty.servlet.ServletHandler   : servlet |/hello|null -> dispatcherServlet@7ef5559e==org.springframework.web.servlet.DispatcherServlet,jsp=null,order=-1,inst=true
o.eclipse.jetty.servlet.ServletHandler   : chain=characterEncodingFilter->hiddenHttpMethodFilter->httpPutFormContentFilter->requestContextFilter->springSecurityFilterChain->Jetty_WebSocketUpgradeFilter->dispatcherServlet@7ef5559e==org.springframework.web.servlet.DispatcherServlet,jsp=null,order=-1,inst=true
o.eclipse.jetty.servlet.ServletHandler   : call filter characterEncodingFilter
o.eclipse.jetty.servlet.ServletHandler   : call filter hiddenHttpMethodFilter
o.eclipse.jetty.servlet.ServletHandler   : call filter httpPutFormContentFilter
o.eclipse.jetty.servlet.ServletHandler   : call filter requestContextFilter
o.s.b.w.f.OrderedRequestContextFilter    : Bound request context to thread: Request(OPTIONS //localhost:8000/hello)@5b6aa5a
o.eclipse.jetty.servlet.ServletHandler   : call filter springSecurityFilterChain
o.s.b.f.s.DefaultListableBeanFactory     : Returning cached instance of singleton bean 'springSecurityFilterChain'
o.s.s.web.util.matcher.OrRequestMatcher  : Trying to match using Ant [pattern='/css/**']
o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/hello'; against '/css/**'
o.s.s.web.util.matcher.OrRequestMatcher  : Trying to match using Ant [pattern='/js/**']
o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/hello'; against '/js/**'
o.s.s.web.util.matcher.OrRequestMatcher  : Trying to match using Ant [pattern='/images/**']
o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/hello'; against '/images/**'
o.s.s.web.util.matcher.OrRequestMatcher  : Trying to match using Ant [pattern='/webjars/**']
o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/hello'; against '/webjars/**'
o.s.s.web.util.matcher.OrRequestMatcher  : Trying to match using Ant [pattern='/**/favicon.ico']
o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/hello'; against '/**/favicon.ico'
o.s.s.web.util.matcher.OrRequestMatcher  : Trying to match using Ant [pattern='/error']
o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/hello'; against '/error'
o.s.s.web.util.matcher.OrRequestMatcher  : No matches found
o.s.security.web.FilterChainProxy        : /hello at position 1 of 11 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
o.s.security.web.FilterChainProxy        : /hello at position 2 of 11 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
o.s.security.web.FilterChainProxy        : /hello at position 3 of 11 in additional filter chain; firing Filter: 'HeaderWriterFilter'
o.s.s.w.header.writers.HstsHeaderWriter  : Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@7d694ffe
o.s.security.web.FilterChainProxy        : /hello at position 4 of 11 in additional filter chain; firing Filter: 'LogoutFilter'
o.s.s.web.util.matcher.OrRequestMatcher  : Trying to match using Ant [pattern='/logout', GET]
o.s.s.w.u.matcher.AntPathRequestMatcher  : Request 'OPTIONS /hello' doesn't match 'GET /logout
o.s.s.web.util.matcher.OrRequestMatcher  : Trying to match using Ant [pattern='/logout', POST]
o.s.s.w.u.matcher.AntPathRequestMatcher  : Request 'OPTIONS /hello' doesn't match 'POST /logout
o.s.s.web.util.matcher.OrRequestMatcher  : Trying to match using Ant [pattern='/logout', PUT]
o.s.s.w.u.matcher.AntPathRequestMatcher  : Request 'OPTIONS /hello' doesn't match 'PUT /logout
o.s.s.web.util.matcher.OrRequestMatcher  : Trying to match using Ant [pattern='/logout', DELETE]
o.s.s.w.u.matcher.AntPathRequestMatcher  : Request 'OPTIONS /hello' doesn't match 'DELETE /logout
o.s.s.web.util.matcher.OrRequestMatcher  : No matches found
o.s.security.web.FilterChainProxy        : /hello at position 5 of 11 in additional filter chain; firing Filter: 'OAuth2AuthenticationProcessingFilter'
hello.MyTokenExtractor                   : **** Calling MyTokenExtractor.extract(): org.springframework.security.web.firewall.RequestWrapper

问题是CORS预检OPTIONS请求。以下更改修复:

public void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests()
            .anyRequest().authenticated();
            .and().cors();
}

0 个答案:

没有答案