我正在进行Spring Boot项目,该项目基于后端的微服务架构和前端的Vue.js。我的项目结构如下:
为了避免CORS错误,我通常会在类中添加@CrossOrigin
注解,它工作正常。这一切都很好,一直工作得很好,直到我添加了安全部分,能够登录用户。
我做了什么:
- 1.**对于构建在
spring-cloud-gateway
上的API Gateway,我添加了AuthFilter
,它用作拦截器来创建和检查JWT:
- 1.**对于构建在
api-gateway/src/main/java/.../AuthFilter.java
@Component
public class AuthFilter extends AbstractGatewayFilterFactory<AuthFilter.Config> {
private final WebClient.Builder webClientBuilder;
@Autowired
public AuthFilter(WebClient.Builder webClientBuilder) {
super(Config.class);
this.webClientBuilder = webClientBuilder;
}
@Override
public GatewayFilter apply(Config config) {
return (exchange, chain) -> {
if(!exchange.getRequest().getHeaders().containsKey(HttpHeaders.AUTHORIZATION)) {
throw new RuntimeException("Missing auth information");
}
String authHeader = exchange.getRequest().getHeaders().get(org.springframework.http.HttpHeaders.AUTHORIZATION).get(0);
String[] parts = authHeader.split(" ");
if(parts.length != 2 || !"Bearer".equals(parts[0])) {
throw new RuntimeException("Incorrect auth structure");
}
return webClientBuilder.build()
.post()
.uri("http://manager-service/api/v1/auth/validateToken?token=" + parts[1])
.retrieve()
.bodyToMono(EmployeeDTO.class) //EmployeeDTO.class is custom DTO that represents User
.map(user -> {
exchange.getRequest()
.mutate()
.header("x-auth-user-id", user.getId());
return exchange;
}).flatMap(chain::filter);
};
}
public static class Config {
//live it empty because we dont need any particular configuration
}
}
- 2.**我已经将
AuthFilter
作为过滤器添加到application.properties
中的每个服务:
- 2.**我已经将
api-gateway/src/resource/application.properties
##Workshop service routes
spring.cloud.gateway.routes[0].id=workshop-service
spring.cloud.gateway.routes[0].uri=lb://workshop-service
spring.cloud.gateway.routes[0].predicates[0]=Path=/api/v1/workshop/**
spring.cloud.gateway.routes[0].filters[0]=AuthFilter
##Manage service routes
spring.cloud.gateway.routes[1].id=manager-service
spring.cloud.gateway.routes[1].uri=lb://manager-service
spring.cloud.gateway.routes[1].predicates[0]=Path=/api/v1/manage/**
spring.cloud.gateway.routes[1].filters[0]=AuthFilter
##Manage service for singIn. Here we dont need to add AuthFilter, cause sign in page should be available for all
spring.cloud.gateway.routes[2].id=manager-service-sign-in
spring.cloud.gateway.routes[2].uri=lb://manager-service
spring.cloud.gateway.routes[2].predicates[0]=Path=/api/v1/auth/signIn
...
- 3.**用于控制系统基本实体(如用户、角色、用户工作所在的组织等)的管理器服务微服务,因此我在这里添加了
SecurityConfig
和WebConfig
,因为此微服务将负责JWT生成:
- 3.**用于控制系统基本实体(如用户、角色、用户工作所在的组织等)的管理器服务微服务,因此我在这里添加了
manager-service/src/main/java/.../SecurityConfig.java
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.csrf().disable()
.authorizeRequests().anyRequest().permitAll();
return httpSecurity.build();
}
}
manager-service/src/main/java/.../WebConfig.java
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
private static final Long MAX_AGE=3600L;
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedHeaders(
HttpHeaders.AUTHORIZATION,
HttpHeaders.CONTENT_TYPE,
HttpHeaders.ACCEPT)
.allowedMethods(
HttpMethod.GET.name(),
HttpMethod.POST.name(),
HttpMethod.PUT.name(),
HttpMethod.DELETE.name())
.maxAge(MAX_AGE)
.allowedOrigins("http://localhost:8100")
.allowCredentials(false);
}
}
- 4.**在控制器中,代表auth,我还向类添加了
@CrossOrigin
注解:
- 4.**在控制器中,代表auth,我还向类添加了
manager-service/src/main/java/.../AuthController.java
@RestController
@RequestMapping("api/v1/auth")
@CrossOrigin(origins = "http://localhost:8100")
@Slf4j
public class AuthController {
private final AuthService authService;
@Autowired
public AuthController(AuthService authService) {
this.authService = authService;
}
@PostMapping("/signIn")
public ResponseEntity<EmployeeDTO> signIn(@RequestBody CredentialsDTO credentialsDTO) {
log.info("Trying to login {}", credentialsDTO.getLogin());
return ResponseEntity.ok(EmployeeMapper.convertToDTO(authService.signIn(credentialsDTO)));
}
@PostMapping("/validateToken")
public ResponseEntity<EmployeeDTO> validateToken(@RequestParam String token) {
log.info("Trying to validate token {}", token);
Employee validatedTokenUser = authService.validateToken(token);
return ResponseEntity.ok(EmployeeMapper.convertToDTO(validatedTokenUser));
}
}
- 5.**对于前端,我使用Vue.js。对于请求,我使用
axios
。下面是post
-请求登录:
- 5.**对于前端,我使用Vue.js。对于请求,我使用
axios.post('http://localhost:8080/api/v1/auth/signIn', this.credentials).then(response => {
console.log('response = ', response)
console.log('token from response', response.data.token)
this.$store.commit('saveToken', response.data.token)
}).catch(error => {
console.log('Error is below')
console.log(error)
})
我得到的只是一个错误:Access to XMLHttpRequest at 'http://localhost:8080/api/v1/auth/signIn' from origin 'http://localhost:8100' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
。下面你会看到标题,显示Chrome浏览器请求:
我一直在尝试添加另一个corsConfiguration
,尝试用CrossOrigin
只标注方法,而不是类,但它没有任何效果。如果我尝试用postman发出这样的请求,它会给我预期的响应,并生成令牌。
如果你知道我做错了什么我会很感激的。
谢谢!
- 更新:正如我所理解的-所有问题都在
api-gateway
中。如果我直接向服务发出请求-我得到了正确的响应,但如果我通过网关**发出请求-我面临着一个错误,api-gateway
的日志如下:
- 更新:正如我所理解的-所有问题都在
2022-07-05 00:34:18.128 TRACE 8105 --- [or-http-epoll-5] o.s.c.g.h.p.PathRoutePredicateFactory : Pattern "[/api/v1/workshop/**]" does not match against value "/api/v1/auth/signIn"
2022-07-05 00:34:18.129 TRACE 8105 --- [or-http-epoll-5] o.s.c.g.h.p.PathRoutePredicateFactory : Pattern "[/api/v1/manage/**]" does not match against value "/api/v1/auth/signIn"
2022-07-05 00:34:18.129 TRACE 8105 --- [or-http-epoll-5] o.s.c.g.h.p.PathRoutePredicateFactory : Pattern "/api/v1/auth/signIn" matches against value "/api/v1/auth/signIn"
2022-07-05 00:34:18.129 DEBUG 8105 --- [or-http-epoll-5] o.s.c.g.h.RoutePredicateHandlerMapping : Route matched: manager-service-sign-in
2022-07-05 00:34:18.129 DEBUG 8105 --- [or-http-epoll-5] o.s.c.g.h.RoutePredicateHandlerMapping : Mapping [Exchange: OPTIONS http://localhost:8080/api/v1/auth/signIn] to Route{id='manager-service-sign-in', uri=lb://manager-service, order=0, predicate=Paths: [/api/v1/auth/signIn], match trailing slash: true, gatewayFilters=[], metadata={}}
2022-07-05 00:34:18.129 DEBUG 8105 --- [or-http-epoll-5] o.s.c.g.h.RoutePredicateHandlerMapping : [e5b87280-8] Mapped to org.springframework.cloud.gateway.handler.FilteringWebHandler@78df1cfc
3条答案
按热度按时间wswtfjt71#
经过研究我解决了问题都是盖特的错
正如我前面提到的,直接请求会给我正确的响应,但只有当我通过
api-gateway
时,它才会给我错误。因此,解决方案是将CORS配置规则添加到网关:
请注意,如果您不添加带有
gateway: default-filters
的节,您将面临包含多个值的标题的类似错误。多亏了answer by Pablo Aragonés in another question和Spring Cloud Documentation
tcomlyy62#
把这个添加到网关中的applications.yml,为我解决了问题:
u3r8eeie3#
我也遇到过类似的问题,通过在我的配置中定义以下bean得到了解决。
您可以注解掉所有其他细节,例如
@CrossOrigin(origins = "http://localhost:8100"), WebConfig.java, SecurityConfig.java
,因为一旦我们定义了上面的bean,就不需要这些东西了。您的代码可能没有运行,因为您定义了bean、安全配置以及webconfig,这些配置在处理您的请求时可能会发生冲突。