简体   繁体   English

ASP.NET 内核6:允许Azure AD认证和本地认证

[英]ASP.NET Core 6 : permit Azure AD authentication and local authentication

I am working on a program which will allow a user to authenticate in two ways.我正在开发一个允许用户以两种方式进行身份验证的程序。 They can create and use a local (homebrew) account with any email;他们可以通过任何 email 创建和使用本地(自制)帐户; or they can use Azure AD OAuth for our organization only.或者他们可以仅将 Azure AD OAuth 用于我们的组织。 No matter what method of authentication is used the user should be treated the same and return true on context.User.Identity.IsAuthenticated .无论使用哪种身份验证方法,用户都应该被同等对待并在context.User.Identity.IsAuthenticated上返回 true。

I have been running into issues where only the Azure AD method worked, to fix this I used authentication policies inspired by this article .我遇到了只有 Azure AD 方法有效的问题,为了解决这个问题,我使用 了受本文启发的身份验证策略。 However, after following it both authentication methods seem to not be working:(但是,在遵循它之后,两种身份验证方法似乎都不起作用:(

Here is the services code in startup.cs :这是startup.cs中的服务代码:

services.AddJwtAuthorization();

services.AddAuthentication(o =>
{
    o.DefaultScheme = "MultiAuthSchemes";
    o.DefaultChallengeScheme = "MultiAuthSchemes";
})
.AddCookie(o =>
{
    o.LoginPath = "/login";
})
.AddJwtBearer("HomebrewScheme", _ => { })
.AddPolicyScheme("MultiAuthSchemes", JwtBearerDefaults.AuthenticationScheme, options =>
{
    options.ForwardDefaultSelector = context =>
    {
        string authorization = context.Request.Headers[HeaderNames.Authorization];
        if (!string.IsNullOrEmpty(authorization) && authorization.Contains("Bearer "))
        {
            var token = authorization["Bearer ".Length..].Trim();
            var jwtHandler = new JwtSecurityTokenHandler();
            return jwtHandler.CanReadToken(token)
                ? "HomebrewScheme" : "AdScheme";
        }
        return CookieAuthenticationDefaults.AuthenticationScheme;
    };
})
.AddMicrosoftIdentityWebApi(Config, "AzureAd", "AdScheme");

services.AddAuthorization(o =>
{
    var defaultAuthorizationPolicyBuilder = new AuthorizationPolicyBuilder(
        JwtBearerDefaults.AuthenticationScheme,
        CookieAuthenticationDefaults.AuthenticationScheme,
        "HomebrewScheme", "AdScheme");
    defaultAuthorizationPolicyBuilder =
        defaultAuthorizationPolicyBuilder.RequireAuthenticatedUser();
    o.DefaultPolicy = defaultAuthorizationPolicyBuilder.Build();
    
    var onlySecondJwtSchemePolicyBuilder = new AuthorizationPolicyBuilder("HomebrewScheme");
    o.AddPolicy("OnlyHomebrewScheme", onlySecondJwtSchemePolicyBuilder
        .RequireAuthenticatedUser()
        .AddAuthenticationSchemes()
        .Build());
    var onlyCookieSchemePolicyBuilder = new AuthorizationPolicyBuilder("AdScheme");
    o.AddPolicy("OnlyAdScheme", onlyCookieSchemePolicyBuilder
        .RequireAuthenticatedUser()
        .Build());
});

Here is the app code in startup.cs :这是startup.cs中的应用程序代码:

app.UseAuthentication()
   
app.UseGraphQLPlayground(new PlaygroundOptions
{
    GraphQLEndPoint = GraphQLApiEndpoint
});
app.UseWebSockets();
app.UseRouting();

app.UseAuthorization();

app.UseEndpoints(x => x.MapGraphQL(path: GraphQLApiEndpoint));

Here is code in startup.cs which I am using to test authentication:这是我用来测试身份验证的startup.cs中的代码:

app.Use((context, next) =>
{
    //Grab the first identity because the authentication type does not matter
    if (context.User.Identity?.IsAuthenticated == true)
    {
        PermissionLevel = Permissions.Authorized;
    }
});

From what I can tell from debugging, the system never detects that I obtain authentication now.从调试中我可以看出,系统永远不会检测到我现在获得了身份验证。 I know the front end works because if I use only services.AddMicrosoftIdentityWebApiAuthentication(Config, "AzureAd", "AdScheme");我知道前端可以工作,因为如果我只使用services.AddMicrosoftIdentityWebApiAuthentication(Config, "AzureAd", "AdScheme"); no matter if it is the default scheme or not, everything works.不管它是否是默认方案,一切正常。

Thank you for your help, stuck on this one谢谢你的帮助,坚持这个

Firstly, I followed this blog to add Cookie authentication in my code.首先,我按照这个博客在我的代码中添加了 Cookie 身份验证。 It provides a login page to let us sign in. And in the AccountController, it provides several mock user account so that we can use them to sign in for test.它提供了一个登录页面让我们登录。并且在 AccountController 中,它提供了几个模拟用户帐户,以便我们可以使用它们登录进行测试。

Then I added code in the login page so that it provides the option to sign in with AAD.然后我在登录页面中添加了代码,以便它提供使用 AAD 登录的选项。

Changing the Program.cs file to add multiple authentication scheme.更改Program.cs文件以添加多个身份验证方案。

Here's my code, Program.cs这是我的代码,Program.cs

using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.Identity.Web;
using Microsoft.Identity.Web.UI;

var builder = WebApplication.CreateBuilder(args);

//set CookieAuthenticationDefaults.AuthenticationScheme as the default authentication scheme
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(x => x.LoginPath = "/account/login");
builder.Services.AddAuthentication()
    .AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"), OpenIdConnectDefaults.AuthenticationScheme, "ADCookies");

// Add microsoft sign in page
builder.Services.AddControllersWithViews().AddMicrosoftIdentityUI();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
    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.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");
app.Run();

My HomeController我的家庭控制器

using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System.Diagnostics;
using WebAppMvcCookieAuthAad.Models;

namespace WebAppMvcCookieAuthAad.Controllers
{
    [AllowAnonymous]
    public class HomeController : Controller
    {
        public IActionResult Index()
        {
            return View();
        }

        [Authorize]
        public async Task<IActionResult> ConfidentialDataAsync()
        {
            return View();
        }
    }
}

My AccountController:我的帐户控制器:

using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Mvc;
using System.Security.Claims;
using WebAppMvcCookieAuthAad.Models;

namespace WebAppMvcCookieAuthAad.Controllers
{
    public class AccountController : Controller
    { 
        public List<UserModel> users = null;
        public AccountController()
        {
            users = new List<UserModel>();
            users.Add(new UserModel()
            {
                UserId = 1,
                Username = "Tiny",
                Password = "123",
                Role = "Admin"
            });
            users.Add(new UserModel()
            {
                UserId = 2,
                Username = "Other",
                Password = "123",
                Role = "User"
            });
        }
        public IActionResult Login(string ReturnUrl = "/")
        {
            LoginModel objLoginModel = new LoginModel();
            objLoginModel.ReturnUrl = ReturnUrl;
            return View(objLoginModel);
        }
        [HttpPost]
        public async Task<IActionResult> Login(LoginModel objLoginModel)
        {
            if (ModelState.IsValid)
            {
                var user = users.Where(x => x.Username == objLoginModel.UserName && x.Password == objLoginModel.Password).FirstOrDefault();
                if (user == null)
                { 
                    ViewBag.Message = "Invalid Credential";
                    return View(objLoginModel);
                }
                else
                {
                    var claims = new List<Claim>() {
                    new Claim(ClaimTypes.NameIdentifier, Convert.ToString(user.UserId)),
                        new Claim(ClaimTypes.Name, user.Username),
                        new Claim(ClaimTypes.Role, user.Role),
                        new Claim("FavoriteDrink", "Tea")
                    };
                    var identity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
                    var principal = new ClaimsPrincipal(identity);
                    await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, principal, new AuthenticationProperties()
                    {
                        IsPersistent = objLoginModel.RememberLogin
                    });
                    return LocalRedirect(objLoginModel.ReturnUrl);
                }
            }
            return View(objLoginModel);
        }
        public async Task<IActionResult> LogOut()
        {
            await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
            return LocalRedirect("/");
        }
    }
}

My LoginModel and UserModel我的 LoginModel 和 UserModel

using System.ComponentModel.DataAnnotations;

namespace WebAppMvcCookieAuthAad.Models
{
    public class LoginModel
    {
        [Required]
        [Display(Name = "Username")]
        public string UserName{get;set;}
        [Required]
        [DataType(DataType.Password)]
        public string Password{get;set;}
        public bool RememberLogin{get;set;}
        public string ReturnUrl{get;set;}
    }
}

namespace WebAppMvcCookieAuthAad.Models
{
    public class UserModel
    {
        public int UserId { get; set; }
        public string Username { get; set; }
        public string Password { get; set; }
        public string Role { get; set; }
    }
}

View -> Account -> Login.cshtml:查看 -> 帐户 -> Login.cshtml:

@model WebAppMvcCookieAuthAad.Models.LoginModel
@{
    ViewData["Title"] = "Login";
    Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Login</h2>
<hr />
<div class="row">
    <div class="col-md-4">
        <form asp-action="Login">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            @if (!string.IsNullOrEmpty(ViewBag.Message))
            {
                <span class="text-danger">
                    @ViewBag.Message
                </span>
            }
            @Html.HiddenFor(x => x.ReturnUrl)
            <div class="form-group">
                <label asp-for="UserName" class="control-label"></label>
                <input asp-for="UserName" class="form-control" />
                <span asp-validation-for="UserName" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Password" class="control-label"></label>
                <input asp-for="Password" class="form-control" />
                <span asp-validation-for="Password" class="text-danger"></span>
            </div>
            <div class="form-group">
                <div class="checkbox">
                    <label>
                        <input asp-for="RememberLogin" /> @Html.DisplayNameFor(model => model.RememberLogin)
                    </label>
                </div>
            </div>
            <div class="form-group">
                <input type="submit" value="Login" />
            </div>
        </form>
    </div>
</div>

<div>
    <label>sign in with aad</label>
    <a asp-area="MicrosoftIdentity" asp-controller="Account" asp-action="SignIn">Sign in with aad</a>
</div>

View -> Home -> ConfidentialData.cshtml查看 -> 主页 -> ConfidentialData.cshtml

@if(User.Identity.IsAuthenticated){
    <table>
        @foreach (var item in User.Claims)
        {
            <tr><td>@item.Type</td><td>@item.Value</td></tr>
        }
    </table>
}

View -> Shared -> _LoginPartial.cshtml, pls don't forget add this partial view to layout.查看 -> 共享 -> _LoginPartial.cshtml,请不要忘记将此部分视图添加到布局中。

@using System.Security.Principal

<ul class="navbar-nav">
    @if (User.Identity.IsAuthenticated)
    {
        <li class="nav-item">
            <span class="navbar-text text-dark">Hello @User.Identity.Name!</span>
        </li>
        <li class="nav-item">
            <a class="nav-link text-dark" asp-controller="Account" asp-action="LogOut">log out</a>
        </li>
        @*asp - area = "MicrosoftIdentity"*@
    }
    else
    {
        <li class="nav-item">
            <a class="nav-link text-dark" asp-controller="Account" asp-action="Login">log in</a>
        </li>
    }
</ul>

appsetting.json: appsetting.json:

{
  "AzureAd": {
    "Instance": "https://login.microsoftonline.com/",
    "Domain": "tenant_name",
    "TenantId": "tenant_id",
    "ClientId": "azure_ad_app_id",
    "ClientSecret": "azure_ad_client_secret",
    "CallbackPath": "/home", //don't forget to set redirect url in azure portal
    "SignedOutCallbackPath ": "/signout-callback-oidc"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*"
}

This code worked well in my side and I can sign in with cookie auth and aad.这段代码在我这边运行良好,我可以使用 cookie auth 和 aad 登录。 I noticed that after signing with aad, @User.Identity.Name won't show user name.我注意到在使用 aad 签名后, @User.Identity.Name不会显示用户名。 But actually the sign in flow succeed.但实际上登录流程成功。

在此处输入图像描述

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

相关问题 ASP.NET Core 3.1 Azure AD 身份验证抛出 OptionsValidationException - ASP.NET Core 3.1 Azure AD Authentication throws OptionsValidationException ASP.NET Core 2.2 中的 Azure AD 身份验证 - Azure AD Authentication in ASP.NET Core 2.2 Azure AD 身份验证对现有 ASP.NET 核心身份应用程序 - Azure AD authentication to existing ASP.NET Core Identity application Azure AD 身份验证与 ASP.Net Core 6 中的自定义授权 - Azure AD Authentication with Custom Authorization in ASP.Net Core 6 ASP.NET MVC 5 中的 Azure AD 身份验证 - Azure AD authentication in ASP.NET MVC 5 Azure 应用服务上的 ASP.NET Core MVC Azure AD 身份验证循环 - ASP.NET Core MVC Azure AD Authentication Loop on Azure App Service Asp.Net Core Azure AD V1.0 JWT身份验证无效签名 - Asp.Net Core Azure AD V1.0 JWT Authentication Invalid Signature 默认Razor Pages应用中的Asp.Net Core 2.0 Azure广告身份验证 - Asp.Net Core 2.0 Azure Ad authentication in default Razor Pages app ASP.NET MVC Core 3.1 中的自定义用户数据与 Azure AD 身份验证? - Custom User data in ASP.NET MVC Core 3.1 with Azure AD Authentication? Azure AD 身份验证似乎在我的 ASP.Net Core 2.2 应用程序中被跳过 - Azure AD Authentication seems to be skipped in my ASP.Net Core 2.2 application
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM