我们以官方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>
我无法在Spring Security Javadocs或reference documentation中找到有关端口的任何信息。
我可以在这做什么?
答案 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();
};
}
}