【问题标题】:How to add AD authentication in my Chat bot - MS bot framework v4如何在我的聊天机器人中添加 AD 身份验证 - MS 机器人框架 v4
【发布时间】:2020-04-29 02:26:17
【问题描述】:

我正在使用 C# 中的 MS bot 框架 v4 SDK 开发一个聊天机器人。

我选择Dispatch bot sample 作为我的基地,因为我使用的是 QnA maker 和 Luis。现在我想添加一个身份验证来访问我的机器人。根据示例Authentication Bot,我已经在我的机器人项目代码中添加了所有依赖项,包括 nuget 包、方法和类,但身份验证仍然对我不起作用,它会引发异常错误并抱歉出现错误错误.集成和调用正确方法的方式可能存在一些问题。

如果有人有同时使用身份验证和 QnA Maker 的机器人解决方案或示例,请分享给我。

我的代码: Dispatchbot.cs

    namespace Microsoft.BotBuilderSamples
{

    public class DispatchBot<T> : ActivityHandler where T : Dialog
    {

        private ILogger _logger;
        private IBotServices _botServices;
        private BotState _conversationState;
        private BotState _userState;
        protected readonly Dialog Dialog;


        public DispatchBot(ConversationState conversationState, UserState userState, T dialog, ILogger<DispatchBot<T>> logger, IBotServices botServices)

        {
            _conversationState = conversationState;
            _userState = userState;
             Dialog = dialog;
            _logger = logger;
            _botServices = botServices;


        }

        [HttpGet]
        protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
        {
            await Dialog.RunAsync(turnContext, _conversationState.CreateProperty<DialogState>(nameof(DialogState)), cancellationToken);
            /*....my custom logic..........*/

        }

        //added for authentication
        protected override async Task OnTokenResponseEventAsync(ITurnContext<IEventActivity> turnContext, CancellationToken cancellationToken)
        {
            _logger.LogInformation("Running dialog with Token Response Event Activity.");

            // Run the Dialog with the new Token Response Event Activity.
            await Dialog.RunAsync(turnContext, _conversationState.CreateProperty<DialogState>(nameof(DialogState)), cancellationToken);
        }

        //added code for welcome message on page load
        protected override async Task OnEventActivityAsync(ITurnContext<IEventActivity> turnContext, CancellationToken cancellationToken)
        {

          var oneventconversationStateAccessors = _conversationState.CreateProperty<oneventvalues>(nameof(oneventvalues));
          var onevntconversationData = await oneventconversationStateAccessors.GetAsync(turnContext, () => new oneventvalues());

            var objectdata = JsonConvert.DeserializeObject<dynamic>(turnContext.Activity.Value.ToString());

               data _data = new data();
               _data.prodselected = objectdata["Product"];
               _data.relselected = objectdata["Release"];
               _data.hour = objectdata["Hour"];

                /*....my custom logic..........*/


        }
        public class data
        {
            public string prodselected { get; set; }
            public string relselected { get; set; }
            public int hour { get; set; }


        }
        protected override async Task OnMembersAddedAsync(IList<ChannelAccount> membersAdded, ITurnContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
        {
            var userStateAccessors = _userState.CreateProperty<UserProfile>(nameof(UserProfile));
            var userProfile = await userStateAccessors.GetAsync(turnContext, () => new UserProfile());

            foreach (var member in membersAdded)
            {
               if (member.Id != turnContext.Activity.Recipient.Id)
             {
                    userProfile.Name = member.Name.Replace('.', ' ');
                    await turnContext.SendActivityAsync(MessageFactory.Text($"Hi  **{member.Name.Replace('.',' ')}**. I'm your Assistant."), cancellationToken);
                    User = member.Name;
                }
            }

        }
        //added from statemanagement
        public override async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken = default(CancellationToken))
        {
            if (turnContext?.Activity?.Type == ActivityTypes.Invoke && turnContext.Activity.ChannelId == "msteams")
                await Dialog.Run(turnContext, _conversationState.CreateProperty<DialogState>(nameof(DialogState)), cancellationToken);
            else
                await base.OnTurnAsync(turnContext, cancellationToken);

            // Save any state changes that might have occured during the turn.
            await _conversationState.SaveChangesAsync(turnContext, false, cancellationToken);
            await _userState.SaveChangesAsync(turnContext, false, cancellationToken);

        }

        private async Task DispatchToTopIntentAsync(ITurnContext<IMessageActivity> turnContext, string intent, RecognizerResult recognizerResult, CancellationToken cancellationToken, ConversationData conversationData)
        {
            switch (intent)
            {
                case "q-qna-1":
                    await Process1(turnContext, cancellationToken,conversationData);
                    break;
                case "q-qna-2":

                    await Process2(turnContext, cancellationToken, conversationData);
                    break;
                default:
                    _logger.LogInformation($"Dispatch unrecognized intent: {intent}.");

            }
        }
               /*....my custom logic methods.........*/

        }

Authbot.cs

namespace Microsoft.BotBuilderSamples
{
    public class AuthBot<T> : DispatchBot<T> where T : Dialog
    {

        public AuthBot(ConversationState conversationState, UserState userState, ILogger<DispatchBot<T>> logger, T dialog, IBotServices botServices )
              : base(conversationState, userState, dialog, logger, botServices)
        {
        }

        protected override async Task OnMembersAddedAsync(IList<ChannelAccount> membersAdded, ITurnContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
        {
            foreach (var member in turnContext.Activity.MembersAdded)
            {
                if (member.Id != turnContext.Activity.Recipient.Id)
                {
                    await turnContext.SendActivityAsync(MessageFactory.Text("Welcome to AuthenticationBot ."), cancellationToken);
                }
            }
        }

    }
}

主对话框:

    namespace Microsoft.BotBuilderSamples
{
    public class MainDialog : LogoutDialog
    {
        protected readonly ILogger Logger;

        public MainDialog(IConfiguration configuration, ILogger<MainDialog> logger)
            : base(nameof(MainDialog), configuration["ConnectionName"])
        {
            Logger = logger;

            AddDialog(new OAuthPrompt(
                nameof(OAuthPrompt),
                new OAuthPromptSettings
                {
                    ConnectionName = ConnectionName,
                    Text = "Please Sign In",
                    Title = "Sign In",
                    Timeout = 300000, // User has 5 minutes to login (1000 * 60 * 5)
                }));

            AddDialog(new ConfirmPrompt(nameof(ConfirmPrompt)));

            AddDialog(new WaterfallDialog(nameof(WaterfallDialog), new WaterfallStep[]
            {
                PromptStepAsync,
                LoginStepAsync,
                DisplayTokenPhase1Async,
                DisplayTokenPhase2Async,
            }));

            // The initial child Dialog to run.
            InitialDialogId = nameof(WaterfallDialog);
        }

        private async Task<DialogTurnResult> PromptStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
        {
            return await stepContext.BeginDialogAsync(nameof(OAuthPrompt), null, cancellationToken);
        }

        private async Task<DialogTurnResult> LoginStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
        {
            // Get the token from the previous step. Note that we could also have gotten the
            // token directly from the prompt itself. There is an example of this in the next method.
            var tokenResponse = (TokenResponse)stepContext.Result;
            if (tokenResponse != null)
            {
                await stepContext.Context.SendActivityAsync(MessageFactory.Text("You are now logged in."), cancellationToken);
                return await stepContext.PromptAsync(nameof(ConfirmPrompt), new PromptOptions { Prompt = MessageFactory.Text("Would you like to view your token?") }, cancellationToken);
            }

            await stepContext.Context.SendActivityAsync(MessageFactory.Text("Login was not successful please try again."), cancellationToken);
            return await stepContext.EndDialogAsync(cancellationToken: cancellationToken);
        }

        private async Task<DialogTurnResult> DisplayTokenPhase1Async(WaterfallStepContext stepContext, CancellationToken cancellationToken)
        {
            await stepContext.Context.SendActivityAsync(MessageFactory.Text("Thank you."), cancellationToken);

            var result = (bool)stepContext.Result;
            if (result)
            {
                // Call the prompt again because we need the token. The reasons for this are:
                // 1. If the user is already logged in we do not need to store the token locally in the bot and worry
                // about refreshing it. We can always just call the prompt again to get the token.
                // 2. We never know how long it will take a user to respond. By the time the
                // user responds the token may have expired. The user would then be prompted to login again.
                //
                // There is no reason to store the token locally in the bot because we can always just call
                // the OAuth prompt to get the token or get a new token if needed.
                return await stepContext.BeginDialogAsync(nameof(OAuthPrompt), cancellationToken: cancellationToken);
            }

            return await stepContext.EndDialogAsync(cancellationToken: cancellationToken);
        }

        private async Task<DialogTurnResult> DisplayTokenPhase2Async(WaterfallStepContext stepContext, CancellationToken cancellationToken)
        {
            var tokenResponse = (TokenResponse)stepContext.Result;
            if (tokenResponse != null)
            {
                await stepContext.Context.SendActivityAsync(MessageFactory.Text($"Here is your token {tokenResponse.Token}"), cancellationToken);
            }

            return await stepContext.EndDialogAsync(cancellationToken: cancellationToken);
        }
    }
}

异常中的最终错误消息:

无法从观众声明中获取机器人 AppId。

【问题讨论】:

  • 您可以分享您的机器人中的代码吗?此外,“出了点问题”消息正是对话中显示的内容。您应该将完整的错误消息记录到控制台(或记录错误的任何位置)。如果你也能提供,那将非常有帮助。
  • @billoverton 感谢您的回复,我已经添加了所需的详细信息,请告诉我我在身份验证方面缺少什么,就我的调试而言,我的机器人项目没有命中 botstate.cs文件,而身份验证示例机器人会执行此操作并提供登录选项

标签: c# authentication botframework qnamaker


【解决方案1】:

对于来自频道的请求,App Id 在 JWT 令牌的受众声明中。错误“无法从观众声明中获取机器人 App ID”是因为它无法从轮流状态中获取 appID 并且它为空。我建议不要直接添加身份验证机器人示例,而是遵循Add Authentication to a bot 文档,该文档提供了有关向机器人添加代码的逐步说明。如果您错过了任何步骤,它会让您轻松缩小范围。我还建议仔细检查 Azure AD 应用程序是否配置正确并且您能够生成令牌。

希望这会有所帮助。

【讨论】:

  • 感谢您的回复,实际上我按照文档在 azure 门户中执行了所有步骤并完成了所有操作,直到连接名称为止,并且在文档中他们再次提到了相同的身份验证机器人作为示例代码,并且因此我解决了这个问题。
猜你喜欢
  • 1970-01-01
  • 2020-03-28
  • 2017-08-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多