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

时间:2016-03-02 18:52:21

标签: java spring spring-security port

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

所有API访问都需要使用OAuth保护,而管理功能应该可以在网络内自由访问。所以我们以这种方式配置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需要身份验证,其他所有内容也需要身份验证(返回401,但在使用身份验证调用时,它返回403)。

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

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

我无法在Spring Security Javadocs或reference documentation中找到有关端口的任何信息。

我可以在这做什么?

3 个答案:

答案 0 :(得分:8)

我找到了解决方案:

这里的authorizeRequests()方法返回ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry,除了一些antMatchers方法之外,它还有(从它的祖先类AbstractRequestMatcherRegistry)一个通用的requestMatchers()方法,它取一个或更多RequestMatcher个对象。事实证明这是我自己可以实现的界面:

/**
 * 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连接(至少this example suggests这个),因此我使用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(); };
}

这并不能完全反映这个问题:管理端口只有&#34; / info&#34;和&#34; / health&#34;公共可达,而不是一切。

您可以使用此

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

使此端口完全未经授权。

答案 1 :(得分:1)

一种允许所有访问管理端点的简单方法,无论它们在哪个端口上运行:

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

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

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

答案 2 :(得分:1)

基于上一个答案,我开发了此类,该类从安全性中排除了管理端口。我希望这会有所帮助:

@EnableWebSecurity
@Configuration
public class WebSecurityAdapter extends WebSecurityConfigurerAdapter {
  /**
   * Puerto de administración por donde escucha Actuator.
   */
  @Value("${management.server.port}")
  private int managementPort;
  /*
   * (non-Javadoc)
   * 
   * @see org.springframework.security.config.annotation.web.configuration.
   * WebSecurityConfigurerAdapter#configure(org.springframework.security.config.
   * annotation.web.builders.WebSecurity)
   */
  @Override
  public void configure(WebSecurity web) throws Exception {
    web.ignoring().requestMatchers(forPort(managementPort));
  }
  /**
   * @param port
   *          Puerto que se desea comparar con el puerto de la respuesta http.
   * @return Verdadero si el puerto de la respuesta http es igual al puerto
   *         especificado.
   */
  private RequestMatcher forPort(int port) {
    return (HttpServletRequest request) -> {
      return port == request.getLocalPort();
    };
  }
}