Spring Cloud Gateway与Spring Authorization Server认证401问题求助
基于OAuth2的Spring Cloud Security网关认证问题排查
我正在基于OAuth2模式实现Spring Cloud Security,将Gateway作为OAuth2客户端,目前遇到认证与授权相关问题,恳请帮忙排查。
具体问题:输入用户名和密码完成认证后,页面跳转至回调URL时出现401 Unauthorized错误,回调URL示例:
https://127.0.0.1:8443/login/oauth2/code/spring?code=axdUp_ImMSt8vdDrN6XqIrQrEjB7P91h2HxNcqasy7JmCsJmw71u59nRUotAzkIEuBIo1quZsz3CP36Cm_xde3dMPDuvTFvs2GMRUjvDZV0BVhg0bN-LqPUp9gjMi7gm&state=BfLaHTmU9TsdJVMgWgviot1GSKHA1QnbFhhH1ESYWbg%3D
授权服务器配置
@Configuration @EnableWebSecurity public class AuthorizationServerConfiguration { @Bean @Order(1) public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception { OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http); http.getConfigurer(OAuth2AuthorizationServerConfigurer.class) .oidc(Customizer.withDefaults()); http .exceptionHandling( (exceptions) -> exceptions.defaultAuthenticationEntryPointFor( new LoginUrlAuthenticationEntryPoint("/login"), new MediaTypeRequestMatcher( org.springframework.http.MediaType.valueOf(MediaType.TEXT_HTML)))) .oauth2ResourceServer((resourceServer) -> resourceServer.jwt(Customizer.withDefaults())); return http.build(); } @Bean @Order(2) public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception { http.authorizeHttpRequests( (authorize) -> authorize .requestMatchers("/ping", "/oauth2/authorize") .permitAll() .anyRequest() .authenticated()) .formLogin(Customizer.withDefaults()); return http.build(); } @Bean public RegisteredClientRepository registeredClientRepository() { RegisteredClient authorizationCodeFlow = RegisteredClient.withId(UUID.randomUUID().toString()) .clientId("ac-client") .clientSecret("{noop}ac-secret") .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC) .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) .authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN) .redirectUri("https://127.0.0.1:8443/login/oauth2/code/spring") .postLogoutRedirectUri("https://127.0.0.1:8443/app/logout") .scopes(scopes -> scopes.addAll(Set.of(OidcScopes.OPENID, OidcScopes.PROFILE))) .clientSettings(ClientSettings.builder().requireAuthorizationConsent(false).build()) .tokenSettings( TokenSettings.builder() .authorizationCodeTimeToLive(Duration.of(5, ChronoUnit.MINUTES)) .accessTokenTimeToLive(Duration.of(30, ChronoUnit.MINUTES)) .build()) .build(); return new InMemoryRegisteredClientRepository(authorizationCodeFlow); } @Bean public UserDetailsService userDetailsService() { UserDetails adminUser = User.builder().username("admin").password("{noop}admin").roles("ADMIN").build(); return new InMemoryUserDetailsManager(adminUser); } }
网关配置
@Configuration @EnableWebFluxSecurity public class SecurityConfiguration { private static final String[] WHITELIST_URL = { "/login/oauth2/code/spring", "/oauth2/authorization/spring", "/app/logout", "favicon.ico" }; @Bean public SecurityWebFilterChain springSecurityFilterChain( ServerHttpSecurity http, ReactiveClientRegistrationRepository clientRegistrationRepository) { http.authorizeExchange( authorizeExchangeSpec -> authorizeExchangeSpec .pathMatchers(WHITELIST_URL) .permitAll() .anyExchange() .authenticated()) .oauth2Login( oAuth2LoginSpec -> oAuth2LoginSpec .authenticationSuccessHandler(this::authenticationSuccessHandler) .authenticationFailureHandler(this::authenticationFailureHandler)) .logout( logoutSpec -> logoutSpec.logoutSuccessHandler( serverLogoutSuccessHandler(clientRegistrationRepository))) .exceptionHandling( exceptionHandlingSpec -> exceptionHandlingSpec.authenticationEntryPoint(this::authenticationEntryPoint)); return http.build(); } private ServerLogoutSuccessHandler serverLogoutSuccessHandler( ReactiveClientRegistrationRepository clientRegistrationRepository) { var logout = new OidcClientInitiatedServerLogoutSuccessHandler(clientRegistrationRepository); logout.setPostLogoutRedirectUri("https://127.0.0.1:8443/app/logout"); return logout; } private Mono<Void> authenticationSuccessHandler( WebFilterExchange webFilterExchange, Authentication authentication) { return buildResponse(webFilterExchange, HttpStatus.OK, Map.of("status", "LoginSuccess")); } private Mono<Void> authenticationFailureHandler( WebFilterExchange webFilterExchange, AuthenticationException exception) { return buildResponse( webFilterExchange, HttpStatus.UNAUTHORIZED, Map.of("status", "Authentication Failed")); } private Mono<Void> authenticationEntryPoint( ServerWebExchange exchange, AuthenticationException ex) { ServerHttpResponse response = exchange.getResponse(); response.setStatusCode(HttpStatus.UNAUTHORIZED); DataBuffer result = response .bufferFactory() .wrap(Map.of("status", "Login to access APIs")); return response.writeWith(Mono.just(result)); } private Mono<Void> buildResponse( WebFilterExchange webFilterExchange, HttpStatus status, Map<String, String> message) { ServerHttpResponse response = webFilterExchange.getExchange().getResponse(); response.setStatusCode(status); response.getHeaders().add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE); DataBuffer result = response.bufferFactory().wrap(message); return response.writeWith(Mono.just(result)); } }
网关application.yml配置
spring: application.name: gateway cloud: gateway: default-filters: - TokenRelay= - RemoveRequestHeader=Cookie routes: - id: product-service uri: lb://product-service predicates: - Path=/product/** security: oauth2: client: provider: spring: issuer-uri: ${ISSUER_URI:http://localhost:9000/auth} registration: spring: provider: spring client-id: ac-client client-secret: ac-secret authorization-grant-type: authorization_code client-authentication-method: client_secret_basic redirect-uri: "https://127.0.0.1:8443/login/oauth2/code/{registrationId}" scope: openid, profile
内容的提问来源于stack exchange,提问作者vishal
相关产品推荐
相关产品推荐

