没有身份的ASP.NET Core 2.0承载身份验证 [英] ASP.NET Core 2.0 Bearer Auth without Identity

查看:71
本文介绍了没有身份的ASP.NET Core 2.0承载身份验证的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我以为我前一天打算在.NET Core 2.0上实现独立的Bearer auth Webapi时就想到了一个非常简单的目标,但是我还没有远程进行任何工作.这是我要执行的操作的列表:

I thought I had a pretty simple goal in mind when I set out a day ago to implement a self-contained bearer auth webapi on .NET core 2.0, but I have yet to get anything remotely working. Here's a list of what I'm trying to do:

  • 实施由承载令牌保护的webapi
  • 问题代币&从同一项目中的端点刷新令牌
  • 使用[Authorize]属性控制对api表面的访问
  • 不使用ASP.Net身份(我的用户/成员资格要求轻得多)

我完全可以在登录时建立身份/声明/原则并将其添加到请求上下文中,但是我没有看到一个有关如何在没有身份的Core 2.0 webapi中发布和使用auth/refresh令牌的示例.我已经看过没有身份的Cookie的1.x MSDN示例,但这不足以使我理解上面的要求.

I'm totally fine with building identity/claims/principal in login and adding that to request context, but I've not seen a single example on how to issue and consume auth/refresh tokens in a Core 2.0 webapi without Identity. I've seen the 1.x MSDN example of cookies without Identity, but that didn't get me far enough in understanding to meet the requirements above.

我觉得这可能是一个常见的情况,不应该那么难(也许不是,可能只是缺乏文档/示例?).据我所知,IdentityServer4与Core 2.0 Auth不兼容,opendiddict似乎需要身份.我也不想将令牌终结点托管在单独的进程中,而是在同一webapi实例中托管.

I feel like this might be a common scenario and it shouldn't be this hard (maybe it's not, maybe just lack of documentation/examples?). As far as I can tell, IdentityServer4 is not compatible with Core 2.0 Auth, opendiddict seems to require Identity. I also don't want to host the token endpoint in a separate process, but within the same webapi instance.

任何人都可以给我指出一个具体的例子,或者至少给出一些最佳步骤/选择的指导吗?

Can anyone point me to a concrete example, or at least give some guidance as to what best steps/options are?

推荐答案

进行了编辑以使其与ASP.NET Core 2.0兼容.

Did an edit to make it compatible with ASP.NET Core 2.0.

首先,一些Nuget软件包:

Firstly, some Nuget packages:

  • Microsoft.AspNetCore.Authentication.JwtBearer
  • Microsoft.AspNetCore.Identity
  • System.IdentityModel.Tokens.Jwt
  • System.Security.Cryptography.Csp

然后是一些基本的数据传输对象.

Then some basic data transfer objects.

// Presumably you will have an equivalent user account class with a user name.
public class User
{
    public string UserName { get; set; }
}

public class JsonWebToken
{
    public string access_token { get; set; }

    public string token_type { get; set; } = "bearer";

    public int expires_in { get; set; }

    public string refresh_token { get; set; }
}

要使用适当的功能,您需要一个登录/令牌网络方法才能将授权令牌实际发送给用户.

Getting into the proper functionality, you'll need a login/token web method to actually send the authorization token to the user.

[Route("api/token")]
public class TokenController : Controller
{
    private ITokenProvider _tokenProvider;

    public TokenController(ITokenProvider tokenProvider) // We'll create this later, don't worry.
    {
        _tokenProvider = tokenProvider;
    }

    public JsonWebToken Get([FromQuery] string grant_type, [FromQuery] string username, [FromQuery] string password, [FromQuery] string refresh_token)
    {
        // Authenticate depending on the grant type.
        User user = grant_type == "refresh_token" ? GetUserByToken(refresh_token) : GetUserByCredentials(username, password);

        if (user == null)
            throw new UnauthorizedAccessException("No!");

        int ageInMinutes = 20;  // However long you want...

        DateTime expiry = DateTime.UtcNow.AddMinutes(ageInMinutes);

        var token = new JsonWebToken {
            access_token = _tokenProvider.CreateToken(user, expiry),
            expires_in   = ageInMinutes * 60
        };

        if (grant_type != "refresh_token")
            token.refresh_token = GenerateRefreshToken(user);

        return token;
    }

    private User GetUserByToken(string refreshToken)
    {
        // TODO: Check token against your database.
        if (refreshToken == "test")
            return new User { UserName = "test" };

        return null;
    }

    private User GetUserByCredentials(string username, string password)
    {
        // TODO: Check username/password against your database.
        if (username == password)
            return new User { UserName = username };

        return null;
    }

    private string GenerateRefreshToken(User user)
    {
        // TODO: Create and persist a refresh token.
        return "test";
    }
}

您可能已经注意到,令牌的创建仍然只是某种虚构的ITokenProvider通过的魔术".定义令牌提供者接口.

You probably noticed the token creation is still just "magic" passed through by some imaginary ITokenProvider. Define the token provider interface.

public interface ITokenProvider
{
    string CreateToken(User user, DateTime expiry);

    // TokenValidationParameters is from Microsoft.IdentityModel.Tokens
    TokenValidationParameters GetValidationParameters();
}

我在JWT上使用RSA安全密钥实现了令牌创建.所以...

I implemented the token creation with an RSA security key on a JWT. So...

public class RsaJwtTokenProvider : ITokenProvider
{
    private RsaSecurityKey _key;
    private string _algorithm;
    private string _issuer;
    private string _audience;

    public RsaJwtTokenProvider(string issuer, string audience, string keyName)
    {
        var parameters = new CspParameters { KeyContainerName = keyName };
        var provider = new RSACryptoServiceProvider(2048, parameters);

        _key = new RsaSecurityKey(provider);

        _algorithm = SecurityAlgorithms.RsaSha256Signature;
        _issuer = issuer;
        _audience = audience;
    }

    public string CreateToken(User user, DateTime expiry)
    {
        JwtSecurityTokenHandler tokenHandler = new JwtSecurityTokenHandler();

        ClaimsIdentity identity = new ClaimsIdentity(new GenericIdentity(user.UserName, "jwt"));

        // TODO: Add whatever claims the user may have...

        SecurityToken token = tokenHandler.CreateJwtSecurityToken(new SecurityTokenDescriptor
        {
            Audience = _audience,
            Issuer = _issuer,
            SigningCredentials = new SigningCredentials(_key, _algorithm),
            Expires = expiry.ToUniversalTime(),
            Subject = identity
        });

        return tokenHandler.WriteToken(token);
    }

    public TokenValidationParameters GetValidationParameters()
    {
        return new TokenValidationParameters
        {
            IssuerSigningKey = _key,
            ValidAudience = _audience,
            ValidIssuer = _issuer,
            ValidateLifetime = true,
            ClockSkew = TimeSpan.FromSeconds(0) // Identity and resource servers are the same.
        };
    }
}

因此,您现在正在生成令牌.是时候实际验证它们并进行连接了.转到您的Startup.cs.

So you're now generating tokens. Time to actually validate them and wire it up. Go to your Startup.cs.

ConfigureServices()

var tokenProvider = new RsaJwtTokenProvider("issuer", "audience", "mykeyname");
services.AddSingleton<ITokenProvider>(tokenProvider);

services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options => {
        options.RequireHttpsMetadata = false;
        options.TokenValidationParameters = tokenProvider.GetValidationParameters();
    });

// This is for the [Authorize] attributes.
services.AddAuthorization(auth => {
    auth.DefaultPolicy = new AuthorizationPolicyBuilder()
        .AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme)
        .RequireAuthenticatedUser()
        .Build();
});

然后Configure()

public void Configure(IApplicationBuilder app)
{
    app.UseAuthentication();

    // Whatever else you're putting in here...

    app.UseMvc();
}

那应该是您所需要的.希望我什么都没错过.

That should be about all you need. Hopefully I haven't missed anything.

令人高兴的结果是...

The happy result is...

[Authorize] // Yay!
[Route("api/values")]
public class ValuesController : Controller
{
    // ...
}

这篇关于没有身份的ASP.NET Core 2.0承载身份验证的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆