如何将 403 错误映射到用户友好的错误?
How to map 403 error to user friendly error?
我是使用 spring 引导构建 rest APi 的新手。
这是我的 controller
片段
@PreAuthorize("hasRole('ADMIN')")
@PostMapping(value = "/api/post/posts", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<PostDto> createPost(@Valid @RequestBody PostDto postDto) {
System.out.println("postDto : " + postDto.getId());
return new ResponseEntity<>(postService.createPost(postDto), HttpStatus.CREATED);
}
这是我的Security
配置
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true) //give method level security
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests().antMatchers(HttpMethod.GET, "/api/**").permitAll().anyRequest()
.authenticated().and().httpBasic();
}
@Override
@Bean
protected UserDetailsService userDetailsService() {
// In Memory Users
UserDetails ashish = User.builder().username("oxana").password(getPasswordEncoder().encode("password")).roles("USER").build();
UserDetails admin = User.builder().username("admin").password(getPasswordEncoder().encode("admin")).roles("ADMIN").build();
return new InMemoryUserDetailsManager(ashish, admin);
}
@Bean
PasswordEncoder getPasswordEncoder() {
return new BCryptPasswordEncoder();
}
}
我在这里尝试着陆异常
@ExceptionHandler(Exception.class)
public ResponseEntity<Errors> handleGlobalException(Exception exception,
WebRequest webRequest){
Error errorDetails = new Error();
errorDetails.setErrorDesc(exception.getMessage());
errorDetails.setErrorCode(Error.ErrorCodeEnum.BAD_REQUEST);
Errors errors = new Errors();
errors.addErrorsItem(errorDetails);
return new ResponseEntity<>(errors, HttpStatus.INTERNAL_SERVER_ERROR);
}
但它没有出现,并给出了一大堆错误,就像这样
"timestamp": "2022-02-21T11:39:28.797+00:00",
"status": 403,
"error": "Forbidden",
"trace": "org.springframework.security.access.AccessDeniedException: Access is denied\r\n\tat org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.java:73)\r\n\tat org.springframework.security.access.intercept.AbstractSecurityInterceptor.attemptAuthorization(AbstractSecurityInterceptor.java:238)\r\n\tat org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:208)\r\n\tat org.springframework.security.access.intercept.aopalliance.
任何人都可以建议我,我如何处理或捕获此异常以自定义错误,用户无权执行某些操作?
谢谢
更新
按以下方式实施AccessDeniedHandler
@ResponseStatus(value = HttpStatus.FORBIDDEN, reason = "Dont have sufficient priviliges to perform this action")
public class AccessDeniedError implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException exec)
throws IOException, ServletException {
response.sendRedirect("Dont have sufficient priviliges to perform this action");
}
}
现在可以收到这样的消息了
{
"timestamp": "2022-02-21T13:29:08.377+00:00",
"status": 404,
"error": "Not Found",
"message": "No message available",
"path": "/api/post/Dont%20have%20sufficient%20priviliges%20to%20perform%20this%20action"
}
它稍微好一些,但是我怎样才能从上面的响应中控制这些变量 ("error", "message", "status")
值,以便我可以在其中添加我的自定义值?
AccessDeniedException
由 ExceptionTranslationFilter
处理,然后委托 AccessDeniedHandler
将相应的响应写入客户端。
如果您想自定义此行为,那么您可以实现一个 AccessDeniedHandler
,然后将您的实现设置为 HttpSecurity
对象。
MyAccessDeniedHandler.java
public class MyAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
writeCustomResponse(response);
}
private void writeCustomResponse(HttpServletResponse response) {
if (!response.isCommitted()) {
try {
response.setStatus(HttpStatus.FORBIDDEN.value());
response.getWriter().write("{ \"error\": \"User is not authorized.\"}");
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
SecurityConfiguration.java
@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// set the customized AccessDeniedHandler to the HttpSecurity object
http.exceptionHandling().accessDeniedHandler(new MyAccessDeniedHandler());
}
}
我是使用 spring 引导构建 rest APi 的新手。
这是我的 controller
片段
@PreAuthorize("hasRole('ADMIN')")
@PostMapping(value = "/api/post/posts", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<PostDto> createPost(@Valid @RequestBody PostDto postDto) {
System.out.println("postDto : " + postDto.getId());
return new ResponseEntity<>(postService.createPost(postDto), HttpStatus.CREATED);
}
这是我的Security
配置
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true) //give method level security
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests().antMatchers(HttpMethod.GET, "/api/**").permitAll().anyRequest()
.authenticated().and().httpBasic();
}
@Override
@Bean
protected UserDetailsService userDetailsService() {
// In Memory Users
UserDetails ashish = User.builder().username("oxana").password(getPasswordEncoder().encode("password")).roles("USER").build();
UserDetails admin = User.builder().username("admin").password(getPasswordEncoder().encode("admin")).roles("ADMIN").build();
return new InMemoryUserDetailsManager(ashish, admin);
}
@Bean
PasswordEncoder getPasswordEncoder() {
return new BCryptPasswordEncoder();
}
}
我在这里尝试着陆异常
@ExceptionHandler(Exception.class)
public ResponseEntity<Errors> handleGlobalException(Exception exception,
WebRequest webRequest){
Error errorDetails = new Error();
errorDetails.setErrorDesc(exception.getMessage());
errorDetails.setErrorCode(Error.ErrorCodeEnum.BAD_REQUEST);
Errors errors = new Errors();
errors.addErrorsItem(errorDetails);
return new ResponseEntity<>(errors, HttpStatus.INTERNAL_SERVER_ERROR);
}
但它没有出现,并给出了一大堆错误,就像这样
"timestamp": "2022-02-21T11:39:28.797+00:00",
"status": 403,
"error": "Forbidden",
"trace": "org.springframework.security.access.AccessDeniedException: Access is denied\r\n\tat org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.java:73)\r\n\tat org.springframework.security.access.intercept.AbstractSecurityInterceptor.attemptAuthorization(AbstractSecurityInterceptor.java:238)\r\n\tat org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:208)\r\n\tat org.springframework.security.access.intercept.aopalliance.
任何人都可以建议我,我如何处理或捕获此异常以自定义错误,用户无权执行某些操作?
谢谢
更新
按以下方式实施AccessDeniedHandler
@ResponseStatus(value = HttpStatus.FORBIDDEN, reason = "Dont have sufficient priviliges to perform this action")
public class AccessDeniedError implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException exec)
throws IOException, ServletException {
response.sendRedirect("Dont have sufficient priviliges to perform this action");
}
}
现在可以收到这样的消息了
{
"timestamp": "2022-02-21T13:29:08.377+00:00",
"status": 404,
"error": "Not Found",
"message": "No message available",
"path": "/api/post/Dont%20have%20sufficient%20priviliges%20to%20perform%20this%20action"
}
它稍微好一些,但是我怎样才能从上面的响应中控制这些变量 ("error", "message", "status")
值,以便我可以在其中添加我的自定义值?
AccessDeniedException
由 ExceptionTranslationFilter
处理,然后委托 AccessDeniedHandler
将相应的响应写入客户端。
如果您想自定义此行为,那么您可以实现一个 AccessDeniedHandler
,然后将您的实现设置为 HttpSecurity
对象。
MyAccessDeniedHandler.java
public class MyAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
writeCustomResponse(response);
}
private void writeCustomResponse(HttpServletResponse response) {
if (!response.isCommitted()) {
try {
response.setStatus(HttpStatus.FORBIDDEN.value());
response.getWriter().write("{ \"error\": \"User is not authorized.\"}");
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
SecurityConfiguration.java
@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// set the customized AccessDeniedHandler to the HttpSecurity object
http.exceptionHandling().accessDeniedHandler(new MyAccessDeniedHandler());
}
}