如何在Spring Boot集成Keycloak时自定义401和403 HTTP响应?
如何自定义Keycloak相关的401/403错误响应
假设你是基于Spring Boot + Spring Security整合Keycloak的场景,以下几种方案可以实现自定义响应:
方案1:全局异常处理器(最简便)
直接捕获Keycloak抛出的认证/授权异常,自定义响应体结构:
@RestControllerAdvice public class CustomAuthExceptionHandler { // 处理令牌无效/过期的401场景 @ExceptionHandler(OAuth2AuthenticationException.class) public ResponseEntity<Map<String, Object>> handleInvalidToken(OAuth2AuthenticationException ex) { Map<String, Object> response = new HashMap<>(); response.put("code", "INVALID_OR_EXPIRED_TOKEN"); response.put("message", "令牌无效或已过期,请重新登录"); response.put("timestamp", LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME)); return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(response); } // 处理访问被拒绝的403场景 @ExceptionHandler(AccessDeniedException.class) public ResponseEntity<Map<String, Object>> handleAccessDenied(AccessDeniedException ex) { Map<String, Object> response = new HashMap<>(); response.put("code", "ACCESS_DENIED"); response.put("message", "您没有权限访问该资源"); // 不需要堆栈的话直接删掉这行 response.put("error_detail", ex.getMessage()); return ResponseEntity.status(HttpStatus.FORBIDDEN).body(response); } }
这个方案无需修改底层过滤器,直接通过Spring的全局异常捕获机制覆盖默认响应,灵活度高,还能自由控制是否返回异常细节。
方案2:扩展Keycloak认证过滤器(底层控制)
如果需要在令牌校验阶段就直接返回自定义响应,绕过默认的无体401,可以扩展Keycloak的认证过滤器:
@Component public class CustomKeycloakAuthFilter extends KeycloakAuthenticationProcessingFilter { public CustomKeycloakAuthFilter(KeycloakAuthenticationManager authManager, KeycloakDeploymentResolver deploymentResolver) { super(authManager, deploymentResolver); } @Override protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException { // 自定义401响应内容 response.setStatus(HttpStatus.UNAUTHORIZED.value()); response.setContentType(MediaType.APPLICATION_JSON_VALUE); Map<String, Object> responseBody = new HashMap<>(); responseBody.put("code", "TOKEN_VALIDATION_FAILED"); responseBody.put("message", "令牌校验失败,请检查令牌有效性"); responseBody.put("timestamp", System.currentTimeMillis()); new ObjectMapper().writeValue(response.getWriter(), responseBody); } }
同理,若要自定义403响应,可以扩展KeycloakAuthorizationFilter,重写其授权失败的处理逻辑。
方案3:Spring Security配置中统一处理
在Spring Security的配置类里,直接配置认证/授权异常的处理器:
@Configuration @EnableWebSecurity public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { super.configure(http); http .exceptionHandling() // 自定义401响应 .authenticationEntryPoint((request, response, authEx) -> { response.setStatus(HttpStatus.UNAUTHORIZED.value()); response.setContentType(MediaType.APPLICATION_JSON_VALUE); String respJson = "{\"code\":\"UNAUTHORIZED\",\"message\":\"令牌无效或已过期\",\"timestamp\":" + System.currentTimeMillis() + "}"; response.getWriter().write(respJson); }) // 自定义403响应 .accessDeniedHandler((request, response, accessDeniedEx) -> { response.setStatus(HttpStatus.FORBIDDEN.value()); response.setContentType(MediaType.APPLICATION_JSON_VALUE); String respJson = "{\"code\":\"FORBIDDEN\",\"message\":\"无访问权限\",\"detail\":\"" + accessDeniedEx.getMessage() + "\"}"; response.getWriter().write(respJson); }); } // 以下是Keycloak整合Spring Security的必要配置 @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { KeycloakAuthenticationProvider authProvider = keycloakAuthenticationProvider(); authProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper()); auth.authenticationProvider(authProvider); } @Bean @Override protected SessionAuthenticationStrategy sessionAuthenticationStrategy() { return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl()); } @Bean public KeycloakConfigResolver keycloakConfigResolver() { return new KeycloakSpringBootConfigResolver(); } }
这种方案把异常处理和Security配置绑定在一起,适合需要统一管理所有安全相关响应的场景。
内容的提问来源于stack exchange,提问作者Askar
相关产品推荐
相关产品推荐

