Spring Boot + REST + Spring Security:如何为HTTP 403生成自定义JSON响应

我有一个受Spring-Security保护的Spring-Boot REST控制器。工作正常。如果客户端发送HTTP请求而在HTTP标头上没有正确的访问令牌,则他会像我期望的那样返回HTTP 403响应。

curl -i localhost:8301 / user / 11:

instance semigroup a => semigroup (Pair a) where
  Pair a1 a2 <> Pair b1 b2 = Pair (a1 <> b1)(a2 <> b2)

instance Monad Pair where
  return = pure
  (Pair a b) >>= f = f a <> f b

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

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"
}

现在我想做的是在HTTP 403的情况下自定义错误响应,我想将自定义JSON响应发送回客户端(与在@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,WebRequest request) { return new ResponseEntity<>(json,httpStatus); } } 中发送回的JSON相同)。

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

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

您能给我正确的方向吗?

woshicgro 回答:Spring Boot + REST + Spring Security:如何为HTTP 403生成自定义JSON响应

您尝试了吗?

@ExceptionHandler({Exception.class})
    public ResponseEntity<Message> handleException(HttpServletRequest httpServletRequest,Throwable ex) {
,

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

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 接口,并将其添加到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,"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");

    }
,

尝试使用这样的自定义身份验证入口点类覆盖WebSecurityConfigurerAdapter中的http.execptionalHandling()。authenticationEntryPoint()。

@Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {

private static final Logger log = 
LoggerFactory.getLogger(JwtAuthenticationEntryPoint.class);

@Override
public void commence(HttpServletRequest request,AuthenticationException authException) throws IOException,ServletException {
    log.error("Responding for UnAuthorized request{} ",authException.getMessage());
    response.sendError(HttpServletResponse.SC_UNAUTHORIZED,authException.getMessage());
}

}

本文链接:https://www.f2er.com/3142934.html

大家都在问