Spring Cloud Gateway无法在Browser发送请求时授权jwt token

noj0wjuj  于 2024-01-06  发布在  Spring
关注(0)|答案(2)|浏览(222)

我使用spring Boot 3.0.2和spring cloud gateway。此外,我使用spring- Boot -starter-oauth2-resource-server在Keycloak服务器上获取和检查jwt token以确保安全。我的build.gradle文件如下:

  1. plugins {
  2. id 'java'
  3. id 'org.springframework.boot' version '3.0.2'
  4. id 'io.spring.dependency-management' version '1.1.3'
  5. }
  6. dependencies {
  7. implementation 'org.springframework.cloud:spring-cloud-starter'
  8. implementation 'org.springframework.boot:spring-boot-starter-webflux'
  9. implementation 'org.springframework.cloud:spring-cloud-starter-gateway'
  10. implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
  11. implementation 'org.springframework.boot:spring-boot-starter-data-mongodb'
  12. implementation 'org.springframework.boot:spring-boot-starter-oauth2-resource-server'
  13. implementation 'org.springframework.boot:spring-boot-starter-actuator'
  14. implementation 'com.shahrtech.service.libs:service-common-libs:0.0.61'
  15. implementation 'org.springframework.cloud:spring-cloud-starter-circuitbreaker-reactor-resilience4j'
  16. implementation 'org.springframework.cloud:spring-cloud-starter-loadbalancer'
  17. }

字符串
而application.yml上的配置文件是:

  1. spring :
  2. cloud:
  3. gateway:
  4. default-filters:
  5. - DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow- Origin
  6. globalcors:
  7. corsConfigurations:
  8. '[/**]':
  9. allowedOrigins: "*"
  10. allowedMethods: "*"
  11. allowedHeaders: "*"
  12. routes:
  13. - id: auth-service
  14. uri: http://x.x.x.x:8085/
  15. predicates:
  16. - Path=/auth-service/v1.0/**
  17. filters:
  18. - RewritePath=/auth-service/v1.0/(?<path>.*),/auth-service/v1.0/$\{path}
  19. - RequestHashing=SHA-256
  20. - id: customer-service
  21. uri: http://x.x.x.x:8090/
  22. predicates:
  23. - Path=/customer-service/v1.0/**
  24. filters:
  25. - RewritePath=/customer-service/v1.0/(?<path>.*),/customer-service/v1.0/$\{path}
  26. - RequestHashing=SHA-256
  27. - TokenRelay=
  28. security:
  29. oauth2:
  30. client:
  31. registration:
  32. keycloak:
  33. client-id: login-app
  34. authorization-grant-type: authorization_code
  35. scope: openid
  36. provider:
  37. keycloak:
  38. issuer-uri: http://x.x.x.x:9002/auth/realms/app
  39. user-name-attribute: preferred_username
  40. resourceserver:
  41. jwt:
  42. issuer-uri: http://x.x.x.x:9002/auth/realms/app


ApiGatewayServiceApplication.java是:

  1. @SpringBootApplication
  2. @EnableDiscoveryClient
  3. @EnableWebFluxSecurity
  4. @EnableReactiveMethodSecurity
  5. public class ApiGatewayServiceApplication {
  6. @Bean
  7. public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
  8. http.cors().and().csrf()
  9. .csrfTokenRepository(CookieServerCsrfTokenRepository.withHttpOnlyFalse())
  10. .and()
  11. .authorizeExchange()
  12. .anyExchange().authenticated()
  13. .and()
  14. .oauth2ResourceServer()
  15. .jwt();
  16. return http.build();
  17. }
  18. @Bean
  19. CorsConfigurationSource corsConfiguration() {
  20. CorsConfiguration corsConfig = new CorsConfiguration();
  21. corsConfig.applyPermitDefaultValues();
  22. corsConfig.addAllowedMethod(HttpMethod.PUT);
  23. corsConfig.addAllowedMethod(HttpMethod.DELETE);
  24. corsConfig.setAllowedOrigins(Arrays.asList("*", "**"));
  25. corsConfig.setAllowCredentials(true);
  26. UrlBasedCorsConfigurationSource source =
  27. new UrlBasedCorsConfigurationSource();
  28. source.registerCorsConfiguration("/**", corsConfig);
  29. return source;
  30. }
  31. public static void main(String[] args) {
  32. SpringApplication.run(ApiGatewayServiceApplication.class, args);
  33. }


}
我也使用这个Bean来更改安全配置:

  1. @Bean
  2. public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
  3. http
  4. .authorizeRequests()
  5. .requestMatchers(new AntPathRequestMatcher("/auth-service/**")).permitAll()
  6. .anyRequest().permitAll()
  7. .and()
  8. .httpBasic()
  9. .and()
  10. .csrf().disable()
  11. .cors().disable();
  12. return http.build();
  13. }


一切都很好。当在Postman中使用头部中的token调用API时,一切都很好。但是我们试图开发一个示例JS和ReactJs代码来获得API,我们收到错误401和控制台日志,并跟踪它显示请求头部授权未被接受。我们研究并找到这篇文章:Spring Gateway and Auth0: IllegalArgumentException: Unable to find GatewayFilterFactory with name TokenRelay,它说在配置中添加TokenRelay。我在配置中添加了TokenRelay,并重试,但错误存在。
一切都很好。当在Postman中使用头部中的token调用API时,一切都很好。但是我们试图开发一个示例JS和ReactJs代码来获得API,我们收到错误401和控制台日志,并跟踪它显示请求头部授权未被接受。我们研究并找到这篇文章:Spring Gateway and Auth0: IllegalArgumentException: Unable to find GatewayFilterFactory with name TokenRelay,它说在配置中添加TokenRelay。我在配置中添加了TokenRelay,并重试,但错误存在。

c86crjj0

c86crjj01#

TokenRelay过滤器将session cookie替换为session中的访问令牌。要在session中拥有此访问令牌,它当然需要sessions,但也需要oauth2Login()(带有authorization_code注册的Spring OAuth2客户端实现)。
不要将oauth2Login()oauth2ResourceServer()混合在一个SecurityFilterChain中,安全需求太不相同:

  • 安全性基于
  • 资源服务器的令牌
  • 使用authorization_code的客户端会话
  • 在没有有效授权的情况下尝试访问受保护的资源时,系统应应答您
  • 来自资源服务器的401 Unauthorized(缺少令牌或令牌无效)
  • 302 Redirect to login(会话尚未包含授权客户端)
  • 关于CSRF保护,它是:
  • 对于登录的客户端是必需的(因为安全性依赖于会话)
  • 在无状态资源服务器上无用

根据最新的建议,我们应该避免使用“公共”OAuth2客户端,在浏览器或移动的应用程序中运行的任何东西都只能是“公共”OAuth2客户端=>浏览器中的代码(纯JavaScript或SPA框架)不应该从授权服务器获取令牌。
你也许应该:

  • 使用oauth2Login()TokenRelay过滤器配置网关(最小依赖项为spring-boot-starter-oauth2-clientspring-cloud-gateway
  • 使用oauth2ResourceServer()配置下游REST API(最小依赖为spring-boot-starter-oauth2-resource-serverspring-boot-starter-webspring-boot-starter-webflux

从浏览器到网关的请求将使用会话cookie进行授权(并且应防止CSRF攻击)。
从Gateway(或Postman)到REST API的请求将使用访问令牌进行授权。

附注

在具有服务器端渲染部分的框架的情况下,例如next.js提供的,可以在该服务器端部分中使用实现OAuth2客户端的库:该服务器端库可以实现关于OAuth2和请求授权的相同重要功能:

  • 成为“机密”客户端(使用客户端ID和客户端secret查询授权服务器令牌端点)
  • 将令牌保存在服务器内存中(不将令牌泄漏到用户设备)
  • 在将请求从应用程序的设备部分转发到资源服务器之前,将会话cookie替换为访问令牌(就像spring-cloud-gateway上的TokenRelay一样)
展开查看全部
xienkqul

xienkqul2#

TokenRelay filter有一个bug,在spring-cloud-gateway https://github.com/spring-cloud/spring-cloud-gateway/releases/tag/v4.1.1的4.1.1版本中得到修复

相关问题