弹簧安全性拒绝获取选项,如果content-type =' application/json;
如果启用弹簧安全性(v 5.6.1)启用服务端点将发送此标头:
headers.append("Content-Type", "application/json");
滤波器链中的任何过滤器都不会处理选项前闪存请求 - 滤波器记录在任何链条过滤器中没有任何响应; org.apache.catalina.connector.requestfacade
甚至没有服务器端的调用。客户端收到的httpresponse始终将显示403未经授权
。
因此,这种请求并不是会导致前飞行前,然后通过启用的Corsfilter来传播过滤器,并通过启用的CorsFilter进行处理,该CorsFilter将添加必要的标头以满足前飞行并返回此响应。如果应用程序/JSON是内容类型,那么前飞行显然会完全拒绝。对此根本没有服务回应。
此情况存在于此配置中:
@Configuration
@EnableWebSecurity(debug = true)
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurity extends WebSecurityConfigurerAdapter {
private Environment environment;
private UserService service;
final Logger logger = LoggerFactory.getLogger(getClass());
@Autowired
public WebSecurity(Environment environment,
UserService service) {
this.environment = environment;
this.service = service;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterBefore(new CustomCorsFilter(), UsernamePasswordAuthenticationFilter.class);
http.csrf().disable();
http.authorizeRequests()
.antMatchers("/users/**")
.hasIpAddress(environment.getProperty("gateway.ip"));
http.headers().frameOptions().disable();
}
}
CustomCorsConfig:
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class CustomCorsFilter extends CorsFilter {
final Logger logger = LoggerFactory.getLogger(getClass());
public CustomCorsFilter() {
super(configurationSource());
}
private static UrlBasedCorsConfigurationSource configurationSource() {
List<String> allHeaders = Arrays.asList("X-Auth-Token",
"Content-Type",
"X-Requested-With",
"XMLHttpRequest",
"Accept",
"Key",
"Authorization",
"X-Authorization");
List<String> allowedMethods = Arrays.asList("GET","POST","OPTIONS");
List<String> allowedOrigins = Arrays.asList("http://localhost:3000", "http://localhost:8082");
CorsConfiguration corsConfig = new CorsConfiguration();
corsConfig.setAllowedHeaders(allHeaders);
corsConfig.setAllowedMethods(allowedMethods);
corsConfig.setAllowedOrigins(allowedOrigins);
corsConfig.setExposedHeaders(allHeaders);
corsConfig.setMaxAge(3600L);
corsConfig.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", corsConfig);
return source;
}
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain)
throws ServletException, IOException {
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
response.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, "http://localhost:3000");
response.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true");
response.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, "GET, POST, OPTIONS");
response.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, "Origin, Content-Type, Accept");
Object[] headerNames = response.getHeaderNames().toArray();
String names = "";
for (Object o : headerNames) {
String s = (String)o;
names += s + ", ";
}
logger.info("\n ** CustomCorsFilter.doFilterInternal(): header names are: " + names + "\n\n");
if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
response.setStatus(HttpServletResponse.SC_OK);
} else {
filterChain.doFilter(request, response);
}
}
@Override
protected boolean shouldNotFilterAsyncDispatch() {
return false;
}
@Override
protected boolean shouldNotFilterErrorDispatch() {
return false;
}
}
Spring Security仅通过过滤器链运行。如果正在使用,则@crossorigin
控制器注释将是无关紧要的(不会应用),因为过滤器链过滤器将在请求到达控制器端点之前执行。
我发现SpringBoot(V 2.6.2)服务处理请求的唯一方法是将标题设置为:
headers.append("Content-Type", "text/plain");
但是,这当然意味着任何/所有服务端点都不能消耗= application = application/ json
,由于从获取客户端指定content-type = application/json
中都将受到选项的访问。
这不可能是Spring Security设想的情况;获取到苏格里式微服务是非常普遍的实现。另外,如果comsumes = text/plain
spring的离框外httpmessageconverters成功地将端点指定的指定@requestbody
映射到指定的域POJO类型 - 尝试
[org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'text/plain;charset=UTF-8' not supported]
此事我知道我在这里缺少什么。另外,是否有办法将这个问题直接置于Spring Security Dev?
If a Fetch POST to a Spring Security (v 5.6.1) enabled service endpoint sends this header:
headers.append("Content-Type", "application/json");
the OPTIONS preflight request will not be handled by any filter in the filter chain - filter logging shows no response whatsoever from any chain filter; there isn't even a server-side invocation of org.apache.catalina.connector.RequestFacade
in response to the preflight call. The HttpResponse the client receives will always just show 403 Unauthorized
.
So it isn't that this kind of request causes a preflight, which would then propagate through the filter chain and be handled by an enabled CorsFilter which would add the requisite headers to satisfy the preflight and return this response. It's that the preflight is apparently simply rejected outright if application/json is the content-type. No service response to it at all.
This situation exists with this config in place:
@Configuration
@EnableWebSecurity(debug = true)
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurity extends WebSecurityConfigurerAdapter {
private Environment environment;
private UserService service;
final Logger logger = LoggerFactory.getLogger(getClass());
@Autowired
public WebSecurity(Environment environment,
UserService service) {
this.environment = environment;
this.service = service;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterBefore(new CustomCorsFilter(), UsernamePasswordAuthenticationFilter.class);
http.csrf().disable();
http.authorizeRequests()
.antMatchers("/users/**")
.hasIpAddress(environment.getProperty("gateway.ip"));
http.headers().frameOptions().disable();
}
}
CustomCorsConfig:
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class CustomCorsFilter extends CorsFilter {
final Logger logger = LoggerFactory.getLogger(getClass());
public CustomCorsFilter() {
super(configurationSource());
}
private static UrlBasedCorsConfigurationSource configurationSource() {
List<String> allHeaders = Arrays.asList("X-Auth-Token",
"Content-Type",
"X-Requested-With",
"XMLHttpRequest",
"Accept",
"Key",
"Authorization",
"X-Authorization");
List<String> allowedMethods = Arrays.asList("GET","POST","OPTIONS");
List<String> allowedOrigins = Arrays.asList("http://localhost:3000", "http://localhost:8082");
CorsConfiguration corsConfig = new CorsConfiguration();
corsConfig.setAllowedHeaders(allHeaders);
corsConfig.setAllowedMethods(allowedMethods);
corsConfig.setAllowedOrigins(allowedOrigins);
corsConfig.setExposedHeaders(allHeaders);
corsConfig.setMaxAge(3600L);
corsConfig.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", corsConfig);
return source;
}
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain)
throws ServletException, IOException {
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
response.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, "http://localhost:3000");
response.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true");
response.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, "GET, POST, OPTIONS");
response.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, "Origin, Content-Type, Accept");
Object[] headerNames = response.getHeaderNames().toArray();
String names = "";
for (Object o : headerNames) {
String s = (String)o;
names += s + ", ";
}
logger.info("\n ** CustomCorsFilter.doFilterInternal(): header names are: " + names + "\n\n");
if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
response.setStatus(HttpServletResponse.SC_OK);
} else {
filterChain.doFilter(request, response);
}
}
@Override
protected boolean shouldNotFilterAsyncDispatch() {
return false;
}
@Override
protected boolean shouldNotFilterErrorDispatch() {
return false;
}
}
Spring Security runs only via the filter chain. If it's in use, the @CrossOrigin
controller annotation will be irrelevant (won't be applied) since the filter chain filters will execute before the request gets to the controller endpoint.
The only way I found to get the request handled by the SpringBoot (v 2.6.2) service at all was to set the header as:
headers.append("Content-Type", "text/plain");
But this of course means that any/all service endpoints can't consume = application/json
, since being called from a fetch client specifying content-type = application/json
they would all be subject to an OPTIONS preflight.
This can't be the situation Spring Security envisioned; Fetch-to-SpringSecurity Microservice is a very prevalent implementation. Also, if comsumes = text/plain
none of Spring's out-of-box HttpMessageConverters successfully map the endpoint's specified @RequestBody
to a specified domain POJO type - trying this causes
[org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'text/plain;charset=UTF-8' not supported]
Please let me know what I'm missing here. Also, is there a way to pose this issue directly to Spring Security dev?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
这是解决方案:
Here's the solution: