【问题标题】:Implementing authorization based on LDAP in blazor在blazor中实现基于LDAP的授权
【发布时间】:2022-12-11 16:00:07
【问题描述】:

我有一个问题和一个简单的请求。我正在尝试在 blazor 中创建简单的身份验证和授权机制。问题是该机制的基础不是数据库而是 ldap(所有示例和教程都基于数据库存储)。

对于我现在的理解,在 blazor 中它看起来像这样

在 startup.cs 中,我添加了默认实体和存储(我已经编写了一个基于 novell LDAP 库的简单库来获取凭据以检查用户是否存在于 LDAP 中并获取用户组)。

使用数据库看起来像(创建默认身份和设置存储)

// replace this with LDAP account validation
services.AddDbContext<ApplicationDbContext>(options =>
            options.UseSqlServer(
                Configuration.GetConnectionString("DefaultConnection")));
services.AddDefaultIdentity<IdentityUser>()
            .AddEntityFrameworkStores<ApplicationDbContext>()

我知道可以使用带路由的控制器来执行此操作,但我想知道是否有比将控制器添加到 Blazor 服务器应用程序更优雅的方法。

接下来我添加 revalidate 以每隔一段时间检查用户:

services.AddScoped<AuthenticationStateProvider, RevalidatingIdentityAuthenticationStateProvider<IdentityUser>>();

然后我向应用程序添加授权和身份验证:

app.UseAuthentication();
app.UseAuthorization();

但是当我想使用其他用户数据存储源(在我的示例 LDAP 中)时,它会是什么样子呢?

【问题讨论】:

标签: c# ldap blazor


【解决方案1】:

您的问题有 2 种解决方案。选择你喜欢的任何一个。

解决方案 1:

您使用 LDAP 对用户进行身份验证,但使用身份来存储角色、声明等并以这种方式授权用户。 如果是这种情况,您可以简单地覆盖 CheckPasswordAsync 方法来检查某些 LDAP 服务器(例如 Active Directory)的密码。

看看这个答案,它确实是这样做的: https://stackoverflow.com/a/74734478/8644294

解决方案 2:

您可以使用 LDAP 在没有身份数据库的情况下对用户进行身份验证和授权。在这种情况下,您正在查看 Cookie 身份验证。 为此,启动一个新的应用程序,不要选择任何身份验证。并遵循本指南: https://learn.microsoft.com/en-us/aspnet/core/security/authentication/cookie?view=aspnetcore-7.0

您不需要添加任何控制器。只需为例如创建一个 Razor 页面:登录.cshtml. 例如:

@page
@model LoginModel

@{
    ViewData["Title"] = "Log in";
}

<h1>@ViewData["Title"]</h1>
<div class="row">
    <div class="col-md-4">
        <section>
            <form id="account" method="post">
                <div asp-validation-summary="ModelOnly" class="text-danger"></div>
                <div class="form-floating">
                    <input asp-for="Input.Username" class="form-control" autocomplete="username" aria-required="true" />
                    <label asp-for="Input.Username" class="form-label"></label>
                    <span asp-validation-for="Input.Username" class="text-danger"></span>
                </div>
                <div class="form-floating">
                    <input asp-for="Input.Password" class="form-control" autocomplete="current-password" aria-required="true" />
                    <label asp-for="Input.Password" class="form-label"></label>
                    <span asp-validation-for="Input.Password" class="text-danger"></span>
                </div>
                <div>
                    <button id="login-submit" type="submit" class="w-100 btn btn-lg btn-primary">Log in</button>
                </div>
            </form>
        </section>
    </div>
</div>

并在后面的代码中实现登录:

using System.Security.Claims;
using Microsoft.AspNetCore.Authentication.Cookies;
using System.DirectoryServices.AccountManagement;

public class LoginModel : PageModel
{
    private readonly ILogger<LoginModel> _logger;

    public LoginModel(ILogger<LoginModel> logger)
    {
        _logger = logger;
    }

    [BindProperty]
    public InputModel Input { get; set; }

    public string ReturnUrl { get; set; }

    [TempData]
    public string ErrorMessage { get; set; }

    public class InputModel
    {
        [Required]
        [Display(Name = "User name")]
        public string Username { get; set; }

        [Required]
        [DataType(DataType.Password)]
        public string Password { get; set; }

        [Display(Name = "Remember me?")]
        public bool RememberMe { get; set; }
    }

    public async Task OnGetAsync(string returnUrl = null)
    {
        if (!string.IsNullOrEmpty(ErrorMessage))
        {
            ModelState.AddModelError(string.Empty, ErrorMessage);
        }

        returnUrl ??= Url.Content("~/");

        // Clear the existing external cookie to ensure a clean login process
        await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme);

        ReturnUrl = returnUrl;
    }

    public async Task<IActionResult> OnPostAsync(string returnUrl = null)
    {
        returnUrl ??= Url.Content("~/");

        if (ModelState.IsValid)
        {
            // Write your logic on how to sign in using LDAP here. 
            // For an example, I'm using Active Direcotry as LDAP server here.
            using PrincipalContext principalContext = new(ContextType.Domain);
            bool adSignOnResult = principalContext.ValidateCredentials(Input.Username.ToUpper(), Input.Password);

            if (!adSignOnResult)
            {
                ModelState.AddModelError(string.Empty, "Invalid login attempt.");
                return Page();
            }
            
            // If LDAP login is successful:
            var roles = // Write logic to grab roles of this user from LDAP server such as Active directory 
            var claims = new List<Claim>();
            foreach (var role in roles)
            {
                var claim = new claim(claimtypes.role, role);
                claims.add(claim);
            }
            
            // Populate other claims
            claims.Add(new Claim(ClaimTypes.Name, username));
            
            // Create claims idenity:
            var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
            
            // Create claims principal
            var claimsPrincipal = new ClaimsPrincipal(claimsIdentity);
            
            // Now signin this claimsPrincipal:
            await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, claimsPrincipal,
                                          new AuthenticationProperties());

            _logger.LogInformation("User logged in.");
            return LocalRedirect(returnUrl);
        }

        // If we got this far, something failed, redisplay form
        return Page();
    }
}

但是当我想使用其他用户数据源时它会是什么样子 存储(在我的示例 LDAP 中)?

就个人而言,我自己还没有这样做,但是您应该能够使用自己的自定义逻辑更新该类,访问 LDAP 服务器并检查一些规则以确保用户应该登录。有关此类的更多信息,@987654323 @.

你不必使用那个类。如果您担心较长的身份验证会话,您可以将 cookie 身份验证间隔设置为一个较小的时间段。 比如设置在Program.cs

services.ConfigureApplicationCookie(ops =>
    ops.ExpireTimeSpan = TimeSpan.FromMinutes(10); <---- This guy
    ops.SlidingExpiration = true;
});

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-09-17
    • 1970-01-01
    • 1970-01-01
    • 2023-03-24
    • 2021-04-10
    • 2020-06-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多