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

如何通过KeyCloak实现Java应用认证授权与跨应用访问?

我来一步步帮你理清这些问题,毕竟Keycloak的客户端定位和认证流程确实容易搞混😉

核心问题拆解与解决方案

1. Keycloak客户端testclient的角色定位

你现在的两个应用分别是:

  • 受保护的Java Servlet:这是资源服务器,负责验证请求合法性,确保只有认证通过的用户/应用能访问
  • 发起请求的Java应用:这是客户端,需要先向Keycloak获取合法令牌,再访问Servlet

所以你的testclient应该代表发起请求的Java应用(也就是那个要调用Servlet的后台服务)。而你的Servlet本身也需要在Keycloak里注册一个独立客户端(比如命名为servlet-resource-client),用来作为资源服务器的身份标识,和Wildfly上的Keycloak适配器做交互。

2. 两端配置步骤

2.1 Servlet端(Wildfly + Keycloak适配器)

  1. 确保Wildfly已安装对应版本的Keycloak适配器(若未安装,先下载适配包并完成安装)
  2. 修改Wildfly的standalone.xml,配置Keycloak子系统:
    • 绑定你的Keycloak独立服务器地址与领域信息
    • 为Servlet注册专属客户端(servlet-resource-client),设置类型为confidential
    • 配置路径保护规则,要求/testservlet路径需testrole角色才能访问
    <subsystem xmlns="urn:jboss:domain:keycloak:1.1">
        <secure-deployment name="testservlet.war">
            <realm>你的领域名称</realm>
            <auth-server-url>http://你的Keycloak地址:8080/auth</auth-server-url>
            <ssl-required>external</ssl-required>
            <resource>servlet-resource-client</resource>
            <credential name="secret">你的servlet客户端密钥</credential>
            <principal-attribute>preferred_username</principal-attribute>
            <policy-enforcer>
                <policy-enforcer-config>
                    <paths>
                        <path name="/testservlet">
                            <methods>
                                <method name="GET">
                                    <scopes>
                                        <scope>testrole</scope>
                                    </scopes>
                                </method>
                            </methods>
                        </path>
                    </paths>
                </policy-enforcer-config>
            </policy-enforcer>
        </secure-deployment>
    </subsystem>
    

2.2 发起请求的Java应用端(Keycloak客户端testclient配置)

  1. 在Keycloak领域内编辑testclient:
    • 设置客户端类型为Confidential(后台服务器应用需存储客户端密钥,不能用公共类型)
    • 开启Resource Owner Password Credentials Grant(该流程适合后台应用用用户名密码直接获取令牌,无需浏览器重定向)
    • 确保testuser拥有的testrole能被该客户端请求到(或直接给testclient分配对应角色,根据你的权限需求选择)
    • 记录testclient的Client ID和Client Secret,后续代码会用到

3. 获取JWT令牌的流程与代码示例

后台Java应用不能沿用手动访问的重定向流程(那是给浏览器设计的Authorization Code Flow),必须用Resource Owner Password Credentials Grant直接向Keycloak请求令牌:

3.1 令牌请求代码示例(Java原生HttpURLConnection)

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;

public class KeycloakTokenFetcher {
    public static String getAccessToken() throws Exception {
        // Keycloak令牌端点地址
        String tokenUrl = "http://你的Keycloak地址:8080/auth/realms/你的领域名/protocol/openid-connect/token";
        
        // 请求参数
        Map<String, String> params = new HashMap<>();
        params.put("grant_type", "password");
        params.put("client_id", "testclient");
        params.put("client_secret", "你的testclient密钥");
        params.put("username", "testuser");
        params.put("password", "testuser的密码");
        params.put("scope", "openid");
        
        // 构建POST请求
        URL url = new URL(tokenUrl);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setRequestMethod("POST");
        conn.setDoOutput(true);
        
        // 写入请求参数
        DataOutputStream out = new DataOutputStream(conn.getOutputStream());
        StringBuilder postData = new StringBuilder();
        for (Map.Entry<String, String> entry : params.entrySet()) {
            if (postData.length() != 0) postData.append('&');
            postData.append(entry.getKey()).append('=').append(entry.getValue());
        }
        out.writeBytes(postData.toString());
        out.flush();
        out.close();
        
        // 读取响应内容
        BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
        String inputLine;
        StringBuilder response = new StringBuilder();
        while ((inputLine = in.readLine()) != null) {
            response.append(inputLine);
        }
        in.close();
        
        // 简单解析JSON获取access_token(生产环境建议用Jackson/Gson等工具解析)
        return response.toString().split("\"access_token\":\"")[1].split("\"")[0];
    }
}

4. 访问受保护的Servlet

拿到access_token后,在访问localhost:8080/testservlet的请求中添加Authorization头即可:

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;

public class ServletClient {
    public static void main(String[] args) throws Exception {
        String accessToken = KeycloakTokenFetcher.getAccessToken();
        
        URL servletUrl = new URL("http://localhost:8080/testservlet");
        HttpURLConnection conn = (HttpURLConnection) servletUrl.openConnection();
        conn.setRequestMethod("GET");
        conn.setRequestProperty("Authorization", "Bearer " + accessToken);
        
        // 读取Servlet响应
        BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
        String inputLine;
        StringBuilder response = new StringBuilder();
        while ((inputLine = in.readLine()) != null) {
            response.append(inputLine);
        }
        in.close();
        
        System.out.println("Servlet响应内容:" + response.toString());
    }
}

补充说明

  • 如果发起请求的Java应用不需要用户身份(仅服务对服务调用),可以改用Client Credentials Grant,无需传入用户名密码,直接用testclient的身份获取令牌,此时需给testclient分配对应的服务角色
  • JWT令牌包含了用户身份、角色等信息,Servlet端的Keycloak适配器会自动验证令牌的合法性、过期时间,以及是否拥有访问所需的角色

内容的提问来源于stack exchange,提问作者Kobbe

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.13 09:05:45