1

我用 Keycloak 4.4.0.Final 和 4.6.0.Final 试过这个。我检查了 keycloak 服务器日志,并在控制台输出中看到了以下警告消息。

10:33:22,882 WARN  [org.keycloak.events] (default task-1) type=REFRESH_TOKEN_ERROR, realmId=master, clientId=security-admin-console, userId=null, ipAddress=127.0.0.1, error=invalid_token, grant_type=refresh_token, client_auth_method=client-secret
10:40:41,376 WARN  [org.keycloak.events] (default task-5) type=LOGOUT_ERROR, realmId=demo, clientId=eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJqYTBjX18xMHJXZi1KTEpYSGNqNEdSNWViczRmQlpGS3NpSHItbDlud2F3In0.eyJqdGkiOiI1ZTdhYzQ4Zi1mYjkyLTRkZTYtYjcxNC01MTRlMTZiMmJiNDYiLCJleHAiOjE1NDM0MDE2MDksIm5iZiI6MCwiaWF0IjoxNTQzNDAxMzA5LCJpc3MiOiJodHRwOi8vMTI3Lj, userId=null, ipAddress=127.0.0.1, error=invalid_client_credentials

那么如何构建 HTTP 请求呢?首先,我从 HttpSession 检索用户主体并转换为内部 Keycloak 实例类型:

KeycloakAuthenticationToken keycloakAuthenticationToken = (KeycloakAuthenticationToken) request.getUserPrincipal();
final KeycloakPrincipal keycloakPrincipal = (KeycloakPrincipal)keycloakAuthenticationToken.getPrincipal();
final RefreshableKeycloakSecurityContext context = (RefreshableKeycloakSecurityContext) keycloakPrincipal.getKeycloakSecurityContext();
final AccessToken accessToken = context.getToken();
final IDToken idToken = context.getIdToken();

其次,我在顶部堆栈溢出答案中创建了注销 URL(见上文):

final String logoutURI = idToken.getIssuer() +"/protocol/openid-connect/logout?"+
            "redirect_uri="+response.encodeRedirectURL(url.toString());

现在我构建 HTTP 请求的其余部分,如下所示:

KeycloakRestTemplate keycloakRestTemplate = new KeycloakRestTemplate(keycloakClientRequestFactory);
HttpHeaders headers = new HttpHeaders();
headers.put("Authorization", Collections.singletonList("Bearer "+idToken.getId()));
headers.put("Content-Type", Collections.singletonList("application/x-www-form-urlencoded"));

并构建正文内容字符串:

StringBuilder bodyContent = new StringBuilder();
bodyContent.append("client_id=").append(context.getTokenString())
            .append("&")
            .append("client_secret=").append(keycloakCredentialsSecret)
            .append("&")
            .append("user_name=").append(keycloakPrincipal.getName())
            .append("&")
            .append("user_id=").append(idToken.getId())
            .append("&")
            .append("refresh_token=").append(context.getRefreshToken())
            .append("&")
            .append("token=").append(accessToken.getId());
HttpEntity<String> entity = new HttpEntity<>(bodyContent.toString(), headers);
//   ...
ResponseEntity<String> forEntity = keycloakRestTemplate.exchange(logoutURI, HttpMethod.POST, entity, String.class); // *FAILURE*

正如您所观察到的,我尝试了许多不同的主题,但我不断收到无效的用户身份验证。哦耶。application.properties我使用 @Value 从对象实例字段中注入了 keycloak 凭据秘密

@Value("${keycloak.credentials.secret}")
private String keycloakCredentialsSecret;

Java Spring Security 经验丰富的工程师有什么想法吗?

附录 我在 KC 中创建了一个名为“demo”的领域和一个名为“web-portal”的客户端,其参数如下:

Client Protocol: openid-connect
Access Type: public
Standard Flow Enabled: On
Implicit Flow Enabled: Off
Direct Access Grants Enabled: On
Authorization Enabled: Off

这是重建重定向 URI 的代码,我忘了在这里包含它。

final String scheme = request.getScheme();             // http
final String serverName = request.getServerName();     // hostname.com
final int serverPort = request.getServerPort();        // 80
final String contextPath = request.getContextPath();   // /mywebapp

// Reconstruct original requesting URL
StringBuilder url = new StringBuilder();
url.append(scheme).append("://").append(serverName);

if (serverPort != 80 && serverPort != 443) {
    url.append(":").append(serverPort);
}

url.append(contextPath).append("/offline-page.html");

就这样

4

0 回答 0