Bot Framework-登录卡,如何获取身份验证结果 [英] Bot Framework - Sign-In Card, how get auth result

查看:59
本文介绍了Bot Framework-登录卡,如何获取身份验证结果的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

使用Microsoft Bot Framework V3,我开始使用登录卡.

working with Microsoft Bot Framework V3 I started using Sign-In Cards.

我从示例代码页进行了简单的剪切并将其粘贴到我的代码中,然后说它可以工作(编译): https://docs.botframework.com/zh-cn/csharp/builder/sdkreference/attachments.html

I did a simple cut and paste from example code page into my code and let's say it works (compiles): https://docs.botframework.com/en-us/csharp/builder/sdkreference/attachments.html

预期的行为类似于oauth流程,因此将其重定向到,做自己的事情,并返回包括所有信息的auth resul.

What was expected is a behavior similar to oauth process so to be redirected to , do it's own stuffs and return the auth resul including all informations.

我意识到,它只是打开了一个新网页,指向我提供的链接,仅此而已...

What I realized is that it simply open a new web page to the link I provided, that's all...

没有其他代码可以建立...

No other code founded elsewere...

到目前为止,这似乎毫无用处,因为我可以基于此行为仅向链接提供正常消息,也没有与bot通信.

So far it seems useless as I could provide the link simply with normal messages based on this behavior, also there is no communication with the bot.

我错过了什么吗?

推荐答案

选项1)使用Windows Active Directory的自定义身份验证

我已经制定了一种自定义身份验证技术,该技术使用Kerberos LDAP协议和PrincipalContext类查询Windows AD.

I have made a custom authentication technique which queries Windows AD using Kerberos LDAP Protocol and using PrincipalContext class.

首先,在根对话框"中,将聊天的上下文保存在ConversationReference中,并使用Base64编码对其进行编码.

Firstly, in Root Dialog save the context of the chat in the ConversationReference and encode it using Base64 encoding.

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页面.

Next, we come to the Auth Dialog in which we create a Sign-In Card and give the URL page that needs to be opened on the click of the authenticate button.

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);
        }
    }
}

接下来,我制作了一个MVC视图,该视图输入了您的用户名和密码,并将其发送到ADAuthController以针对Windows Active Directory进行查询.

Next I made a MVC view which inputs your username and password and sends it to the ADAuthController to query it against the Windows Active Directory.

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();
        }

    }
}

接下来,我创建了一个简单的Razor视图,该视图使用jQuery AJAX调用来发送用户名和密码,方法是使用Javascript的btoa()函数对用户名和密码进行编码.

Next I created a simple Razor view which uses jQuery AJAX call to send username and password by encoding it in base64 encoding by using Javascript's btoa() function.

<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>

我制作了一个模型类来存储是否标识了用户.

I made a model class to store whether a user is identified or not.

namespace ADAuthService.Models
{
    public class AuthenticatedUser
    {
        public string AuthenticatedUserName { get; set; } = string.Empty;
        public bool IsAuthenticated { get; set; } = false;
    }
}

和一个模型类,以从MVC View获取详细信息.

and a model class to get details from MVC View.

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框架.

Now the main content is to write a method which queries the Windows Active Directory by taking username, password and domain as input. After authenticating I am using the Service URL to send the authenticated user's name to the bot framework by resolving the scope using Autofac IoC Container.

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)使用以下链接中描述的模式:

查看全文

登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆