以前は、Web 側の ID 認証は Cookie | セッション認証に基づいていましたが、Web API の時代では、ブラウザだけではありません。さまざまなクライアントも存在するため、これらのクライアントは Cookie が何であるかを知りません。 (Cookie は実際にはセッションを維持するためにブラウザーによって行われる小さなトリックですが、HTTP 自体はステートレスであり、さまざまなクライアントが提供できるのはすべて HTTP 操作用の API です)
そして、トークンベースの ID 認証は、この変化に応じて生まれました。よりオープンでより安全に。
トークンベースの ID 認証を実装するには多くの方法がありますが、ここでは Microsoft が提供する API のみを使用します。
次の例では、Microsoft JwtSecurityTokenHandler を使用してベア トークンに基づく ID 認証を完了します。
注: この種の記事はステップバイステップのチュートリアルです。めまいがしないように、完全なコードをダウンロードしてコード構造を分析することをお勧めします。
プロジェクトを作成する
VS で新しいプロジェクトを作成し、プロジェクトの種類として ASP.NET Core Web アプリケーション (.NET Core) を選択し、プロジェクト名を CSTokenBaseAuth として入力します
Coding
いくつかの補助クラスを作成します
1 つ作成しますプロジェクトのルート ディレクトリの Folder Auth に、RSAKeyHelper.cs と TokenAuthOption.cs の 2 つのファイルを追加します。
ConfigureServices 内次のコードを追加します:
using System.Security.Cryptography; namespace CSTokenBaseAuth.Auth { public class RSAKeyHelper { public static RSAParameters GenerateKey() { using (var key = new RSACryptoServiceProvider(2048)) { return key.ExportParameters(true); } } } }
完全なコードは次のようになります
using System; using Microsoft.IdentityModel.Tokens; namespace CSTokenBaseAuth.Auth { public class TokenAuthOption { public static string Audience { get; } = "ExampleAudience"; public static string Issuer { get; } = "ExampleIssuer"; public static RsaSecurityKey Key { get; } = new RsaSecurityKey(RSAKeyHelper.GenerateKey()); public static SigningCredentials SigningCredentials { get; } = new SigningCredentials(Key, SecurityAlgorithms.RsaSha256Signature); public static TimeSpan ExpiresSpan { get; } = TimeSpan.FromMinutes(20); } }
Configure メソッドに次のコードを追加します
services.AddAuthorization(auth => { auth.AddPolicy("Bearer", new AuthorizationPolicyBuilder() .AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme) .RequireAuthenticatedUser().Build()); });
このコードは主にエラー処理に使用されます。 ID 認証が失敗すると例外がスローされ、この例外はここで処理される場合などです。
次に、同じメソッドに次のコードを追加します。
public void ConfigureServices(IServiceCollection services) { // Add framework services. services.AddApplicationInsightsTelemetry(Configuration); // 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()); }); services.AddMvc(); }
Apply JwtBearerAuthentication
app.UseExceptionHandler(appBuilder => { appBuilder.Use(async (context, next) => { var error = context.Features[typeof(IExceptionHandlerFeature)] as IExceptionHandlerFeature; //when authorization has failed, should retrun a json message to client if (error != null && error.Error is SecurityTokenExpiredException) { context.Response.StatusCode = 401; context.Response.ContentType = "application/json"; await context.Response.WriteAsync(JsonConvert.SerializeObject( new { authenticated = false, tokenExpired = true } )); } //when orther error, retrun a error message json to client else if (error != null && error.Error != null) { context.Response.StatusCode = 500; context.Response.ContentType = "application/json"; await context.Response.WriteAsync(JsonConvert.SerializeObject( new { success = false, error = error.Error.Message } )); } //when no error, do next. else await next(); }); });
完全なコードは次のようになります
app.UseExceptionHandler(appBuilder => { appBuilder.Use(async (context, next) => { var error = context.Features[typeof(IExceptionHandlerFeature)] as IExceptionHandlerFeature; //when authorization has failed, should retrun a json message to client if (error != null && error.Error is SecurityTokenExpiredException) { context.Response.StatusCode = 401; context.Response.ContentType = "application/json"; await context.Response.WriteAsync(JsonConvert.SerializeObject( new { authenticated = false, tokenExpired = true } )); } //when orther error, retrun a error message json to client else if (error != null && error.Error != null) { context.Response.StatusCode = 500; context.Response.ContentType = "application/json"; await context.Response.WriteAsync(JsonConvert.SerializeObject( new { success = false, error = error.Error.Message } )); } //when no error, do next. else await next(); }); });
Cコントローラーで新しい Web API コントローラーを作成しますTokenAuthController.cs という名前のクラス。ここでログイン認証を完了しますユーザーモデルとユーザーストレージをシミュレートするために使用される2つのクラスを同じファイルの下に追加します。コードは次のようになります 次に、TokenAuthControllerに次のメソッドを追加します。 .cs
app.UseJwtBearerAuthentication(new JwtBearerOptions { TokenValidationParameters = new TokenValidationParameters { IssuerSigningKey = TokenAuthOption.Key, ValidAudience = TokenAuthOption.Audience, ValidIssuer = TokenAuthOption.Issuer, ValidateIssuerSigningKey = true, ValidateLifetime = true, ClockSkew = TimeSpan.FromMinutes(0) } });このメソッドは認証トークンを生成するだけです。次に、それを呼び出す別のメソッドを追加しましょう同じファイルに次のコードを追加します
using System; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authentication.JwtBearer; using CSTokenBaseAuth.Auth; using Microsoft.AspNetCore.Diagnostics; using Microsoft.IdentityModel.Tokens; using Microsoft.AspNetCore.Http; using Newtonsoft.Json; namespace CSTokenBaseAuth { public class Startup { public Startup(IHostingEnvironment env) { var builder = new ConfigurationBuilder() .SetBasePath(env.ContentRootPath) .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true); if (env.IsEnvironment("Development")) { // This will push telemetry data through Application Insights pipeline faster, allowing you to view results immediately. builder.AddApplicationInsightsSettings(developerMode: true); } builder.AddEnvironmentVariables(); Configuration = builder.Build(); } public IConfigurationRoot Configuration { get; } // This method gets called by the runtime. Use this method to add services to the container public void ConfigureServices(IServiceCollection services) { // Add framework services. services.AddApplicationInsightsTelemetry(Configuration); // 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()); }); services.AddMvc(); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { loggerFactory.AddConsole(Configuration.GetSection("Logging")); loggerFactory.AddDebug(); app.UseApplicationInsightsRequestTelemetry(); app.UseApplicationInsightsExceptionTelemetry(); #region Handle Exception app.UseExceptionHandler(appBuilder => { appBuilder.Use(async (context, next) => { var error = context.Features[typeof(IExceptionHandlerFeature)] as IExceptionHandlerFeature; //when authorization has failed, should retrun a json message to client if (error != null && error.Error is SecurityTokenExpiredException) { context.Response.StatusCode = 401; context.Response.ContentType = "application/json"; await context.Response.WriteAsync(JsonConvert.SerializeObject( new { authenticated = false, tokenExpired = true } )); } //when orther error, retrun a error message json to client else if (error != null && error.Error != null) { context.Response.StatusCode = 500; context.Response.ContentType = "application/json"; await context.Response.WriteAsync(JsonConvert.SerializeObject( new { success = false, error = error.Error.Message } )); } //when no error, do next. else await next(); }); }); #endregion #region UseJwtBearerAuthentication app.UseJwtBearerAuthentication(new JwtBearerOptions { TokenValidationParameters = new TokenValidationParameters { IssuerSigningKey = TokenAuthOption.Key, ValidAudience = TokenAuthOption.Audience, ValidIssuer = TokenAuthOption.Issuer, ValidateIssuerSigningKey = true, ValidateLifetime = true, ClockSkew = TimeSpan.FromMinutes(0) } }); #endregion app.UseMvc(routes => { routes.MapRoute( name: "default", template: "{controller=Login}/{action=Index}"); }); } } }ファイルの完全なコードは次のとおりです。このように
public class User { public Guid ID { get; set; } public string Username { get; set; } public string Password { get; set; } } public static class UserStorage { public static List<User> Users { get; set; } = new List<User> { new User {ID=Guid.NewGuid(),Username="user1",Password = "user1psd" }, new User {ID=Guid.NewGuid(),Username="user2",Password = "user2psd" }, new User {ID=Guid.NewGuid(),Username="user3",Password = "user3psd" } }; }次に認可検証部分を完成させますControllersに新しいWeb APIコントローラークラスを作成し、ValuesController.csという名前を付けますその中に次のコードを追加します
private string GenerateToken(User user, DateTime expires) { var handler = new JwtSecurityTokenHandler(); ClaimsIdentity identity = new ClaimsIdentity( new GenericIdentity(user.Username, "TokenAuth"), new[] { new Claim("ID", user.ID.ToString()) } ); var securityToken = handler.CreateToken(new SecurityTokenDescriptor { Issuer = TokenAuthOption.Issuer, Audience = TokenAuthOption.Audience, SigningCredentials = TokenAuthOption.SigningCredentials, Subject = identity, Expires = expires }); return handler.WriteToken(securityToken); }
装飾属性メソッドを追加します
[HttpPost] public string GetAuthToken(User user) { var existUser = UserStorage.Users.FirstOrDefault(u => u.Username == user.Username && u.Password == user.Password); if (existUser != null) { var requestAt = DateTime.Now; var expiresIn = requestAt + TokenAuthOption.ExpiresSpan; var token = GenerateToken(existUser, expiresIn); return JsonConvert.SerializeObject(new { stateCode = 1, requertAt = requestAt, expiresIn = TokenAuthOption.ExpiresSpan.TotalSeconds, accessToken = token }); } else { return JsonConvert.SerializeObject(new { stateCode = -1, errors = "Username or password is invalid" }); } }最後にビューを追加しましょうControllersで新しいWebコントローラクラスを作成し、LoginController.csという名前を付けますコードは次のようになります
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Newtonsoft.Json; using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using System.Security.Principal; using Microsoft.IdentityModel.Tokens; using CSTokenBaseAuth.Auth; namespace CSTokenBaseAuth.Controllers { [Route("api/[controller]")] public class TokenAuthController : Controller { [HttpPost] public string GetAuthToken(User user) { var existUser = UserStorage.Users.FirstOrDefault(u => u.Username == user.Username && u.Password == user.Password); if (existUser != null) { var requestAt = DateTime.Now; var expiresIn = requestAt + TokenAuthOption.ExpiresSpan; var token = GenerateToken(existUser, expiresIn); return JsonConvert.SerializeObject(new { stateCode = 1, requertAt = requestAt, expiresIn = TokenAuthOption.ExpiresSpan.TotalSeconds, accessToken = token }); } else { return JsonConvert.SerializeObject(new { stateCode = -1, errors = "Username or password is invalid" }); } } private string GenerateToken(User user, DateTime expires) { var handler = new JwtSecurityTokenHandler(); ClaimsIdentity identity = new ClaimsIdentity( new GenericIdentity(user.Username, "TokenAuth"), new[] { new Claim("ID", user.ID.ToString()) } ); var securityToken = handler.CreateToken(new SecurityTokenDescriptor { Issuer = TokenAuthOption.Issuer, Audience = TokenAuthOption.Audience, SigningCredentials = TokenAuthOption.SigningCredentials, Subject = identity, Expires = expires }); return handler.WriteToken(securityToken); } } public class User { public Guid ID { get; set; } public string Username { get; set; } public string Password { get; set; } } public static class UserStorage { public static List<User> Users { get; set; } = new List<User> { new User {ID=Guid.NewGuid(),Username="user1",Password = "user1psd" }, new User {ID=Guid.NewGuid(),Username="user2",Password = "user2psd" }, new User {ID=Guid.NewGuid(),Username="user3",Password = "user3psd" } }; } }Inプロジェクトの Views ディレクトリ Login という名前の新しいディレクトリを作成し、その中に新しい Index.cshtml ファイルを作成します。 コードは次のようになります
public string Get() { var claimsIdentity = User.Identity as ClaimsIdentity; var id = claimsIdentity.Claims.FirstOrDefault(c => c.Type == "ID").Value; return $"Hello! {HttpContext.User.Identity.Name}, your ID is:{id}"; }以上がこの記事の全内容です。皆さんの学習に役立つことを願っています。また、皆さんが php 中国語の Web サイトをサポートしてくれることを願っています。 ASP.NET Core でのトークン ベース ID 認証の例の実装に関するその他の関連記事については、PHP 中国語 Web サイトに注目してください。