-
Notifications
You must be signed in to change notification settings - Fork 2
Charlie edited this page Jan 5, 2021
·
1 revision
注意,此版本与 v1.0.4 在配置上存在很大差异,如果您之前使用的是 1.0.4 版本,建议先查看下 2.0 的变化
- 开发者自行在钉钉开发者平台中注册 企业内部应用-小程序 (可选)
- 开发者自行在钉钉开发者平台中注册 移动接入应用-登陆
- 引入 nuget package:
Install-Package Charlie.AspNetCore.Authentication.DingTalk
- 增加如下代码
services.AddAuthentication() .AddDingTalk(opts => { opts.ClientId = 钉钉扫码登录 的 AppKey opts.ClientSecret = 钉钉扫码登录的 AppSecret; // 以下为非必填项 opts.IncludeUserInfo = 是否包含该用户在企业内的用户信息 (默认为 false); opts.AppKey = 企业内部开发小程序的 App Key; opts.AppSecret = 企业内部开发小程序的 App Secret; }
下面以2种扫码方式进行分别说明
- 创建一个 Asp.Net Core Web 应用程序(MVC)
- 修改身份验证,选择 个人用户账户
- 在
Startup.cs
的ConfigureServices
中,增加如下代码:services.AddAuthentication() .AddDingTalk(opts => { opts.ClientId = 钉钉扫码登录 的 AppKey opts.ClientSecret = 钉钉扫码登录的 AppSecret; // 以下为非必填项 opts.IncludeUserInfo = 是否包含该用户在企业内的用户信息 (默认为 false); opts.AppKey = 企业内部开发小程序的 App Key; opts.AppSecret = 企业内部开发小程序的 App Secret; }
相比钉钉自带的扫码页面,使用自定义页面需要额外几个步骤
- 【同上】创建一个 Asp.Net Core Web 应用程序(MVC)
- 【同上】修改身份验证,选择 个人用户账户
- 右键单击项目,选择 添加-新搭建基架的项目...,然后选择 标识,在弹出框中,选择 Account\Login。这个步骤会在项目中创建 Asp.Net Core Identity 的 Login Razor Page
- 在 Areas\Identity\Pages\Account 目录中,添加一个 DingTalkLogin Razor Page。这个 Page 就是用来渲染自己的 QR 页面,在 cshtml 中加入:
<script src="https://g.alicdn.com/dingding/dinglogin/0.0.5/ddLogin.js"></script> <script> var dingtalk = "https://oapi.dingtalk.com/connect/oauth2/sns_authorize?appid=@(Model.AppId)&response_type=@(Model.ResponseType)&scope=@(Model.Scope)&state=@(Model.State)&redirect_uri=@(Model.RedirectUri)"; DDLogin({ id: "code-img", goto: encodeURIComponent(dingtalk), style: "border:none;background-color:#FFFFFF;margin-top:-40px;", width: "400", height: "300" }); var handleMessage = function (event) { if (event.origin == "https://login.dingtalk.com") { window.top.location.href = dingtalk + "&loginTmpCode=" + event.data; } }; if (typeof window.addEventListener != 'undefined') { window.addEventListener('message', handleMessage, false); } else if (typeof window.attachEvent != 'undefined') { window.attachEvent('onmessage', handleMessage); } </script>
- 修改 Areas\Identity\Pages\Account 目录中的 Login.cshtml, 增加一个 Iframe 用于显示扫码页面:
<div style="height:300px"> <form id="external-account" asp-page="./ExternalLogin" asp-route-returnUrl="@Model.ReturnUrl" method="post" target="ifmDingTalk" style="display:none"> <div> <p> <button id="dingTalkLoginBtn" type="submit" class="btn btn-primary" name="provider" value="DingTalk"></button> </p> </div> </form> <iframe name="ifmDingTalk" scrolling="no" style="width:450px;height:300px"> </iframe> </div> @section Scripts { <script> $("#dingTalkLoginBtn").click(); </script> }
- 修改
Startup.cs
在ConfigureServices
中增加:services.AddAuthentication().AddDingTalk(opts => { opts.ClientId = 钉钉扫码登录 的 AppKey opts.ClientSecret = 钉钉扫码登录的 AppSecret; // 以下为非必填项 opts.IncludeUserInfo = 是否包含该用户在企业内的用户信息 (默认为 false); opts.AppKey = 企业内部开发小程序的 App Key; opts.AppSecret = 企业内部开发小程序的 App Secret; opts.SignInScheme = IdentityConstants.ExternalScheme; // 由于是使用自己的 扫码 页面,则必须定义自己的 授权节点 opts.AuthorizationEndpoint = "/Identity/Account/DingTalkLogin"; // 演示如何把外部登陆的错误信息显示在 Razor Page 上 opts.Events.OnRemoteFailure = async ctx => { var tempDataProvider = ctx.HttpContext.RequestServices.GetRequiredService<ITempDataProvider>(); tempDataProvider.SaveTempData(ctx.HttpContext, new Dictionary<string, object> { { "ErrorMessage",ctx.Failure.Message } }); ctx.Response.Redirect("/Identity/Account/Login"); ctx.HandleResponse(); await Task.CompletedTask; }; });
- 默认情况下,Asp.Net Core Identity 在外部登录成功后,只会把
name
及nameidentifier
两个 claim 的信息作为用户的 claim 保存下来,如果想获取更多的外部登录用户的信息,需要进行如下操作: - 右键单击项目,选择 添加-新搭建基架的项目...,然后选择 标识,在弹出框中,选择 Account\ExternalLogin。这个步骤会在项目中创建 Asp.Net Core Identity 的 ExternalLogin Razor Page
- 在 ExternalLogin.cshtml.cs 中的
OnPostConfirmationAsync
方法里,当AddLoginAsync
成功后,可以通过info.Prinicpal.Claims
获取钉钉返回的所有 Claim。开发者可以自行决定如何使用这些 Claim,如:
public async Task<IActionResult> OnPostConfirmationAsync(string returnUrl = null)
{
// 省略部分无关代码
result = await _userManager.AddLoginAsync(user, info);
if (result.Succeeded)
{
_logger.LogInformation("User created an account using {Name} provider.", info.LoginProvider);
// 增加来自于 DingTalk 的额外的 claim
await _userManager.AddClaimsAsync(user, info.Principal.Claims);
//省略部分无关代码
}
}
/// <summary>
/// 钉钉特有的 Claim
/// </summary>
public static class DingTalkClaimTypes
{
/// <summary>
/// 员工在当前开发者企业账号范围内的唯一标识
/// </summary>
public const string UnionId = "urn:dingtalk:unionid";
/// <summary>
/// 员工工号
/// </summary>
public const string JobNumber = "urn:dingtalk:jobno";
/// <summary>
/// 职位信息
/// </summary>
public const string Position = "urn:dingtalk:position";
/// <summary>
/// 是否是高管
/// </summary>
public const string IsSenior = "urn:dingtalk:is_senior";
/// <summary>
/// 员工的企业邮箱
/// </summary>
public const string OrgEmail= "urn:dingtalk:ogr_email";
/// <summary>
/// 是否实名认证
/// </summary>
public const string RealAuthed = "urn:dingtalk:real_authed";
/// <summary>
/// 是否是老板
/// </summary>
public const string IsBoss = "urn:dingtalk:is_boss";
/// <summary>
/// 是否为企业的管理员
/// </summary>
public const string IsAdmin = "urn:dingtalk:is_admin";
/// <summary>
/// 在对应的部门中是否为主管:Map结构的json字符串,key是部门的id,value是人员在这个部门中是否为主管,true表示是,false表示不是
/// </summary>
public const string IsLeaderInDepts = "urn:dingtalk:is_leader_in_depts";
/// <summary>
/// 入职时间。Unix时间戳
/// </summary>
public const string HiredDate = "urn:dingtalk:hired_date";
/// <summary>
/// 扩展属性
/// </summary>
public const string Extattr = "urn:dingtalk:ext_attr";
}
详见解决方案中对应的 Demo 项目