Asp.Net Core Authentication Middleware And Generate Token
或者應(yīng)該包含什么信息呢?
1.這個人是誰?
2.這個人可以用此token訪問什么樣的內(nèi)容?(scope)
3.token的過期時間 (expire)
4.誰發(fā)行的token。
5.其他任何你希望加入的聲明(Claims)
那我們?yōu)槭裁匆褂胻oken呢?使用session或者用redis來實現(xiàn)stateServer不好嗎?
1.token是低(無)狀態(tài)的,Statelessness
2.token可以與移動端應(yīng)用緊密結(jié)合
3.支持多平臺服務(wù)器和分布式微服務(wù)
拿到token后如何帶入HTTP請求傳給后臺?
? 答案是兩種方式,Cookies和Authorization Header。那么什么時候放到Cookies中,什么時候又放到Authentication中呢?
第一,如果是在Web應(yīng)用,則放到Cookies當(dāng)中,并且應(yīng)該是HttpOnly的,js不能直接對其進行操作,安全性會比將其存在Web Stroage中好一些,因為在Web Storage當(dāng)中的內(nèi)容,可以很容的被潛在的XSS腳本攻擊并獲取。在HttpOnly的cookies當(dāng)中會相對安全一些,不過也有潛在的CSRF跨站偽造請求的危險,不過這種hack的手段成功率是很低的,有興趣的朋友可以自行看一下CSRF原理。
第二,如果是手機移動端應(yīng)用的話,那一定是存儲在App本地,并由Authorization Header帶到后臺并得到身份認證。
WebApp Cookies Authentication
上一段前兩周寫的最原始的小Demo吧,沒有數(shù)據(jù)庫訪問等,可根據(jù)demo自行改變 ,現(xiàn)在的新代碼已經(jīng)加入了很多業(yè)務(wù)在其中
startup.cs代碼
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Authentication;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System.Collections.Generic;
using System.Security.Claims;
using Wings.AuthenticationApp.Middleware;
namespace Wings.AuthenticationApp
{
? ? public class Startup
? ? {
? ? ? ? public Startup(IHostingEnvironment env)
? ? ? ? {
? ? ? ? ? ? var builder = new ConfigurationBuilder()
? ? ? ? ? ? ? ? .SetBasePath(env.ContentRootPath)
? ? ? ? ? ? ? ? .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
? ? ? ? ? ? ? ? .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
? ? ? ? ? ? ? ? .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.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.UseCookieAuthentication(CookieAuthMiddleware.GetOptions());
? ? ? ? ? ? app.UseOwin();
? ? ? ? ? ? app.UseCors(a => { a.AllowAnyOrigin(); });
? ? ? ? ? ? app.UseMvc();
? ? ? ? ? ? // Listen for login and logout requests
? ? ? ? ? ? app.Map("/login", builder =>
? ? ? ? ? ? {
? ? ? ? ? ? ? ? builder.Run(async context =>
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? var name = context.Request.Form["name"];
? ? ? ? ? ? ? ? ? ? var pwd = context.Request.Form["pwd"];
? ? ? ? ? ? ? ? ? ? if (name == "wushuang" && pwd == "wushuang")
? ? ? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? ? ? var claims = new List<Claim>() { new Claim("name", name), new Claim("role", "admin") };
? ? ? ? ? ? ? ? ? ? ? ? var identity = new ClaimsIdentity(claims, "password");
? ? ? ? ? ? ? ? ? ? ? ? var principal = new ClaimsPrincipal(identity);
? ? ? ? ? ? ? ? ? ? ? ? await context.Authentication.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, principal);
? ? ? ? ? ? ? ? ? ? ? ? context.Response.Redirect("http://www.baidu.com");
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? else
? ? ? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? ? ? await context.Authentication.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
? ? ? ? ? ? ? ? ? ? ? ? context.Response.Redirect("http://www.google.com");
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? });
? ? ? ? ? ? });
? ? ? ? ? ? //app.Map("/logout", builder =>
? ? ? ? ? ? //{
? ? ? ? ? ? // ? ?builder.Run(async context =>
? ? ? ? ? ? // ? ?{
? ? ? ? ? ? // ? ? ? ?// Sign the user out / clear the auth cookie
? ? ? ? ? ? // ? ? ? ?await context.Authentication.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
? ? ? ? ? ? // ? ? ? ?// Perform a simple redirect after logout
? ? ? ? ? ? // ? ? ? ?context.Response.Redirect("/");
? ? ? ? ? ? // ? ?});
? ? ? ? ? ? //});
? ? ? ? ? ??
? ? ? ? }
? ? }
}
下面是Middleware---->CookieAuthMiddleware.cs的代碼,
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Security.Principal;
using System.Threading.Tasks;
namespace Wings.AuthenticationApp.Middleware
{
? ? public class CookieAuthMiddleware
? ? {
? ? ? ? public static CookieAuthenticationOptions GetOptions()
? ? ? ? {
? ? ? ? ? ? return new CookieAuthenticationOptions
? ? ? ? ? ? {
? ? ? ? ? ? ? ? AutomaticAuthenticate = true,
? ? ? ? ? ? ? ? AutomaticChallenge = true,
? ? ? ? ? ? ? ? LoginPath = new PathString("/login"),
? ? ? ? ? ? ? ? LogoutPath = new PathString("/logout"),
? ? ? ? ? ? ? ? AccessDeniedPath = new PathString("/test"),
? ? ? ? ? ? ? ? CookieHttpOnly = false, ?//默認就是True了
? ? ? ? ? ? ? ? CookieName = "wings_access_token",
? ? ? ? ? ? ? ? SlidingExpiration = true,
? ? ? ? ? ? ? ? CookieManager = new ChunkingCookieManager()
? ? ? ? ? ? };
? ? ? ? }
? ? }
? ? public static class IdentityExtension
? ? {
? ? ? ? public static string FullName(this IIdentity identity)
? ? ? ? {
? ? ? ? ? ? var claim = ((ClaimsIdentity)identity).FindFirst("name");
? ? ? ? ? ? return (claim != null) ? claim.Value : string.Empty;
? ? ? ? }
? ? ? ? public static string Role(this IIdentity identity)
? ? ? ? {
? ? ? ? ? ? var claim = ((ClaimsIdentity)identity).FindFirst("role");
? ? ? ? ? ? return (claim != null) ? claim.Value : string.Empty;
? ? ? ? }
? ? }
}
對應(yīng)如上demo,簡單測試一下,結(jié)果如下:
首先使用錯誤的密碼,來請求token endpoint,接下來我們看一下即使窗口,當(dāng)有請求進入的時候,我用如下代碼判斷用戶的認證情況,拿到的結(jié)果必然是false:
接下來,我使用正確的賬號密碼,來打入token,判斷結(jié)果一定為true,所以我使用自定義的拓展方法,來獲取下,該用戶token的信息:
如上demo沒有加入一些容錯機制,請注意。在用戶認證成功后,可以進入帶有Authorize Attribute的Action,否則401.如下是幾個重要參數(shù)的解釋
?
自定義Authentication Middle生產(chǎn)Token
?Startup.cs?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Wings.TokenAuth.Middleware;
using System.Security.Claims;
using Microsoft.IdentityModel.Tokens;
using System.Text;
using Microsoft.Extensions.Options;
namespace Wings.TokenAuth
{
? ? public class Startup
? ? {
? ? ? ? public Startup(IHostingEnvironment env)
? ? ? ? {
? ? ? ? ? ? var builder = new ConfigurationBuilder()
? ? ? ? ? ? ? ? .SetBasePath(env.ContentRootPath)
? ? ? ? ? ? ? ? .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
? ? ? ? ? ? ? ? .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
? ? ? ? ? ? ? ? .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.AddMvc();
? ? ? ? }
? ? ? ? // The secret key every token will be signed with.
? ? ? ? // In production, you should store this securely in environment variables
? ? ? ? // or a key management tool. Don't hardcode this into your application!
? ? ? ? private static readonly string secretKey = "mysupersecret_secretkey!123";
? ? ? ? public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
? ? ? ? {
? ? ? ? ? ? loggerFactory.AddConsole(LogLevel.Debug);
? ? ? ? ? ? loggerFactory.AddDebug();
? ? ? ? ? ? app.UseStaticFiles();
? ? ? ? ? ? // Add JWT generation endpoint:
? ? ? ? ? ? var signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(secretKey));
? ? ? ? ? ? var options = new TokenProviderOptions
? ? ? ? ? ? {
? ? ? ? ? ? ? ? Audience = "ExampleAudience",
? ? ? ? ? ? ? ? Issuer = "ExampleIssuer",
? ? ? ? ? ? ? ? SigningCredentials = new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256),
? ? ? ? ? ? };
? ? ? ? ? ? app.UseMiddleware<TokenProviderMiddleware>(Options.Create(options));
? ? ? ? ? ? app.UseMvc();
? ? ? ? }
? ? }
}
TokenProviderOptions.cs
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
namespace Wings.TokenAuth.Middleware
{
? ? public class TokenProviderOptions
? ? {
? ? ? ? public string Path { get; set; } = "/token";
? ? ? ? public string Issuer { get; set; }
? ? ? ? public string Audience { get; set; }
? ? ? ? public TimeSpan Expiration { get; set; } = TimeSpan.FromMinutes(5);
? ? ? ? public SigningCredentials SigningCredentials { get; set; }
? ? }
? ? public class TokenProviderMiddleware
? ? {
? ? ? ? private readonly RequestDelegate _next;
? ? ? ? private readonly TokenProviderOptions _options;
? ? ? ? public TokenProviderMiddleware(
? ? ? ? ? RequestDelegate next,
? ? ? ? ? IOptions<TokenProviderOptions> options)
? ? ? ? {
? ? ? ? ? ? _next = next;
? ? ? ? ? ? _options = options.Value;
? ? ? ? }
? ? ? ? public Task Invoke(HttpContext context)
? ? ? ? {
? ? ? ? ? ? // If the request path doesn't match, skip
? ? ? ? ? ? if (!context.Request.Path.Equals(_options.Path, StringComparison.Ordinal))
? ? ? ? ? ? {
//use new JwtSecurityTokenHandler().ValidateToken() to valid token
? ? ? ? ? ? ? ? return _next(context);
? ? ? ? ? ? }
? ? ? ? ? ? // Request must be POST with Content-Type: application/x-www-form-urlencoded
? ? ? ? ? ? if (!context.Request.Method.Equals("POST")
? ? ? ? ? ? ? || !context.Request.HasFormContentType)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? context.Response.StatusCode = 400;
? ? ? ? ? ? ? ? return context.Response.WriteAsync("Bad request.");
? ? ? ? ? ? }
? ? ? ? ? ? return GenerateToken(context);
? ? ? ? }
? ? ? ? private async Task GenerateToken(HttpContext context)
? ? ? ? {
? ? ? ? ? ? var username = context.Request.Form["username"];
? ? ? ? ? ? var password = context.Request.Form["password"];
? ? ? ? ? ? var identity = await GetIdentity(username, password);
? ? ? ? ? ? if (identity == null)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? context.Response.StatusCode = 400;
? ? ? ? ? ? ? ? await context.Response.WriteAsync("Invalid username or password.");
? ? ? ? ? ? ? ? return;
? ? ? ? ? ? }
? ? ? ? ? ? var now = DateTime.UtcNow;
? ? ? ? ? ? // Specifically add the jti (random nonce), iat (issued timestamp), and sub (subject/user) claims.
? ? ? ? ? ? // You can add other claims here, if you want:
? ? ? ? ? ? var claims = new Claim[]
? ? ? ? ? ? {
? ? new Claim(JwtRegisteredClaimNames.Sub, username),
? ? new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
? ? new Claim(JwtRegisteredClaimNames.Iat, ToUnixEpochDate(now).ToString(), ClaimValueTypes.Integer64)
? ? ? ? ? ? };
? ? ? ? ? ? // Create the JWT and write it to a string
? ? ? ? ? ? var jwt = new JwtSecurityToken(
? ? ? ? ? ? ? issuer: _options.Issuer,
? ? ? ? ? ? ? audience: _options.Audience,
? ? ? ? ? ? ? claims: claims,
? ? ? ? ? ? ? notBefore: now,
? ? ? ? ? ? ? expires: now.Add(_options.Expiration),
? ? ? ? ? ? ? signingCredentials: _options.SigningCredentials);
? ? ? ? ? ? var encodedJwt = new JwtSecurityTokenHandler().WriteToken(jwt);
? ? ? ? ? ? var response = new
? ? ? ? ? ? {
? ? ? ? ? ? ? ? access_token = encodedJwt,
? ? ? ? ? ? ? ? expires_in = (int)_options.Expiration.TotalSeconds
? ? ? ? ? ? };
? ? ? ? ? ? // Serialize and return the response
? ? ? ? ? ? context.Response.ContentType = "application/json";
? ? ? ? ? ? await context.Response.WriteAsync(JsonConvert.SerializeObject(response, new JsonSerializerSettings { Formatting = Formatting.Indented }));
? ? ? ? }
? ? ? ? private Task<ClaimsIdentity> GetIdentity(string username, string password)
? ? ? ? {
? ? ? ? ? ? // DON'T do this in production, obviously!
? ? ? ? ? ? if (username == "wushuang" && password == "wushuang")
? ? ? ? ? ? {
? ? ? ? ? ? ? ? return Task.FromResult(new ClaimsIdentity(new System.Security.Principal.GenericIdentity(username, "Token"), new Claim[] { }));
? ? ? ? ? ? }
? ? ? ? ? ? // Credentials are invalid, or account doesn't exist
? ? ? ? ? ? return Task.FromResult<ClaimsIdentity>(null);
? ? ? ? }
? ? ? ? public static long ToUnixEpochDate(DateTime date)
? => (long)Math.Round((date.ToUniversalTime() - new DateTimeOffset(1970, 1, 1, 0, 0, 0, TimeSpan.Zero)).TotalSeconds);
? ? }
}
下面上測試結(jié)果:
使用錯誤的賬戶和密碼請求token
使用正確的賬戶和密碼來請求,返回結(jié)果如下:
如果,您認為閱讀這篇博客讓您有些收獲,不妨點擊一下右下加【推薦】按鈕。
如果,您希望更容易地發(fā)現(xiàn)我的新博客,不妨點擊下方紅色【關(guān)注】的。
因為,我的分享熱情也離不開您的肯定支持。
感謝您的閱讀,我將持續(xù)輸出分享,我是蝸牛, 保持學(xué)習(xí),謹記謙虛。不端不裝,有趣有夢。
參考文章和論文,不僅限于如下幾篇,感謝國外大佬們有深度的分享:
http://stackoverflow.com/questions/29055477/oauth-authorization-service-in-asp-net-core
https://stormpath.com/blog/token-authentication-asp-net-core
https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware#fundamentals-middleware
https://docs.microsoft.com/en-us/aspnet/core/security/authentication/cookie#controlling-cookie-options
https://stormpath.com/blog/token-authentication-asp-net-core
相關(guān)文章:
AspNet Identity 和 Owin 誰是誰
ASP.NET Core 之 Identity 入門(一)
ASP.NET Core 之 Identity 入門(二)
ASP.NET Core 之 Identity 入門(三)
原文鏈接:http://www.cnblogs.com/tdws/p/6536864.html
.NET社區(qū)新聞,深度好文,微信中搜索dotNET跨平臺或掃描二維碼關(guān)注
總結(jié)
以上是生活随笔為你收集整理的Asp.Net Core Authentication Middleware And Generate Token的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 后端架构高可用可伸缩讲解之高可用
- 下一篇: 微软的.NET Core开始支持Rasp