ASP.NET Core中的密钥管理
在 ASP.NET Core 中配置证书身份验证 (TLS,HTTPS 证书)
在 ASP.NET 应用中共享身份验证 cookie
在 ASP.NET Core 中的密钥存储提供程序 【文件系统、Azure 存储、Redis、注册表、数据库、自定义密钥存储库】
配置 ASP.NET Core 数据保护
ASP.NET核心中的关键存储格式 【秘钥存储格式】
ASP.NET Core 身份验证及鉴权
1、Startup.cs AddAuthentication AddCookie app.UseAuthentication() app.UseAuthorization()
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
namespace Web.Test1
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
// 认证
services.AddAuthentication("MyCookieAuthenticationScheme")
.AddCookie("MyCookieAuthenticationScheme", options =>
{
options.LoginPath = "/Account/Login";//用户尝试访问资源但尚未认证时,这是请求重定向的相对路径。
options.LogoutPath = "/Account/Logout";//指定登出的路径
options.AccessDeniedPath = "/Account/AccessDenied";//用户尝试访问资源但没有通过任何授权策略时,这是请求会重定向的相对路径资源。
options.ExpireTimeSpan = TimeSpan.FromMinutes(30);//指定Cookie的过期时间
options.SlidingExpiration = true;//当Cookie过期时间已达一半时,是否重置为ExpireTimeSpan
options.Events = new CookieAuthenticationEvents//可用于拦截和重写Cookie身份验证
{
OnValidatePrincipal = Filter.LastChangedValidator.ValidateAsync
};
options.Events.OnRedirectToLogin = z =>//api接口判断
{
if (z.HttpContext.Request.Path.StartsWithSegments("/api", StringComparison.OrdinalIgnoreCase))
{
z.HttpContext.Response.Redirect("/api/Login/UnAuth");//未授权错误信息的接口地址,返回json
}
else
{
z.HttpContext.Response.Redirect(z.RedirectUri);//其它安装默认处理
}
return Task.CompletedTask;
};
options.Cookie.Name = "AuthCookie";
//options.Cookie.Domain = "contoso.com";
options.Cookie.Path = "/";
options.Cookie.HttpOnly = true;
options.Cookie.SameSite = SameSiteMode.Lax;
//options.Cookie.SecurePolicy = CookieSecurePolicy.Always;//谨慎使用
});
// 授权
services.AddAuthorization(options =>
{
// options.AddPolicy("MyPolicy", policy => { });
});
services.AddSession(options =>
{
options.IdleTimeout = TimeSpan.FromMinutes(10); //session活期时间
options.Cookie.HttpOnly = true;//设为httponly
});
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseSession();
//Adds the authentication middleware to the pipeline
app.UseAuthentication();
app.UseAuthorization();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
}
}
2、Controller(AccountController)
[Authorize]、Claim、ClaimsIdentity、ClaimsPrincipal、
await HttpContext.SignInAsync(claimsPrincipal) 用户登录成功后颁发一个证书(加密的用户凭证),用来标识用户的身份。
await HttpContext.SignOutAsync(); 退出登录,如清除Coookie等
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Web.Test1.Controllers
{
public class AccountController : Controller
{
[Authorize]
public IActionResult Index()
{
///判断用户是否已经登录,如果已经登录,那么读取登录用户的用户名
if (HttpContext.User.Identity.IsAuthenticated)
{
//这里通过 HttpContext.User.Claims 可以将我们在Login这个Action中存储到cookie中的所有
//claims键值对都读出来,比如我们刚才定义的UserName的值Wangdacui就在这里读取出来了
var userName = HttpContext.User.Claims.First().Value;
var adminAccount = HttpContext.User.Claims.SingleOrDefault(t => t.Type == "UserName");
string account = adminAccount == null ? "" : adminAccount.Value;
var adminPwd = HttpContext.User.Claims.SingleOrDefault(t => t.Type == "Password");
string pwd = adminPwd == null ? "" : adminPwd.Value;
var rememberMe = HttpContext.User.Claims.SingleOrDefault(t => t.Type == "RememberMe");
string rem = rememberMe == null ? "" : rememberMe.Value;
}
return View();
}
public IActionResult Login()
{
return View();
}
public IActionResult Logout()
{
#region 方式1
HttpContext.SignOutAsync("MyCookieAuthenticationScheme");
#endregion
#region 方式2
Task.Run(async () =>
{
//注销登录的用户,相当于ASP.NET中的FormsAuthentication.SignOut
await HttpContext.SignOutAsync();
}).Wait();
#endregion
return View();
}
public IActionResult AccessDenied()
{
return View();
}
[HttpPost]
public async Task<IActionResult> LoginAsync()
{
#region 方式1(ClaimsPrincipal)
///身份信息
var claims = new[]
{
new Claim("UserName", "Wangdacui"),
new Claim("Password", "123456"),
new Claim(ClaimTypes.Sid, "123"),
new Claim(ClaimTypes.Name, "name"),
};
///身份证
var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
///证件所有者
ClaimsPrincipal user = new ClaimsPrincipal(claimsIdentity);
#endregion
#region 方式2(ClaimsPrincipal)
var claimIdentity = new ClaimsIdentity("Cookie");
claimIdentity.AddClaim(new Claim("UserName", "Wangdacui"));
claimIdentity.AddClaim(new Claim("Password", "123456"));//自己随便写一个名字
claimIdentity.AddClaim(new Claim("RememberMe", "on"));
claimIdentity.AddClaim(new Claim(ClaimTypes.Sid, "123"));
claimIdentity.AddClaim(new Claim(ClaimTypes.NameIdentifier, "1"));
claimIdentity.AddClaim(new Claim(ClaimTypes.Name, "name"));
claimIdentity.AddClaim(new Claim(ClaimTypes.Email, "emial"));
claimIdentity.AddClaim(new Claim(ClaimTypes.MobilePhone, "PhoneNumber"));
claimIdentity.AddClaim(new Claim(ClaimTypes.DateOfBirth, "2018-7-1"));
var claimsPrincipal = new ClaimsPrincipal(claimIdentity);
#endregion
var options = new CookieOptions
{
HttpOnly = true,
SameSite = Microsoft.AspNetCore.Http.SameSiteMode.None
};
#region 方式1(SignInAsync)
// 在上面注册AddAuthentication时,指定了默认的Scheme,在这里便可以不再指定Scheme。
await HttpContext.SignInAsync(claimsPrincipal);
#endregion
#region 方式2(SignInAsync)
await HttpContext.SignInAsync("MyCookieAuthenticationScheme", claimsPrincipal);
#endregion
#region 方式3(SignInAsync)
await HttpContext.SignInAsync(
"MyCookieAuthenticationScheme",
claimsPrincipal,
new AuthenticationProperties
{
IsPersistent = true,//持久Cookie
ExpiresUtc = DateTime.UtcNow.AddMinutes(20),//设置cookie过期时间
AllowRefresh = false,
});
#region 方式4(SignInAsync)
Task.Run(async () =>
{
//登录用户,相当于ASP.NET中的FormsAuthentication.SetAuthCookie
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, claimsPrincipal);
}).Wait();
#endregion
#endregion
return RedirectToAction(nameof(HomeController.Index), "Home");
}
public async Task<IActionResult> LogoutAsync()
{
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
return RedirectToAction(nameof(HomeController.Index), "Home");
}
}
}
3、Controller(HomeController)[Authorize]
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Web.Test1.Models;
namespace Web.Test1.Controllers
{
[Authorize]
public class HomeController : Controller
{
public IActionResult Index()
{
return View();
}
public IActionResult Privacy()
{
return View();
}
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error()
{
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}
}
}
4、LastChangedValidator 可用于拦截和重写Cookie身份验证
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.Extensions.DependencyInjection;
namespace Web.Test1.Filter
{
public interface IUserRepository
{
bool ValidateLastChanged(ClaimsPrincipal claims, string lastChanged);
}
public class UserRepository : IUserRepository
{
public bool ValidateLastChanged(ClaimsPrincipal claims, string lastChanged)
{
return false;
}
}
public static class LastChangedValidator
{
public static async Task ValidateAsync(CookieValidatePrincipalContext context)
{
// Pull database from registered DI services.
var userRepository = context.HttpContext.RequestServices.GetRequiredService<IUserRepository>();
var userPrincipal = context.Principal;
// Look for the last changed claim.
string lastChanged;
lastChanged = (from c in userPrincipal.Claims
where c.Type == "LastUpdated"
select c.Value).FirstOrDefault();
if (string.IsNullOrEmpty(lastChanged) ||
!userRepository.ValidateLastChanged(userPrincipal, lastChanged))
{
context.RejectPrincipal();
await context.HttpContext.SignOutAsync("MyCookieAuthenticationScheme");
}
}
}
}
设置
HttpContext.Response.Cookies.Append("password","123456");
获取
string value = "";
HttpContext.Request.Cookies.TryGetValue("password", out value);
string auth = "";
HttpContext.Request.Cookies.TryGetValue("AuthCookie", out auth);
var sid = HttpContext.User.FindFirst(ClaimTypes.Sid);
var userName = HttpContext.User.Claims.First().Value;
var adminAccount = HttpContext.User.Claims.SingleOrDefault(t => t.Type == "UserName");
string account = adminAccount == null ? "" : adminAccount.Value;
if (HttpContext.User.Identity.IsAuthenticated)
{
var uName = HttpContext.User.Claims.First().Value;
}
else
{
string a = "";
}
删除
HttpContext.Response.Cookies.Delete("password");
Cookie 跨域
1、是否在CookiePolicyOptions中设置了SameSite为No
2、AllowCredentials是否配置了
*
*
*
*
*
*
*
*
*