我正在Spring中创建一个REST API,该API需要在Oauth2客户端凭证流中充当ResouceServer。我试图在开发和测试期间取消对客户端证书的需求,因此我创建了两个配置文件:“本地”和“云”。这是每个配置文件的安全性配置。
云:
@Configuration
@EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true, proxyTargetClass = true)
@Profile("cloud")
public class CloudSecurityConfiguration extends ResourceServerConfigurerAdapter {
@Autowired
private MyEntryPoint myEntryPoint;
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.resourceId("res-id");
resources.authenticationEntryPoint(myEntryPoint);
}
@Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/.../**").fullyAuthenticated();
}
}
-
@Component
@Profile("cloud")
public class MyEntryPoint implements AuthenticationEntryPoint {
@Autowired
private HandlerExceptionResolver handlerExceptionResolver;
@Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException, ServletException {
handlerExceptionResolver.resolveException(request, response, null, authException);
}
}
本地:
@Configuration
@EnableWebSecurity
@Profile("local")
@Order(Ordered.HIGHEST_PRECEDENCE)
@EnableGlobalMethodSecurity(prePostEnabled = true, proxyTargetClass = true)
public class LocalSecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().permitAll();
}
}
({@EnableWebSecurity
用于本地配置文件而不是@EnableResourceServer
,因为它解决了一个奇怪的问题:如果标头中存在令牌(即使不需要),则本地配置文件中的每个端点总是返回错误响应Invalid access token
)
现在的问题是:API仅支持GET方法,我必须遵循严格的规范,即当将具有其他方法(如POST)的请求发送到服务器时,该方法应返回405错误代码。使用安全层,它可以与扩展@ControllerAdvice
的{{1}}类一起正常工作,但是当我尝试通过切换到“本地”配置文件来删除安全层时,在测试中我收到了403-禁止错误,而不是405。
这是我的ResponseEntityExceptionHandler
:
ResponseEntityExceptionHandler
Ex(具有“本地”配置文件):
@ControllerAdvice
public class MyErrorHandler extends ResponseEntityExceptionHandler {
@ExceptionHandler
@ResponseBody
@ResponseStatus(HttpStatus.BAD_REQUEST)
protected ErrorModel handleAllExceptions(ConstraintViolationException e, ServletWebRequest request) {
return exceptionMapping(e, request, HttpStatus.BAD_REQUEST, TypeEnum.V);
}
@ExceptionHandler
@ResponseBody
@ResponseStatus(HttpStatus.UNAUTHORIZED)
protected ErrorModel handleAllExceptions(AuthenticationException e, ServletWebRequest request) {
return exceptionMapping(e, request, HttpStatus.UNAUTHORIZED, TypeEnum.S);
}
@ExceptionHandler
@ResponseBody
@ResponseStatus(HttpStatus.FORBIDDEN)
protected ErrorModel handleAllExceptions(AccessDeniedException e, ServletWebRequest request) {
return exceptionMapping(e, request, HttpStatus.FORBIDDEN, TypeEnum.S);
}
@ExceptionHandler
@ResponseBody
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
protected ErrorModel handleAllExceptions(Exception e, ServletWebRequest request) {
return exceptionMapping(e, request, HttpStatus.INTERNAL_SERVER_ERROR, TypeEnum.S);
}
@Override
protected ResponseEntity<Object> handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException ex,
HttpHeaders headers, HttpStatus status, WebRequest request) {
ErrorModel error = exceptionMapping(ex, (ServletWebRequest) request, status, TypeEnum.S);
return new ResponseEntity<>(error, headers, status);
}
private ErrorModel exceptionMapping(Exception e, ServletWebRequest request, HttpStatus status, TypeEnum type) {
/* build and returns the error view */
}
}
响应:POST http://localhost:8080/.../my-end-point
status 403
此响应具有错误的格式和状态代码!
预期:响应{
"timestamp": "2019-05-30T15:44:36.021-0400",
"status": 403,
"error": "Forbidden",
"message": " Forbidden",
"path": "/.../my-end-point"
}
,根据ErrorModel类格式化。
Ex(具有“云”配置文件):
status: 405
响应:POST http://localhost:8080/.../my-end-point
status 405
此回复具有正确的格式和状态代码。这是期望和要求的!
当在本地配置文件中时,如何确保对POST的POST(或GET以外的任何其他方法)返回405错误?再次强调一下,当应用程序在“云”配置文件中运行时,一切都会按预期运行。