[英]Spring Boot Custom error object for InsufficientAuthenticationException
我已經為我的 API 實現了身份驗證,它按預期工作。 用戶首先訪問 auth api 通過傳遞用戶名和密碼來獲取令牌。 這個 api 返回一個令牌。 然后,用戶通過傳遞令牌來調用安全 API。 此問題是當用戶傳遞無效令牌或未傳遞令牌時,從 Spring 引導返回默認錯誤 object。 我想自定義這個 object 並且為此,我編寫了一個自定義異常處理程序擴展 ResponseEntityExceptionHandler 但這沒有被觸發,因為在 controller 啟動之前引發了異常。
@ExceptionHandler(value = {InsufficientAuthenticationException.class})
public final ResponseEntity<Object>
authenticationException(InsufficientAuthenticationException ex) {
List<String> details = new ArrayList<>();
details.add("Authentication is required to access this resource");
ErrorResponse error = new ErrorResponse("error", "Unauthorized", details);
return new ResponseEntity(error, HttpStatus.FORBIDDEN);
}
AuthenticationProvider 負責根據客戶端在 header 中發送的身份驗證令牌找到用戶。 這就是我們基於 Spring 的令牌身份驗證提供程序的樣子:
@Component
public class AuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
@Autowired
CustomerService customerService;
@Override
protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken) throws AuthenticationException {
//
}
@Override
protected UserDetails retrieveUser(String userName, UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken) throws AuthenticationException {
Object token = usernamePasswordAuthenticationToken.getCredentials();
return Optional
.ofNullable(token)
.map(String::valueOf)
.flatMap(customerService::findByToken)
.orElseThrow(() -> new UsernameNotFoundException("Cannot find user with authentication token=" + token));
}
令牌認證過濾器負責從header獲取認證過濾器並調用認證管理器進行認證。 這是身份驗證過濾器的樣子:
public class AuthenticationFilter extends AbstractAuthenticationProcessingFilter {
AuthenticationFilter(final RequestMatcher requiresAuth) {
super(requiresAuth);
}
@Override
public Authentication attemptAuthentication(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws AuthenticationException, IOException, ServletException {
Optional tokenParam = Optional.ofNullable(httpServletRequest.getHeader(AUTHORIZATION)); //Authorization: Bearer TOKEN
String token= httpServletRequest.getHeader(AUTHORIZATION);
token= StringUtils.removeStart(token, "Bearer").trim();
Authentication requestAuthentication = new UsernamePasswordAuthenticationToken(token, token);
return getAuthenticationManager().authenticate(requestAuthentication);
}
@Override
protected void successfulAuthentication(final HttpServletRequest request, final HttpServletResponse response, final FilterChain chain, final Authentication authResult) throws IOException, ServletException {
SecurityContextHolder.getContext().setAuthentication(authResult);
chain.doFilter(request, response);
}
}
Spring 安全配置如下所示:
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
private static final RequestMatcher PROTECTED_URLS = new OrRequestMatcher(
new AntPathRequestMatcher("/api/**")
);
AuthenticationProvider provider;
public SecurityConfiguration(final AuthenticationProvider authenticationProvider) {
super();
this.provider = authenticationProvider;
}
@Override
protected void configure(final AuthenticationManagerBuilder auth) {
auth.authenticationProvider(provider);
}
@Override
public void configure(final WebSecurity webSecurity) {
webSecurity.ignoring().antMatchers("/token/**");
}
@Override
public void configure(HttpSecurity http) throws Exception {
http.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.exceptionHandling()
.and()
.authenticationProvider(provider)
.addFilterBefore(authenticationFilter(), AnonymousAuthenticationFilter.class)
.authorizeRequests()
.requestMatchers(PROTECTED_URLS)
.authenticated()
.and()
.csrf().disable()
.formLogin().disable()
.httpBasic().disable()
.logout().disable();
http
.exceptionHandling()
.authenticationEntryPoint(authenticationEntryPoint());
}
@Bean
AuthenticationFilter authenticationFilter() throws Exception {
final AuthenticationFilter filter = new AuthenticationFilter(PROTECTED_URLS);
filter.setAuthenticationManager(authenticationManager());
//filter.setAuthenticationSuccessHandler(successHandler());
return filter;
}
@Bean
AuthenticationEntryPoint forbiddenEntryPoint() {
return new HttpStatusEntryPoint(HttpStatus.FORBIDDEN);
}
@Autowired
private HandlerExceptionResolver handlerExceptionResolver;
public AuthenticationEntryPoint authenticationEntryPoint() {
log.error("in authenticationEntryPoint");
return new AuthenticationEntryPoint() {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException, ServletException {
log.error("in commence");
try {
log.error(authException.getLocalizedMessage());
handlerExceptionResolver.resolveException(request, response, null, authException);
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new ServletException(e);
}
}
};
}
}
PS:參考https://www.javadevjournal.com/spring/securing-a-restful-web-service-with-spring-security/
由於您正在自定義AbstractAuthenticationProcessingFilter
,因此您還可以自定義它的AuthenticationFailureHandler
,它會在attemptAuthentication()
拋出AuthenticationException
時被調用。 然后,您可以在那里處理錯誤。
一個例子是:
public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler{
@Override
public void onAuthenticationFailure(HttpServletRequest request,
HttpServletResponse response, AuthenticationException exception)
throws IOException, ServletException {
//create your custom error object
CustomError error = xxxxx;
response.setStatus(HttpStatus.FORBIDDEN.value());
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
// Format the custom error object as JSON string , for example using Jackson :
ObjectMapper mapper = new ObjectMapper();
response.getWriter().write(mapper.writeValueAsString(error));
}
}
並配置使用它:
@Bean
AuthenticationFilter authenticationFilter() throws Exception {
final AuthenticationFilter filter = new AuthenticationFilter(PROTECTED_URLS);
filter.setAuthenticationFailureHandler(new MyAuthenticationFailureHandler());
return filter;
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.