Spring Boot 3+OAuth2+Keycloak 无效会话ID及401授权问题求助
Spring Boot 3.1.5 + Keycloak 22.0.5 集成Basic Authentication遇401及无效会话ID问题
我基于Spring Boot 3.1.5、OAuth2集成Keycloak 22.0.5,期望使用Basic Authentication。已在Keycloak端创建Realm、客户端、用户及对应接口权限角色,但请求接口时持续报错:
DEBUG o.s.s.w.s.SessionManagementFilter.doFilter - Request requested invalid session id SESSION_ID_TOKEN
每次请求的SESSION_ID_TOKEN均相同,且始终返回401 Unauthorized。
当前配置
application.yml
origins: http://localhost:9999 issuer: **KEYCLOAK-URL** com: c4-soft: springaddons: oidc: ops: - iss: ${issuer} username-claim: preferred_username authorities: - path: $.realm_access.roles prefix: ROLE_ - path: $.resource_access.*.roles resourceserver: cors: - path: /** allowed-origin-patterns: ${origins} permit-all: - "/example"
WebSecurityConfig
@Configuration @EnableMethodSecurity public class WebSecurityConfig { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http.csrf(AbstractHttpConfigurer::disable); http.authorizeHttpRequests(accessManagement -> accessManagement .anyRequest().authenticated()); http.sessionManagement(sm -> sm.sessionCreationPolicy(SessionCreationPolicy.STATELESS)); http.exceptionHandling(eh -> eh.authenticationEntryPoint((request, response, authException) -> { response.addHeader(HttpHeaders.WWW_AUTHENTICATE, "Bearer realm=\"Restricted Content\""); response.sendError(HttpStatus.UNAUTHORIZED.value(), HttpStatus.UNAUTHORIZED.getReasonPhrase()); })); return http.build(); } }
Keycloak Realm会话配置


问题排查与修复方案
1. 核心配置冲突:未启用Basic Authentication
当前SecurityFilterChain配置的认证入口返回Bearer类型的WWW-Authenticate头,完全没启用Basic Auth支持,这和需求直接冲突。需修改配置添加Basic Auth支持,并整合Keycloak用户校验逻辑:
@Configuration @EnableMethodSecurity public class WebSecurityConfig { @Value("${issuer}") private String issuerUri; @Value("${keycloak.client-id}") private String clientId; @Value("${keycloak.client-secret}") private String clientSecret; @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http.csrf(AbstractHttpConfigurer::disable); http.authorizeHttpRequests(accessManagement -> accessManagement .requestMatchers("/example").permitAll() .anyRequest().authenticated()); // 启用Basic Authentication,替换认证入口为Basic类型 http.httpBasic(basic -> basic.authenticationEntryPoint((request, response, authException) -> { response.addHeader(HttpHeaders.WWW_AUTHENTICATE, "Basic realm=\"Restricted Content\""); response.sendError(HttpStatus.UNAUTHORIZED.value(), HttpStatus.UNAUTHORIZED.getReasonPhrase()); })); http.sessionManagement(sm -> sm.sessionCreationPolicy(SessionCreationPolicy.STATELESS)); return http.build(); } // 配置从Keycloak校验用户凭证的UserDetailsService @Bean public UserDetailsService keycloakUserDetailsService() { return username -> { // 从请求头解析密码,调用Keycloak接口校验用户名密码 RestTemplate restTemplate = new RestTemplate(); MultiValueMap<String, String> params = new LinkedMultiValueMap<>(); params.add("grant_type", "password"); params.add("client_id", clientId); params.add("client_secret", clientSecret); params.add("username", username); // 实际代码中需从Basic Auth头解析密码,此处为示例占位 params.add("password", "用户提交的密码"); ResponseEntity<Map> response = restTemplate.postForEntity( issuerUri + "/protocol/openid-connect/token", params, Map.class ); if (response.getStatusCode().is2xxSuccessful()) { Map<String, Object> body = response.getBody(); List<String> realmRoles = (List<String>) ((Map) body.get("realm_access")).get("roles"); Collection<GrantedAuthority> authorities = realmRoles.stream() .map(role -> new SimpleGrantedAuthority("ROLE_" + role)) .collect(Collectors.toList()); return new User(username, "", authorities); } throw new UsernameNotFoundException("用户不存在或凭证错误"); }; } }
2. 无效会话ID问题处理
报错提示无效会话ID,是因为请求携带了JSESSIONID Cookie,但服务配置了STATELESS(无状态),不会创建或识别会话。解决方式:
- 在请求工具中禁用Cookie(如Postman关闭"Send cookies"选项)
- 或在SecurityFilterChain中添加无效会话处理逻辑:
http.sessionManagement(sm -> sm .sessionCreationPolicy(SessionCreationPolicy.STATELESS) .invalidSessionStrategy((request, response) -> { response.sendError(HttpStatus.UNAUTHORIZED.value(), "服务为无状态,无效会话"); }));
3. Keycloak客户端配置检查
- 确保客户端Access Type设置为
confidential(Basic Auth需要客户端密钥) - 开启Direct Access Grants Enabled(允许使用password grant类型验证用户凭证)
- 确认客户端会话配置继承Realm设置(从截图看Realm会话超时30分钟,无异常)
4. 验证请求格式
请求时必须在Authorization头中传递Base64编码的用户名:密码,格式示例:Authorization: Basic dXNlcjE6cGFzc3dvcmQ=
(dXNlcjE6cGFzc3dvcmQ=是user1:password的Base64编码)
内容的提问来源于stack exchange,提问作者D. D.
相关产品推荐
相关产品推荐

