gpt4 book ai didi

java - Spring 启动+休息+ Spring 安全: how to generate a custom JSON response for HTTP 403

转载 作者:行者123 更新时间:2023-12-01 21:53:38 25 4
gpt4 key购买 nike

我有一个 Spring-Boot REST Controller ,受 Spring-Security 保护。效果很好。如果客户端发送 HTTP 请求,但 HTTP header 上没有正确的访问 token ,那么他会返回 HTTP 403 响应,正如我所期望的那样。

curl -i localhost:8301/user/11:

HTTP/1.1 403 
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Thu, 07 Nov 2019 16:25:45 GMT

{
"timestamp" : 1573143945072,
"status" : 403,
"error" : "Forbidden",
"message" : "Access Denied",
"path" : "/user/11"
}

我还有一个自定义错误处理程序,它处理 REST 方法中出现的所有错误:

@Order(Ordered.HIGHEST_PRECEDENCE)
@ControllerAdvice
public class ControllerExceptionHandler extends ResponseEntityExceptionHandler {

@Override
protected ResponseEntity<Object> handleHttpMessageNotReadable(
HttpMessageNotReadableException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
...
return new ResponseEntity<>(json, httpStatus);
}

@Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(
MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {

return new ResponseEntity<>(json, httpStatus);
}
}

现在我想做的是在 HTTP 403 的情况下自定义错误响应,并且我想向客户端发送回自定义 JSON 响应(与我在 ControllerExceptionHandler 中发送回的 JSON 相同) >).

不幸的是,上面的错误处理程序无法处理 HTTP 403,因为请求在到达我的 REST 方法之前被 Spring-Security 阻止了。

似乎我需要向 Spring Security 添加一些额外的代码,但我不确定。

你能帮我指明正确的方向吗?

最佳答案

Spring boot 使用 BasicErrorController 作为全局错误处理程序。即不由 @ExceptionHander 方法处理的异常。要覆盖此默认行为,您需要实现 ErrorController 接口(interface),如下所示。

CustomErrorController.java

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping(path = "/error", produces = MediaType.APPLICATION_JSON_VALUE)
public class CustomErrorController implements ErrorController {

@Override
public String getErrorPath() {
return "/errror";
}

@RequestMapping
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
HttpStatus status = getStatus(request);
if (status == HttpStatus.NO_CONTENT) {
return new ResponseEntity<Map<String, Object>>(status);
}
Map<String, Object> body = new HashMap<String, Object>();
body.put("timestamp", new Date());
body.put("status", HttpStatus.FORBIDDEN.value());
body.put("error", "Forbidden");
body.put("message", "My Custom Error Message");
return new ResponseEntity<>(body, status);
}

protected HttpStatus getStatus(HttpServletRequest request) {
Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");
if (statusCode == null) {
return HttpStatus.INTERNAL_SERVER_ERROR;
}
try {
return HttpStatus.valueOf(statusCode);
} catch (Exception ex) {
return HttpStatus.INTERNAL_SERVER_ERROR;
}
}

}

请注意,使用此方法,您将覆盖对其他 @ExceptionHandler 方法未处理的所有异常(不仅仅是 AccessDeniedException)的响应。

如果您不希望这样,并且只想重写 AccessDeniedException 的响应,那么您需要实现 AccessDeniedHandler 接口(interface),如下所示,并将其添加到您的 spring 中安全的http 配置。

CustomAccessDeniedHandler.java

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;

import com.fasterxml.jackson.core.type.TypeReference;
import com.google.gson.Gson;

public class CustomAccessDeniedHandler implements AccessDeniedHandler {

@Override
public void handle(HttpServletRequest request, HttpServletResponse response,
AccessDeniedException accessDeniedException) throws IOException, ServletException {
Map<String, Object> body = new HashMap<String, Object>();
body.put("timestamp", new Date());
body.put("status", HttpStatus.FORBIDDEN.value());
body.put("error", "Forbidden");
body.put("message", "Custom Error Message from CustomAccessDeniedHandler");
response.setStatus(HttpStatus.FORBIDDEN.value());
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
response.setCharacterEncoding(StandardCharsets.UTF_8.toString());
new Gson().toJson(body, new TypeReference<Map<String, Object>>() {
}.getType(), response.getWriter());

}

}

WebSecurityConfig.java

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {

http.exceptionHandling().accessDeniedHandler(new CustomAccessDeniedHandler()).and().httpBasic().and()
.authorizeRequests().antMatchers("/rest/**").hasAnyRole("ROLE_ADMIN").anyRequest().authenticated().and()
.formLogin().disable();

}

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {

auth.inMemoryAuthentication().withUser("user").password("{noop}password").roles("USER").and().withUser("admin")
.password("{noop}password").roles("USER", "ADMIN");

}

关于java - Spring 启动+休息+ Spring 安全: how to generate a custom JSON response for HTTP 403,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58753546/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com