ASP.NET Core中基于简单令牌的Mongodb数据存储身份验证/授权

12

我需要实现一个相当简单的身份验证机制,基本上有两个角色:拥有者用户。我认为使用枚举就足够了。App 本身是一个 SPA,并通过 Asp.net core 实现了 WebAPI。我看到了一篇文章 - 如何使用 EF Identity 实现它,但是他们的模型看起来比我实际需要的要复杂得多,而且 EF 面向 SQL 数据库,我使用的是 mongodb。因此,我的用户会看起来像这样:

class UserModel{
    Id, 
    Token, 
    Roles: ["Owners", "Users"],
    ...
}

我需要实现哪些接口并将它们添加到 DI 中,才能使用 [Authorize][Authorize(Roles="Users")]属性,并使它们根据我在标题中发送的令牌正确地工作?


最好在应用程序的启动中使用基于策略的授权,您可以在其中定义策略,这样可以在不更改安全属性的情况下更改允许的角色。https://docs.asp.net/en/latest/security/authorization/index.html - Joe Audette
你能解释一下如何验证用户吗?UserModel中的Token是什么意思? - adem caglin
没关系!我错过了最后一句话。 - adem caglin
2个回答

24

让我稍微澄清一下 @Adem 的答案。您需要按特定方式实现自定义中间件。需要实现3个抽象类来实现这一点(顺便说一句,这个答案对于 asp.net core rc2 是正确的):

Microsoft.AspNetCore.Builder.AuthenticationOptions Microsoft.AspNetCore.Authentication.AuthenticationMiddleware<TOptions> Microsoft.AspNetCore.Authentication.AuthenticationHandler<TOptions>

然后将此中间件添加到启动类中。

代码示例:

public class TokenOptions : AuthenticationOptions
    {
        public TokenOptions() : base()
        {
            AuthenticationScheme = "Bearer";
            AutomaticAuthenticate = true;
        }
    }

public class AuthMiddleware : AuthenticationMiddleware<TokenOptions>
{
    protected override AuthenticationHandler<TokenOptions> CreateHandler()
    {
       return new AuthHandler(new TokenService());
    }

    public AuthMiddleware(RequestDelegate next, IOptions<TokenOptions> options, ILoggerFactory loggerFactory, UrlEncoder encoder) : base(next, options, loggerFactory, encoder)
    {
    }
}

public class AuthHandler : AuthenticationHandler<TokenOptions>
{
    private ITokenService _tokenService;

    public AuthHandler(ITokenService tokenService)
    {
        _tokenService = tokenService;
    }

    protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
    {
        string token = null;
        AuthenticateResult result = null;
        string token = Helper.GetTokenFromHEader(Request.Headers["Authorization"]);
        // If no token found, no further work possible
        if (string.IsNullOrEmpty(token))
        {
            result = AuthenticateResult.Skip();
        }
        else
        {
            bool isValid = await _tokenService.IsValidAsync(token);
            if (isValid)
            {
                //assigning fake identity, just for illustration
                ClaimsIdentity claimsIdentity = new ClaimsIdentity("Custom");
                var claims = new List<Claim>();
                claims.Add(new Claim(ClaimTypes.Name, "admin"));
                claims.Add(new Claim(ClaimTypes.NameIdentifier, "admin"));
                claims.Add(new Claim(ClaimTypes.Role, "admin"));
                ClaimsPrincipal claimsPrincipal = new ClaimsPrincipal(claimsIdentity);
                result =
                    AuthenticateResult.Success(new AuthenticationTicket(claimsPrincipal,
                        new AuthenticationProperties(), Options.AuthenticationScheme));
            }
            else
            {
                result = AuthenticateResult.Skip();
            }
        }

        return result;
    }
}`

p.s. 这段代码只是为了阐述想法,你需要自己实现处理程序。


嗨,Phoenix,在调用 AuthenticateResult.Success 后, Context.User.Identity.IsAuthenticated 仍然为 false,我错过了什么吗? - Jack Wang
好的,别担心。使用GenericIdentity而不是ClaimsIdentity可以解决这个问题。 - Jack Wang
3
在ASP.NET Core 2中,那已经过时了:/ - Ernis

13

您可以使用自定义中间件来对用户进行身份验证并设置声明(名称、角色等)。

我将尝试编写一个简单的中间件

首先创建一个中间件

public class CustomMiddleware
{
    private readonly RequestDelegate _next;
    private readonly UserRepository _userRepository;

    public CustomMiddleware(RequestDelegate next, UserRepository userRepository)
    {
        _next = next;
        _userRepository = userRepository; 
    }

    public async Task Invoke(HttpContext context)
    {
        string token = context.Request.Headers["Token"];
        var user = _userRepository.Get(token);
        ClaimsIdentity claimsIdentity = new ClaimsIdentity("Custom");
        var claims = new List<Claim>();
        claims.Add(new Claim(ClaimTypes.Name, "admin"));
        claims.Add(new Claim(ClaimTypes.NameIdentifier, "admin"));
        foreach(var role in user.Roles)
        {
            claims.Add(ClaimTypes.Role, role);
        }
        ClaimsPrincipal claimsPrincipal = new ClaimsPrincipal(claimsIdentity);
        context.User = claimsPrincipal;
        await _next(context);
    }
}

然后在 Startup.cs 中像这样使用 中间件

   public void Configure(IApplicationBuilder app)
    {
        app.UseMiddleware<CustomMiddleware>();
        ...
    }

最后使用Authorize属性:

[Authorize(Roles = "Users")]
public IActionResult Index()
{
} 

谢谢你的回答。如果在此期间没有人提出更简单的方法,我会接受它。制作自定义中间件是自定义逻辑的标准方法吗?我想 - 或许存在类似于IAuthorizationProvider的东西可以被覆盖。 - silent_coder
@adem caglin,很好,简单明了。他们为什么需要那么多其他的东西呢?顺便说一下,你的代码没有使用“claims”集合。 - Ian Warburton

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接