選項1)使用自定義身份驗證Windows Active Directory的
我做了一個自定義驗證技術e使用Kerberos LDAP協議並使用PrincipalContext類來查詢Windows AD。
首先,在根對話框中保存ConversationReference中聊天的上下文並使用Base64編碼對其進行編碼。
using System;
using System.Threading.Tasks;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Connector;
using Microsoft.Bot.Builder.ConnectorEx;
using System.Threading;
namespace ADAuthBot.Dialogs
{
[Serializable]
public class RootDialog : IDialog<object>
{
public async Task StartAsync(IDialogContext context)
{
await context.PostAsync("Welcome to Auth Bot!");
context.Wait(MessageReceivedAsync);
}
private async Task MessageReceivedAsync(IDialogContext context, IAwaitable<object> result)
{
var message = await result as Activity;
ConversationReference conversationReference = message.ToConversationReference();
string username = string.Empty;
context.PrivateConversationData.SetValue<string>("usertext", message.Text);
if (!context.PrivateConversationData.TryGetValue<string>("Username", out username))
{
string encodedCookie = UrlToken.Encode(conversationReference);
await AuthDialog.createPromptForLogin(context, encodedCookie);
}
else
{
context.Call(this, ResumeAfter);
}
}
private async Task ResumeAfter(IDialogContext context, IAwaitable<object> result)
{
var item = await result;
context.Wait(MessageReceivedAsync);
}
}
}
接下來,我們就來在我們創建一個登錄卡並給需要的身份驗證點擊按鈕來打開的URL頁面的驗證對話框。
using System;
using System.Threading.Tasks;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Connector;
using Microsoft.Bot.Builder.ConnectorEx;
using System.Threading;
using System.Collections.Generic;
using System.Configuration;
namespace ADAuthBot.Dialogs
{
[Serializable]
public class AuthDialog: IDialog<object>
{
static string authenticationUrl = string.Empty; //Authentication URL is the MVC View URL, which will have the username and password window.
static string callbackurl = string.Empty;
static AuthDialog()
{
authenticationUrl = ConfigurationManager.AppSettings["AuthenticationUrl"];
callbackurl = ConfigurationManager.AppSettings["AuthCallbackUrl"];
}
public async Task StartAsync(IDialogContext context)
{
context.Wait(MessageReceivedAsync);
}
private async Task MessageReceivedAsync(IDialogContext context, IAwaitable<object> result)
{
}
public static async Task createPromptForLogin(IDialogContext context, string encodedCookie)
{
IMessageActivity response = context.MakeMessage();
response.Attachments = new List<Attachment>();
SigninCard signincard = new SigninCard()
{
Text = "Click here to sign in",
Buttons = new List<CardAction>() {
new CardAction()
{
Title = "Authentication Required",
Type = ActionTypes.OpenUrl,
Value = $"{authenticationUrl}?{encodedCookie}"
}
}
};
response.Attachments.Add(signincard.ToAttachment());
await context.PostAsync(response);
}
}
}
接下來我做它輸入您的用戶名和密碼,並將其發送到ADAuthController查詢它針對的Windows Active Directory中的MVC視圖。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace ADAuthService.Controllers
{
public class LoginADController : Controller
{
// GET: LoginAD
[Route("Login")]
public ActionResult LoginUsingAD()
{
return View();
}
}
}
接下來,我創建了用了jQuery AJAX調用由Base64編碼的編碼它使用JavaScript的BTOA()函數來發送用戶名和密碼的簡單Razor視圖。
<script src="~/scripts/jquery-3.2.1.min.js"></script>
<script src="~/scripts/bootstrap.min.js"></script>
<link href="~/Content/bootstrap.min.css" rel="stylesheet" />
<script>
$(function() {
$("#txtUserName").html("");
$("#txtPassword").html("");
function make_base64_auth(username, password) {
var tok = username + ' ' + password;
var hash = btoa(tok);
return hash;
}
$("#btnSubmit").click(function() {
var userName = $("#txtUserName").val();
var passWord = $("#txtPassword").val();
var conversationReference = $(location).attr('search');
console.log(conversationReference);
var dataToBeSent = {
"ConversationReference": conversationReference,
"HashedUserCredentials": make_base64_auth(userName, passWord)
};
$.ajax({
url: "http://localhost:1070/api/Login",
method: "POST",
dataType: "json",
data: dataToBeSent,
contentType: "application/json",
crossDomain: true,
success: function (data) {
debugger;
console.log(data);
if(!$.isEmptyObject(data))
alert(data);
},
error: function (jqXHR, textStatus, errorThrown) {
debugger;
if (!$.isEmptyObject(jqXHR))
alert("Something happened wrong because: " + jqXHR.responseText);
}
});
});
});
</script>
<div class="panel-info">
<div class="panel panel-heading">
Enter your credentials
</div>
<div class="panel panel-body">
<div class="form-group">
<label for="username">Username: </label> <input id="txtUserName" type="text" placeholder="Enter username" required class="form-control" />
<label for="password">Password: </label> <input id="txtPassword" type="password" placeholder="Enter password" required class="form-control" />
<button id="btnSubmit" class="btn btn-info">Submit</button>
<button id="btnReset" class="btn btn-danger" type="reset">Reset</button>
</div>
</div>
</div>
我做了一個模型類來存儲用戶是否被識別。
namespace ADAuthService.Models
{
public class AuthenticatedUser
{
public string AuthenticatedUserName { get; set; } = string.Empty;
public bool IsAuthenticated { get; set; } = false;
}
}
和模型類從MVC視圖中獲取詳細信息。
namespace ADAuthService.Models
{
public class UserDetailsHashed
{
public string HashedUserCredentials { get; set; } = string.Empty;
public string ConversationReference { get; set; } = string.Empty;
}
}
現在主要內容是編寫一個方法,通過以用戶名,密碼和域作爲輸入來查詢Windows Active Directory。通過身份驗證後,我使用服務URL通過使用Autofac IoC容器解析範圍來將經過身份驗證的用戶名發送到bot框架。
using ADAuthService.Models;
using Autofac;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Builder.Dialogs.Internals;
using Microsoft.Bot.Connector;
using System;
using System.Collections.Generic;
using System.DirectoryServices.AccountManagement;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Security.Authentication;
using System.Text;
using System.Threading.Tasks;
using System.Web.Http;
using System.Web.Http.Cors;
namespace ADAuthService.Controllers
{
public class ADAuthController : ApiController
{
[NonAction]
private void extractUserDetailsFromHash(UserDetailsHashed userDetails, out string username, out string password, out string conversationReference)
{
try
{
string[] userCredentials = userDetails.HashedUserCredentials.Split(' ');
byte[] userCredentialsBinary = Convert.FromBase64String(userCredentials.Last());
string decodedString = Encoding.UTF8.GetString(userCredentialsBinary);
string[] decodedStringArray = decodedString.Split(' ');
username = decodedStringArray[0];
password = decodedStringArray[1];
string[] userConversationReference = userDetails.ConversationReference.Split('?');
conversationReference = userConversationReference[1];
}
catch (Exception ex)
{
throw ex;
}
}
[NonAction]
private Task<AuthenticatedUser> ValidateUserAgainstAD(string username, string password)
{
AuthenticatedUser user = new AuthenticatedUser();
return Task.Run<AuthenticatedUser>(() => {
string ADDisplayName = string.Empty;
try
{
using (PrincipalContext ctx = new PrincipalContext(ContextType.Domain, System.Environment.UserDomainName))
{
bool isValidCredentials = ctx.ValidateCredentials(username, password, ContextOptions.Negotiate);
// Additional check to search user in directory.
if (isValidCredentials)
{
UserPrincipal prUsr = new UserPrincipal(ctx);
prUsr.SamAccountName = username;
PrincipalSearcher srchUser = new PrincipalSearcher(prUsr);
UserPrincipal foundUsr = srchUser.FindOne() as UserPrincipal;
if (foundUsr != null)
{
user.AuthenticatedUserName = foundUsr.DisplayName;
user.IsAuthenticated = isValidCredentials;
}
}
else
throw new AuthenticationException($"Couldn't query no such credentials in Microsoft Active Directory such as Username: {username} and Password: {password}. Try entering a valid username and password combination.");
}
}
catch (Exception ex)
{
throw ex;
}
return user;
});
}
[NonAction]
public async Task ReplyToBot(string userName, string encodedConversationReference)
{
Activity reply = null;
ConversationReference decodedConversationReference = UrlToken.Decode<ConversationReference>(encodedConversationReference);
bool writeSuccessful = false;
IMessageActivity msgToBeSent = decodedConversationReference.GetPostToUserMessage();
using (ILifetimeScope scope = DialogModule.BeginLifetimeScope(Conversation.Container, msgToBeSent))
{
try
{
IConnectorClient client = scope.Resolve<IConnectorClient>();
IStateClient sc = scope.Resolve<IStateClient>();
BotData userData = sc.BotState.GetPrivateConversationData(msgToBeSent.ChannelId, msgToBeSent.From.Id, msgToBeSent.Id);
userData.SetProperty("Username", userName);
sc.BotState.SetPrivateConversationData(msgToBeSent.ChannelId, msgToBeSent.Conversation.Id, msgToBeSent.Id, userData);
writeSuccessful = true;
}
catch (Exception ex)
{
writeSuccessful = false;
throw ex;
}
if (!writeSuccessful)
{
msgToBeSent.Text = string.Empty;
await Conversation.ResumeAsync(decodedConversationReference, msgToBeSent);
}
if (writeSuccessful)
{
reply = msgToBeSent as Activity;
var connector = new ConnectorClient(new Uri(msgToBeSent.ServiceUrl));
reply.Text = $"Welcome {userName}!";
connector.Conversations.SendToConversation(reply);
}
}
}
[HttpPost]
[EnableCors("*", "*", "*")]
[Route("api/Login")]
public async Task<HttpResponseMessage> Login(UserDetailsHashed userDetails)
{
try
{
string username = string.Empty;
string password = string.Empty;
string conversationReference = string.Empty;
AuthenticatedUser userToBeAuthenticated = new AuthenticatedUser();
extractUserDetailsFromHash(userDetails, out username, out password, out conversationReference);
userToBeAuthenticated = await ValidateUserAgainstAD(username, password);
if (userToBeAuthenticated.IsAuthenticated)
{
await ReplyToBot(userName: userToBeAuthenticated.AuthenticatedUserName, encodedConversationReference: conversationReference);
return new HttpResponseMessage { StatusCode = HttpStatusCode.OK, Content = new StringContent($"Thanks, {userToBeAuthenticated.AuthenticatedUserName} you're now logged in!") };
}
else
{
return new HttpResponseMessage { StatusCode = HttpStatusCode.Forbidden, Content = new StringContent($"Couldn't query no such credentials in Microsoft Active Directory such as Username: {username} and Password: {password}. Try entering a valid username and password combination.") };
}
}
catch(Exception ex)
{
throw new HttpResponseException(new HttpResponseMessage() { StatusCode = HttpStatusCode.Forbidden, Content = new StringContent($"Couldn't query no such credentials in Microsoft Active Directory. Try entering a valid username and password combination.") });
}
}
}
}
選項2)使用下面的鏈接描述的模式:
MSDN Magic number Pattern
做u有任何替代AuthBot節點 – aWebDeveloper
的看看到https://github.com/CatalystCode /節點authbot –