我有一个现有的 Blazor 服务器应用程序,可以很好地连接到 Microsoft Entra id 和 Azure SQL 数据库。客户希望为患者创建一个移动应用程序。 blazor 服务器应用程序仅供客户端管理员使用。
我尝试创建一个 .NET 8 MAUI Blazor 混合应用程序(我使用 Jetbrains Rider,因为这是我可以让 Android 和 iOS 模拟器在我的 Mac 上运行的唯一方法)。我几乎遵循了我能找到的所有教程,但没有运气。我什至问过ChatGPT,它给了我很多有用的信息,但仍然不知道该怎么做。
只有一种解决方案打开了 Microsoft 登录屏幕并让我输入密码和身份验证码,但它没有进行身份验证。我也没有收到任何错误。该项目的链接如下。
https://github.com/VladislavAntonyuk/MauiSamples/tree/main/Auth
我尝试了很多不同的解决方案,但我在这里不知所措。我也只有大约 3 个月的时间来构建这个移动应用程序(Android 和 iOS)。
有人可以帮忙吗?我可以给出我所拥有的代码,但它有很多不同的“解决方案”,因此我不会在这里列出任何代码。
更新:这是我从 ChatGPT 获得的一个解决方案,但仍然不起作用,我知道我说过我不会发布代码,因为有很多“解决方案”。这似乎是最深思熟虑的,但仍然存在类似的问题。 “.WithParentActivityOrWindow(MauiApplication.CurrentActivity)//将MainActivity调整为您的实际主要活动”这一行给我带来了问题。它显示“无法解析符号‘CurrentActivity’”(在 Rider IDE 中)。 ChatGPT 最初说使用“MainActivity.CurrentActivity”,但给出了相同的错误。
AuthenticationService.cs
namespace MauiApp7;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Identity.Client;
public class AuthenticationService
{
private readonly IPublicClientApplication _publicClientApplication;
public AuthenticationService(IPublicClientApplication publicClientApplication)
{
_publicClientApplication = publicClientApplication;
}
public string UserDisplayName { get; private set; }
// Method to update user display name
public async Task UpdateUserDisplayName()
{
try
{
var accounts = await _publicClientApplication.GetAccountsAsync();
var firstAccount = accounts.FirstOrDefault();
if (firstAccount != null)
{
// Retrieve user info from the account or any other source
UserDisplayName = firstAccount.Username; // For example, use username
}
}
catch (MsalException ex)
{
// Handle exception
}
}
public async Task<AuthenticationResult?> SignInInteractively(CancellationToken cancellationToken)
{
try
{
var result = await _publicClientApplication.AcquireTokenInteractive(Constants.Scopes)
.ExecuteAsync(cancellationToken);
return result;
}
catch (MsalException ex)
{
// Handle exception
return null;
}
}
public async Task<AuthenticationResult?> AcquireTokenSilent(CancellationToken cancellationToken)
{
try
{
var accounts = await _publicClientApplication.GetAccountsAsync();
var firstAccount = accounts.FirstOrDefault();
if (firstAccount == null)
{
return null;
}
var result = await _publicClientApplication.AcquireTokenSilent(Constants.Scopes, firstAccount)
.ExecuteAsync(cancellationToken);
return result;
}
catch (MsalUiRequiredException)
{
// Silent token acquisition failed, user interaction required
return null;
}
catch (MsalException ex)
{
// Handle specific MSAL exceptions if needed
Console.WriteLine($"MSAL Exception: {ex.Message}");
return null;
}
catch (Exception ex)
{
// Handle other exceptions
Console.WriteLine($"Exception: {ex.Message}");
return null;
}
}
public async Task SignOutAsync(CancellationToken cancellationToken)
{
try
{
var accounts = await _publicClientApplication.GetAccountsAsync();
foreach (var account in accounts)
{
await _publicClientApplication.RemoveAsync(account);
}
}
catch (MsalException ex)
{
// Handle exception
}
}
}
home.razor 页面
@page "/"
@using Microsoft.Identity.Client
@inject IPublicClientApplication PublicClientApp
@inject AuthenticationService AuthenticationService
<AuthorizeView>
<Authorized>
<p>Hello, @AuthenticationService.UserDisplayName!</p>
<button @onclick="SignOut">Sign out</button>
</Authorized>
<NotAuthorized>
<button @onclick="SignIn">Sign in</button>
</NotAuthorized>
</AuthorizeView>
@code {
private async Task SignIn()
{
var result = await AuthenticationService.SignInInteractively(CancellationToken.None);
if (result != null)
{
await AuthenticationService.UpdateUserDisplayName();
// Optionally, you can navigate to a different page after sign-in
}
}
private async Task SignOut()
{
await AuthenticationService.SignOutAsync(CancellationToken.None);
// Optionally, you can navigate to a different page after sign-out
}
}
Constants.cs
public class Constants
{
// Define your scopes here
public static readonly string[] Scopes = { "user.read" };
}
App.xaml.cs 文件
public partial class App : Application
{
public App()
{
InitializeComponent();
MainPage = new MainPage();
}
public static AzureAdConfig AzureAdConfiguration { get; } = new AzureAdConfig
{
ClientId = "<clientId>",
TenantId = "<tenantId>",
RedirectUri = "msal<clientId>://auth",
Scopes = new string[] { "user.read" }
};
}
MauiProgram.cs
using Microsoft.Extensions.Logging;
using Microsoft.AspNetCore.Components.WebView.Maui;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Maui;
using Microsoft.Maui.Controls.Hosting;
using Microsoft.Maui.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Identity.Client;
namespace MauiApp7;
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.ConfigureFonts(fonts => { fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular"); });
builder.Services.AddMauiBlazorWebView();
#if DEBUG
builder.Services.AddBlazorWebViewDeveloperTools();
builder.Logging.AddDebug();
#endif
// Add authentication service directly within CreateMauiApp
builder.Services.AddSingleton<IPublicClientApplication>(provider =>
{
var config = new AzureAdConfig
{
// Load configuration from appsettings.json or any other configuration source
ClientId = "<clientId>",
TenantId = "<tenantId>",
RedirectUri = "msal<clientId>://auth",
Scopes = new[] { "user.read" }
};
return PublicClientApplicationBuilder
.Create(config.ClientId)
.WithRedirectUri(config.RedirectUri)
.WithAuthority(AzureCloudInstance.AzurePublic, config.TenantId)
// Specify the current Activity
.WithParentActivityOrWindow(MauiApplication.CurrentActivity) // Adjust MainActivity to your actual main activity
.Build();
});
builder.Services.AddScoped<AuthenticationService>();
return builder.Build();
}
}
AzureAdConfig.cs
public class AzureAdConfig
{
public string ClientId { get; set; }
public string TenantId { get; set; }
public string RedirectUri { get; set; }
public string[] Scopes { get; set; }
}