找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

3533

积分

0

好友

469

主题
发表于 1 小时前 | 查看: 3| 回复: 0

1. 功能概述

Envoy 的 OAuth2 过滤器是一个强大的认证和授权工具,它提供了对 OAuth 2.0 协议的完整支持,允许 Envoy 代理在不修改后端服务代码的情况下,实现安全的用户认证和授权功能。该过滤器可以处理 OAuth 2.0 协议的完整流程,包括用户认证、授权码获取、访问令牌交换和会话管理。

OAuth2 过滤器的主要功能特点:

  • 完全支持 OAuth 2.0 协议的授权码流程
  • 内置会话管理功能,使用 HMAC 签名的 Cookie 存储认证信息
  • 支持自定义 Cookie 名称和路径
  • 提供灵活的配置选项,支持各种 OAuth 2.0 提供商
  • 与 Envoy 的架构深度集成,提供高效的请求处理

主要优势:

  1. 完全支持 OAuth 2.0 协议:提供对授权码流程的完整支持
  2. 内置会话管理:使用 HMAC 签名的 Cookie 存储认证信息,确保会话的安全性
  3. 灵活的配置:支持各种 OAuth 2.0 提供商,可自定义 Cookie 名称和路径
  4. 高性能:与 Envoy 的架构深度集成,提供高效的请求处理
  5. 可扩展性:与其他 Envoy 过滤器配合使用,实现更复杂的安全策略

使用场景:

  • 需要实现统一认证架构的微服务系统
  • 希望在不修改后端服务代码的情况下添加认证功能
  • 需要支持多种 OAuth 2.0 提供商的应用程序
  • 需要实现安全的会话管理和访问控制

最佳实践:

  1. 与 CSRF 过滤器配合使用,防止跨站请求伪造攻击
  2. 使用 HTTPS 传输,确保认证过程的安全性
  3. 合理配置 Cookie 的属性,如 HttpOnly 和 Secure
  4. 定期轮换 HMAC 和 Token 密钥,增强安全性

通过合理配置和使用 OAuth2 过滤器,开发人员可以快速实现安全的用户认证和授权功能,同时提高系统的可维护性和可扩展性。

2. 解决的问题

2.1 统一认证架构

  • 提供一种集中式的认证解决方案,避免在每个后端服务中重复实现认证逻辑
  • 简化后端服务的开发和维护,让开发人员专注于业务逻辑
  • 提供一致的用户体验,无论后端服务是如何实现的

2.2 安全性增强

  • 防止未授权的访问,保护后端服务的安全
  • 提供强大的会话管理机制,使用 HMAC 签名的 Cookie 存储认证信息
  • 支持 HTTPS 传输,确保认证过程的安全性
  • 提供签名退出功能,允许用户安全地结束会话

2.3 开发效率提升

  • 简化认证相关的开发工作,减少代码重复
  • 提供灵活的配置选项,支持各种 OAuth 2.0 提供商
  • 与 Envoy 的其他功能(如路由、负载均衡)配合使用,实现复杂的认证策略

2.4 可扩展性

  • 支持添加自定义的认证逻辑
  • 可以与其他 Envoy 过滤器配合使用,实现更复杂的安全策略
  • 提供统计和监控功能,帮助了解认证过程的性能和行为

3. 架构设计

3.1 核心组件架构

Envoy OAuth2 过滤器架构流程

3.2 类图

OAuth2 过滤器核心类与依赖关系

4. 配置用例

4.1 基础配置

http_filters:
- name: envoy.filters.http.oauth2
  typed_config:
    "@type": type.googleapis.com/envoy.extensions.filters.http.oauth2.v3.OAuth2
    config:
      token_endpoint:
        cluster: oauth
        uri: oauth.com/token
        timeout: 3s
      authorization_endpoint: https://oauth.com/oauth/authorize/
      redirect_uri: "%REQ(:x-forwarded-proto)%://%REQ(:authority)%/callback"
      redirect_path_matcher:
        path:
          exact: /callback
      signout_path:
        path:
          exact: /signout
      credentials:
        client_id: foo
        token_secret:
          name: token
          sds_config:
            path: "/etc/envoy/token-secret.yaml"
        hmac_secret:
          name: hmac
          sds_config:
            path: "/etc/envoy/hmac.yaml"
      auth_scopes:
      - user
      - openid
      - email
      resources:
      - oauth2-resource
      - http://example.com

- name: envoy.filters.http.router

route_config:
  name: local_route
  virtual_hosts:
  - name: backend
    domains: ["*"]
    routes:
    - match: { prefix: "/" }
      route: { cluster: backend_cluster }

static_resources:
  clusters:
  - name: backend_cluster
    connect_timeout: 0.25s
    type: STRICT_DNS
    lb_policy: ROUND_ROBIN
    load_assignment:
      cluster_name: backend_cluster
      endpoints:
      - lb_endpoints:
        - endpoint:
            address:
              socket_address:
                address: backend.example.com
                port_value: 80
  - name: oauth
    connect_timeout: 0.25s
    type: STRICT_DNS
    lb_policy: ROUND_ROBIN
    load_assignment:
      cluster_name: oauth
      endpoints:
      - lb_endpoints:
        - endpoint:
            address:
              socket_address:
                address: oauth.com
                port_value: 443
http_filters:
- name: envoy.filters.http.oauth2
  typed_config:
    "@type": type.googleapis.com/envoy.extensions.filters.http.oauth2.v3.OAuth2
    config:
      token_endpoint:
        cluster: oauth
        uri: oauth.com/token
        timeout: 3s
      authorization_endpoint: https://oauth.com/oauth/authorize/
      redirect_uri: "%REQ(:x-forwarded-proto)%://%REQ(:authority)%/callback"
      redirect_path_matcher:
        path:
          exact: /callback
      signout_path:
        path:
          exact: /signout
      credentials:
        client_id: foo
        token_secret:
          name: token
          sds_config:
            path: "/etc/envoy/token-secret.yaml"
        hmac_secret:
          name: hmac
          sds_config:
            path: "/etc/envoy/hmac.yaml"
        cookie_names:
          bearer_token: "MyAccessToken"
          oauth_hmac: "MyHMAC"
          oauth_expires: "MyExpires"
      auth_scopes:
      - user

4.3 与其他过滤器配合使用

http_filters:
- name: envoy.filters.http.csrf
  typed_config:
    "@type": type.googleapis.com/envoy.extensions.filters.http.csrf.v3.CsrfPolicy
    allow_origins:
    - exact: "https://example.com"
    check_request_origin: true

- name: envoy.filters.http.oauth2
  typed_config:
    "@type": type.googleapis.com/envoy.extensions.filters.http.oauth2.v3.OAuth2
    config:
      token_endpoint:
        cluster: oauth
        uri: oauth.com/token
        timeout: 3s
      authorization_endpoint: https://oauth.com/oauth/authorize/
      redirect_uri: "%REQ(:x-forwarded-proto)%://%REQ(:authority)%/callback"
      redirect_path_matcher:
        path:
          exact: /callback
      signout_path:
        path:
          exact: /signout
      credentials:
        client_id: foo
        token_secret:
          name: token
          sds_config:
            path: "/etc/envoy/token-secret.yaml"
        hmac_secret:
          name: hmac
          sds_config:
            path: "/etc/envoy/hmac.yaml"
      auth_scopes:
      - user
      - openid

- name: envoy.filters.http.router

4.4 路由级配置

route_config:
  name: local_route
  virtual_hosts:
  - name: backend
    domains: ["*"]
    routes:
    - match: { prefix: "/public" }
      route: { cluster: public_cluster }
    - match: { prefix: "/api" }
      route: { cluster: api_cluster }
      per_filter_config:
        envoy.filters.http.oauth2:
          "@type": type.googleapis.com/envoy.extensions.filters.http.oauth2.v3.OAuth2
          config:
            token_endpoint:
              cluster: oauth
              uri: oauth.com/token
              timeout: 3s
            authorization_endpoint: https://oauth.com/oauth/authorize/
            redirect_uri: "%REQ(:x-forwarded-proto)%://%REQ(:authority)%/callback"
            redirect_path_matcher:
              path:
                exact: /callback
            signout_path:
              path:
                exact: /signout
            credentials:
              client_id: foo
              token_secret:
                name: token
                sds_config:
                  path: "/etc/envoy/token-secret.yaml"
              hmac_secret:
                name: hmac
                sds_config:
                  path: "/etc/envoy/hmac.yaml"
            auth_scopes:
            - user
            - openid

5. 工作流程分析

5.1 过滤器执行流程

OAuth2 请求处理决策流程

5.2 OAuth 2.0 流程

用户、Envoy、OAuth 提供商交互序列

6. 代码实现 ER 图

OAuth2 过滤器模块与数据流关系

7. 最佳实践

7.1 与 CSRF 过滤器配合使用

http_filters:
- name: envoy.filters.http.csrf
  typed_config:
    "@type": type.googleapis.com/envoy.extensions.filters.http.csrf.v3.CsrfPolicy
    allow_origins:
    - exact: "https://example.com"
    - suffix: ".example.com"
    check_request_origin: true

- name: envoy.filters.http.oauth2
  typed_config:
    "@type": type.googleapis.com/envoy.extensions.filters.http.oauth2.v3.OAuth2
    config:
      # OAuth2 配置

- name: envoy.filters.http.router

7.2 使用 HTTPS 传输

static_resources:
  listeners:
  - name: listener_0
    address:
      socket_address:
        protocol: TCP
        address: 127.0.0.1
        port_value: 443
    filter_chains:
    - filters:
      - name: envoy.filters.network.http_connection_manager
        typed_config:
          "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
          http_filters:
          - name: envoy.filters.http.oauth2
            typed_config:
              "@type": type.googleapis.com/envoy.extensions.filters.http.oauth2.v3.OAuth2
              config:
                # OAuth2 配置
          - name: envoy.filters.http.router
            typed_config:
              "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
          tracing: {}
          codec_type: "AUTO"
          stat_prefix: ingress_http
          route_config:
            virtual_hosts:
            - name: backend
              domains: ["*"]
              routes:
              - match: { prefix: "/" }
                route: { cluster: backend_cluster }
    transport_socket:
      name: envoy.transport_sockets.tls
      typed_config:
        "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext
        common_tls_context:
          tls_certificates:
          - certificate_chain: { filename: "/etc/envoy/cert.pem" }
            private_key: { filename: "/etc/envoy/key.pem" }
http_filters:
- name: envoy.filters.http.oauth2
  typed_config:
    "@type": type.googleapis.com/envoy.extensions.filters.http.oauth2.v3.OAuth2
    config:
      token_endpoint:
        cluster: oauth
        uri: oauth.com/token
        timeout: 3s
      authorization_endpoint: https://oauth.com/oauth/authorize/
      redirect_uri: "%REQ(:x-forwarded-proto)%://%REQ(:authority)%/callback"
      redirect_path_matcher:
        path:
          exact: /callback
      signout_path:
        path:
          exact: /signout
      credentials:
        client_id: foo
        token_secret:
          name: token
          sds_config:
            path: "/etc/envoy/token-secret.yaml"
        hmac_secret:
          name: hmac
          sds_config:
            path: "/etc/envoy/hmac.yaml"
        cookie_names:
          bearer_token: "MyAccessToken"
          oauth_hmac: "MyHMAC"
          oauth_expires: "MyExpires"
      auth_scopes:
      - user

7.4 配置密码资源

static_resources:
  secrets:
  - name: token
    generic_secret:
      secret: <Your token secret here>
  - name: hmac
    generic_secret:
      secret: <Your hmac secret here>

http_filters:
- name: envoy.filters.http.oauth2
  typed_config:
    "@type": type.googleapis.com/envoy.extensions.filters.http.oauth2.v3.OAuth2
    config:
      # OAuth2 配置
      credentials:
        client_id: foo
        token_secret:
          name: token
          sds_config:
            path: "/etc/envoy/token-secret.yaml"
        hmac_secret:
          name: hmac
          sds_config:
            path: "/etc/envoy/hmac.yaml"

8. 代码实现细节

8.1 过滤器初始化

OAuth2Filter::OAuth2Filter(FilterConfigSharedPtr config, std::unique_ptr<OAuth2Client>&& oauth_client, TimeSource& time_source)
    : validator_(std::make_shared<OAuth2CookieValidator>(time_source, config->cookieNames())),
      oauth_client_(std::move(oauth_client)),
      config_(std::move(config)),
      time_source_(time_source) {
  oauth_client_->setCallbacks(*this);
}
bool OAuth2Filter::canSkipOAuth(Http::RequestHeaderMap& headers) const {
  validator_->setParams(headers, config_->tokenSecret());
  if (validator_->isValid()) {
    config_->stats().oauth_success_.inc();
    if (config_->forwardBearerToken() && !validator_->token().empty()) {
      setBearerToken(headers, validator_->token());
    }
    return true;
  }

  for (const auto& matcher : config_->passThroughMatchers()) {
    if (matcher.matchesHeaders(headers)) {
      return true;
    }
  }

  return false;
}

8.3 处理回调请求

Http::FilterHeadersStatus OAuth2Filter::decodeHeaders(Http::RequestHeaderMap& headers, bool) {
  // 检查是否是回调请求
  if (config_->redirectPathMatcher().match(headers.Path()->value().getStringView())) {
    const auto query_parameters = Http::Utility::parseQueryString(headers.Path()->value().getStringView());

    // 获取授权码和状态
    const auto auth_code = query_parameters["code"];
    const auto state = Http::Utility::PercentEncoding::decode(query_parameters["state"]);

    // 交换授权码获取访问令牌
    oauth_client_->asyncGetAccessToken(auth_code, config_->clientId(), config_->clientSecret(), config_->redirectUri());

    return Http::FilterHeadersStatus::StopAllIterationAndBuffer;
  }

  // 其他处理逻辑
  return Http::FilterHeadersStatus::Continue;
}

8.4 交换授权码获取访问令牌

void OAuth2ClientImpl::asyncGetAccessToken(const std::string& auth_code, const std::string& client_id,
                                           const std::string& secret, const std::string& cb_url) {
  const auto encoded_client_id = Http::Utility::PercentEncoding::encode(client_id, ":/=&?");
  const auto encoded_secret = Http::Utility::PercentEncoding::encode(secret, ":/=&?");
  const auto encoded_cb_url = Http::Utility::PercentEncoding::encode(cb_url, ":/=&?");

  Http::RequestMessagePtr request = createPostRequest();
  const std::string body = fmt::format(GetAccessTokenBodyFormatString, auth_code, encoded_client_id,
                                       encoded_secret, encoded_cb_url);
  request->body().add(body);
  request->headers().setContentLength(body.length());

  dispatchRequest(std::move(request));
}
void OAuth2Filter::onGetAccessTokenSuccess(const std::string& access_code,
                                           const std::string& id_token,
                                           const std::string& refresh_token,
                                           std::chrono::seconds expires_in) {
  // 生成新的 Cookie
  const auto new_epoch = time_source_.systemTime() + expires_in;
  new_expires_ = std::to_string(
      std::chrono::duration_cast<std::chrono::seconds>(new_epoch.time_since_epoch()).count());

  finishFlow();
}

void OAuth2Filter::finishFlow() {
  // 计算 HMAC 签名
  std::string token_payload = absl::StrCat(host_, new_expires_, access_token_, id_token_, refresh_token_);
  auto& crypto_util = Envoy::Common::Crypto::UtilitySingleton::get();
  auto token_secret = config_->tokenSecret();
  std::vector<uint8_t> token_secret_vec(token_secret.begin(), token_secret.end());
  const std::string pre_encoded_token =
      Hex::encode(crypto_util.getSha256Hmac(token_secret_vec, token_payload));
  std::string encoded_token;
  absl::Base64Escape(pre_encoded_token, &encoded_token);

  // 设置 Cookie
  Http::ResponseHeaderMapPtr response_headers{Http::createHeaderMap<Http::ResponseHeaderMapImpl>(
      {{Http::Headers::get().Status, std::to_string(enumToInt(Http::Code::Found))}})};

  const CookieNames& cookie_names = config_->cookieNames();
  const std::string cookie_tail = fmt::format(CookieTailFormatString, new_expires_);

  response_headers->addReferenceKey(
      Http::Headers::get().SetCookie,
      absl::StrCat(cookie_names.oauth_hmac_, "=", encoded_token, cookie_tail));
  response_headers->addReferenceKey(
      Http::Headers::get().SetCookie,
      absl::StrCat(cookie_names.oauth_expires_, "=", new_expires_, cookie_tail));

  // 重定向到原始路径
  response_headers->setLocation(state_);
  decoder_callbacks_->encodeHeaders(std::move(response_headers), true);
}

以上便是 Envoy OAuth2 过滤器的核心实现分析。将认证授权下沉至 Service Mesh 代理层,让后端不再关心安全协议,是云原生架构中常见的解耦手段。更多围绕 Service Mesh 和中间件技术的讨论,欢迎来云栈社区一同交流。




上一篇:H200单日暴涨30%,H100供应告急,Karpathy也被算力难住
下一篇:Google搜索告别链接:AI Mode颠覆Web,网站沦为Agent饲料
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-5-24 21:34 , Processed in 0.608824 second(s), 42 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表