天天看點

C#實作釘釘企業應用鑒權

本文屬于個人原創作品、個人總結,謝絕轉載、抄襲。如果您有疑問或者希望溝通交流,可以聯系QQ:865562060。

一、寫在前面

    鑒權:釘釘提供了一些Native能力的JSAPI,這些API有很多是手機的基礎能力,對這些api的調用不需要進行鑒權(即不需要進行dd.config),隻需要保證在dd.ready裡面調用即可。對于一些釘釘業務相關、安全相關的API調用,需要開發者先進行鑒權後再調用。官方文檔位址:企業應用鑒權

釘釘的接入工作還有很多,我這裡有一個集合體的github項目:https://github.com/Menyoupingxiaoguo/DDHelper,供大家參考。以下文檔的代碼均可以在此項目中找到,如果對大家有幫助,請點給項目點個star!

    C#實作釘釘企業應用鑒權,主要分為以下步驟:

    1、鑒權-擷取access_token:

        1. 在企業管理背景:https://oa.dingtalk.com/上注冊企業完成之後,在企業應用-工作台設定頁面裡面,可以擷取到企業的corpId和CorpSecret、應用的appkey和appSecret。

        2.擷取企業的access_token。

    2、鑒權-擷取jsticket:  

        企業應用如果配置了IP白名單,則請求域名的位址必須在IP白名單裡面,且2小時之内通過接口重新請求的jsticket值都會變。如果企業沒有配置IP白名單,則2小時之内通過接口重新請求的jsticket值不會變,隻是jsticket值的生命周期重新延長2小時。通過調用擷取jsticket的接口擷取企業的jsticket。

    3、鑒權-擷取簽名參數:

        在前端進行免登鑒權之前,我們要先拿到一些免登鑒權的參數,主要有'url','nonceStr','agentId','timeStamp','corpId'

參數 說明 企業
url 目前網頁的URL,不包含#及其後面部分 //
nonceStr 随機串,自己定義 //
agentId 應用的辨別 編輯企業應用可以看到
timeStamp 時間戳 目前時間,但是前端和服務端進行校驗時候的值要一緻
corpId 企業ID 企業ID,在//open-dev.dingtalk.com/上企業視圖下開發者賬号設定裡面可以看到

    4、鑒權-計算簽名資訊:

        在服務端通過sign(ticket, nonceStr, timeStamp, url)計算前端校驗需要使用的簽名資訊。

二、代碼實作

    1、擷取access_token。釘釘為AccessToken提供的有效時長為7200s,在有效時間内每次請求都将自動延時,而釘釘提的要求是不允許對AccessToken進行高頻率請求。是以我們可以采用緩存的方式,将AccessToken緩存起來,緩存時間少于7200s,即可在每次失效前再次延長。

public string dd_host = ConfigurationManager.AppSettings["DDHost"];
public string dd_corpid = ConfigurationManager.AppSettings["DD_corpid"];
public string dd_corpsecret = ConfigurationManager.AppSettings["DD_corpsecret"];
public string appkey = ConfigurationManager.AppSettings["appkey"];
public string appsecret = ConfigurationManager.AppSettings["appsecret"];
 
/// <summary>
/// 擷取AccessToken
/// </summary>
public string GetAccessToken()
{
	//從緩存中擷取Token,如果緩存中已經過期,再從接口擷取;
	object DDToken = CacheHelper.GetCache("dd_accesstoken");
	if (DDToken == null)
	{
		//擷取token
		IDingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/gettoken");
		OapiGettokenRequest request = new OapiGettokenRequest();
		request.Corpid = dd_corpid;
		request.Corpsecret = dd_corpsecret;
		request.SetHttpMethod("GET");
		OapiGettokenResponse response = client.Execute(request);
 
		if(response.Errcode == 0)
			//将Token存入緩存
			CacheHelper.AddCache("dd_accesstoken", response.AccessToken, 115);
	}
	else
		return DDToken.ToString();
 
	return "";
}
/// <summary>
/// 擷取AccessToken
/// </summary>
public string GetAccessTokenByApp()
{
	//從緩存中擷取Token,如果緩存中已經過期,再從接口擷取;
	object DDToken = CacheHelper.GetCache("dd_app_accesstoken");
	if (DDToken == null)
	{
		//擷取token
		IDingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/gettoken");
		OapiGettokenRequest request = new OapiGettokenRequest();
		request.Appkey = appkey;
		request.Appsecret = appsecret;
		request.SetHttpMethod("GET");
		OapiGettokenResponse response = client.Execute(request);
 
		if (response.Errcode == 0)
			//将Token存入緩存
			CacheHelper.AddCache("dd_app_accesstoken", JsonConvert.SerializeObject(response), 115);
	}
	else
		return DDToken.ToString();
 
	return "";
}
           

    2、擷取jsticket。

/// <summary>
/// 擷取JsapiTicket
/// </summary>
/// <returns></returns>
public OapiGetJsapiTicketResponse GetJsapiTicket()
{
	string token = GetAccessToken();
	IDingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/get_jsapi_ticket");
	OapiGetJsapiTicketRequest request = new OapiGetJsapiTicketRequest();
	request.SetHttpMethod("GET");

	OapiGetJsapiTicketResponse response = client.Execute(request, token);
	return response;
	/*
	{
		"errcode": 0,
		"errmsg": "ok",
		"ticket": "dsf8sdf87sd7f87sd8v8ds0vs09dvu09sd8vy87dsv87",
		"expires_in": 7200
	}
	 */
}
           

    3、擷取簽名參數,計算簽名資訊。

/// <summary>
/// 登入擷取簽名
/// </summary>
/// <param name="url">擷取的url</param>
/// <returns>成功時,傳回簽名資訊</returns> 
[AnonymousAttribute]
public ResultObject DDGetSign(string url)
{
	try
	{
		String jsTicket = DDHelper.GetTicket();
		if (jsTicket != "")
		{
			String noncestr = DDHelper.GetRandomString(20, true, true, true, false, "");
			String jsUrl = System.Web.HttpUtility.UrlDecode(url);
			int timeStamp = Convert.ToInt32((DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0)).TotalSeconds);
			String plainTex = "jsapi_ticket=" + jsTicket + "&noncestr=" + noncestr + "&timestamp=" + timeStamp.ToString() + "&url=" + jsUrl;
			object SignObj = new object();
			SignObj = new
			{
				corpId = ConfigurationManager.AppSettings["DD_corpid"],
				nonceStr = noncestr,
				timeStamp = timeStamp,
				signature = EncryptHelper.SHA1(plainTex).ToLower()
			};
			return new ResultObject { result = SignObj, code = 1, message = "擷取成功!" };
		}
		else
		{
			return new ResultObject { result = null, code = 9, message = "擷取Ticket失敗!" };
		}
	}
	catch( Exception ex) 
	{
		return new ResultObject { result = null, code = 10, message = "擷取Ticket出現異常!" };
	}
}
           

    4、将corpid、nonceStr、timeStamp、signature都傳回給前端,前端進行對比鑒權即完成。

5、通過使用者code擷取使用者資訊。

/// <summary>
/// 通過前端code擷取使用者資訊
/// </summary>
/// <param name="code"></param>
/// <returns></returns>
public OapiUserGetuserinfoResponse GetUserInfoByCode(string code)
{
	string token = GetAccessToken();
	IDingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/user/getuserinfo");
	OapiUserGetuserinfoRequest request = new OapiUserGetuserinfoRequest();
	request.Code = code;
	request.SetHttpMethod("GET");
	OapiUserGetuserinfoResponse response = client.Execute(request, token);
	return response;
	/*
	{
		"userid": "****",
		"sys_level": 1,
		"errmsg": "ok",
		"is_sys": true,
		"errcode": 0
	} 
	 */
}