РЕДАКТИРОВАТЬ : Пожалуйста, прочтите обновления, проблема была значительно изменена.
Я очень сильно заблокирован по этому поводу. У меня есть весеннее приложение webflux, и я пытаюсь включить заголовки CORS, чтобы я мог выполнять запросы из разных источников в одних и тех же сеансах браузера. Но что бы я ни делал, заголовки CORS удаляются (даже если я помещаю их вручную в ServerResponse). Вот некоторые классы в security / и config /, которые я использую:
package com.document.feed.security;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.security.config.annotation.method.configuration.EnableReactiveMethodSecurity;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.web.server.SecurityWebFilterChain;
import reactor.core.publisher.Mono;
@EnableWebFluxSecurity
@EnableReactiveMethodSecurity
public class SecurityConfig {
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private SecurityContextRepository securityContextRepository;
@Bean
SecurityWebFilterChain springWebFilterChain(ServerHttpSecurity http) {
String[] patterns = new String[] {"/auth/**", "/vanillalist"};
return http
.exceptionHandling()
.authenticationEntryPoint((swe, e) -> Mono.fromRunnable(() -> {
swe.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
})).accessDeniedHandler((swe, e) -> Mono.fromRunnable(() -> {
swe.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
})).and()
.csrf().disable()
.authenticationManager(authenticationManager)
.securityContextRepository(securityContextRepository)
.authorizeExchange()
.pathMatchers(patterns).permitAll()
.pathMatchers(HttpMethod.OPTIONS).permitAll()
.anyExchange().authenticated()
.and()
.build();
}
}
SecurityContextRepository. java
package com.document.feed.security;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.context.SecurityContextImpl;
import org.springframework.security.web.server.context.ServerSecurityContextRepository;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import com.document.feed.config.JwtTokenUtil;
import reactor.core.publisher.Mono;
@Component
public class SecurityContextRepository implements ServerSecurityContextRepository {
private static final Logger logger = LoggerFactory.getLogger(SecurityContextRepository.class);
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Override
public Mono save(ServerWebExchange serverWebExchange, SecurityContext sc) {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public Mono load(ServerWebExchange serverWebExchange) {
System.out.println("serverWebExchange:" + serverWebExchange.getAttributes());
ServerHttpRequest request = serverWebExchange.getRequest();
String authHeader = request.getHeaders().getFirst(HttpHeaders.AUTHORIZATION);
String authToken = null;
if (authHeader != null && authHeader.startsWith(JwtTokenUtil.TOKEN_PREFIX)) {
authToken = authHeader.replace(JwtTokenUtil.TOKEN_PREFIX, "");
}else {
logger.warn("couldn't find bearer string, will ignore the header.");
}
System.out.println("SecurityContextRepository.authToken=" + authToken +
"\nauthHeader=" + authHeader);
String username;
try {
username = jwtTokenUtil.getUsernameFromToken(authToken);
} catch (Exception e) {
username = null;
}
System.out.println("SecurityContextRepository.username:" + username);
if (authToken != null) {
Authentication auth = new UsernamePasswordAuthenticationToken(authToken, authToken);
return authenticationManager.authenticate(auth).map((authentication) -> {
SecurityContextHolder
.getContext().setAuthentication((Authentication) authentication);
return new SecurityContextImpl((Authentication) authentication);
});
} else {
return Mono.empty();
}
}
}
package com.document.feed.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.reactive.config.CorsRegistry;
import org.springframework.web.reactive.config.EnableWebFlux;
import org.springframework.web.reactive.config.WebFluxConfigurer;
import com.sun.org.apache.xerces.internal.parsers.SecurityConfiguration;
@Configuration
@EnableWebFlux
@Import({CorsConfiguration.class, SecurityConfiguration.class})
public class CorsGlobalConfiguration implements WebFluxConfigurer {
@Override
public void addCorsMappings(CorsRegistry corsRegistry) {
corsRegistry.addMapping("/**")
.allowedOrigins("*")
.allowedHeaders("*")
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowCredentials(false)
.exposedHeaders("Access-Control-Allow-Origin",
"Access-Control-Allow-Methods",
"Access-Control-Allow-Headers",
"Access-Control-Max-Age",
"Access-Control-Request-Headers",
"Access-Control-Request-Method");
}
}
Посмотрите, как отбрасываются заголовки Access-Control-Allow-*
, а Access-Control-Request-*
сохраняются в заголовках ответа. ![enter image description here](https://i.stack.imgur.com/P8OHQ.png)
Ошибка, замеченная в консоли chrome:
fetch('http://localhost:8080/vanillalist', {
method: 'GET',
headers: {
'Content-type': 'application/json; charset=UTF-8'
}
})
.then(res => res.json())
.then(console.log)
Promise {<pending>}
2VM778:1 OPTIONS http://localhost:8080/vanillalist 404 (Not Found)
(anonymous) @ VM778:1
:3000/#/:1 Access to fetch at 'http://localhost:8080/vanillalist' from origin 'http://localhost:3000' 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. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
:3000/#/:1 Uncaught (in promise) TypeError: Failed to fetch
Обновление: Изображение для третьего комментария. ![3rd comment](https://i.stack.imgur.com/FsBk0.png)
Update2: curl -v
запрошено @mikeb, после добавления OPTIONS
в заголовки запроса.
(venv) NB292:scaligent devansh.dalal$ curl -v http://localhost:8080/vanillalist > /tmp/r.txt
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0* Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 8080 (#0)
> GET /vanillalist HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.54.0
> Accept: */*
>
< HTTP/1.1 200 OK
< transfer-encoding: chunked
< Vary: Origin
< Vary: Access-Control-Request-Method
< Vary: Access-Control-Request-Headers
< Content-Type: application/json
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Expires: 0
< X-Content-Type-Options: nosniff
< X-Frame-Options: DENY
< X-XSS-Protection: 1 ; mode=block
< Referrer-Policy: no-referrer
<
{ [8 bytes data]
100 1350k 0 1350k 0 0 19.9M 0 --:--:-- --:--:-- --:--:-- 19.9M
* Connection #0 to host localhost left intact
Update3 : теперь проблема уменьшена для обработки запросов OPTIONS
.