关于钉钉

钉钉是阿里推出的企业移动OA平台,本身提供了丰富的通用应用,同时其强大的后台API接入能力让企业接入自主开发的应用成为可能,可以让开发者实现几乎任何需要的功能。

近期因为工作需要研究了一下钉钉的接入,发现其接入文档、SDK都是基于java编写的,而我们的企业网站使用Asp.Net MVC(C#)开发,所以接入只能从头自己做SDK。

接入主要包括免登、获取数据、修改数据等接口。

免登流程

首先需要理解一下钉钉的免登流程,借用官方文档的图片:

是不是很熟悉?是的,基本是按照OAUTH的原理来的,版本嘛,里面有计算签名的部分,我觉得应该是OAUTH1.0。

有的读者会问,那第一步是不是应该跳转到第三方认证页面啊。我觉得“魔法”就藏在用来打开页面的钉钉内置浏览器里,在dd.config()这一步里,“魔法”就生效了。

 

其实简单来说,主要分为五步:

  1. 在你的Web服务器端调用api,传入CorpId和CorpSecret,获取accessToken,即访问令牌。

  2. 在服务器端调用api,传入accessToken,获取JsApiTicket,即JsApi的访问许可(门票)。

  3. 按照既定规则,在后台由JsApiTicket、NonceStr、Timestamp、本页面Url生成字符串,计算SHA1消息摘要,即签名Signature。

  4. 将AgentId、CorpId、Timestamp、NonceStr、Signature等参数传递到前台,在前台调用api,得到authCode,即授权码。

  5. 根据授权码,在前台或后台调用api,获得userId,进而再根据userId,调用api获取用户详细信息。

PS:为什么需要在后台完成一些api的调用呢?应该是因为js跨域调用的问题,我具体没有深究。

实践方法

理解了上述步骤,我对登陆过程的实现也大致有了一个设想,既然免登需要前后端一起来完成,那就添加一个专门的登陆页面,将登陆过程都在里面实现,将登陆结果写入到Session,并重定向回业务页面,即算完成。图示如下:

移动开发培训,Android培训,安卓培训,手机开发培训,手机维修培训,手机软件培训

其中每个api的调用方式,在官方文档中都有说明。同时,我在阿里云开发者论坛找到了网友提供的SDK,有兴趣可以下载:钉钉非官方.Net SDK

另外,GitHub上还有官方的JQuery版免登开发Demo,可以参考:GitHub JQuery免登

我参考的是.Net SDK,将其中的代码,提取出了我所需要的部分,做了简化处理。基本原理就是每次调用API都是发起HttpRequest,将结果做JSON反序列化。

核心代码如下:

移动开发培训,Android培训,安卓培训,手机开发培训,手机维修培训,手机软件培训

  1 using System;  2 using System.Collections.Generic;  3 using System.Linq;  4 using System.Web;  5 using System.IO;  6 using Newtonsoft.Json;  7 using Newtonsoft.Json.Linq;  8 using DDApi.Model;  9  10 namespace DDApi 11 { 12     public static class DDHelper 13     { 14         public static string GetAccessToken(string corpId, string corpSecret) 15         { 16             string url = string.Format("https://oapi.dingtalk.com/gettoken?corpid={0}&corpsecret={1}", corpId, corpSecret); 17             try 18             { 19                 string response = HttpRequestHelper.Get(url); 20                 AccessTokenModel oat = Newtonsoft.Json.JsonConvert.DeserializeObject<AccessTokenModel>(response); 21  22                 if (oat != null) 23                 { 24                     if (oat.errcode == 0) 25                     { 26                         return oat.access_token; 27                     } 28                 } 29             } 30             catch (Exception ex) 31             { 32                 throw; 33             } 34             return string.Empty; 35         } 36  37         /* https://oapi.dingtalk.com/get_jsapi_ticket?access_token=79721ed2fc46317197e27d9bedec0425 38          * 
 39          * errmsg    "ok" 40          * ticket    "KJWkoWOZ0BMYaQzWFDF5AUclJOHgO6WvzmNNJTswpAMPh3S2Z98PaaJkRzkjsmT5HaYFfNkMdg8lFkvxSy9X01" 41          * expires_in    7200 42          * errcode    0 43          */ 44         public static string GetJsApiTicket(string accessToken) 45         { 46             string url = string.Format("https://oapi.dingtalk.com/get_jsapi_ticket?access_token={0}", accessToken); 47             try 48             { 49                 string response = HttpRequestHelper.Get(url); 50                 JsApiTicketModel model = Newtonsoft.Json.JsonConvert.DeserializeObject<JsApiTicketModel>(response); 51  52                 if (model != null) 53                 { 54                     if (model.errcode == 0) 55                     { 56                         return model.ticket; 57                     } 58                 } 59             } 60             catch (Exception ex) 61             { 62                 throw; 63             } 64             return string.Empty; 65         } 66  67         public static long GetTimeStamp() 68         { 69             TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0); 70             return Convert.ToInt64(ts.TotalSeconds); 71         } 72  73         public static string GetUserId(string accessToken, string code) 74         { 75             string url = string.Format("https://oapi.dingtalk.com/user/getuserinfo?access_token={0}&code={1}", accessToken, code); 76             try 77             { 78                 string response = HttpRequestHelper.Get(url); 79                 GetUserInfoModel model = Newtonsoft.Json.JsonConvert.DeserializeObject<GetUserInfoModel>(response); 80  81                 if (model != null) 82                 { 83                     if (model.errcode == 0) 84                     { 85                         return model.userid; 86                     } 87                     else 88                     { 89                         throw new Exception(model.errmsg); 90                     } 91                 } 92             } 93             catch (Exception ex) 94             { 95                 throw; 96             } 97             return string.Empty; 98         } 99 100         public static string GetUserDetailJson(string accessToken, string userId)101         {102             string url = string.Format("https://oapi.dingtalk.com/user/get?access_token={0}&userid={1}", accessToken, userId);103             try104             {105                 string response = HttpRequestHelper.Get(url);106                 return response;107             }108             catch (Exception ex)109             {110                 throw;111             }112             return null;113         }114 115         public static UserDetailInfo GetUserDetail(string accessToken, string userId)116         {117             string url = string.Format("https://oapi.dingtalk.com/user/get?access_token={0}&userid={1}", accessToken, userId);118             try119             {120                 string response = HttpRequestHelper.Get(url);121                 UserDetailInfo model = Newtonsoft.Json.JsonConvert.DeserializeObject<UserDetailInfo>(response);122 123                 if (model != null)124                 {125                     if (model.errcode == 0)126                     {127                         return model;128                     }129                 }130             }131             catch (Exception ex)132             {133                 throw;134             }135             return null;136         }137 138         public static List<DepartmentInfo> GetDepartmentList(string accessToken, int parentId = 1)139         {140             string url = string.Format("https://oapi.dingtalk.com/department/list?access_token={0}", accessToken);141             if (parentId >= 0)142             {143                 url += string.Format("&id={0}", parentId);144             }145             try146             {147                 string response = HttpRequestHelper.Get(url);148                 GetDepartmentListModel model = Newtonsoft.Json.JsonConvert.DeserializeObject<GetDepartmentListModel>(response);149 150                 if (model != null)151                 {152                     if (model.errcode == 0)153                     {154                         return model.department.ToList();155                     }156                 }157             }158             catch (Exception ex)159             {160                 throw;161             }162             return null;163         }164     }165 }

移动开发培训,Android培训,安卓培训,手机开发培训,手机维修培训,手机软件培训

移动开发培训,Android培训,安卓培训,手机开发培训,手机维修培训,手机软件培训 HttpRequestHelper View Code

其中的Model,就不再一一贴出来了,大家可以根据官方文档自己建立,这里只举一个例子,即GetAccessToken的返回结果:

移动开发培训,Android培训,安卓培训,手机开发培训,手机维修培训,手机软件培训

    public class AccessTokenModel
    {        public string access_token { get; set; }        public int errcode { get; set; }        public string errmsg { get; set; }
    }

移动开发培训,Android培训,安卓培训,手机开发培训,手机维修培训,手机软件培训

 我创建了一个类DDApiService,将上述方法做了封装:

移动开发培训,Android培训,安卓培训,手机开发培训,手机维修培训,手机软件培训 DDApiService View Code

以上是底层核心部分。登录页面的实现在控制器DDController中,代码如下:

移动开发培训,Android培训,安卓培训,手机开发培训,手机维修培训,手机软件培训 DDController View Code

视图View的代码:

移动开发培训,Android培训,安卓培训,手机开发培训,手机维修培训,手机软件培训 Login.cshtml View Code

其中nonstr理论上最好应该每次都随机,留待读者去完成吧:-)

钉钉免登就是这样,只要弄懂了就会觉得其实不难,还顺便理解了OAUTH。

后续改进

这个流程没有考虑到AccessToken、JsApiTicket的有效期时间(2小时),因为整个过程就在一个页面中都完成了。如果想要进一步扩展,多次调用api的话,需要考虑到上述有效期。

如果为了图简便每都去获取AccessToken也是可以的,但是会增加服务器负担,而且api的调用频率是有限制的(1500次/s好像),所以应当采取措施控制。例如可以将AccessToken、JsApiTicket存放在this.HttpContext.Application["accessToken"]中,每次判断有效期是否过期,如果过期就调用api重新申请一个。

以上就是这样,感谢阅读。

http://www.cnblogs.com/pleiades/p/7140318.html

延伸阅读

企业日历-Java培训,做最负责任的教育,学习改变命运,软件学习,再就业,大学生如何就业,帮大学生找到好工作,lphotoshop培训,电脑培训,电脑维修培训,移动软件开发培训,网站设计培训,网站建设培训企业日历