使用钉钉认证实现免注册登录

为了限制数据的可访问性,用户模块是大多数系统必不可少的功能。
于是对于开发者来说,用户模块开发就成了一件重复度很高的事情。
对于重复度高的事情,我们自然而然想到的是抽取成公共服务(代码)。
比如设计一个统一的用户管理服务来处理,但似乎也会出现一些问题:

  • 功能问题。有的只需要简单的登录、注册、管理(增删改查),有的还需要角色设置、授权管理。对于简单的需求会提供较多的冗余信息,而对于复杂的需求会频繁修改接口。
  • 界面问题。不同系统UI风格不同,导致前端页面仍需要重新开发。
  • 其它问题。需要单独部署服务,同时可能由于数据库不同也要修改。

最后的结果往往是对于不同的系统独立开发用户模块,或者集中依赖一个用户服务,然后再根据各个系统的需求去修改这个服务。

或许我们可以采取一种更高效的方式——使用第三方免注册登录。

第三方免注册怎么选

第三方免注册是指一些第三方网站基于 OAuth 2.0 提供的免注册接口,我们可以通过用户授权信息
提供免登录功能的第三方应用很多:GitHub、谷歌、微信、QQ、钉钉等。
如果系统本身对第三方的资源有需求,肯定优先使用对应的免注册服务,比如某个插件需要获取用户的 GitHub 账号信息,肯定选择 GitHub 免登录。
而无资源需求的系统一般会支持多种免注册
而我这次开发的项目是供公司同事使用的内部系统,对比之后选择钉钉,原因有下面2个:

  1. 钉钉是内部统一的通信软件,每个人都有钉钉账号。GitHub 账号就不适合,因为只有技术人员有。
  2. 可以从钉钉账号中获取用户部门、职位等信息,实现权限管理更容易。微信、QQ 等就无法提供此信息。

钉钉对于4种应用提供免注册功能:

  • 企业内部应用。面向企业内部开发人员和定制服务商,自主开发内部应用或工作台,供企业或组织内部使用。
  • 第三方企业应用。面向应用服务商,开发应用接入钉钉,管理员开通后,钉钉上的企业/组织即可使用。
  • 第三方个人应用。面向应用服务商,开发应用接入钉钉,提供给钉钉个人用户使用。此类应用不需要管理员安装,个人即可直接使用。
  • 移动应用接入。接入钉钉开放平台,让你的移动应用可以支持钉钉分享、钉钉帐号登录。

由于项目并不需要提供给其他企业应用,故第三方企业应用不适合,考虑到项目需要在 PC 端和移动端运行,所以开发成 HTML5 应用,放弃移动应用,采用第三方个人应用企业内部应用

登录流程

第三方个人应用(PC端免登录)

  1. 跳转到配置的URL1
  2. 用户扫码授权登录
  3. 跳转到配置的URL2
  4. 将URL2中的请求参数code传给服务端
  5. 服务端向钉钉服务器请求access_token
  6. 服务端通过access_token签名code发送给钉钉服务器,获取用户信息

企业内部应用(移动端登录)

  1. 浏览器调用钉钉 JSSDK 通过 corpId 获取code
  2. 浏览器将获取的 code 传给服务端
  3. 服务端向钉钉服务器请求access_token
  4. 服务端调用REST API获取用户信息

总结起来,大体流程一致,就是将浏览器端获取的code服务端获取access_token发送给钉钉服务器获取用户信息。
获取code的过程在移动端较为简单,将corpId传入SDK调用函数即可,用户无需操作。PC端则需要两次跳转,需要用户扫码授权。
获取 access_token 则是通过创建应用时钉钉给出的 appKey 和 appSecret 换取。

具体实现

第三方个人应用(PC端免登录)部分 Python 代码:

  1. 通过 key 和 secret 获取 access_token。

    1
    2
    3
    4
    5
    6
    def get_token():
    pc_access_token_url = basename + 'sns/gettoken?appid={0}&appsecret={1}'.format(
    cfg['pc_key'], cfg['pc_secret'])
    r = requests.get(pc_access_token_url)
    obj = json.loads(r.text)
    return obj['access_token]
  2. 先通过 secret 将 timestamp 生成签名,将 key 和 签名、签名参数、浏览器传来的code发送给钉钉服务端获取用户信息。

    1
    2
    3
    4
    5
    6
    7
    8
    def get_user():
    user_info_url = basename + 'sns/getuserinfo_bycode?accessKey={0}&timestamp={1}&signature={2}'
    signature = base64.b64encode(hmac.new(bytes(
    cfg['pc_secret'], 'utf-8'),
    bytes(timestamp, 'utf-8'), hashlib.sha256).digest())
    r = requests.post(user_info_url.format(cfg['pc_key'], timestamp, quote(
    signature)), json.dumps({"tmp_auth_code": code}))
    json.loads(r.text)

企业内部应用(移动端登录)部分 Python 代码:

  1. 通过 appId 和 secret 获取 access_token。

    1
    2
    3
    4
    5
    6
    7
    def get_token():
    access_token_url = basename + \
    'gettoken?appkey={0}&appsecret={1}'.format(
    cfg['app_id'], cfg['secret'])
    r = requests.get(access_token_url)
    obj = json.loads(r.text)
    return obj['access_token]
  2. 将 access_token 和 code 发往钉钉服务器获取用户信息。

    1
    2
    3
    4
    user_info_url = basename + 'user/getuserinfo?access_token={0}&code={1}'
    r = requests.get(user_info_url.format(rds.get('access_token'), code),
    {'referer': cfg['redirect_url']})
    return json.loads(r.text)

总结

采用第三方登录授权可以让用户跳过繁琐的注册、输入账号密码过程,提升用户体验的同时也减轻了开发压力,加快了项目进度。即使需要实现复杂的用户管理功能,也可以后期结合第三方信息进行开发。


原文链接:https://tech.gtxlab.com/dingding-auth.html
作者信息:朱德龙,人和未来高级前端工程师。