【问题标题】:Use Dependency Injection to pass token acquisition from startup.cs to HomeController.cs使用依赖注入将令牌获取从 startup.cs 传递到 HomeController.cs
【发布时间】:2020-11-25 23:58:41
【问题描述】:

我正在尝试为 Startup.cs 文件中的以下代码创建 DI。但是作为 .net 的新手,我很难找到解决方案。

var tokenAcquisition = context.HttpContext.RequestServices
     .GetRequiredService<ITokenAcquisition>() as ITokenAcquisition;

var graphClient = GraphServiceClientFactory
    .GetAuthenticatedGraphClient(async () =>
    {
        return await tokenAcquisition
            .GetAccessTokenForUserAsync(GraphConstants.Scopes);
    }
);

Startup.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.Authorization;
using Microsoft.Identity.Web;
using Microsoft.Identity.Web.TokenCacheProviders.InMemory;
using Microsoft.Identity.Web.UI;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Microsoft.Graph;
using System.Net;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace AD_Lookup
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddRazorPages().AddRazorRuntimeCompilation();
            // Add Microsoft Identity Platform sign-in
            services.AddSignIn(options =>
            {
                Configuration.Bind("AzureAd", options);

                options.Prompt = "select_account";

                var authCodeHandler = options.Events.OnAuthorizationCodeReceived;
                options.Events.OnAuthorizationCodeReceived = async context => {
                    // Invoke the original handler first
                    // This allows the Microsoft.Identity.Web library to
                    // add the user to its token cache
                    await authCodeHandler(context);

                    var tokenAcquisition = context.HttpContext.RequestServices
                        .GetRequiredService<ITokenAcquisition>() as ITokenAcquisition;

                    var graphClient = GraphServiceClientFactory
                        .GetAuthenticatedGraphClient(async () =>
                        {
                            return await tokenAcquisition
                                .GetAccessTokenForUserAsync(GraphConstants.Scopes);
                        }
                    );

                    // Get user information from Graph
                    var user = await graphClient.Users["carvalhostephen20@hotmail.com"]
                        .Request()
                        .GetAsync();

                    context.Principal.AddUserGraphInfo(user);

                };


                options.Events.OnAuthenticationFailed = context => {
                    var error = WebUtility.UrlEncode(context.Exception.Message);
                    context.Response
                        .Redirect($"/Home/ErrorWithMessage?message=Authentication+error&debug={error}");
                    context.HandleResponse();

                    return Task.FromResult(0);
                };

                options.Events.OnRemoteFailure = context => {
                    if (context.Failure is OpenIdConnectProtocolException)
                    {
                        var error = WebUtility.UrlEncode(context.Failure.Message);
                        context.Response
                            .Redirect($"/Home/ErrorWithMessage?message=Sign+in+error&debug={error}");
                        context.HandleResponse();
                    }

                    return Task.FromResult(0);
                };
            }, options =>
            {
                Configuration.Bind("AzureAd", options);
            });

            

            // Add ability to call web API (Graph)
            // and get access tokens
            services.AddWebAppCallsProtectedWebApi(Configuration,
                GraphConstants.Scopes)
                // Use in-memory token cache
                // See https://github.com/AzureAD/microsoft-identity-web/wiki/token-cache-serialization
                .AddInMemoryTokenCaches();

            // Require authentication
            services.AddControllersWithViews(options =>
            {
                var policy = new AuthorizationPolicyBuilder()
                    .RequireAuthenticatedUser()
                    .Build();
                options.Filters.Add(new AuthorizeFilter(policy));
            })
            // Add the Microsoft Identity UI pages for signin/out
            .AddMicrosoftIdentityUI();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            //if (env.IsDevelopment())
            //{
            //    app.UseDeveloperExceptionPage();
            //}
            //else
            //{
            //    app.UseExceptionHandler("/Home/Error");
            //    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
            //    app.UseHsts();
            //}
            app.UseDeveloperExceptionPage();
            app.UseHttpsRedirection();
            app.UseStaticFiles();

            app.UseRouting();

            app.UseAuthentication();

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Home}/{action=Index}/{id?}");
            });
        }
    }
}

Homecontroller.cs

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using AD_Lookup.Models;
using Microsoft.Graph;
using Microsoft.AspNetCore.Mvc.Authorization;
using Microsoft.Identity.Web;
using Microsoft.Identity.Web.TokenCacheProviders.InMemory;
using Microsoft.Identity.Web.UI;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using System.Net;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace AD_Lookup.Controllers
{

    public class HomeController : Controller
    {

    private readonly ILogger<HomeController> _logger;
        private ITokenAcquisition _tokenAcquisition;

        public HomeController(ILogger<HomeController> logger, ITokenAcquisition tokenAcquisition)
        {
            _logger = logger;
            _tokenAcquisition = tokenAcquisition;
        }


        public IActionResult Index()
        {
            return View();
        }

        
        public IActionResult UserSearch()
        {
            //string searchText = model.SearchBoxText;
            //ViewData["searchData"] = searchText;
            return View();
        }

        [HttpPost, ValidateAntiForgeryToken]
        public async Task<IActionResult> SearchBox(UserSearchModel model)
        {
            string searchText = model.SearchBoxText;
            ViewData["searchData"] = searchText;

            var tokenAcquisition = this.HttpContext.RequestServices
                    .GetRequiredService<ITokenAcquisition>() as ITokenAcquisition;

            var graphClient = GraphServiceClientFactory
                    .GetAuthenticatedGraphClient(async () =>
                    {
                        var accessToken =
 await tokenAcquisition.GetAccessTokenForUserAsync(GraphConstants.Scopes, "2b51e32f-e865-48ef-b075-0a456dc76468");
                        return accessToken;
                        //    .GetAccessTokenForUserAsync(GraphConstants.Scopes, "2b51e32f-e865-48ef-b075-0a456dc76468");
                    }
                );
            var user = await graphClient.Users[searchText]
                    .Request()
                    .GetAsync();

            return View();
            //return Content($"Hello {model.SearchBoxText}");
        }

        public IActionResult GroupSearch()
        {
            return View();
        }

        public IActionResult Privacy()
        {
            return View();
        }

        [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
        public IActionResult Error()
        {
            return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
        }
    }
}

我不想在我的控制器中复制相同的代码,而是想使用 DI 传递 tokenacquisition 的值。

使用图形 API,我尝试根据在文本框中输入的电子邮件 ID 打印用户信息。 graphAPI 提供了我尝试实现的以下代码 sn-p。

GraphServiceClient graphClient = new GraphServiceClient( authProvider );

var user = await graphClient.Users["carvalhostephen20@hotmail.com"]
    .Request()
    .GetAsync();

这可以在https://developer.microsoft.com/en-us/graph/graph-explorer找到 通过电子邮件搜索示例查询用户。这是我正在尝试实现的功能。

将 Startup.cs 代码复制到控制器会导致解决方案失败并返回以下错误页面:

【问题讨论】:

    标签: c# .net asp.net-core dependency-injection asp.net-core-mvc


    【解决方案1】:

    对不起,我第一次把你的问题弄错了。所以基本上你在打电话services.AddWebAppCallsProtectedWebApi()时注册你的ITokenAcquisition。尝试修改您的Homecontroller。删除这一行:

    var tokenAcquisition = this.HttpContext.RequestServices
                    .GetRequiredService<ITokenAcquisition>() as ITokenAcquisition;
    

    改用这个实例:_tokenAcquisition

    【讨论】:

    • @lobster,瞬态对象总是新的。每次请求新对象时都会创建新对象(通过 DI 请求实例)。如果我们想在 Web 请求级别使用它,它应该是 Scoped 生命周期(每个新请求都不同)。更多信息在这里 - docs.microsoft.com/en-us/aspnet/core/fundamentals/…
    • @SaiGummaluri 感谢您的观察,但我的意思是请求服务容器。
    • 这需要在 startup.cs 文件中添加对吧?而不是以下行:var tokenAcquisition = context.HttpContext.RequestServices .GetRequiredService&lt;ITokenAcquisition&gt;() as ITokenAcquisition; @lobstar
    • 还有参数部分的sp这个词是什么意思? @lobstar
    • @stephencarvalho 您需要将此代码片段添加到 startup.cs,但不是。 spIServiceProvider 的一个实例,您可以使用它从服务容器中获取另一个依赖项
    【解决方案2】:

    我也遇到过几次“没有帐户或登录提示被传递给 AcquireTokenSilent”。

    就我而言,每次发生这种情况时,解决方案都会清除完整的浏览器历史记录(包括 cookie)。我没有调查确切的原因,因为这个解决方案对我来说已经足够好了,但它似乎是由于浏览器缓存了一些令牌,在对令牌获取代码进行了某些更改后需要刷新。

    【讨论】:

      猜你喜欢
      • 2018-01-08
      • 1970-01-01
      • 2019-04-18
      • 2017-03-11
      • 2023-03-09
      • 1970-01-01
      • 2018-03-27
      • 1970-01-01
      • 2019-12-28
      相关资源
      最近更新 更多