Java 如何告诉spring security仅为特定端口应用授权请求?
我们将新的微服务(使用Spring Boot)配置为官方API位于端口8080上(在虚拟网络外部映射到端口443上的正常HTTPS),而一些管理功能位于辅助HTTP端口7979上。这些仅用于虚拟网络内部,并用于监视、负载平衡等 所有API访问都需要使用OAuth进行保护,而管理功能应该可以在网络内自由访问。因此,我们以这种方式配置Spring安全性(http是一个HttpSecurity对象): 这对两个端口都有影响:Java 如何告诉spring security仅为特定端口应用授权请求?,java,spring,spring-security,port,Java,Spring,Spring Security,Port,我们将新的微服务(使用Spring Boot)配置为官方API位于端口8080上(在虚拟网络外部映射到端口443上的正常HTTPS),而一些管理功能位于辅助HTTP端口7979上。这些仅用于虚拟网络内部,并用于监视、负载平衡等 所有API访问都需要使用OAuth进行保护,而管理功能应该可以在网络内自由访问。因此,我们以这种方式配置Spring安全性(http是一个HttpSecurity对象): 这对两个端口都有影响:/info和/health未经授权,而/warehouses需要身份验证,其他
/info
和/health
未经授权,而/warehouses
需要身份验证,其他所有端口也需要身份验证(返回401,但使用身份验证调用时,返回403)
由于公共端口上没有/info
或/health
,因此对于未经授权的用户,这些端口返回404,而其他所有端口返回401。我对此感到不满意,并希望
- 在公共端口上,要求对所有内容进行身份验证(仅在经过身份验证后返回404或403)
- 在管理端口上,根本不需要身份验证(对于不是配置的端点之一的所有内容,返回404)
authorizeRequests()
方法返回一个ExpressionUrlAuthorizationConfigurer.expressionIntercepturRegistry
,它(从其祖先类AbstractRequestMatcherRegistry)在一些antMatchers
方法旁边还有一个泛型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();
}
(这是Java8语法,在以前的Java版本中,您必须在这里编写一个anyonymous类。)
虽然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”,而不是所有内容
你可以用这个
使此端口完全未经授权。允许对管理终结点进行所有访问的简单方法,无论它们在哪个端口上运行:
http.authorizeRequests()
.requestMatchers(EndpointRequest.toAnyEndpoint()).permitAll();
该方法返回一个仅匹配执行器请求的匹配器
请参阅以获取更完整的示例。基于上一个答案,我开发了一个将管理端口排除在安全性之外的类。我希望这有助于:
@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();
};
}
}
我仍在寻找解决方案+1是的,我有点作弊。我正在打问题,然后(在提交之前)对答案有了想法,并在决定提交问题之前进行了尝试。然后,我只需根据我已有的代码键入答案。好的解决方案是,没有找到一个好的方法来测试运行在动态端口上的服务器。我在测试类中使用了
@SpringBootTest(webEnvironment=SpringBootTest.webEnvironment.DEFINED_PORT)
,否则private int-apiPort
将保持为零,我的所有接口都将被禁止使用403。感谢您的回答,但我们不希望所有端点都使用“allow all”,由于这违背了在所有位置进行授权的目的。EndpointRequest仅匹配执行器请求,因此并非所有请求()都会在答案中添加此澄清,因为Spring选择的类名可能会令人困惑。
.requestMatchers(forPort(managementPort)).anonymous()
@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();
};
}
}