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

免登录步奏
- 已知Corpld和CropSecret,获取accessToken,即访问令牌。
- 通过accessToken,获取JsApiTicket,即JsApi的访问许可(门票)。
- 按照规则在后台由JsApiTicket、NonceStr、Timestamp、前端页面Url生成字符串,计算SHA1消息摘要,即签名Signature。
- 把AgentId、CorpId、Timestamp、NonceStr、Signature等参数传递到前台,在前台调用api,得到authCode,即授权码。
- 根据授权码,在前台或后台调用api,获得userId,进而再根据userId,调用api获取用户详细信息
主要代码
DingServerController
using Common; using Newtonsoft.Json; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; using System.Net.Http; using System.Web.Http; using WebApIs.BLL; using WebApIs.Models; namespace WebApIs.Controllers {/// <summary> /// /// </summary> public class DingServerController : ApiController { /// <summary> /// /// </summary> /// <returns></returns> public ResultJson GetLoginSign() { string corpId = "dinga85d93", corpSecret = "D5r1G6cejOlZmEaB8PNlI9J5D"; string nonceStr = new Random().Next(1000, 10000).ToString(); //"helloDD";//todo:随机 string NonceStr = nonceStr; string AccessToken = GetAccessToken(corpId, corpSecret); string ticket = GetJsApiTicket(AccessToken); TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0); long timeStamp = Convert.ToInt64(ts.TotalSeconds); //?后端缺获取签名摘要 string url = "http://10.10.10.10:8084/Index.html";//访问的页面 Request.Url.ToString(); string signature = ""; int sta = DDev.GetSign(ticket, nonceStr, timeStamp, url, ref signature); return new ResultJson { agentId = "207623633", corpId = corpId, timeStamp = timeStamp, nonceStr = NonceStr, signature = signature }; } /// <summary> /// / /// </summary> /// <param name="code"></param> /// <returns></returns> // [HttpPost] public ResultJson GetCurrentUser(string code) { HROCPeopleBll ppbll = new HROCPeopleBll(); string corpId = "dinga85d9", corpSecret = "D5r1G6cejOlZmEaB8PNlI9J5"; string access_token = GetAccessToken(corpId, corpSecret); string tagUrl = "https://oapi.dingtalk.com/user/getuserinfo?access_token=" + access_token + "&code=" + code; string result = GetContents(tagUrl); var userModel = JsonConvert.DeserializeObject<UserInfo>(result); string ddid = userModel.userid; HROCPeople pp = new HROCPeople(); pp = ppbll.GetPeopleModel(ddid); userModel.BpmLoginName = pp.LoginName; userModel.BpmUserID = pp.ID; userModel.BpmUserName = pp.Name; return new ResultJson { DetailData = userModel }; // var dtmodel= JsonConvert.DeserializeObject<HROCPeople>(dt); //return new ResultJson { DetailData= pp.Name,data=pp.LoginName }; } /// <summary> /// /// </summary> /// <param name="DDID"></param> /// <returns></returns> public ResultJson GetCurrentUsers(string DDID) { HROCPeopleBll ppbll = new HROCPeopleBll(); HROCPeople hrp = ppbll.GetPeopleModel(DDID); return new ResultJson { DetailData = hrp }; } /// <summary> /// /// </summary> /// <param name="DDID"></param> /// <returns></returns> public string GetName(string DDID) { //return string.Format("姓名:{0}", Name); HROCPeopleBll ppbll = new HROCPeopleBll(); HROCPeople hrp = ppbll.GetPeopleModel(DDID); return string.Format("姓名:{0},登录名:{1},ID:{2} ", hrp.Name, hrp.LoginName, hrp.ID); //return new ResultJson { data = hrp.Name, signature= hrp.LoginName }; } //POST api/DingServer/GetCurrentUser? code = { code } /// <summary> /// /// </summary> /// <param name="corpId"></param> /// <param name="corpSecret"></param> /// <returns></returns> public string GetAccessToken(string corpId, string corpSecret) { string url = string.Format("https://oapi.dingtalk.com/gettoken?corpid={0}&corpsecret={1}", corpId, corpSecret); try { string response = GetContents(url); AccessTokenModel oat = Newtonsoft.Json.JsonConvert.DeserializeObject<AccessTokenModel>(response); if (oat != null) { if (oat.errcode == 0) { return oat.access_token; } } } catch (Exception ex) { throw; } return string.Empty; } public string GetJsApiTicket(string accessToken) { string url = string.Format("https://oapi.dingtalk.com/get_jsapi_ticket?access_token={0}", accessToken); try { string response = GetContents(url); JsApiTicketModel model = Newtonsoft.Json.JsonConvert.DeserializeObject<JsApiTicketModel>(response); if (model != null) { if (model.errcode == 0) { return model.ticket; } } } catch (Exception ex) { throw ex; } return string.Empty; } public static string GetContents(string url) { WebRequest request = HttpWebRequest.Create(url); WebResponse response = request.GetResponse(); Stream stream = response.GetResponseStream(); StreamReader reader = new StreamReader(stream); string content = reader.ReadToEnd(); return content; } } public class AccessTokenModel { public string access_token { get; set; } public int errcode { get; set; } public string errmsg { get; set; } } public class JsApiTicketModel { public string ticket { get; set; } public int errcode { get; set; } public string errmsg { get; set; } } public class UserIdModel { public string userid { get; set; } public int errcode { get; set; } public string errmsg { get; set; } } public class UserDetailInfo { public string userid { get; set; } public int errcode { get; set; } public string username { get; set; } } }
HROCPeople
using System; using System.Collections.Generic; using System.Linq; using System.Web; namespace WebApIs.Models { public class HROCPeople { public string ID { get; set; } public int IntID { get; set; } public string JobID { get; set; } public string CreateTime { get; set; } public string Name { get; set; } public string LoginName { get; set; } public string CompanyID { get; set; } public string DepartmentID { get; set; } //钉钉号 格式:{"DING":"211202322424329696"} public string other1 { get; set; } } }
钉钉帮助类
using System; using System.Text; using System.Security.Cryptography; using System.Collections; namespace Common { public static class DDev { public static int GenSigurate(string jsapi_ticket, string noncestr, string sTimeStamp, string url, ref string signature) { string assemble = string.Format("jsapi_ticket={0}&noncestr={1}×tamp={2}&url={3}", jsapi_ticket, noncestr, sTimeStamp, url); SHA1 sha; ASCIIEncoding enc; string hash = ""; try { sha = new SHA1CryptoServiceProvider(); enc = new ASCIIEncoding(); byte[] dataToHash = enc.GetBytes(assemble); byte[] dataHashed = sha.ComputeHash(dataToHash); hash = BitConverter.ToString(dataHashed).Replace("-", ""); hash = hash.ToLower(); } catch (Exception) { return 2; } signature = hash; return 0; } public static int GetSign(string ticket, string nonceStr, long timeStamp, string url, ref string signature) { String plain = string.Format("jsapi_ticket={0}&noncestr={1}×tamp={2}&url={3}", ticket, nonceStr, timeStamp, url); try { byte[] bytes = Encoding.UTF8.GetBytes(plain); byte[] digest = SHA1.Create().ComputeHash(bytes); string digestBytesString = BitConverter.ToString(digest).Replace("-", ""); signature = digestBytesString.ToLower(); return 1; } catch (Exception e) { throw e; } } /// <summary> /// 获取时间戳timestamp(当前时间戳,具体值为当前时间到1970年1月1号的秒数) /// </summary> /// <returns></returns> public static string GetTimeStamp() { TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0); return Convert.ToInt64(ts.TotalSeconds).ToString(); } /// <summary> /// 字典排序 /// </summary> public class DictionarySort : System.Collections.IComparer { public int Compare(object oLeft, object oRight) { string sLeft = oLeft as string; string sRight = oRight as string; int iLeftLength = sLeft.Length; int iRightLength = sRight.Length; int index = 0; while (index < iLeftLength && index < iRightLength) { if (sLeft[index] < sRight[index]) return -1; else if (sLeft[index] > sRight[index]) return 1; else index++; } return iLeftLength - iRightLength; } } } }
前端代码
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>钉钉服务接口调试</title>
<!--<script type="text/javascript" src="script/jquery.min.js"></script>
<script type="text/javascript" src="script/bootstrap.min.js"></script>-->
<script src="//cdn.bootcss.com/jquery/1.12.4/jquery.min.js"></script>
<script type="text/javascript" src="http://g.alicdn.com/dingding/open-develop/1.5.1/dingtalk.js"></script>
<link type="text/css" rel="stylesheet" href="content/bootstrap.css" media="screen" />
<style>
body {
margin: 20px 20px;
}
</style>
</head>
<body>
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title">用户接口</h3>
</div>
<input id="notice" type="text" />
<div class="panel-body">
<div class="method-margin col-md-12">
<div style="margin-bottom:5px;">
<button type="button" class="btn btn-default" data-loading-text="正在请求..." id="getUserDetail">获取用户详情</button>
</div>
<div class="method-output-padding">
<div class="logs-margin alert alert-danger alert-dismissible" role="alert" name="showErrorAlert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>
<span name="errorText"></span>
</div>
<div class="logs-margin alert alert-success alert-dismissible" role="alert" name="showInfoAlert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
<span name="successText"></span>
</div>
</div>
</div>
</div>
</div>
<script type="text/javascript">
function login1(id) {
$.ajax({
url: 'http://10.10.10.10:8081/api/DingServer/GetName',
type: 'get',
// dataType: 'JSON',
data: { "DDID": id },
success: function (data) {
// alert(JSON.stringify(data))
alert(data)
//var DDID = data.DetailData.userid;
//alert(DDID)
//login1(data.DetailData.userid)
},
error: function (data) {
// alert(JSON.stringify(data))
}
})
}
//function login1(id) {
// $.ajax({
// url: 'http://10.10.10.10:8081/api/DingServer/GetCurrentUsers',
// type: 'get',
// // dataType: 'JSON',
// data: { DDID: id },
// success: function (data) {
// alert(data)
// //var DDID = data.DetailData.userid;
// //alert(DDID)
// //login1(data.DetailData.userid)
// },
// error: function (data) {
// alert(JSON.stringify(data))
// }
// })
//}
function login(code) {
$.ajax({
url: 'http://10.10.10.10:8081/api/DingServer/GetCurrentUser',
type: 'GET',
// dataType: 'JSON',
data: {code:code },
success: function (data, status, xhr) {
//alert(JSON.stringify(data))
alert(data.DetailData.BpmUserID + "-" + data.DetailData.BpmUserName + "-" + data.DetailData.BpmLoginName)
},
error: function (data) {
alert(JSON.stringify(data))
}
})
}
//获取签名摘要??
$(document).ready(function () {
$.ajax({
url: "http://10.10.10.10:8081/api/DingServer/GetLoginSign",
type: 'GET',
success: function (data, status, xhr) {
if (data.code != 0) {
} else {
var str = JSON.stringify(data);
// alert(str);
dd.config({
agentId: data.agentId,
corpId: data.corpId,
timeStamp: data.timeStamp,
nonceStr: data.nonceStr,
signature: data.signature,
jsApiList: ['runtime.info', 'biz.contact.choose',
'device.notification.confirm', 'device.notification.alert',
'device.notification.prompt', 'biz.ding.post',
'biz.util.openLink']
});
dd.error(function (err) {
alert('dd error: ' + JSON.stringify(err))
})
dd.ready(function () {
dd.runtime.permission.requestAuthCode({
corpId: data.corpId,
onSuccess: function (result) {
alert(JSON.stringify(result))
login(result.code)
}
})
})
}
},
error: function (data) {
console.log('err:', data)
}
})
})
</script>
</body>
</html>
钉钉管理员配置应用

钉钉免登就是这样,只要弄懂了就会觉得其实不难。感谢阅读。
