以Azure为例的SSO

由于文章的篇幅有限,无法将全部的代码贴上来,如想要看完整案例,请在公众号文章中留言(其他平台很少看…毕竟最近印度同事的UI组件库搞得我好烦)


1.关于SSO

单点登录又称之为SSO,全称为 Single Sign On ,一般在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。

比如,当我们使用一个腾讯旗下的产品时,我们一般会接入QQ登陆,此时就可以认为QQ登陆为我们的SSO。只要我们登录了QQ,就可以根据凭证获取到你在QQ中的信息,并登录该平台。

2.SSO流程

无论使用第三方或者是自己的sso,下面将以一个开发人员的角度,以QQ作为例子讲解一个SSO的过程,

1.当用户第一次登录平台A的时候,由于该平台使用了QQ的服务去获取用户信息,该平台会自动调用QQ的服务(比如跳转QQ登录),此时我们会设置一个RedirectURL的参数到qq的login服务上。

2.当用户从qq登录后,qq的后台服务会根据我们的 RedirectURL 参数,将一个token传到我们的Redirect的地址。

3.当我们的服务接收到了来自QQ服务器的一个重定向请求,且该请求还会带一个token,我们可以根据QQ文档去调用Api获取我们想要的信息,比如获取用户信息等。

4.当我们获取到用户的信息之后,我们用 JsonWebToken 的形式重新去设置我们的token,并且使用其作为前后端通讯的token。

在上述的过程中,我们使用第三方的SSO,是基于以下几个原因

  • qq本质上提供了用户的信息给我们,并且提供了一个便捷的,获取用户信息的api。
  • QQ作为一个维护多年的平台,对于权限以及用户管理等模块已经很完善了
  • 我们自己开发的话,我们需要花费大量的时间和精力,还不能保证一定没问题

3.关于Azure

上面的过程中,已经知道SSO的流程,想必大家都已经对SSO有了初步的认知,而选择一个SSO是要根据市场以及客户所用的SSO有关,比如对于外企,我们会选择Azure作为SSO,而不是选择很少外国人用的QQ。

Microsoft Azure 作为微软云计算,大公司背书.

最主要是我们的客户选择使用它,所以下面的例子会以其作为例子

4.使用SSO

不同平台的sso参数思路‘大差不差’,都是用 AppID + AppScrect 这一套,所以下面的例子也按照这个套路来介绍。

1.准备各类参数,其中最主要的参数是CLIENT_ID,TENANT_ID,CLIENT_SECRET。

//当前域名
LOGIN_REDIRECT=https://xxx.com
//重定向地址
OAUTH2_REDIRECT_URL=https://xxx.com/user/login_callback
//client_id,在app的详情中查看,由管理员给的
OAUTH2_CLIENT_ID=6aaaaaae-7aaa-4aaa-baaa-aaaaaaaaad89
//tent_id,可以理解为密钥。由管理员给的
OAUTH2_TENANT_ID=4266ec6c-fe9f-4893-82e9-996189e0b81b
//在Azure上生成的,验证机器是否允许登录
OAUTH2_CLIENT_SECRET=mvaaa~.qLgH8aaaaaaaaaTpnWaLD9Em-H3Z6gb_T

2.准备我们的登陆接口重定向到auzre的登陆接口

当用户调用我们的登陆接口时,我们会马上调用到Azure的服务去登陆。

  @Get('user/login')
  login(@Response() res) {
    res.redirect(this.userServie.processLogin());
  }
​

此时,浏览器的弹窗如下:

3.登陆成功后获取到用户的凭证

@Get('user/login_callback')
  async loginCallback(@Request() req, @Response() res) {
    let code = '';
    if (req.query.code) {
      code = req.query.code;
      const tokenInfo = await this.userServie.getAccessTokenByCode(code, req.log);
      // if redirect error, check cookie has refresh_token
      if (tokenInfo.error) {
        req.log.error(`user login callback error will redirect to login`);
        res.redirect('/login');
      } else {
        const { claimsInfo, user, groups } = this.userServie.processAccessToken(tokenInfo.access_token);
​
        if (!groups.includes(environmentConfig.azure.adGroupName)) {
          req.log.error('User not in AD group');
          res.status(400).json({ message: 'User not in AD group' });
        }
​
​
        req.log.info(`login user name is ${user.id}`);
        const redirectUrl = `${environmentConfig.cx.frontend_url}?t=${claimsInfo}`;
        res.redirect(redirectUrl);
      }
    } else {
      req.log.error('ADFS grant code not found');
      res.status(400).json({ message: 'ADFS grant code not found' });
    }
  }
​

上述代码中,流程在于获取到了azure的token之后,调用api获取用户信息,并生成新的token并给到前端。

4.根据凭证获取到用户的信息。

在上述代码中,我们完成了整个流程,但是最主要的核心代码如下

processAccessToken(azureToken) {
    const auzraUserInfo = JWT.decode(azureToken);
    const {
      onPremisesSamAccountName = '',
      cn = '',
      name = '',
      family_name = '',
      given_name = '',
      username = '',
      groups = []
    } = auzraUserInfo;
    let adKeyWord = '';
    let userName = '';
      adKeyWord = name;
      userName = `${given_name} ${family_name}`;
    const jwtToken = JWT.sign(
      {
        cn: adKeyWord,
        sAMAccountName: adKeyWord,
        username: userName,
        auth: 'saml',
        thumbnail: ''
      },
      environmentConfig.cx.jwt_token_secret,
      // { expiresIn: 7 * 24 * 60 * 60 }
      { expiresIn: 1 * 24 * 60 * 60 }
    );
    return { claimsInfo: jwtToken, user: { id: userName }, groups: groups };
  }

至此,一个流程就结束了,我们将生成的 token 放到前端就可以了。

需要注意的是,我们的凭证是有expiry date的。

多谢关注~ 公众号求关注~

公众号文章

  • 20
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值