简体   繁体   English

在 Blazor 服务器应用程序中,是否可以将 GraphServiceClient 注入范围服务?

[英]In a Blazor Server app, is it possible to inject a GraphServiceClient into a scoped service?

I've been experimenting with the new Blazor features and I'm attempting to pull user data from our Azure AD into a test app.我一直在试验新的 Blazor 功能,并试图将用户数据从 Azure AD 提取到测试应用程序中。 These are the relevant snippets:这些是相关的片段:

My Service我的服务

public class UserService
{

    GraphServiceClient _graphClient { get; set; }
    protected User _user = null;

    public UserService(GraphServiceClient graphClient)
    {
        _graphClient = graphClient;
    }

    public string GetUserName()
    {
        return User()?.DisplayName ?? "";
    }

Startup启动

public void ConfigureServices(IServiceCollection services)
    {
        var initialScopes = Configuration.GetValue<string>("DownstreamApi:Scopes")?.Split(' ');

        services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)

            // Add sign-in with Microsoft
            .AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAd"))

            // Add the possibility of acquiring a token to call a protected web API
            .EnableTokenAcquisitionToCallDownstreamApi(initialScopes)

                   // Enables controllers and pages to get GraphServiceClient by dependency injection
                   // And use an in memory token cache
                   .AddMicrosoftGraph(Configuration.GetSection("DownstreamApi"))
                   .AddInMemoryTokenCaches();

        services.AddControllersWithViews()
            .AddMicrosoftIdentityUI();

        services.AddRazorPages()
            .AddMicrosoftIdentityUI();
        services.AddServerSideBlazor()
            .AddMicrosoftIdentityConsentHandler();

        services.AddSingleton<WeatherForecastService>();
        services.AddScoped<UserService>();

The GraphServiceClient does get initialized in my.cs script but I get the error message: GraphServiceClient 确实在 my.cs 脚本中初始化,但我收到错误消息:

Error: No account or login hint was passed to the AcquireTokenSilent call错误:没有帐户或登录提示传递给 AcquireTokenSilent 调用

Its not a problem (I think) with any azure configuration as everything works fine if I use the Microsoft sample and make a ComponentBase.任何 azure 配置都不是问题(我认为),因为如果我使用 Microsoft 示例并制作 ComponentBase,一切正常。

public class UserProfileBase : ComponentBase
{
    [Inject]
    GraphServiceClient GraphClient { get; set; }

    protected User _user = new User();
    protected override async Task OnInitializedAsync()
    {
        await GetUserProfile();
    }

    /// <summary>
    /// Retrieves user information from Microsoft Graph /me endpoint.
    /// </summary>
    /// <returns></returns>
    private async Task GetUserProfile()
    {
        try
        {
            var request = GraphClient.Me.Request();
            _user = await request.GetAsync();
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

My current thought is that the Authorize tag that the profile component uses (and thus the ComponentBase?) is doing something behind the scenes with the access token even though I am already authenticated?我目前的想法是,即使我已经通过身份验证,配置文件组件使用的 Authorize 标记(以及 ComponentBase?)正在使用访问令牌在幕后做一些事情?

@page "/profile"
@using Microsoft.AspNetCore.Authorization
@attribute [Authorize]
@inherits UserProfileBase
public class UserAccount
{
 public int Id {get; set;}
 public string FirstName {get; set;}
 public string LastName {get; set;}
 public string Email {get; set;}
}
public class UserService
{

  GraphServiceClient _graphClient;
  protected UserAccount _user {get; set;}

  public UserService(GraphServiceClient graphClient)
  {
    _graphClient = graphClient;
  }
  public async Task<string> GetUserName()
  {
    UserAccount = await GetUserAsync();
    return $"{UserAccount.FirstName} {UserAccount.LastName}";
  }
  public async Task<UserAccount> GetUserAsync()
  {
    var user = awiat __graphClient.Me.Request.Select( e => new
       {
         e.Id,
         e.GivenName,
         e.Surname,
         e.Identities,
       }).GetAsync();
    if(user != null)
    {
      var email = user.Identities.ToList().FirstOrDefault(x => x.SignInType == "emailAddress")?.IssuerAssignedId;
      return new UserAccount
         {
           Id= user.Id,
           FirstName= user.GivenName,
           LastName= user.Surname,
           Email= email
         };
    }
    else {return null;}
  }
}

Sorry this answer is over a year late - hopefully this will help someone else in the future.抱歉,这个答案晚了一年多 - 希望这会在未来对其他人有所帮助。 I was trying to solve this exact issue today too, and I got the missing pieces of this puzzle from this demo project .我今天也试图解决这个确切的问题,我从这个演示项目中得到了这个难题的缺失部分。

In addition to your ConfigureServices method, you need to make sure that you have controllers mapped in your endpoints so that the Identity UI can map responses.除了ConfigureServices方法之外,您还需要确保在端点中映射了控制器,以便身份 UI 可以 map 响应。

app.UseEndpoints(endpoints =>
{
    endpoints.MapControllers(); // Important for Microsoft Identity UI
    endpoints.MapBlazorHub();
    endpoints.MapFallbackToPage("/_Host");
});

And then you need to have some exception handling on your scoped service.然后你需要对你的作用域服务进行一些异常处理。 I'm pretty sure this is because of Blazor's pre-rendering feature not initially authenticating the user in the first render.我很确定这是因为 Blazor 的预渲染功能最初并未在第一次渲染中对用户进行身份验证。 But don't quote me on that但不要引用我的话

I can't see enough of the OP's service, so here's mine:我看不到 OP 的服务,所以这是我的:

using Microsoft.Graph;
using Microsoft.Identity.Web;

namespace MyProject.Services
{
    public class UserService
    {
        private readonly GraphServiceClient _graphServiceClient;
        private readonly MicrosoftIdentityConsentAndConditionalAccessHandler _consentHandler;

        public UserService(
            GraphServiceClient graphServiceClient,
            MicrosoftIdentityConsentAndConditionalAccessHandler consentHandler)
        {
            _graphServiceClient = graphServiceClient;
            _consentHandler = consentHandler;
        }

        public async Task<User?> GetUserAsync()
        {
            try
            {
                return await _graphServiceClient.Me.Request().GetAsync();
            }
            catch (Exception ex)
            {
                _consentHandler.HandleException(ex);
                return null;
            }
        }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM