gpt4 book ai didi

nginx - 通过 openresty 使用 keycloak 对 websocket 进行身份验证

转载 作者:行者123 更新时间:2023-12-03 18:45:11 29 4
gpt4 key购买 nike

目前我有一个包含以下组件的工作解决方案:

  • 带有自定义应用程序的网络服务器
  • Openresty 与 lua
  • key 斗篷

  • 这允许我使用 keycloak 进行身份验证。
    因为我的网络服务器还公开了一个 websocket 主机,所以我也想对这些 websocket 进行身份验证。有没有人有一个示例(nginx 文件作为 lua 文件)可用于使用 openresty 验证 websocket 连接?我看过 https://github.com/openresty/lua-resty-websocket但似乎无法在身份验证部分找到插件的位置。
    一个测试这个的示例客户端应用程序也很棒!

    最佳答案

    我自己想通了。在这里发布我的解决方案以帮助其他人实现同样的目标。
    我有以下代码片段:

    Openresty 配置

    仅适用于 websocket,应放置在服务器部分内:

    set $resty_user 'not_authenticated_resty_user';
    location /ws {
    access_by_lua_file /usr/local/openresty/nginx/conf/lua_access.lua;
    proxy_pass http://<backend-websocket-host>/ws;
    proxy_http_version 1.1;
    proxy_set_header Host $http_host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header X-Forwared-User $resty_user;
    proxy_read_timeout 1d;
    proxy_send_timeout 1d;
    }

    lua_acces.lua
    local opts = {
    redirect_uri = "/*",
    discovery = "http://<keycloak-url>/auth/realms/realm/.well-known/openid-configuration",
    client_id = "<client-id>",
    client_secret = "<client-secret>",
    redirect_uri_scheme = "https",
    logout_path = "/logout",
    redirect_after_logout_uri = "http://<keycloak-url>/auth/realms/realm/protocol/openid-connect/logout?redirect_uri=http%3A%2F%2google.com",
    redirect_after_logout_with_id_token_hint = false,
    session_contents = {id_token=true},
    ssl_verify=no
    }

    -- call introspect for OAuth 2.0 Bearer Access Token validation
    local res, err = require("resty.openidc").bearer_jwt_verify(opts)
    if err or not res then
    print("Token authentication not succeeded")
    if err then
    print("jwt_verify error message:")
    print(err)
    end
    if res then
    print("jwt_verify response:")
    tprint(res)
    end
    res, err = require("resty.openidc").authenticate(opts)
    if err then
    ngx.status = 403
    ngx.say(err)
    ngx.exit(ngx.HTTP_FORBIDDEN)
    end
    end

    if res.id_token and res.id_token.preferred_username then
    ngx.var.resty_user = res.id_token.preferred_username
    else
    ngx.var.resty_user = res.preferred_username
    end

    这仅在 websocket 连接从 keycloak 服务中检索到有效 token 时才允许。
    最后填写resty用户,将认证用户传递给后端应用。

    示例 Java 客户端应用程序

    获取 key 斗篷 token
    package test;

    import org.keycloak.admin.client.Keycloak;
    import org.keycloak.representations.AccessTokenResponse;

    public class KeycloakConnection {
    private Keycloak _keycloak;

    public KeycloakConnection(final String host, String username, String password, String clientSecret, String realm, String clientId) {

    _keycloak = Keycloak.getInstance(
    "http://" + host + "/auth",
    realm,
    username,
    password,
    clientId,
    clientSecret);
    }

    public String GetAccessToken()
    {
    final AccessTokenResponse accessToken = _keycloak.tokenManager().getAccessToken();
    return accessToken.getToken();
    }
    }

    网络套接字

    这个片段只包含我调用来设置 websocket 连接的函数。您仍然必须实例化 _keycloakConnection 对象,在我的情况下,我有一个通用的 _session 字段,可以在每次需要时重复使用 session 。
    private Session GetWebsocketSession(String host)
    {
    URI uri = URI.create("wss://" + host);
    ClientUpgradeRequest request = new ClientUpgradeRequest();
    request.setHeader("Authorization", "Bearer " + _keycloakConnection.GetAccessToken());
    _client = new WebSocketClient();
    try {
    _client.start();
    // The socket that receives events
    WebsocketEventHandler socketEventHandler = new WebsocketEventHandler(this::NewLiveMessageReceivedInternal);
    // Attempt Connect
    Future<Session> fut = _client.connect(socketEventHandler, uri, request);
    // Wait for Connect
    _session = fut.get();

    return _session;
    } catch (Throwable t) {
    _logger.error("Error during websocket session creation", t);
    }
    return null;
    }

    WebsocketEventHandler

    在这个类中注入(inject)一个消费者来消费另一个类中的消息
    package test;

    import org.apache.log4j.Logger;
    import org.eclipse.jetty.websocket.api.Session;
    import org.eclipse.jetty.websocket.api.WebSocketAdapter;

    import java.util.function.Consumer;

    public class WebsocketEventHandler extends WebSocketAdapter
    {
    private final Logger _logger;
    private Consumer<String> _onMessage;

    public WebsocketEventHandler(Consumer<String> onMessage) {
    _onMessage = onMessage;
    _logger = Logger.getLogger(WebsocketEventHandler.class);
    }

    @Override
    public void onWebSocketConnect(Session sess)
    {
    super.onWebSocketConnect(sess);
    _logger.info("Socket Connected: " + sess);
    }

    @Override
    public void onWebSocketText(String message)
    {
    super.onWebSocketText(message);
    _logger.info("Received TEXT message: " + message);
    _onMessage.accept(message);
    }

    @Override
    public void onWebSocketClose(int statusCode, String reason)
    {
    super.onWebSocketClose(statusCode,reason);
    _logger.info("Socket Closed: [" + statusCode + "] " + reason);
    }

    @Override
    public void onWebSocketError(Throwable cause)
    {
    super.onWebSocketError(cause);
    _logger.error("Websocket error", cause);
    }
    }

    发送消息

    创建 _session 后,您可以使用以下行发送数据:
    _session.getRemote().sendString("Hello world");

    这些片段都是我整个解决方案的一小部分。我可能错过了什么。如果有人有问题或这不适用于您的情况,请联系我们,我会提供更多信息。

    关于nginx - 通过 openresty 使用 keycloak 对 websocket 进行身份验证,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55027785/

    29 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com