ASP.NET Core中基于令牌的身份验证如何实现?

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (19)

我正在使用ASP.NET Core应用程序。我试图实现基于令牌的身份验证,但无法弄清楚如何使用新的安全系统

我的场景: 客户端请求令牌。我的服务器应授权用户并返回客户端在以下请求中使用的access_token。

这里有两篇关于实现我需要的精彩文章:

问题是 - 对我来说,如何在ASP.NET Core中做同样的事情并不明显。

我的问题是:如何配置ASP.NET Core Web Api应用程序以使用基于令牌的身份验证?我应该追求什么方向?你有没有写过关于最新版本的文章,或者知道我能找到哪些文章?

提问于
用户回答回答于

我创建了一个基于令牌的身份验证的完整工作示例,针对ASP.NET Core(1.0.1)进行了工作。你可以找到完整的代码在这个仓库在GitHub上(替代分支1.0.0-RC1beta8β7的),但在短暂的,重要的步骤是:

为应用程序生成密钥

在我的示例中,每次应用程序启动时都会生成一个随机密钥,需要生成一个并将其存储在某处并将其提供给您的应用程序。查看这个文件,了解我如何生成随机密钥以及如何从.json文件导入它Data Protection API似乎是“正确”管理密钥的理想人选,但我还没有制定出如果可能的话。如果你解决了问题,请提交一个拉取请求!

Startup.cs - ConfigureServices

在这里,我们需要加载一个用于我们的令牌签名的私钥,我们也将在验证令牌时使用它们来验证令牌。我们将密钥存储在类级变量中key,我们将在下面的配置方法中重用该变量。TokenAuthOptions是一个简单的类,它持有我们在TokenController中创建密钥所需的签名身份,受众和签发者。

// Replace this with some sort of loading from config / file.
RSAParameters keyParams = RSAKeyUtils.GetRandomKey();

// Create the key, and a set of token options to record signing credentials 
// using that key, along with the other parameters we will need in the 
// token controlller.
key = new RsaSecurityKey(keyParams);
tokenOptions = new TokenAuthOptions()
{
    Audience = TokenAudience,
    Issuer = TokenIssuer,
    SigningCredentials = new SigningCredentials(key, SecurityAlgorithms.Sha256Digest)
};

// Save the token options into an instance so they're accessible to the 
// controller.
services.AddSingleton<TokenAuthOptions>(tokenOptions);

// Enable the use of an [Authorize("Bearer")] attribute on methods and
// classes to protect.
services.AddAuthorization(auth =>
{
    auth.AddPolicy("Bearer", new AuthorizationPolicyBuilder()
        .AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme‌​)
        .RequireAuthenticatedUser().Build());
});

我们还制定了授权政策,允许我们使用[Authorize("Bearer")]我们希望保护的端点和类别。

Startup.cs - 配置

在这里,我们需要配置JwtBearerAuthentication:

app.UseJwtBearerAuthentication(new JwtBearerOptions {
    TokenValidationParameters = new TokenValidationParameters {
        IssuerSigningKey = key,
        ValidAudience = tokenOptions.Audience,
        ValidIssuer = tokenOptions.Issuer,

        // When receiving a token, check that it is still valid.
        ValidateLifetime = true,

        // This defines the maximum allowable clock skew - i.e.
        // provides a tolerance on the token expiry time 
        // when validating the lifetime. As we're creating the tokens 
        // locally and validating them on the same machines which 
        // should have synchronised time, this can be set to zero. 
        // Where external tokens are used, some leeway here could be 
        // useful.
        ClockSkew = TimeSpan.FromMinutes(0)
    }
});

TokenController

在令牌控制器中,需要使用在Startup.cs中加载的密钥来生成签名密钥的方法。我们在Startup中注册了一个TokenAuthOptions实例,所以我们需要在T​​okenController的构造函数中注入它:

[Route("api/[controller]")]
public class TokenController : Controller
{
    private readonly TokenAuthOptions tokenOptions;

    public TokenController(TokenAuthOptions tokenOptions)
    {
        this.tokenOptions = tokenOptions;
    }
...

然后,需要在处理程序中为登录端点生成令牌,在我的示例中,我使用用户名和密码并使用if语句验证这些令牌,但您需要做的关键是创建或加载声明为基础的身份并为此生成令牌:

public class AuthRequest
{
    public string username { get; set; }
    public string password { get; set; }
}

/// <summary>
/// Request a new token for a given username/password pair.
/// </summary>
/// <param name="req"></param>
/// <returns></returns>
[HttpPost]
public dynamic Post([FromBody] AuthRequest req)
{
    // Obviously, at this point you need to validate the username and password against whatever system you wish.
    if ((req.username == "TEST" && req.password == "TEST") || (req.username == "TEST2" && req.password == "TEST"))
    {
        DateTime? expires = DateTime.UtcNow.AddMinutes(2);
        var token = GetToken(req.username, expires);
        return new { authenticated = true, entityId = 1, token = token, tokenExpires = expires };
    }
    return new { authenticated = false };
}

private string GetToken(string user, DateTime? expires)
{
    var handler = new JwtSecurityTokenHandler();

    // Here, you should create or look up an identity for the user which is being authenticated.
    // For now, just creating a simple generic identity.
    ClaimsIdentity identity = new ClaimsIdentity(new GenericIdentity(user, "TokenAuth"), new[] { new Claim("EntityID", "1", ClaimValueTypes.Integer) });

    var securityToken = handler.CreateToken(new Microsoft.IdentityModel.Tokens.SecurityTokenDescriptor() {
        Issuer = tokenOptions.Issuer,
        Audience = tokenOptions.Audience,
        SigningCredentials = tokenOptions.SigningCredentials,
        Subject = identity,
        Expires = expires
    });
    return handler.WriteToken(securityToken);
}

这应该是。只需添加[Authorize("Bearer")]任何要保护的方法或类,并且如果尝试在没有令牌存在的情况下访问它,则应该会出现错误。如果你想返回一个401而不是500的错误,你需要注册一个自定义的异常处理程序,就像我在这里的示例中那样

用户回答回答于
  1. 为应用程序生成一个RSA密钥。下面是一个非常基本的例子,但是有很多关于如何在.Net Framework中处理安全密钥的信息;。 private static string GenerateRsaKeys() { RSACryptoServiceProvider myRSA = new RSACryptoServiceProvider(2048); RSAParameters publicKey = myRSA.ExportParameters(true); return myRSA.ToXmlString(includePrivateParameters: true); } 将其保存到一个.xml文件并将其包含在应用程序中; 我将它嵌入到我的DLL中,因为它是一个小型的个人项目,我认为任何人都无法访问我的程序集,但有很多理由说明这不是一个好主意,所以我没有在这里提供这个例子。最终,你必须决定什么是最适合你的项目的。 注:有人指出,ToXmlStringFromXmlString没有在.NET中提供的核心。相反,可以使用RSAParameters ExportParameters(bool includePrivateParameters)void ImportParameters(RSAParameters parameters)Core一致的方式自行保存/加载值,例如使用JSON。
  2. 创建一些稍后我们将使用的常量; 这是我所做的: const string TokenAudience = "Myself"; const string TokenIssuer = "MyProject";
  3. 将此添加到您的Startup.cs的ConfigureServices。稍后我们将使用依赖注入来访问这些设置。我正在离开访问RSA xml流; 但我假设你有一个stream变量访问它。 RsaSecurityKey key; using (var textReader = new System.IO.StreamReader(stream)) { RSACryptoServiceProvider publicAndPrivate = new RSACryptoServiceProvider(); publicAndPrivate.FromXmlString(textReader.ReadToEnd()); key = new RsaSecurityKey(publicAndPrivate.ExportParameters(true)); } services.AddInstance(new SigningCredentials(key, SecurityAlgorithms.RsaSha256Signature, SecurityAlgorithms.Sha256Digest)); services.Configure<OAuthBearerAuthenticationOptions>(bearer => { bearer.TokenValidationParameters.IssuerSigningKey = key; bearer.TokenValidationParameters.ValidAudience = TokenAudience; bearer.TokenValidationParameters.ValidIssuer = TokenIssuer; });
  4. 设置承载认证。如果使用Identity,请UseIdentity之前执行此操作。请注意,任何第三方认证线,比如UseGoogleAuthentication,必须走之前UseIdentity线。UseCookieAuthentication如果您使用Identity,则不需要任何操作。 app.UseOAuthBearerAuthentication();
  5. 你可能想指定一个AuthorizationPolicy。这将允许指定仅允许使用承载令牌作为认证的控制器和操作[Authorize("Bearer")]services.ConfigureAuthorization(auth => { auth.AddPolicy("Bearer", new AuthorizationPolicyBuilder() .AddAuthenticationTypes(OAuthBearerAuthenticationDefaults.AuthenticationType) .RequireAuthenticatedUser().Build()); });
  6. 棘手的部分来了:构建令牌。我不会在这里提供所有的代码,但它应该足以再现。(我在自己的代码库中有一些与这些代码无关的私有事物。) 这个位是从构造函数注入的; 这就是为什么我们配置上面的选项而不是简单地将它们传递给UseOAuthBearerAuthentication() private readonly OAuthBearerAuthenticationOptions bearerOptions; private readonly SigningCredentials signingCredentials; 然后,在你的/Token行动中...... // add to using clauses: // using System.IdentityModel.Tokens.Jwt; var handler = bearerOptions.SecurityTokenValidators.OfType<JwtSecurityTokenHandler>() .First(); // The identity here is the ClaimsIdentity you want to authenticate the user as. // You can add your own custom claims to it if you like. // You can get this using the SignInManager if you're using Identity. var securityToken = handler.CreateToken( issuer: bearerOptions.TokenValidationParameters.ValidIssuer, audience: bearerOptions.TokenValidationParameters.ValidAudience, signingCredentials: signingCredentials, subject: identity); var token = handler.WriteToken(securityToken);var token是不记名令牌 - 可以将其作为字符串返回给用户,以便按照对承载身份验证的期望进行传递。
  7. 如果在HTML页面中以局部视图呈现此视图,并结合使用.Net 4.5中的仅带有承载的身份验证,则现在可以使用a ViewComponent执行相同操作。它大部分与上面的Controller Action代码相同。

扫码关注云+社区