本文介绍了如何通过钉钉的免登授权码和API接口,实现钉钉微应用与金蝶云星空系统的单点登录功能。首先,通过中转页面获取钉钉的免登授权码,并重定向到服务端代码文件。该文件中的JavaScript代码调用钉钉JS API获取授权码,并通过该码请求用户身份信息。随后,在C#后端代码中,使用钉钉的API接口和授权码及App密钥获取accessToken,并结合accessToken和授权码调用用户信息API,最终获取并打印用户名称。过程中还涉及了HTTP POST请求的发送和JSON数据的处理。此外,还提醒了在内网使用时避免使用HTTPS以及如何通过URL传递参数等问题。
1.首先是一个中转页,用来获取钉钉的免登授权码,打开一个新窗口重定向到第二部分写的代码文件,这样写点击钉钉工作台应用后,可以直接在电脑的默认浏览器打开页面,这个文件存放路径我是放在了服务器的html5路径下。注意一下,钉钉微应用的pc端首页地址应该是这个形式http://你的服务器ip/k3cloud/html5/你的第一部分代码文件名称.html?corpId=$CORPID$,还有一点就是,如果你是在内网使用,并且不做穿透的话,不要用https,钉钉会直接无法访问。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=devicewidth, initialscale=1.0">
<script src="https://g.alicdn.com/dingding/dingtalk-jsapi/2.11.1/dingtalk.open.js" charset="UTF-8"></script>
<title>示例</title>
</head>
<body>
<script>
window.onload = function() {
dd.ready(function() {
dd.runtime.permission.requestAuthCode({
corpId: getURLParameter('corpId'), // 企业id
onSuccess: function (info) {
code = info.code // 通过该免登授权码可以获取用户身份
window.close();
window.open('http://你的服务器ip/k3cloud/SSTO.DTalk.SYS.GetLogUrl.ExecuteService,SSTO.DTalk.SYS.common.kdsvc?code='+code);
}
});
});
function getURLParameter(name) {
name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"),
results = regex.exec(document.location.href);
return results[1];
};
};
</script>
</body>
</html>
2.获取钉钉的accessToken,并且和第一部分获取的免登授权码code组合,获取钉钉的用户名称。
using Kingdee.BOS.ServiceFacade.KDServiceFx;
using Kingdee.BOS.Util;
using Kingdee.BOS.WebApi.ServicesStub;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.ComponentModel;
using System.IO;
using System.Net;
namespace SSTO.DTalk.SYS
{
[Kingdee.BOS.Util.HotUpdate]
[Description("钉钉单点登陆金蝶云星空")]
public class GetLogUrl : AbstractWebApiBusinessService
{
//定义一些必要变量
static string accessToken;
static string name;
static string userName; //实际为code,此处暂为这样
public GetLogUrl(KDServiceContext context) : base(context)
{
}
public JObject ExecuteService(JObject parameter)
{
JObject messages = new JObject();
userName = this.KDContext.WebContext.Context.Request["code"];
if (!string.IsNullOrEmpty(userName))
{
try
{
// 钉钉API的URL
string url = "https://api.dingtalk.com/v1.0/oauth2/accessToken";
var postData = new
{
appKey = "你的钉钉微应用appkey",
appSecret = "你的钉钉微应用appSecret "
};
// 将对象序列化为JSON字符串
string json = JsonConvert.SerializeObject(postData);
// 发送HTTP POST请求并等待结果
var responseBody = JObject.Parse(SendPostRequest(url, json));
accessToken = (string)responseBody["accessToken"];
Console.WriteLine(accessToken);
}
catch (AggregateException ex)
{
// 捕获由于等待Task时发生的异常
foreach (var innerEx in ex.InnerExceptions)
{
Console.WriteLine($"Inner Exception: {innerEx.Message}");
}
}
catch (Exception e)
{
// 捕获其他类型的异常
Console.WriteLine($"Exception: {e.Message}");
}
}
if (!string.IsNullOrEmpty(accessToken))
{
try
{
// API的URL,利用了上方接口返回值,需要code和accessToken才可调用此接口返回信息
string userurl = "https://oapi.dingtalk.com/topapi/v2/user/getuserinfo?access_token=" + accessToken;
var postData = new
{
code = userName
//上方提到过,此处userName实际为获取的免登授权码code
};
// 将对象序列化为JSON字符串
string json = JsonConvert.SerializeObject(postData);
// 发送HTTP POST请求并等待结果
JObject responseBody = JObject.Parse(SendPostRequest(userurl, json));
JObject resObject = (JObject)responseBody["result"];
name = (string)resObject["name"];
Console.WriteLine(name);
}
catch (AggregateException ex)
{
// 捕获由于等待Task时发生的异常
foreach (var innerEx in ex.InnerExceptions)
{
Console.WriteLine($"Inner Exception: {innerEx.Message}");
}
}
catch (Exception e)
{
// 捕获其他类型的异常
Console.WriteLine($"Exception: {e.Message}");
}
}
//此处获取钉钉用户名后,调用第三部分单点登录的api,不做在一起,是方便后续单独使用此api,钉钉这傻东西获取用户名太麻烦了
string html = "http://你的服务器ip/k3cloud/SSTO.K3Cloud.SYS.GetLogUrl.ExecuteService,SSTO.K3Cloud.SYS.common.kdsvc?code=" + name;
this.KDContext.WebContext.Context.Response.Redirect(html);
messages.Add("issuccess", "true");
return messages;
}
static string SendPostRequest(string url, string json)
{
WebRequest request = WebRequest.Create(url);
request.Method = "POST";
request.ContentType = "application/json; charset=utf-8";
using (var streamWriter = new StreamWriter(request.GetRequestStream()))
{
streamWriter.Write(json);
}
var httpResponse = (HttpWebResponse)request.GetResponse();
using (var streamReader = new StreamReader(httpResponse.GetResponseStream()))
{
return streamReader.ReadToEnd();
}
}
}
}
3.适用于传统oa单点登录的api,有用户名拼接后可以直接登录到erp,第二部分代码重定向的时候就是调用的这个,这块可以直接参考社区内一个大佬的代码,是用于和泛微的单点登录集成的。
推荐阅读