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);
      })
相关推荐
云攀登者-望正茂24 分钟前
60个GitLab CI/CD 面试问题和答案
ci/cd·gitlab·devops
zhang238390615415 小时前
IDEA add gitlab account 提示
java·gitlab·intellij-idea·idea
宁酱醇21 小时前
各种各样的bug合集
开发语言·笔记·python·gitlab·bug
啊花是条龙1 天前
在 Angular 中使用 ECharts 并处理 xAxis 标签的点击事件
前端·angular.js
极小狐1 天前
极狐GitLab Git LFS 速率限制如何设置?
运维·git·ssh·gitlab·github
极小狐1 天前
如何解决极狐GitLab 合并冲突?
人工智能·git·机器学习·gitlab
量子位2 天前
无需数据标注!测试时强化学习,模型数学能力暴增 | 清华 & 上海 AI Lab
人工智能·gitlab·aigc
啊花是条龙2 天前
Angular 开发指南:组件、数据绑定、指令、服务、HTTP、路由和表单
前端·angular.js
梁萌3 天前
10-DevOps-Jenkins参数化构建实现多版本发布
运维·gitlab·jenkins·devops·tag