Keycloak securityContext为空,如何获取已登录用户详情?
问题描述
已从以下地址获取access token:
https://localhost:1099/auth/realms/myrealm/protocol/openid-connect/token
调用/myresources端点时,尝试从HttpSession或HttpServletRequest属性中获取KeycloakSecurityContext,但始终返回No Keycloak Security Context found,securityContext和principal均为空。使用嵌入式Spring Boot Keycloak 21.0.0版本,未集成Spring Security,需要从access token中提取preferred_username等用户详情。
端点代码如下:
import org.keycloak.KeycloakSecurityContext; import org.keycloak.representations.AccessToken; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; @RestController public class userDetails { @GetMapping(value = "/myresources", produces = MediaType.APPLICATION_JSON_VALUE) public String handleUserInfoRequest(HttpServletRequest request) { System.out.println("Using HttpServletRequest to inspect authentication..."); // 尝试从Session获取 HttpSession session = request.getSession(); KeycloakSecurityContext securityContext = (KeycloakSecurityContext) session.getAttribute(KeycloakSecurityContext.class.getName()); // 也尝试过从Request属性获取 // KeycloakSecurityContext securityContext = (KeycloakSecurityContext) request.getAttribute(KeycloakSecurityContext.class.getName()); if (securityContext != null) { AccessToken token = securityContext.getToken(); String userId = token.getId(); String userName = token.getPreferredUsername(); String securityContextDetails = "User ID: " + userId + "\n" + "Username: " + userName; System.out.println("Security Context Details:\n" + securityContextDetails); return securityContextDetails; } else { return "No Keycloak Security Context found"; } } }
解决方案
方法一:配置Keycloak Servlet过滤器(推荐)
未集成Spring Security时,需手动配置Keycloak Servlet过滤器,让它自动解析请求中的access token并将KeycloakSecurityContext注入到request或session中。
- 添加Keycloak Servlet依赖
在pom.xml中加入:
<dependency> <groupId>org.keycloak</groupId> <artifactId>keycloak-servlet-filter</artifactId> <version>21.0.0</version> </dependency>
- 注册Keycloak过滤器
创建配置类注册过滤器:
import org.keycloak.adapters.KeycloakConfigResolver; import org.keycloak.adapters.servlet.KeycloakOIDCFilter; import org.keycloak.adapters.springboot.KeycloakSpringBootConfigResolver; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class KeycloakFilterConfig { @Bean public KeycloakConfigResolver keycloakConfigResolver() { return new KeycloakSpringBootConfigResolver(); } @Bean public FilterRegistrationBean<KeycloakOIDCFilter> keycloakOIDCFilterRegistrationBean() { FilterRegistrationBean<KeycloakOIDCFilter> registrationBean = new FilterRegistrationBean<>(); registrationBean.setFilter(new KeycloakOIDCFilter()); registrationBean.addUrlPatterns("/myresources/*"); // 指定需要拦截的路径 registrationBean.setOrder(1); // 设置过滤器优先级 return registrationBean; } }
- 配置Keycloak客户端信息
在application.properties中添加:
keycloak.realm=myrealm keycloak.auth-server-url=https://localhost:1099/auth keycloak.resource=你的客户端ID keycloak.credentials.secret=你的客户端密钥 keycloak.public-client=false keycloak.bearer-only=true # 资源服务仅验证token,不重定向登录
配置完成后,请求/myresources时需在请求头携带Authorization: Bearer <你的access-token>,过滤器会自动解析token并注入KeycloakSecurityContext,原有代码即可正常提取用户信息。
方法二:手动解析Access Token
若不想配置过滤器,可直接从请求头获取token并手动解析:
import org.keycloak.TokenVerifier; import org.keycloak.representations.AccessToken; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpServletRequest; @RestController public class userDetails { @GetMapping(value = "/myresources", produces = MediaType.APPLICATION_JSON_VALUE) public String handleUserInfoRequest(HttpServletRequest request) { String authHeader = request.getHeader("Authorization"); if (authHeader == null || !authHeader.startsWith("Bearer ")) { return "未提供access token"; } String accessToken = authHeader.substring(7); // 移除"Bearer "前缀 try { // 使用Keycloak TokenVerifier验证并解析(生产环境推荐,会校验签名和有效期) TokenVerifier<AccessToken> verifier = TokenVerifier.create(accessToken, AccessToken.class); // 如需严格验证,可配置Keycloak公钥或让验证器连接服务器校验 // verifier.withPublicKey(你的Keycloak公钥); AccessToken token = verifier.verify().getToken(); String userId = token.getId(); String userName = token.getPreferredUsername(); String email = token.getEmail(); return String.format("User ID: %s\nUsername: %s\nEmail: %s", userId, userName, email); // 仅测试用:跳过签名校验直接解析JWT payload // String[] parts = accessToken.split("\\."); // return new String(Base64.getUrlDecoder().decode(parts[1])); } catch (Exception e) { return "无效的access token: " + e.getMessage(); } } }
注意:跳过签名校验存在安全风险,生产环境必须使用TokenVerifier确保token合法性。
内容的提问来源于stack exchange,提问作者jane
相关产品推荐
相关产品推荐

