SpringBoot集成Keycloak开启策略执行器后未认证请求返回403而非401如何解决
问题根因
开启Keycloak策略执行器后,默认执行逻辑会优先进行权限校验,未携带有效令牌的请求会被直接判定为无访问权限返回403,跳过了原本的身份认证校验环节,因此不会返回预期的401响应。
解决方案
有两种可行的调整方式,优先选择配置修改方式,无需改动代码即可生效。
方式一:调整策略执行器配置
在policy-enforcer-config配置项下新增autodetect-bearer-only: true即可,修改后完整配置如下:
keycloak: realm: ${KEYCLOAK_REALM} auth-server-url: ${KEYCLOAK_AUTH_SERVER_URL} ssl-required: external resource: ${KEYCLOAK_CLIENT_ID} credentials.secret: ${KEYCLOAK_CLIENT_SECRET} use-resource-role-mappings: true cors: true public-client: false bearer-only: true policy-enforcer-config: lazy-load-paths: true http-method-as-scope: true # 新增如下配置,自动识别bearer-only客户端,无令牌时优先返回401 autodetect-bearer-only: true path-cache-config: max-entries: 1000 lifespan: 1000 paths: - name: Insecure Resource path: / enforcement-mode: DISABLED - name: Swagger UI path: /swagger-ui/* enforcement-mode: DISABLED - name: Swagger Resources path: /swagger-resources/* enforcement-mode: DISABLED - name: Swagger api Resources path: /api-docs enforcement-mode: DISABLED securityConstraints: - authRoles: - '*' securityCollections: - name: protected patterns: - '/v1/*' - '/intranet/*'
该配置的作用是让策略执行器自动识别bearer-only类型的客户端,请求无有效令牌时优先触发身份认证逻辑返回401,令牌有效但无对应接口权限时才返回403。
方式二:自定义异常处理器(兼容性更强)
如果配置调整后未生效,可以通过自定义认证入口点的方式强制返回401响应:
步骤1:实现自定义认证入口点
import com.fasterxml.jackson.databind.ObjectMapper; import org.keycloak.adapters.AdapterDeploymentContext; import org.keycloak.adapters.springsecurity.authentication.KeycloakAuthenticationEntryPoint; import org.springframework.http.MediaType; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.time.LocalDateTime; import java.time.ZoneOffset; import java.util.HashMap; import java.util.Map; @Component public class CustomKeycloakAuthenticationEntryPoint extends KeycloakAuthenticationEntryPoint { public CustomKeycloakAuthenticationEntryPoint(AdapterDeploymentContext adapterDeploymentContext) { super(adapterDeploymentContext); } @Override protected void commenceUnauthorizedResponse(HttpServletRequest request, HttpServletResponse response) throws IOException { response.setContentType(MediaType.APPLICATION_JSON_VALUE); response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); ObjectMapper objectMapper = new ObjectMapper(); Map<String, Object> errorMap = new HashMap<>(); errorMap.put("timestamp", LocalDateTime.now(ZoneOffset.UTC)); errorMap.put("status", HttpServletResponse.SC_UNAUTHORIZED); errorMap.put("error", "Unauthorized"); errorMap.put("message", "Invalid or missing access token"); errorMap.put("path", request.getRequestURI()); response.getWriter().write(objectMapper.writeValueAsString(errorMap)); } }
步骤2:注册自定义入口点到安全配置
import org.keycloak.adapters.springsecurity.KeycloakSecurityComponents; import org.keycloak.adapters.springsecurity.authentication.KeycloakAuthenticationProvider; import org.keycloak.adapters.springsecurity.config.KeycloakWebSecurityConfigurerAdapter; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; 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.core.authority.mapping.SimpleAuthorityMapper; import org.springframework.security.web.authentication.session.NullAuthenticatedSessionStrategy; import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy; @Configuration @EnableWebSecurity @ComponentScan(basePackageClasses = KeycloakSecurityComponents.class) public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter { @Autowired private CustomKeycloakAuthenticationEntryPoint customEntryPoint; @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) { KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider(); keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper()); auth.authenticationProvider(keycloakAuthenticationProvider); } @Bean @Override protected SessionAuthenticationStrategy sessionAuthenticationStrategy() { return new NullAuthenticatedSessionStrategy(); } @Override protected void configure(HttpSecurity http) throws Exception { super.configure(http); http.csrf().disable() .exceptionHandling() .authenticationEntryPoint(customEntryPoint) .and() .authorizeRequests() .anyRequest().authenticated(); } }
配置完成后,未携带有效令牌的请求会直接返回符合预期格式的401响应,不会再出现空403的问题。
内容的提问来源于stack exchange,提问作者PRAJIN PRAKASH
相关产品推荐
相关产品推荐

