我正在开发一个 Java Springboot 服务,该服务托管在 Google Cloud Platform 上的 GKE 中。我正在使用 OAuth2.0 授权对服务端点的请求。
当我在本地运行服务时,Spring 的 DefaultSecurityChain 正确包含 BearerTokenAuthenticationFilter,并且如果请求具有有效的承载令牌,则允许该请求。 当我在 GKE 中部署并运行该服务时,它不包含过滤器,并且所有请求都被拒绝。
我有一个配置类,我在其中定义了以下filterChain。
@Configuration
@EnableMethodSecurity
public class AuthConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.csrf(csrf -> csrf.disable());
http.authorizeHttpRequests(authorize -> authorize.anyRequest().authenticated()).oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults()));
return http.build();
}
在 application.properties 中我设置了以下属性:
spring.security.oauth2.resourceserver.jwt.cloak-skew
spring.security.oauth2.resourceserver.jwt.issuer-uri
spring.security.oauth2.resourceserver.jwt.jwk-set-uri
spring.security.oauth2.client.provider.<my_provider>.token-uri
我还临时设置了这些以公开其他日志以进行故障排除:
logging.level.org.springframework.web=TRACE
logging.level.org.springframework.security=TRACE
spring.mvc.log-request-details=true
当服务运行时本地Spring 会向日志输出以下内容:
Trying to match request against DefaultSecurityFilterChain
[
RequestMatcher=any request,
Filters=[
org.springframework.security.web.session.DisableEncodeUrlFilter,
org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter,
org.springframework.security.web.context.SecurityContextHolderFilter,
org.springframework.security.web.header.HeaderWriterFilter,
org.springframework.web.filter.CorsFilter,
org.springframework.security.web.authentication.logout.LogoutFilter,
org.springframework.security.oauth2.server.resource.web.authentication.BearerTokenAuthenticationFilter,
org.springframework.security.web.savedrequest.RequestCacheAwareFilter,
org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter,
org.springframework.security.web.authentication.AnonymousAuthenticationFilter,
org.springframework.security.web.access.ExceptionTranslationFilter,
org.springframework.security.web.access.intercept.AuthorizationFilter]] (1/1)
当服务运行时在 GKE 中 Spring 会向日志输出以下内容:
Trying to match request against DefaultSecurityFilterChain
[
RequestMatcher=any request,
Filters=[
org.springframework.security.web.session.DisableEncodeUrlFilter,
org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter,
org.springframework.security.web.context.SecurityContextHolderFilter,
org.springframework.security.web.header.HeaderWriterFilter,
org.springframework.web.filter.CorsFilter,
org.springframework.security.web.authentication.logout.LogoutFilter,
org.springframework.security.web.savedrequest.RequestCacheAwareFilter,
org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter,
org.springframework.security.web.authentication.AnonymousAuthenticationFilter,
org.springframework.security.web.access.ExceptionTranslationFilter,
org.springframework.security.web.access.intercept.FilterSecurityInterceptor]] (1/1)
所有本地日志:
[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
o.s.web.servlet.DispatcherServlet : Detected org.springframework.web.multipart.support.StandardServletMultipartResolver
o.s.web.servlet.DispatcherServlet : Detected org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver
o.s.web.servlet.DispatcherServlet : Detected org.springframework.web.servlet.theme.FixedThemeResolver
o.s.web.servlet.DispatcherServlet : Detected DefaultRequestToViewNameTranslator
o.s.web.servlet.DispatcherServlet : Detected SessionFlashMapManager
o.s.web.servlet.DispatcherServlet : enableLoggingRequestDetails='true': request parameters and headers will be shown which may lead to unsafe logging of potentially sensitive data
o.s.web.servlet.DispatcherServlet : Completed initialization in 4 ms
o.s.security.web.FilterChainProxy : Trying to match request against DefaultSecurityFilterChain <see above for where I've provided this>
o.s.security.web.FilterChainProxy : Securing GET /<my_endpoint>/?<param1>=<value1>&<param2>=<value2>
o.s.security.web.FilterChainProxy : Invoking DisableEncodeUrlFilter (1/12)
o.s.security.web.FilterChainProxy : Invoking WebAsyncManagerIntegrationFilter (2/12)
o.s.security.web.FilterChainProxy : Invoking SecurityContextHolderFilter (3/12)
o.s.security.web.FilterChainProxy : Invoking HeaderWriterFilter (4/12)
o.s.security.web.FilterChainProxy : Invoking CorsFilter (5/12)
o.s.security.web.FilterChainProxy : Invoking LogoutFilter (6/12)
o.s.s.w.a.logout.LogoutFilter : Did not match request to Or [Ant [pattern='/logout', GET], Ant [pattern='/logout', POST], Ant [pattern='/logout', PUT], Ant [pattern='/logout', DELETE]]
o.s.security.web.FilterChainProxy : Invoking BearerTokenAuthenticationFilter (7/12)
o.s.s.authentication.ProviderManager : Authenticating request with JwtAuthenticationProvider (1/2)
o.s.web.client.RestTemplate : HTTP GET https://<JWK_URL>
o.s.web.client.RestTemplate : Accept=[text/plain, application/json, application/*+json, */*]
o.s.web.client.RestTemplate : Response 200 OK
o.s.web.client.RestTemplate : Reading to [java.lang.String] as "application/json;charset=utf-8"
s.o.s.r.a.JwtGrantedAuthoritiesConverter : Looking for scopes in claim scope
o.s.s.o.s.r.a.JwtAuthenticationProvider : Authenticated token
.s.r.w.a.BearerTokenAuthenticationFilter : Set SecurityContextHolder to JwtAuthenticationToken [Principal=org.springframework.security.oauth2.jwt.Jwt@da27f0e1, Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=<some_IP>, SessionId=null], Granted Authorities=[SCOPE_<the_required_scope>]]
o.s.security.web.FilterChainProxy : Invoking RequestCacheAwareFilter (8/12)
o.s.s.w.s.HttpSessionRequestCache : matchingRequestParameterName is required for getMatchingRequest to lookup a value, but not provided
o.s.security.web.FilterChainProxy : Invoking SecurityContextHolderAwareRequestFilter (9/12)
o.s.security.web.FilterChainProxy : Invoking AnonymousAuthenticationFilter (10/12)
o.s.security.web.FilterChainProxy : Invoking ExceptionTranslationFilter (11/12)
o.s.security.web.FilterChainProxy : Invoking AuthorizationFilter (12/12)
estMatcherDelegatingAuthorizationManager : Authorizing SecurityContextHolderAwareRequestWrapper[ org.springframework.security.web.header.HeaderWriterFilter$HeaderWriterRequest]
estMatcherDelegatingAuthorizationManager : Checking authorization on SecurityContextHolderAwareRequestWrapper[ org.springframework.security.web.header.HeaderWriterFilter$HeaderWriterRequest] using org.springframework.security.authorization.AuthenticatedAuthorizationManager
o.s.s.w.a.AnonymousAuthenticationFilter : Did not set SecurityContextHolder since already authenticated JwtAuthenticationToken [Principal=org.springframework.security.oauth2.jwt.Jwt, Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=<some_IP>, SessionId=null], Granted Authorities=[SCOPE_<the_required_scope>]]
o.s.security.web.FilterChainProxy : Secured GET /<my_endpoint>/?<param1>=<value1>&<param2>=<value2>
logging.WebApiLoggingFilter : Server request: method=GET uri=/<my_endpoint>/?<param1>=<value1>&<param2>=<value2>
o.s.web.servlet.DispatcherServlet : GET "/<my_endpoint>/?<param1>=<value1>&<param2>=<value2>", parameters={<param1>:<value1>, <param2>:<value2>}, headers={authorization:[Bearer <bearer_token>], user-agent:[PostmanRuntime/7.43.0], accept:[*/*], cache-control:[no-cache], postman-token:[<some_token>], host:[localhost:8080], accept-encoding:[gzip, deflate, br], connection:[keep-alive]} in DispatcherServlet 'dispatcherServlet'
所有 GKE 日志:
[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
o.s.web.servlet.DispatcherServlet : Detected org.springframework.web.multipart.support.StandardServletMultipartResolver
o.s.web.servlet.DispatcherServlet : Detected org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver
o.s.web.servlet.DispatcherServlet : Detected org.springframework.web.servlet.theme.FixedThemeResolver
o.s.web.servlet.DispatcherServlet : Detected DefaultRequestToViewNameTranslator
o.s.web.servlet.DispatcherServlet : Detected SessionFlashMapManager
o.s.web.servlet.DispatcherServlet : enableLoggingRequestDetails='true': request parameters and headers will be shown which may lead to unsafe logging of potentially sensitive data
o.s.web.servlet.DispatcherServlet : Completed initialization in 4 ms
o.s.security.web.FilterChainProxy : Trying to match request against DefaultSecurityFilterChain <see above for where I've provided this>
o.s.security.web.FilterChainProxy : Securing GET /<my_endpoint>/?<param1>=<value1>&<param2>=<value2>
o.s.security.web.FilterChainProxy : Invoking DisableEncodeUrlFilter (1/11)
o.s.security.web.FilterChainProxy : Invoking WebAsyncManagerIntegrationFilter (2/11)
o.s.security.web.FilterChainProxy : Invoking SecurityContextHolderFilter (3/11)
o.s.security.web.FilterChainProxy : Invoking HeaderWriterFilter (4/11)
o.s.security.web.FilterChainProxy : Invoking CorsFilter (5/11)
o.s.security.web.FilterChainProxy : Invoking LogoutFilter (6/11)
o.s.s.w.a.logout.LogoutFilter : Did not match request to Or [Ant [pattern='/logout', GET], Ant [pattern='/logout', POST], Ant [pattern='/logout', PUT], Ant [pattern='/logout', DELETE]]
o.s.security.web.FilterChainProxy : Invoking RequestCacheAwareFilter (7/11)
o.s.s.w.s.HttpSessionRequestCache : matchingRequestParameterName is required for getMatchingRequest to lookup a value, but not provided
o.s.security.web.FilterChainProxy : Invoking SecurityContextHolderAwareRequestFilter (8/11)
o.s.security.web.FilterChainProxy : Invoking AnonymousAuthenticationFilter (9/11)
o.s.security.web.FilterChainProxy : Invoking ExceptionTranslationFilter (10/11)
o.s.security.web.FilterChainProxy : Invoking FilterSecurityInterceptor (11/11)
HttpSessionSecurityContextRepository : No HttpSession currently exists
SupplierDeferredSecurityContext : Created SecurityContextImpl [Null authentication]
SupplierDeferredSecurityContext : Created SecurityContextImpl [Null authentication]
o.s.s.w.a.AnonymousAuthenticationFilter : Set SecurityContextHolder to AnonymousAuthenticationToken [Principal=anonymousUser, Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=<some_IP>, SessionId=null], Granted Authorities=[ROLE_ANONYMOUS]]
o.s.s.w.a.i.FilterSecurityInterceptor : Did not re-authenticate AnonymousAuthenticationToken [Principal=anonymousUser, Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=<some_IP>, SessionId=null], Granted Authorities=[ROLE_ANONYMOUS]] before authorizing
o.s.s.w.a.i.FilterSecurityInterceptor : Authorizing filter invocation [GET /<my_endpoint>/?<param1>=<value1>&<param2>=<value2>] with attributes [permitAll]
o.s.s.w.a.i.FilterSecurityInterceptor : Authorized filter invocation [GET /<my_endpoint>/?<param1>=<value1>&<param2>=<value2>] with attributes [permitAll]
o.s.s.w.a.i.FilterSecurityInterceptor : Did not switch RunAs authentication since RunAsManager returned null
o.s.security.web.FilterChainProxy : Secured GET /<my_endpoint>/?<param1>=<value1>&<param2>=<value2>
WebApiLoggingFilter : Server request: method=GET uri=/<my_endpoint>/?<param1>=<value1>&<param2>=<value2>
o.s.web.servlet.DispatcherServlet : GET "<my_endpoint>/?<param1>=<value1>&<param2>=<value2>", parameters={<param1>:<value1>, <param2>:<value2>}, headers={authorization:[Bearer <bearer_token>], user-agent:[PostmanRuntime/7.43.0], accept:[*/*], cache-control:[no-cache], postman-token:[<some_token>], accept-encoding:[gzip, deflate, br],]} in DispatcherServlet 'dispatcherServlet'
.RequestMappingHandlerMapping : Mapped to az.supplychain.order.fulfillment.controller.WaveV1Controller#getWaves(String, boolean, LocalDate, LocalDate, Integer, Integer)
horizationManagerBeforeMethodInterceptor : Authorizing method invocation ReflectiveMethodInvocation: public org.springframework.http.ResponseEntity <my_controller>.<my_method>(<args>); target is of class [<my_controller>]
horizationManagerBeforeMethodInterceptor : Failed to authorize ReflectiveMethodInvocation: public org.springframework.http.ResponseEntity <my_controller>.<my_method>(<args>); target is of class [<my_controller>] with authorization manager org.springframework.security.config.annotation.method.configuration.DeferringObservationAuthorizationManager and decision ExpressionAuthorizationDecision [granted=false, expressionAttribute=hasAuthority('SCOPE_'+(@environment.getProperty('<the_required_scope>') ?: 'default').toLowerCase())]
.ExceptionHandlerExceptionResolver : Using @ExceptionHandler <my_controller_advice>.ServiceExceptionHandler#handleUnexpectedException(Exception)
o.s.web.method.HandlerMethod : Arguments: [org.springframework.security.access.AccessDeniedException: Access Denied]
ServiceExceptionHandler : Unexpected error
org.springframework.security.access.AccessDeniedException: Access Denied
我认为您遇到了 cors 问题,请尝试将其添加到您的默认过滤器链中
.cors(Customizer.withDefaults())