自动化登录服务的实践

在公司内部基础设施建设中,我们计划自建代码托管服务,并选择了 GitLab 作为核心平台。同时,公司日常办公依赖飞书,因此希望实现 基于飞书账号的统一登录(SSO)。

背景与目标

当前环境:

  • 自部署 GitLab 服务(带公网域名)。
  • 公司统一使用飞书账号体系。

目标:

  • 支持通过飞书账号登录 GitLab。
  • 在飞书客户端中访问 GitLab 时,实现自动登录(无感知)。
  • 避免用户手动点击“飞书登录”按钮。

GitLab OAuth 接入

GitLab 本身支持通过 OmniAuth 集成第三方 OAuth 登录。通过修改配置文件 /etc/gitlab/gitlab.rb 即可启用。

# 其他配置 ...

# 启用 OAuth
gitlab_rails['omniauth_enabled'] = true
# OAuth 方式列表
gitlab_rails['omniauth_allow_single_sign_on'] = ['oauth2_generic']
# OAuth 不会禁用自动创建的用户
gitlab_rails['omniauth_block_auto_created_users'] = false
# OAuth 认证方式列表
gitlab_rails['omniauth_providers'] = [
  {
    "name" => "oauth2_generic",
    # 显示的登录按钮文本
    "label" => "飞书登录",
    "app_id" => "自建飞书应用的 APP ID",
    "app_secret" => "自建飞书应用的 APP Secret",
    "args" => {
      "client_options" => {
        # SSO 地址,这里是做了一个中间转发服务,用于统一用户系统入口
        "site" => "https://open-api.example.com/",
        # 认证的 URL
        "authorize_url" => "/authen/v1/index",
        # 获取 Token 的 URL
        "token_url" => "/authen/v1/access_token",
        # 获取用户信息的 URL
        "user_info_url" => "/authen/v1/user_info"
      },
      # 用户信息响应的结构体
      "user_response_structure" => {
        "root_path" => ['data'],   # 飞书用户在 data 里
        "id_path" => "open_id",    # 唯一ID(推荐用 open_id)
        "attributes" => {
          "name" => "name", # 用户名
          "email" => "email", # 用户邮箱
          "image" => "avatar", # 用户头像
        }
      },
      "strategy_class" => "OmniAuth::Strategies::OAuth2Generic"
    }
  }
]

关于 user_response_structure 的更多配置,可以查看文档获取。

配置完毕后,执行 sudo gitlab-ctl reconfigure 重载配置即可。

回到 gitlab.example.com/users/sign_in,就可以在登录界面下方看到自定义的飞书登录了。

自动登录的分析

虽然 OAuth 接入完成,但用户在飞书客户端中访问 GitLab 时,仍需手动点击“飞书登录”按钮。

因为 GitLab 并未提供“自动跳转到某个 OAuth Provider”的能力,登录流程依赖用户主动触发表单提交,
因此需要一种方式来绕过点击行为,实现自动触发登录流程。

在分析 GitLab 登录页时,可以发现:

“飞书登录”本质是一个表单提交,而表单中包含 authenticity_token(CSRF 防护)。

因此,只要我们能获取 authenticity_token 就能构造并提交相同的表单请求。从而实现“模拟点击登录”。

实现自动登录

因为自部署的 GitLab 服务使用了 Nginx 作为反向代理。

因此可以使用 Nginx 增加一个自动化登录的路径。

Nginx 配置

location = /feishu/auto-login {
  # 已登录则直接跳转
  if ($cookie__gitlab_session != "") {
    return 302 /;
  }

  # 非飞书客户端禁止访问
  if ($http_user_agent !~* "(feishu|lark)") {
    return 302 /404;
  }

  # 返回 HTML
  default_type text/html;
  charset utf-8;
  return 200 '<HTML内容见下面>';
}

自动化页面代码

<!DOCTYPE html>
<html>
  <body>
    <p>正在通过飞书登录...</p>
    <script>
      fetch("/users/sign_in", { credentials: "include" })
        .then(res => { 
          if (res.redirected) location.href="/"; 
          return res;
        })
        .then(res => res.text()).then(html => {
          const doc = new DOMParser().parseFromString(html, "text/html");
          const token = doc.querySelector("input[name=\'authenticity_token\']").value;
          const form = document.createElement("form");form.method = "POST";form.action = "/users/auth/oauth2_generic";
          const input = document.createElement("input");
        
          input.name = "authenticity_token";input.value = token;
          input.type = "hidden";form.appendChild(input);
        
          document.body.appendChild(form);
        
          form.submit();
        });
    </script>
  </body>
</html>

完成后,将自建应用的网页入口更换为 https://gitlab.example.com/feishu/auto-login 就可以实现自动跳转 OAuth,自动完成登录流程而无需手动点击按钮。


自动化登录服务的实践
https://www.inksha.com/archives/zi-dong-hua-deng-lu-fu-wu-de-shi-jian
作者
inksha
发布于
2026年04月02日
许可协议