GitLab Auth2授权-Express + passport-gitlab2

GitLab授权流程

前期准备

  • 需要服务端支持,Node.js
  • 如果需要将GitLab的Token和其他服务关联,以达到一个Token即可以访问GitLab和其他服务,则需要后端支持

授权流程

  1. 用户点击授权

  2. 重定向到GitLab授权页面

    //gitlab-xxx.com/oauth/authorize

    query参数:

    response_type: 授权类型,授权码为 'code'

    redirect_uri: 回调URL

    scope:授权的权限

    client_id: 应用ID

    输入账号密码,登录

    PS: 首次登录该应用需要授权

  3. GitLab重定向到回调URL

    query参数中携带授权码

    //192.168.1.10:3090/oauth/authorize?code=cdc03d4d23333cbcfcdb58c736762af854bc

  4. 根据授权码获取Token和用户信息

  5. 如果是私有化部署,需要将GitLab地址改为部署地址

授权步骤

此案例讨论Token关联的情况,需要后端支持

  1. 创建应用

    获取应用ID(clientID)、应用密钥(clientSecret)

    注意

    回调URL(Redirect URI)

    1. 不能使用localhost
    2. 不能直接写前端项目地址
    3. 需要关联Token的,请填写后端服务的接口地址,GitLab返回的用户信息&Token后端需要处理
  2. 安装相关依赖

    复制代码
    npm install passport-gitlab2 express-session
  3. 在Node.js中添加依赖信息

    ini 复制代码
    const passport = require('passport');
    const session = require('express-session');
    const GitLabStrategy = require('passport-gitlab2').Strategy;
  4. 启用会话的定义描述

    php 复制代码
    // session 配置,要在接口路由之前
      SERVER.use(session({
        secret: '自己项目的密钥',
        resave: false,
        saveUninitialized: false,
        cookie: {
          httpOnly: true,
          secure: false, // 开发环境设为 false
          maxAge: 1000 * 60 * 30
        }
      }));
  5. 使用passport中间件

    ini 复制代码
    SERVER.use(passport.initialize());
    SERVER.use(passport.session());
  6. 描述认证URL和回调URL的定义

    typescript 复制代码
    // 触发 GitLab OAuth 授权
    ROUTER.get('/auth/gitlab', passport.authenticate('gitlab'));
    ​
    // GitLab OAuth 授权处理回调
    ROUTER.get('/auth/gitlab/callback',
      (req: any, res: any) => {
        const QUERY: ITokenQuery = req.query;
        const SESSION = req.session;
        // 设置 token 信息
        const TOKEN: IToken = {
          access_token: QUERY.access_token,
          created_at: QUERY.expires_in,
          refresh_token: QUERY.refresh_token,
          scope: GITLAB_AUTH.scope,
          token_type: QUERY.token_type
        };
        // 在 session 中设置 token 信息
        if (SESSION) {
          SESSION[IP_CONFIG.store.token] = TOKEN;
        }
        const GITLAB_API = new Gitlab({
          host: GITLAB_AUTH.baseURL,
          oauthToken: QUERY.access_token?.toString() || '',
        });
        // 获取用户信息
        GITLAB_API.Users.showCurrentUser().then((user) => {
          if (user !== null && SESSION) {
            SESSION[IP_CONFIG.store.userInfo] = user;
          }
          // 授权成功重定向到加载页面,设置Token和用户信息,完成后跳转到指定系统页面
          res.redirect('/ipmanage/blank/loading');
        }).catch((error) => {
          // eslint-disable-next-line no-console
          console.error('error=========', error);
          res.redirect('/ipmanage/blank/loading');
        });
      });
  7. 定义了从GitLab返回的认证处理

    less 复制代码
    // Passport 策略配置
    passport.use(
      new GitLabStrategy({
        baseURL: GITLAB_AUTH.baseURL,
        authorizationURL: GITLAB_AUTH.authorizationURL,
        clientSecret: GITLAB_AUTH.clientSecret,
        clientID: GITLAB_AUTH.clientID,
        callbackURL: GITLAB_AUTH.callbackURL,
        scope: GITLAB_AUTH.scope,
        // 设置回调函数第一个参数为 request
        passReqToCallback: true
      },
      // 回调函数,如果回调地址为Node接口,接口使用passport.authenticate('gitlab')才会调用次回调
      (request: IncomingMessage,
        accessToken: string,
        refreshToken: string,
        profile: any,
        done: any) => { })
    );

    如果回调URL为后端接口地址,则不会触发此回调

  8. 定义获取Session中的Token和用户信息的接口

    由于Node.js是服务端,无法操作SessionStorage和LocalStorage

    所以需要前端调用Node服务端接口获取再保存

    ini 复制代码
    ROUTER.get('/get-session', (req: any, res: any) => {
      const SESSION = req.session;
      if (SESSION) {
        const DATA: NodeSessionResult = {
          token: SESSION[IP_CONFIG.store.token],
          userInfo: SESSION[IP_CONFIG.store.userInfo]
        };
        res.json(successHandle(req, DATA, 'Token和用户信息获取成功'));
      } else {
        res.status(500).json(errorHandle(req, -1, 'Token和用户信息获取失败'));
      }
    });
  9. 前端触发GitLab OAuth授权

    login.component.html

    xml 复制代码
    <!-- 登录页面 -->
    <button nz-button class="login-form-button login-form-margin" [nzType]="'primary'" (click)="authLogin()">登录</button>

    login.component.ts

    ini 复制代码
    authLogin() {
        const URL = '/apis/auth/gitlab';
        // 重定向到GitLab授权页面
        window.location.href = URL;
    }
  10. 前端定义获取用户信息的接口

    yunAdmin.service.ts

    kotlin 复制代码
    /**
     * @description 获取Session中的用户&登录信息
     */
    getNodeSessionInfo(): Observable<NodeSessionResult> {
      const URL = '/apis/get-session';
      return this.http.get<NodeSessionResult>(URL)
        .pipe(tap(data => this._logger.log('获取Session信息', {
            params: {},
            result: data
        })));
    }
  11. 加载页面获取用户信息&Token

    地址:/ipmanage/blank/loading 对应的页面

    javascript 复制代码
    /**
     * 获取登录数据
     */
    getLoginData() {
        // 获取session中的token和用户信息
        this.yunAdminService.getNodeSessionInfo().subscribe((data: NodeSessionResult) => {
          // 设置用户信息&token信息
            // .....
          // 跳转至系统信息页面
          this.router.navigate(['/default/system-info']);
        }, (error) => {
          this.page = 'error';
          return throwError(error);
        });
    }
  12. 涉及到的类型

    css 复制代码
    /**
     * gitlab token-query
     */
    export interface ITokenQuery {
        access_token: string;
        expires_in: string;
        refresh_token: string;
        token_type: string;
    }
    ​
    ​
    /**
     * Node服务-Session信息
     */
    export interface NodeSessionResult {
        token: IToken;
        userInfo: IUserInfo;
    }
    ​
    /**
     * gitlab token
     */
    export interface IToken {
        access_token: string;
        created_at: string;
        refresh_token: string;
        scope: string;
        token_type: string;
    }

使用Node处理GitLab返回的信息

  1. 修改get('/auth/gitlab/callback')方法

    less 复制代码
    // GitLab OAuth 授权处理回调
    ROUTER.get('/auth/gitlab/callback',
      // 使用passprot中间件处理
      passport.authenticate('gitlab', {
        successRedirect: '/ipmanage/blank/loading',
        failureRedirect: '/ipmanage/blank/loading'
      }),
      (req: any, res: any) => {
        // 设置Token和用户信息,完成后跳转到指定系统页面
        // ...Code
        // 授权成功重定向到前端页面
          res.redirect('/ipmanage/dashboard');
      });
  2. 修改Passport 策略配置的回调

    less 复制代码
    // Passport 策略配置
    passport.use(
      new GitLabStrategy({
        baseURL: GITLAB_AUTH.baseURL,
        authorizationURL: GITLAB_AUTH.authorizationURL,
        clientSecret: GITLAB_AUTH.clientSecret,
        clientID: GITLAB_AUTH.clientID,
        callbackURL: GITLAB_AUTH.callbackURL,
        scope: GITLAB_AUTH.scope,
        // 设置回调函数第一个参数为 request
        passReqToCallback: true
      },
      // 回调函数,如果回调地址为Node接口,接口使用passport.authenticate('gitlab')才会调用次回调
      (request: IncomingMessage,
        accessToken: string,
        refreshToken: string,
        profile: any,
        done: any) => {
        // 处理用户信息
        if (profile) {
          return done(null, { id: profile.id });
        }
        return done(null, false);
      })
    );

可能遇到的问题

  1. 直接使用GET方式调用GitLab OAuth 授权接口,出现跨域

    尽管授权接口使用了代理,但在GitLab授权重定向时,域名和协议都发生了变化

    解决方案:

    使用window.location.href

    ini 复制代码
    // 重定向到GitLab授权页面
    window.location.href = URL;
  2. Node.js使用session会话报错: session is undefined

    解决方案:session 配置,要在接口路由之前

    php 复制代码
    // session 配置,要在接口路由之前
    SERVER.use(session({
      ...options
    }));
    ​
    // Example Express Rest API endpoints
    SERVER.use('/api', ROUTER);
  3. Passport 策略配置回调方法:done is not a function

    参数 passReqToCallback

    如果设置为true, 回调方法第一个参数为request

    typescript 复制代码
    new GitLabStrategy({
        ...options,
        passReqToCallback: true
      },
      (request: IncomingMessage,
        accessToken: string,
        refreshToken: string,
        profile: any,
        done: any) => {
        // 处理用户信息
        if (profile) {
          return done(null, { id: profile.id });
        }
        return done(null, false);
      })

    如果不设置,或设置为false

    需要去除第一个request参数

    typescript 复制代码
    new GitLabStrategy({
        ...options
      },
      (accessToken: string,
        refreshToken: string,
        profile: any,
        done: any) => {
        // 处理用户信息
        if (profile) {
          return done(null, { id: profile.id });
        }
        return done(null, false);
      })
相关推荐
starrysky8101 天前
sqlite3.OperationalError: database is locked 为什么 timeout=10 秒没生效?SQLite 锁升级死锁路径完整排障
angular.js
starrysky8101 天前
SMBus is busy, can't use it! + task blocked 120s - i2c-i801 错误路径释放未获取资源致 hung task(CVE-2026-64205)
angular.js
Jeanyia1 天前
GitLab 配套 Git 常用命令
git·gitlab
极小狐2 天前
极狐GitLab 补丁版本:19.2.1、19.1.3、19.0.5
网络·安全·gitlab·极狐gitlab·安全更新·补丁版本
rustfs3 天前
GitLab 如何与 RustFS 集成?
分布式·rust·gitlab
郝亚军4 天前
gitlab常用命令
gitlab
xixingzhe28 天前
Java 使用 GitLab4J 实现 WebHook
gitlab
童槿顏丶9 天前
GitLab部署到Linux
linux·运维·gitlab
InfinitePlus10 天前
Gitlab docker版本安装
docker·容器·gitlab
进击切图仔10 天前
GitLab 新用户创建与项目授权操作指南
gitlab