Проверка подлинности на основе маркеров в ASP.NET Керн
Я работаю с ASP.NET основное применение. Я пытаюсь реализовать аутентификацию на основе токенов, но не могу понять, как использовать new Система Безопасности для моего случая.
Я прошел через примеры но они мне не очень помогли, они используют либо аутентификацию cookie, либо внешнюю аутентификацию (GitHub, Microsoft, Twitter).
Каков мой сценарий: приложение angularjs должно запросить /token url передача имени пользователя и пароля. Веб-API должен авторизовать пользователя и вернуть access_token который будет использоваться приложением angularjs в следующих запросах.
Я нашел отличную статью о реализации именно то, что мне нужно в текущей версии ASP.NET -аутентификация на основе токенов с использованием ASP.NET Web API 2, Owin и Identity. Но это не очевидно для меня, как сделать то же самое в ASP.NET ядро.
мой вопрос: Как настроить ASP.NET основное приложение WebApi для работы с аутентификацией на основе токенов?
4 ответов:
обновлено для .Net Core 2:
предыдущие версии этого ответа использовали RSA; это действительно не обязательно, если ваш же код, который генерирует токены, также проверяет токены. Однако, если вы распределяете ответственность, вы, вероятно, все еще хотите сделать это с помощью экземпляра
Microsoft.IdentityModel.Tokens.RsaSecurityKey.
Создайте несколько констант, которые мы будем использовать позже; вот что я сделал:
const string TokenAudience = "Myself"; const string TokenIssuer = "MyProject";добавьте это к вашему Запуск.в 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; });я видел другие ответы изменить другие параметры, такие как
ClockSkew; по умолчанию установлены таким образом, что он должен работать для распределенных сред, в которых часы не синхронизированы. Это единственные настройки, которые нужно изменить.настройка аутентификации. Вы должны иметь эту строку перед любым промежуточным программным обеспечением, которое требует вашего
Userинформация, например:app.UseMvc().app.UseAuthentication();обратите внимание, что это не приведет к тому, что ваш токен будет выпущен с
SignInManagerили что-нибудь еще. Вам нужно будет предоставить свой собственный механизм для вывода вашего JWT-см. ниже.вы можете указать
AuthorizationPolicy. Это позволит вам чтобы указать контроллеры и действия, разрешающие только маркеры предъявителя в качестве проверки подлинности с помощью[Authorize("Bearer")].services.AddAuthorization(auth => { auth.AddPolicy("Bearer", new AuthorizationPolicyBuilder() .AddAuthenticationTypes(JwtBearerDefaults.AuthenticationType) .RequireAuthenticatedUser().Build()); });здесь идет сложная часть: создание токена.
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); } }затем, в вашем контроллере, где вы хотите свой токен, что-то вроде следующего:
[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); }здесь, я предполагаю, что у вас уже есть директор. Если вы используете Identity, вы можете использовать
IUserClaimsPrincipalFactory<>чтобы превратить вашUserв aClaimsPrincipal.чтобы проверить его: получить маркер, положить его в форму на jwt.io. инструкции, которые я предоставил выше, также позволяют вам использовать секрет из вашей конфигурации для проверки подписи!
если вы отображали это в частичном представлении на своей HTML-странице в сочетании с аутентификацией только для предъявителя в .Net 4.5, теперь вы можете использовать
ViewComponentсделать то же самое. Это в основном то же самое, что и контроллер Код действия выше.
работает с сказочный ответ Мэтта Декри, Я создал полностью рабочий пример аутентификации на основе токенов, работающий против ASP.NET ядро (1.0.1). Вы можете найти полный код в этом репозитории на GitHub (альтернативные ветки для 1.0.0-rc1,beta8,beta7), но вкратце, важными шагами являются:
создать ключ для вашего приложения
в моем примере, я генерируйте случайный ключ каждый раз, когда приложение запускается, вам нужно будет создать один и сохранить его где-нибудь и предоставить его в свое приложение. смотрите этот файл для того, как я генерирую случайный ключ и как вы можете импортировать его из a .файл JSON. Как было предложено в комментариях @kspearrin,API защиты данных кажется идеальным кандидатом для управления ключами "правильно", но я еще не решил, возможно ли это. Пожалуйста, отправьте запрос на вытягивание, если вы его разрабатываете!
Автозагрузка.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")]на конечных точках и классах, которые мы хотим защитить.Автозагрузка.cs-Configure
здесь нам нужно настроить 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
в контроллере токенов необходимо иметь метод для создания подписанных Ключей с помощью ключа, который был загружен при запуске.цезий. Мы зарегистрировал экземпляр TokenAuthOptions при запуске, поэтому нам нужно ввести его в конструктор для TokenController:
[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")]to любой метод или класс, который вы хотите защитить, и вы должны получить сообщение об ошибке, если вы попытаетесь получить к нему доступ без токена. Если вы хотите вернуть ошибку 401 вместо ошибки 500, вам нужно будет зарегистрировать пользовательский обработчик исключений а у меня в моем примере здесь.
вы можете посмотреть на образцы OpenId connect, которые иллюстрируют, как работать с различными механизмами аутентификации, включая токены JWT:
https://github.com/aspnet-contrib/AspNet.Security.OpenIdConnect.Samples
Если вы посмотрите на проект Cordova Backend, конфигурация для API выглядит так:
// Create a new branch where the registered middleware will be executed only for non API calls. app.UseWhen(context => !context.Request.Path.StartsWithSegments(new PathString("/api")), branch => { // Insert a new cookies middleware in the pipeline to store // the user identity returned by the external identity provider. branch.UseCookieAuthentication(new CookieAuthenticationOptions { AutomaticAuthenticate = true, AutomaticChallenge = true, AuthenticationScheme = "ServerCookie", CookieName = CookieAuthenticationDefaults.CookiePrefix + "ServerCookie", ExpireTimeSpan = TimeSpan.FromMinutes(5), LoginPath = new PathString("/signin"), LogoutPath = new PathString("/signout") }); branch.UseGoogleAuthentication(new GoogleOptions { ClientId = "560027070069-37ldt4kfuohhu3m495hk2j4pjp92d382.apps.googleusercontent.com", ClientSecret = "n2Q-GEw9RQjzcRbU3qhfTj8f" }); branch.UseTwitterAuthentication(new TwitterOptions { ConsumerKey = "6XaCTaLbMqfj6ww3zvZ5g", ConsumerSecret = "Il2eFzGIrYhz6BWjYhVXBPQSfZuS4xoHpSSyD9PI" }); });логика в / Providers / AuthorizationProvider.cs и RessourceController этого проекта также стоят взглянув на ;).
В качестве альтернативы вы также можете использовать следующий код для проверки токенов (есть также фрагмент, чтобы заставить его работать с signalR):
// Add a new middleware validating access tokens. app.UseOAuthValidation(options => { // Automatic authentication must be enabled // for SignalR to receive the access token. options.AutomaticAuthenticate = true; options.Events = new OAuthValidationEvents { // Note: for SignalR connections, the default Authorization header does not work, // because the WebSockets JS API doesn't allow setting custom parameters. // To work around this limitation, the access token is retrieved from the query string. OnRetrieveToken = context => { // Note: when the token is missing from the query string, // context.Token is null and the JWT bearer middleware will // automatically try to retrieve it from the Authorization header. context.Token = context.Request.Query["access_token"]; return Task.FromResult(0); } }; });для выдачи токена вы можете использовать пакеты openId Connect server следующим образом:
// Add a new middleware issuing access tokens. app.UseOpenIdConnectServer(options => { options.Provider = new AuthenticationProvider(); // Enable the authorization, logout, token and userinfo endpoints. //options.AuthorizationEndpointPath = "/connect/authorize"; //options.LogoutEndpointPath = "/connect/logout"; options.TokenEndpointPath = "/connect/token"; //options.UserinfoEndpointPath = "/connect/userinfo"; // Note: if you don't explicitly register a signing key, one is automatically generated and // persisted on the disk. If the key cannot be persisted, an exception is thrown. // // On production, using a X.509 certificate stored in the machine store is recommended. // You can generate a self-signed certificate using Pluralsight's self-cert utility: // https://s3.amazonaws.com/pluralsight-free/keith-brown/samples/SelfCert.zip // // options.SigningCredentials.AddCertificate("7D2A741FE34CC2C7369237A5F2078988E17A6A75"); // // Alternatively, you can also store the certificate as an embedded .pfx resource // directly in this assembly or in a file published alongside this project: // // options.SigningCredentials.AddCertificate( // assembly: typeof(Startup).GetTypeInfo().Assembly, // resource: "Nancy.Server.Certificate.pfx", // password: "Owin.Security.OpenIdConnect.Server"); // Note: see AuthorizationController.cs for more // information concerning ApplicationCanDisplayErrors. options.ApplicationCanDisplayErrors = true // in dev only ...; options.AllowInsecureHttp = true // in dev only...; });EDIT: я реализовал одностраничное приложение с реализацией аутентификации на основе токенов с использованием интерфейсной платформы Aurelia и ASP.NET ядро. Существует также сигнал R постоянное соединение. Однако я не сделал никакой реализации БД. Код можно посмотреть здесь: https://github.com/alexandre-spieser/AureliaAspNetCoreAuth
надеюсь, это поможет,
лучший
Алекс
взгляните на OpenIddict - это новый проект (на момент написания статьи), который позволяет легко настроить создание токенов JWT и обновить токены в ASP.NET 5. Проверка токенов осуществляется другим программным обеспечением.
если вы используете
IdentityСEntity Frameworkпоследняя строка-это то, что вы хотели добавить в свойConfigureServicesспособ:services.AddIdentity<ApplicationUser, ApplicationRole>() .AddEntityFrameworkStores<ApplicationDbContext>() .AddDefaultTokenProviders() .AddOpenIddictCore<Application>(config => config.UseEntityFramework());на
Configure, вы настроили OpenIddict для обслуживания токенов JWT:app.UseOpenIddictCore(builder => { // tell openiddict you're wanting to use jwt tokens builder.Options.UseJwtTokens(); // NOTE: for dev consumption only! for live, this is not encouraged! builder.Options.AllowInsecureHttp = true; builder.Options.ApplicationCanDisplayErrors = true; });вы также настраиваете проверку жетоны в
Configure:// use jwt bearer authentication app.UseJwtBearerAuthentication(options => { options.AutomaticAuthenticate = true; options.AutomaticChallenge = true; options.RequireHttpsMetadata = false; options.Audience = "http://localhost:58292/"; options.Authority = "http://localhost:58292/"; });есть одна или две другие незначительные вещи, такие как ваш DbContext должен быть получен из OpenIddictContext.
вы можете увидеть полное объяснение в этом блоге:http://capesean.co.za/blog/asp-net-5-jwt-tokens/
функциональная демонстрация доступна по адресу:https://github.com/capesean/openiddict-test
Comments