我怎样才能告诉Spring Security仅对特定端口应用authorizeRequests? [英] How can I tell spring security to apply authorizeRequests just for a specific port?

查看:780
本文介绍了我怎样才能告诉Spring Security仅对特定端口应用authorizeRequests?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我们以新的微服务(使用Spring-Boot)配置了以下方式:官方API位于端口8080上(该端口映射到虚拟网络之外,映射至端口443上的常规HTTPS),而某些管理功能处于启用状态辅助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). /p>

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

  • 在公共端口上,要求对所有内容进行身份验证(并且仅在身份验证后返回404或403)
  • 在管理端口上,完全不需要身份验证(对于不是已配置端点之一的所有内容,返回404).

我在Spring Security Javadocs或解决方案

我找到了解决方法:

此处的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连接的(至少

这不能完全反映问题:在此managementPort只是"/info"和"/health"公共可访问的,而不是所有内容.

您可以使用此

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

以使此端口完全未经授权.

We configured our new micro service (using Spring-Boot) in a way that the official API is on port 8080 (which is be mapped outside of our virtual network to normal HTTPS on port 443), while some management functions are on a secondary HTTP port 7979. These are only used inside the virtual network, and used for monitoring, loadbalancing etc.

All API access needs to be secured with OAuth, while the management functions should be accessible freely inside the network. So we configured Spring security this way (http is a HttpSecurity object):

    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();

This has the effect on both ports: /info and /health are unauthorized, while /warehouses needs authentication, and everything else also needs authentication (returns 401, but when invoking with authentication, it returns 403).

As there is no /info or /health on the public port, these return 404 for unauthorized users, while everything else returns 401. I'm unsatisfied with this and would like to have

  • on the public port, require authentication for everything (and return 404 or 403 only after being authenticated)
  • on the admin port, require no authentication at all (return 404 for everything which is not one of the configured endpoints).

I couldn't find anything about ports in the Spring Security Javadocs or reference documentation.

What can I do here?

解决方案

I found a solution:

The authorizeRequests() method here returns an ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry, which has (from its ancestor class AbstractRequestMatcherRegistry) beside some antMatchers methods also a generic requestMatchers() method, which takes one or more RequestMatcher objects. It turns out this is an interface I can implement myself:

/**
 * 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();
}

(This is Java 8 syntax, with previous Java versions you'll had to write an anyonymous class here.)

While requestMatchers takes several such matchers, it looks like those are connected by OR (at least this example suggests this), thus I used an AndRequestMatcher to connect it to the matcher for the path (and HTTP method)).

The final code looked like this:

@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(); };
}

This does not fully reflect the question: the managementPort has here just "/info" and "/health" public reachable, instead of everything.

You could use this

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

to make this port fully unauthorized.

这篇关于我怎样才能告诉Spring Security仅对特定端口应用authorizeRequests?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆