Pat*_*ski 68 c# authentication authorization asp.net-web-api asp.net-core
我正在使用ASP.NET Core应用程序.我正在尝试实现基于令牌的身份验证,但无法弄清楚如何使用新的安全系统.
我的场景: 客户端请求令牌.我的服务器应该授权用户并返回access_token,客户端将在以下请求中使用该access_token.
这里有两篇关于正确实现我需要的文章:
问题是 - 对于我来说,如何在ASP.NET Core中执行相同的操作并不明显.
我的问题是:如何配置ASP.NET Core Web Api应用程序以使用基于令牌的身份验证?我应该追求什么方向?你有没有写过关于最新版本的文章,或者知道我在哪里可以找到它?
谢谢!
Mar*_*hes 71
根据Matt Dekrey的精彩回答,我创建了一个基于令牌的身份验证的完整工作示例,针对ASP.NET Core(1.0.1).你可以找到完整的代码在这个仓库在GitHub上(替代分支1.0.0-RC1,beta8,β7的),但在短暂的,重要的步骤是:
为您的应用程序生成密钥
在我的例子,我生成应用程序启动随机密钥每一次,你需要生成一个和它存储在某处,并将其提供给您的应用程序.看到这个文件,我如何生成一个随机密钥,以及如何你可能会从一个以.json文件中导入.作为由@kspearrin的意见建议,对数据保护API似乎是"正确的"管理密钥的理想人选,但我从来没有制定出如果可能的话呢.如果您解决了,请提交拉取请求!
Startup.cs - ConfigureServices
在这里,我们需要为我们的令牌加载私钥,我们还将使用它来验证令牌.我们将密钥存储在类级变量中key
,我们将在下面的Configure方法中重复使用它.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());
});
Run Code Online (Sandbox Code Playgroud)
我们还设置了授权策略,允许我们使用[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)
}
});
Run Code Online (Sandbox Code Playgroud)
TokenController
在令牌控制器中,您需要有一个方法来使用Startup.cs中加载的密钥生成签名密钥.我们在Startup中注册了一个TokenAuthOptions实例,所以我们需要在TokenController的构造函数中注入它:
[Route("api/[controller]")]
public class TokenController : Controller
{
private readonly TokenAuthOptions tokenOptions;
public TokenController(TokenAuthOptions tokenOptions)
{
this.tokenOptions = tokenOptions;
}
...
Run Code Online (Sandbox Code Playgroud)
然后,您需要在处理程序中为登录端点生成令牌,在我的示例中,我使用用户名和密码并使用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);
}
Run Code Online (Sandbox Code Playgroud)
这应该是它.只需添加[Authorize("Bearer")]
到要保护的任何方法或类,如果在没有令牌存在的情况下尝试访问它,则应该收到错误.如果要返回401而不是500错误,则需要在我的示例中注册自定义异常处理程序.
Mat*_*rey 24
这真的是我的另一个答案的重复,我倾向于保持更新,因为它得到更多的关注.那里的评论也可能对你有用!
此答案的先前版本使用RSA; 如果生成令牌的相同代码也在验证令牌,则实际上没有必要.但是,如果您正在分配责任,您可能仍希望使用的实例执行此操作Microsoft.IdentityModel.Tokens.RsaSecurityKey
.
创建一些我们稍后将使用的常量; 这就是我做的:
const string TokenAudience = "Myself";
const string TokenIssuer = "MyProject";
Run Code Online (Sandbox Code Playgroud)将其添加到您的Startup.cs中ConfigureServices
.我们稍后将使用依赖注入来访问这些设置.我假设你authenticationConfiguration
是一个ConfigurationSection
或一个Configuration
对象,你可以有一个不同的配置进行调试和生产.确保您安全地存放钥匙!它可以是任何字符串.
var keySecret = authenticationConfiguration["JwtSigningKey"];
var symmetricKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(keySecret));
services.AddTransient(_ => new JwtSignInHandler(symmetricKey));
services.AddAuthentication(options =>
{
// This causes the default authentication scheme to be JWT.
// Without this, the Authorization header is not checked and
// you'll get no results. However, this also means that if
// you're already using cookies in your app, they won't be
// checked by default.
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
options.TokenValidationParameters.ValidateIssuerSigningKey = true;
options.TokenValidationParameters.IssuerSigningKey = symmetricKey;
options.TokenValidationParameters.ValidAudience = JwtSignInHandler.TokenAudience;
options.TokenValidationParameters.ValidIssuer = JwtSignInHandler.TokenIssuer;
});
Run Code Online (Sandbox Code Playgroud)
我已经看到其他答案改变了其他设置,例如ClockSkew
; 设置默认值,使其适用于时钟不完全同步的分布式环境.这些是您需要更改的唯一设置.
设置身份验证.在任何需要您的User
信息的中间件之前,您应该拥有此行,例如app.UseMvc()
.
app.UseAuthentication();
Run Code Online (Sandbox Code Playgroud)
请注意,这不会导致令牌与SignInManager
其他任何内容一起发出.您需要提供自己的输出JWT的机制 - 见下文.
您可能想要指定一个AuthorizationPolicy
.这将允许您指定仅允许使用承载令牌作为身份验证的控制器和操作[Authorize("Bearer")]
.
services.AddAuthorization(auth =>
{
auth.AddPolicy("Bearer", new AuthorizationPolicyBuilder()
.AddAuthenticationTypes(JwtBearerDefaults.AuthenticationType)
.RequireAuthenticatedUser().Build());
});
Run Code Online (Sandbox Code Playgroud)这里有一个棘手的部分:构建令牌.
class JwtSignInHandler
{
public const string TokenAudience = "Myself";
public const string TokenIssuer = "MyProject";
private readonly SymmetricSecurityKey key;
public JwtSignInHandler(SymmetricSecurityKey symmetricKey)
{
this.key = symmetricKey;
}
public string BuildJwt(ClaimsPrincipal principal)
{
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken(
issuer: TokenIssuer,
audience: TokenAudience,
claims: principal.Claims,
expires: DateTime.Now.AddMinutes(20),
signingCredentials: creds
);
return new JwtSecurityTokenHandler().WriteToken(token);
}
}
Run Code Online (Sandbox Code Playgroud)
然后,在您需要令牌的控制器中,如下所示:
[HttpPost]
public string AnonymousSignIn([FromServices] JwtSignInHandler tokenFactory)
{
var principal = new System.Security.Claims.ClaimsPrincipal(new[]
{
new System.Security.Claims.ClaimsIdentity(new[]
{
new System.Security.Claims.Claim(System.Security.Claims.ClaimTypes.Name, "Demo User")
})
});
return tokenFactory.BuildJwt(principal);
}
Run Code Online (Sandbox Code Playgroud)
在这里,我假设你已经有了校长.如果您使用的是身份,则可以使用IUserClaimsPrincipalFactory<>
将您User
转换为ClaimsPrincipal
.
测试它:获取一个令牌,将其放入jwt.io的表单中.我上面提供的说明还允许您使用配置中的秘密来验证签名!
如果您在HTML页面上的部分视图中结合使用.Net 4.5中的仅承载身份验证,您现在可以使用a ViewComponent
来执行相同操作.它与上面的Controller Action代码大致相同.
归档时间: |
|
查看次数: |
46306 次 |
最近记录: |