如何告诉spring security仅为特定端口应用authorizeRequests?

我们以官方API在端口8080(在我们的虚拟网络之外映射到端口443上的普通HTTPS)的方式配置我们的新微服务(使用Spring-Boot),而一些管理function在辅助HTTP上端口7979.这些只在虚拟网络中使用,用于监控,负载均衡等。

所有API访问都需要使用OAuth保护,而管理function应该可以在网络内自由访问。 所以我们用这种方式配置Spring安全性(http是一个HttpSecurity对象):

http .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER) .and() .authorizeRequests() .antMatchers("/info").anonymous() .antMatchers("/health").anonymous() .antMatchers(HttpMethod.GET, "/warehouses/**").access(oauthScopeRead) .antMatchers(HttpMethod.PUT, "/warehouses/**").access(oauthScopeWrite) .anyRequest().denyAll(); 

这对两个端口都有影响: /info/health是未授权的,而/warehouses需要身份validation,其他一切也需要身份validation(返回401,但是在使用身份validation调用时,它返回403)。

由于公共端口上没有/info/health ,因此未经授权的用户返回404,而其他所有用户返回401.我对此不满意并希望拥有

  • 在公共端口上,要求对所有内容进行身份validation(并且仅在经过身份validation后返回404或403)
  • 在管理端口上,根本不需要身份validation(对于不是其中一个配置端点的所有内容,返回404)。

我在Spring Security Javadocs或参考文档中找不到任何有关端口的信息 。

我能在这做什么?

我找到了解决方案:

这里的authorizeRequests()方法返回一个ExpressionUrlAuthorizationConfigurer.ExpressionInterceptUrlRegistry ,它(从其祖先类AbstractRequestMatcherRegistry)旁边的一些antMatchers方法也是一个通用的requestMatchers()方法,它接受一个或多个RequestMatcher对象。 事实certificate这是我自己可以实现的接口:

 /** * A request matcher which matches just a port. * * @param port the port to match. * * @return the new matcher. */ private RequestMatcher forPort(final int port) { return (HttpServletRequest request) -> port == request.getLocalPort(); } 

(这是Java 8语法,对于以前的Java版本,您必须在此处编写任何匿名类。)

虽然requestMatchers需要几个这样的匹配器,看起来它们通过OR连接(至少这个例子暗示了这一点),因此我使用AndRequestMatcher将它连接到路径的匹配器(和HTTP方法))。

最终代码如下所示:

 @Value("${management.port}") private int managementPort; @Value("${server.port}") private int apiPort; /** * Configure scopes for specific controller/httpmethods/roles here. */ @Override public void configure(final HttpSecurity http) throws Exception { //J- http .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER) .and() .authorizeRequests() .requestMatchers(forPortAndPath(managementPort, "/info")).anonymous() .requestMatchers(forPortAndPath(managementPort, "/health")).anonymous() .requestMatchers(forPortAndPath(apiPort, HttpMethod.GET, "/warehouses/**")).access(oauthScopeRead) .requestMatchers(forPortAndPath(apiPort, HttpMethod.PUT, "/warehouses/**")).access(oauthScopeWrite) .anyRequest().denyAll(); //J+ } /** * Creates a request matcher which only matches requests for a specific local port and path (using an * {@link AntPathRequestMatcher} for the path part). * * @param port the port to match * @param pathPattern the pattern for the path. * * @return the new request matcher. */ private RequestMatcher forPortAndPath(final int port, @Nonnull final String pathPattern) { return new AndRequestMatcher(forPort(port), new AntPathRequestMatcher(pathPattern)); } /** * Creates a request matcher which only matches requests for a specific local port, path and request method (using * an {@link AntPathRequestMatcher} for the path part). * * @param port the port to match * @param pathPattern the pattern for the path. * @param method the HttpMethod to match. Requests for other methods will not be matched. * * @return the new request matcher. */ private RequestMatcher forPortAndPath(final int port, @Nonnull final HttpMethod method, @Nonnull final String pathPattern) { return new AndRequestMatcher(forPort(port), new AntPathRequestMatcher(pathPattern, method.name())); } /** * A request matcher which matches just a port. * * @param port the port to match. * * @return the new matcher. */ private RequestMatcher forPort(final int port) { return (HttpServletRequest request) -> { return port == request.getLocalPort(); }; } 

这并没有完全反映这个问题:managementPort在这里只有“/ info”和“/ health”公共可达,而不是一切。

你可以用它

  .requestMatchers(forPort(managementPort)).anonymous() 

使这个端口完全未经授权。

允许所有访问管理端点的直接方式,无论它们运行在哪个端口上:

 http.authorizeRequests() .requestMatchers(EndpointRequest.toAnyEndpoint()).permitAll(); 

org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest #toAnyEndpoint方法返回仅匹配执行器请求的匹配器。

有关更完整的示例,请参阅spring-boot-samples 。