14 9 2021

1.用了两天的时间,一直在研究微应用的,免登录及获取用户信息这一块。

但是钉钉官网写的太繁杂了,看了一天,硬是没看明白,最后,硬着头皮,一点点查资料,借鉴别人的代码,终于 研究 出来了,为了防止 年轻的码农 步入后尘,浪费时间,特此写一篇文章,记录一下,如有不懂,可邮件我,
wh_djj@163.com

2. 废话不多说了,直接上教程

3. 准备工作

3.1 首先 你需要有一个 自己的 企业钉钉,进入企业钉钉管理,地址是:

https://oa.dingtalk.com/index.htm#/microApp/microAppList
  • 1

3.2 具体的操作:

https://open-doc.dingtalk.com/docs/doc.htm?spm=a219a.7629140.0.0.7h8XIj&treeId=367&articleId=104937&docType=1
  • 1

3.3 添加一个应用

记住这个,AgentID,这个值后面会有用处

3.4 进入开发者平台 地址是:

https://open-dev.dingtalk.com/#/corpAuthInfo?_k=rh11ax

3.5 记住 你的企业编号里面的, CorpId和CorpSecret

4. 代码阶段

4.1 服务层(核心代码)

/// <summary>
/// 至三个值都是之前让你们 记下的 三个重要的东西
/// </summary>
public static string AppId = "";
public static string AppSecret = "";
public static string agentId = "";


/// <summary>
/// 获取钉钉的Token
/// </summary>
/// <param name="code"></param>
/// <returns></returns>
private string GetToken()
{
    string DD_Token = CookieHelper.Instance.GetCookie("DD_Token");
    if (string.IsNullOrEmpty(DD_Token))
    {
        string url = string.Format("https://oapi.dingtalk.com/gettoken?corpid={0}&corpsecret={1}", AppId, AppSecret);
        string json = HttpHelper.GetHtml(url);
        Access_Token access_token = JsonHelper.DeserializeObject<Access_Token>(json);
        DD_Token = access_token.access_token;
        CookieHelper.Instance.SaveCookie("DD_Token", DD_Token,7100);
    }
    return DD_Token;
}

/// <summary>
/// 通过用户编号 获取 用户信息
/// </summary>
/// <param name="uid"></param>
/// <returns></returns>
public Dingding_User GetUeser(string uid)
{
    string url = string.Format("https://oapi.dingtalk.com/user/get?access_token={0}&userid={1}", GetToken(), uid);
    string json = HttpHelper.GetHtml(url);
    return JsonHelper.DeserializeObject<Dingding_User>(json);
}

/// <summary>
/// 通过用户编号 获取 用户信息
/// </summary>
/// <param name="uid"></param>
/// <returns></returns>
public Access_UserInfo GetUserInfo(string code)
{
    string url = string.Format("https://oapi.dingtalk.com/user/getuserinfo?access_token={0}&code={1}", GetToken(), code);
    string json = HttpHelper.GetHtml(url);
    return JsonHelper.DeserializeObject<Access_UserInfo>(json);
}

/// <summary>
/// 获取 ticker
/// </summary>
/// <returns></returns>
private Access_Ticket GetTicket()
{
    string url = string.Format("https://oapi.dingtalk.com/get_jsapi_ticket?access_token={0}", GetToken());
    string json = HttpHelper.GetHtml(url);
    return JsonHelper.DeserializeObject<Access_Ticket>(json);
}


/// <summary>
/// 签名算法 对string1进行sha1签名,得到signature
/// </summary>
/// <param name="url"></param>
/// <returns></returns>
private Access_Sdk GetSdk(string url)
{
    string noncestr = GuidHelper.GuidTo16String();
    string timestamp = DateTime.Now.Ticks.ToString();
    Access_Ticket access_Ticket = GetTicket();
    string string1 = "jsapi_ticket=" + access_Ticket.ticket + "&noncestr=" + noncestr + "&timestamp=" + timestamp + "&url=" + url + "";
    string signature = MD5Helper.SHA1(string1);
    Access_Sdk sdk = new Access_Sdk();
    sdk.noncestr = noncestr;
    sdk.timestamp = timestamp;
    sdk.signature = signature;
    return sdk;
}


/// <summary>
/// 合并 config 需要的参数
/// </summary>
/// <param name="url"></param>
/// <returns></returns>
public ResultInfo<object> GetDingdingConfig(string url)
{
    Access_Sdk sdk = GetSdk(url);
    Dictionary<string, string> dic = new Dictionary<string, string>();
    dic.Add("agentId", agentId);
    dic.Add("corpId", AppId);
    dic.Add("timeStamp", sdk.timestamp);
    dic.Add("nonceStr", sdk.noncestr);
    dic.Add("signature", sdk.signature);
    ResultInfo<object> result = new ResultInfo<object>();
    result.Code = ResultCode.Success;
    result.Data = dic;
    result.Message = "ok";
    return result;
}

4.2 生成随机码 帮助类

        /// <summary> 
        /// 根据GUID获取16位的唯一字符串 
        /// </summary> 
        /// <param name=\"guid\"></param> 
        /// <returns></returns> 
        public static string GuidTo16String()
        {
            long i = 1;
            foreach (byte b in Guid.NewGuid().ToByteArray())
                i *= ((int)b + 1);
            return string.Format("{0:x}", i - DateTime.Now.Ticks);
        }

4.3 以GET方式抓取远程页面内容

public static string GetHtml(string tUrl)
{
    string strResult;
    try
    {
        HttpWebRequest hwr = (HttpWebRequest)HttpWebRequest.Create(tUrl);
        hwr.Timeout = 19600;
        HttpWebResponse hwrs = (HttpWebResponse)hwr.GetResponse();
        Stream myStream = hwrs.GetResponseStream();
        StreamReader sr = new StreamReader(myStream, Encoding.UTF8);
        //StringBuilder sb = new StringBuilder();
        strResult = sr.ReadToEnd();
        //while (-1 != sr.Peek())
        //{
        //    sb.Append(sr.ReadLine() + "\r\n");
        //}
        //strResult = sb.ToString();
        hwrs.Close();
    }
    catch (Exception ee)
    {
        strResult = ee.Message;
    }
    return strResult;
}

4.4 Json 帮助类 (需要在 在 nuget下载 Newtonsoft.Json )

 public static string GetJson(object obj)
 {
     return JsonConvert.SerializeObject(obj);
 }
 public static T DeserializeObject<T>(string json)
 {
     return JsonConvert.DeserializeObject<T>(json);
 }

 public static dynamic ConvertDynamic(object obj)
 {
     return JsonConvert.DeserializeObject<dynamic>(GetJson(obj));
 }

4.5 MD5加密帮助类

/// <summary>
        /// 16位小写
        /// </summary>
        /// <returns></returns>
        public static string Lower16(string s)
        {
            s = System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile(s, "md5").ToString();
            return s.ToLower().Substring(8, 16);
        }
        /// <summary>
        /// 32位小写
        /// </summary>
        /// <returns></returns>
        public static string Lower32(string s)
        {
            s = System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile(s, "md5").ToString();
            return s.ToLower();
        }

        /// <summary>
        /// 32位小写
        /// </summary>
        /// <returns></returns>
        public static string SHA1(string s)
        {
            s = System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile(s, "SHA1").ToString();
            return s.ToLower();
        }

4.6 获取当前地址 帮助类

        /// <summary>
        /// 获得当前完整Url地址
        /// </summary>
        /// <returns>当前完整Url地址</returns>
        public static string GetUrl()
        {
            return HttpContext.Current.Request.Url.ToString();
        }

5.0 实体类

5.1 Token实体类

public class Access_Token
{
    public string errcode;
    public string errmsg;

    public string expires_in;
    public string access_token;
}

5.2 Ticket 实体类

public class Access_Ticket
{
     public string errcode;
     public string errmsg;
     public string ticket;
     public string expires_in;
 }

5.3 Sdk 签名实体列

 public class Access_Sdk
 {
     public string signature { get; set; }
     public string noncestr { get; set; }
     public string timestamp { get; set; }
 }

5.4 整合config所需要的参数 实体类

 public class Access_Sign
 {
     public String agentId { get; set; }

     public String corpId { get; set; }

     public String timeStamp { get; set; }

     public String nonceStr { get; set; }

     public String signature { get; set; }

     public String url { get; set; }

     public String rawstring { get; set; }

     public string jsticket { get; set; }
 }

5.5 当前登录人实体类

 public class Access_UserInfo
    {
        public string errcode { get; set; }
        public string errmsg { get; set; }
        public string userid { get; set; }
        /// <summary>
        /// 手机设备号,由钉钉在安装时随机产生
        /// </summary>
        public string deviceId { get; set; }
        /// <summary>
        /// 是否是管理员
        /// </summary>
        public string is_sys { get; set; }

        /// <summary>
        /// 级别,0:非管理员 1:超级管理员(主管理员) 2:普通管理员(子管理员) 100:老板
        /// </summary>
        public string sys_level { get; set; }
    }

5.6 用户信息实体类

 public class Dingding_User
    {
        public string errcode;
        public string errmsg;
        public string userid;
        /// <summary>
        /// 成员名称
        /// </summary>
        public string name;
        /// <summary>
        /// 分机号(仅限企业内部开发调用)
        /// </summary>
        public string tel;
        /// <summary>
        /// 办公地点(ISV不可见)
        /// </summary>
        public string workPlace;
        /// <summary>
        /// 备注(ISV不可见)
        /// </summary>
        public string remark;
        /// <summary>
        /// 手机号码(ISV不可见)
        /// </summary>
        public string mobile;
        /// <summary>
        /// 员工的电子邮箱(ISV不可见)
        /// </summary>
        public string email;
        /// <summary>
        /// 员工的企业邮箱,如果员工已经开通了企业邮箱,接口会返回,否则不会返回(ISV不可见)
        /// </summary>
        public string orgEmail;
        /// <summary>
        /// 是否已经激活, true表示已激活, false表示未激活
        /// </summary>
        public bool active;
        /// <summary>
        /// 在对应的部门中的排序, Map结构的json字符串, key是部门的Id, value是人员在这个部门的排序值
        /// </summary>
        public string orderInDepts;
        /// <summary>
        /// 是否为企业的管理员, true表示是, false表示不是
        /// </summary>
        public bool isAdmin;
        /// <summary>
        /// 是否为企业的老板, true表示是, false表示不是(【设置负责人】:主管理员登陆钉钉手机客户端 -【通讯录】-【企业名后面的管理】-【企业通讯录】-【负责人设置】进行添加则可。)
        /// </summary>
        public bool isBoss;
        /// <summary>
        /// 钉钉Id,在钉钉全局范围内标识用户的身份(不可修改
        /// </summary>
        public string dingId;
        public string unionid;
        /// <summary>
        /// 在对应的部门中是否为主管, Map结构的json字符串, key是部门的Id, value是人员在这个部门中是否为主管, true表示是, false表示不是
        /// </summary>
        public string isLeaderInDepts;
        /// <summary>
        /// 是否号码隐藏, true表示隐藏, false表示不隐藏
        /// </summary>
        public bool isHide;
        /// <summary>
        /// 成员所属部门id列表
        /// </summary>
        public string[] department;
        /// <summary>
        /// 职位信息
        /// </summary>
        public string position;
        /// <summary>
        /// 头像url
        /// </summary>
        public string avatar;

        /// <summary>
        /// 入职时间
        /// </summary>
        public string hiredDate;
        /// <summary>
        /// 员工工号
        /// </summary>
        public string jobnumber;
        /// <summary>
        /// 扩展属性,可以设置多种属性(但手机上最多只能显示10个扩展属性,具体显示哪些属性,请到OA管理后台->设置->通讯录信息设置和OA管理后台->设置->手机端显示信息设置)
        /// </summary>
        public string extattr;

        ///// <summary>
        ///// 角色信息(ISV不可见),json数组格式
        ///// </summary>
        //public Roles roles;

        /// <summary>
        /// 手机号码区号
        /// </summary>
        public string stateCode;

        /// <summary>
        /// 是否是高管
        /// </summary>
        public string isSenior;
    }

6 呈现页面

6.1 default.aspx.cs 页面

using Dingding.Helper;
using Dingding.Model.Result;
using Dingding.Service;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace Dingding
{
    public partial class _default : System.Web.UI.Page
    {
        protected Dictionary<string, string> dic = new Dictionary<string, string>();
        private DingdingService dingdingService;
        public _default()
        {
            dingdingService = new DingdingService();
        }

        protected void Page_Load(object sender, EventArgs e)
        {
            //DingdingService.GetUeser("5");
            var data = dingdingService.GetDingdingConfig(RequsertHelper.GetUrl());
            if (data.Code == ResultCode.Success)
            {
                dic = (Dictionary<String, String>)data.Data;
            }
        }
    }
}

6.2 default.aspx 页面

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="default.aspx.cs" Inherits="Dingding._default" %>

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title></title>
    <script src="/js/jquery-1.10.2.min.js"></script>
    <script type="text/javascript" src="https://g.alicdn.com/dingding/dingtalk-pc-api/2.7.0/index.js"></script>
    <script type="text/javascript">
        DingTalkPC.config({
            agentId: '<%=dic["agentId"]%>', // 必填,微应用ID
            corpId: '<%=dic["corpId"]%>',//必填,企业ID
            timeStamp: '<%=dic["timeStamp"]%>', // 必填,生成签名的时间戳
            nonceStr: '<%=dic["nonceStr"]%>', // 必填,生成签名的随机串
            signature: '<%=dic["signature"]%>', // 必填,签名
            jsApiList: ['runtime.info',
                'biz.contact.choose',
                'device.notification.confirm',
                'device.notification.alert',
                'device.notification.prompt',
                'biz.ding.post',
                'runtime.permission.requestAuthCode',
                'device.geolocation.get',
                'biz.ding.post',
                'biz.contact.complexChoose']
        });
        DingTalkPC.ready(function () {
            //获取免登授权码 -- 注销获取免登服务,可以测试jsapi的一些方法
            DingTalkPC.runtime.permission.requestAuthCode({
                corpId: '<%=dic["corpId"]%>',
                onSuccess: function (result) {
                    $.getJSON("getuserinfo.aspx?code=" + result["code"], function (data) {
                        if (data.Code == 0) {
                            $("#img").append("<img src=\"" + data.Data.avatar + "\" height=\"100\" width=\"100\">")
                            $("#isAdmin").text(data.Data.isAdmin);
                            $("#name").text(data.Data.name);
                            $("#mobile").text(data.Data.mobile);
                            $("#workPlace").text(data.Data.workPlace);
                            $("#position").text(data.Data.position);
                            $("#jobnumber").text(data.Data.jobnumber);
                            $("#hiredDate").text(data.Data.hiredDate);
                        }
                    })
                },
                onFail: function (err) {
                }
            });;k
        });
    </script>
</head>
<body>
    <form id="form1" runat="server">
        <div>
            <div id="img"></div>
            是否为企业的管理员:<span id="isAdmin"></span><br /><br />
            姓名:<span id="name"></span><br /><br />
            手机号:<span id="mobile"></span><br /><br />
            办公地点<span id="workPlace"></span><br /><br />
            职位信息<span id="position"></span><br /><br />
            员工工号<span id="jobnumber"></span><br /><br />
            入职时间<span id="hiredDate"></span><br /><br />
        </div>
    </form>
</body>
</html>

7 已经得到了用户所需的 Code,直接用Code换取 用户信息
7.1 getUserInfo.aspx.cs 页面

using Dingding.Helper;
using Dingding.Model.Result;
using Dingding.Service;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace Dingding
{
    public partial class getUserInfo : System.Web.UI.Page
    {
        protected string code;

        private DingdingService dingdingService;

        public getUserInfo()
        {
            dingdingService = new DingdingService();
        }

        protected void Page_Load(object sender, EventArgs e)
        {
            code = Request.QueryString["code"];
            string uid = dingdingService.GetUserInfo(code).userid;
            ResultInfo<object> result = new ResultInfo<object>();
            result.Data = dingdingService.GetUeser(uid);
            result.Code = ResultCode.Success;
            result.Message = "免登录,获取个人信息成功!";
            Response.Write(JsonHelper.GetJson(result));
        }
    }
}

8.测试
8.1 下载 钉钉RC版 地址是:
https://open-doc.dingtalk.com/docs/doc.htm?spm=a219a.7629140.0.0.5xokux&treeId=378&articleId=104958&docType=1

8.2 调试

8.3 成功呈现界面

整个钉钉免登录及获取当前用户信息全部写完了,有什么疑问可以@我

这两天一直都有人@我,都是需要源码的
源码地址:
https://download.csdn.net/download/u014479921/10388639

延伸阅读
  1. JAVA-JSAPI微信公众号-微信支付(非常详细)