我如何说服 spring 4.2 将 OPTIONS 请求传递给控制器
Posted
技术标签:
【中文标题】我如何说服 spring 4.2 将 OPTIONS 请求传递给控制器【英文标题】:how can i convince spring 4.2 to pass OPTIONS request through to the controller 【发布时间】:2016-06-29 04:19:24 【问题描述】:我们在控制器上使用带有@RestController 注释的spring mvc,并且我们正在控制器中处理授权。我们使用相同的代码来设置响应 CORS 飞行前请求的允许方法。为此,我们有:
<init-param>
<param-name>dispatchOptionsRequest</param-name>
<param-value>true</param-value>
</init-param>
在dispatcher servlet的配置中,然后我们有:
@RequestMapping(value="/some/collections", method=RequestMethod.OPTIONS)
public void collectionOptions(
HttpServletRequest req,
HttpServletResponse res)
List<RequestMethod> methods = new ArrayList<>();
// check actual permissions, add the appropriate methods
CORS.setAllowedMethodHeaders(res,methods);
我们还有一个拦截器,可以在 CORS 飞行前对 CORS 进行基本检查,以查看来源是否可能拥有任何权限。
我们这样做主要是因为某些请求的权限实际上取决于@RequestParams,即:
OPTIONS /api/collections?userId=122
如果您具有管理权限或者您实际上是 ID 为 122 的用户,则可能会被允许。此外,我们有 API 密钥,所以
OPTIONS /api/collections?userId=122&apiKey=ABC
对于一个来源可能没问题,但对于另一个来源则不行。
这工作正常,但 Spring 4.2 现在决定是否处理 OPTIONS 请求,方法是调用:
CorsUtils.isCorsRequest(request);
在AbstractHandlerMapping中然后返回
HandlerInterceptor[] interceptors = chain.getInterceptors();
chain = new HandlerExecutionChain(new PreFlightHandler(config), interceptors);
而不是 HandlerMethod ...
我们需要的是某种方式告诉 spring 让控制器处理 OPTIONS 请求,无论存在什么预检请求处理程序。
我们似乎无法找到一个点,我们可以告诉内置的 CORS 处理是安静的,或者在某处配置一些子类以允许我们绕过新添加的代码:
AbstractHandlerMapping.getHandler(HSR request)
这有可能吗?在我主动启用它之前(通过 WebMvcConfigurerAdapter 或通过那些 @CrossOrigin 注释),让这样的功能保持安静不是很好吗?
-------- 编辑 -------------
HTTP 标准对 OPTIONS 方法有如下说明:
OPTIONS 方法表示请求有关在由 Request-URI 标识的请求/响应链上可用的通信选项的信息。此方法允许客户端确定与资源相关的选项和/或要求,或服务器的功能,而无需暗示资源操作或启动资源检索。
只考虑 CORS,我认为拦截 CORS 选项调用虽然在控制器上映射了相应的方法不是正确的方法。是的,CORS 是您可以通过 OPTIONS 调用完成的一件事。但它绝不是唯一的。
if 没有任何映射,并且如果处理程序方法映射到不同的请求方法和 @CrossOrigin 注释,我希望触发内置 CORS 支持的假设会很好,但我不认为任何设置了原始标头的请求都应该自动只转到 CORS 处理程序。
【问题讨论】:
混合访问控制和 CORS 是个坏主意。例如,GET 请求没有预检请求,因此无论如何您都需要一个单独的安全层。 CORS 是关于浏览器,而不是服务器。 我并没有幻想 CORS 实际上阻止任何人调用东西。但是我见过的一些例子(Access-Control-Allow-Origin:*)对东西并不完全挑剔。我们有更多的信息可以用来做这个决定。而且我们显然也在实际通话中强制执行授权。我们使用 OPTIONS 调用的目的是允许 FE 根据用户的实际权限启用/禁用某些事情,而无需重复授权决策逻辑。 为什么不直接将OPTIONS
更改为GET
,以允许mvc 处理程序照常进行映射?
【参考方案1】:
我刚刚说服 Spring 4.3 通过添加自定义处理程序映射将 CORS 预检传递给我的控制器:
public class CorsNoopHandlerMapping extends RequestMappingHandlerMapping
public CorsNoopHandlerMapping()
setOrder(0); // Make it override the default handler mapping.
@Override
protected HandlerExecutionChain getCorsHandlerExecutionChain(HttpServletRequest request,
HandlerExecutionChain chain, CorsConfiguration config)
return chain; // Return the same chain it uses for everything else.
注意:您仍然需要告诉 Spring 将 OPTIONS 请求发送到您的控制器开始 - 也就是说,在 dispatcherServlet 中将 dispatchOptionsRequest
设置为 true
,就像它在 this question 中所说的那样。
为什么会这样
Sébastien 的above answer 建议使用您自己的CorsProcessor
。据我所知,这仍然不会使用您的控制器作为处理程序;它只会将不同的 CorsProcessor 传递给它自己的 CORS 处理程序。
默认情况下,AbstractHandlerMapping#getCorsHandlerExecutionChain
方法在检测到预检时会丢弃您的控制器。它不使用您的控制器作为处理程序,而是实例化一个新的 PreFlightHandler 并使用它。见Spring source code。这是有问题的行:
chain = new HandlerExecutionChain(new PreFlightHandler(config), interceptors);
它在这里所做的是用 PreFlightHandler 而不是您的控制器重建执行链。这不是我们想要的,所以我们可以重写它以返回输入链。
【讨论】:
【参考方案2】:正如 zeroflagl 所建议的,我也认为混合访问控制和 CORS 并不是一个好主意。您应该记住,只有预检 CORS 请求是 OPTIONS
的。
如果您需要自定义 Spring CORS 处理,您可以使用 AbstractHandlerMapping#setCorsProcessor()
提供您自己的实现,该实现最终可以扩展 DefaultCorsProcessor
。
请注意,默认情况下,CORS 处理已启用,但不允许远程来源,因此很少需要自定义 CorsProcessor
。有关this blog post 的更多信息。
您的访问控制检查可以作为常规的HandlerInterceptor
进行,因为成功的 CORS 预检请求之后将是一个实际的请求。
【讨论】:
对预检请求的响应实际上与访问控制无关。我们只是在那里使用相同的基本逻辑。我们用有效的方法来回答,以便让FE提前确定授权。问题是“我可以在这种情况下创建/删除/更新/读取这个对象吗”并且相同的答案用于 ACTUAL 调用(当调用实际控制器时)和预检请求(以便 FE 可以确定特定对象的权限,而无需实际尝试)。 @zeroflagL 我们遇到的问题是 OPTIONS 请求永远不会到达控制器(它已经必须知道授权问题的答案)。所以我们想要的只是一个直接通过的选项。 @rmalchowGET
请求(=读取)不受 CORS 约束。对于不是由浏览器发出的请求也是如此。 CORS 的想法是允许(而不是拒绝)在浏览器中运行的 javascript 应用程序发出本来会被禁止的请求。正如你所说,挑剔是没有意义的。老实说,我看不出你的方法的价值。如果您坚持,我会遵循@Sebestien 的回答。毕竟是权威答案。 +1
@zeroflag 我添加了更多上下文。我的观点是 CORS 不是 OPTIONS 调用的唯一应用程序。我想要的是有可能告诉请求映射器直接通过它,或者可能以某种方式让一个通用的 CORS 处理程序反过来通过。但目前的实现似乎不允许这样做。例如,将 实际上 映射到 DefaultCorsHandler 中的 HandlerMethod 会有所帮助,这样我就可以从自定义子类中调用它。
@rmalchow “CORS 不是 OPTIONS 调用的唯一应用程序” 没错,但您指的是专门的预检请求。您的客户不会故意发送OPTIONS
请求,是吗?将 CORS 与其他任何东西混合并不是一个好主意,这是您尝试做的事情。以上是关于我如何说服 spring 4.2 将 OPTIONS 请求传递给控制器的主要内容,如果未能解决你的问题,请参考以下文章