You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.01 03:48:02