我们在使用ASP.NET的时候一定都用过FormsAuthentication做登录用户的身份认证,FormsAuthentication的核心就是Cookie,ASP.NET会将用户名存储在Cookie中。
现在到了ASP.NET CORE的时代,但是ASP.NET CORE中没有FormsAuthentication这个东西,那么怎么做身份认证呢?答案是ASP.NET CORE已经为我们内置了Cookie身份认证的功能,而且使用起来非常方便,注意本文是基于ASP.NET CORE 2.2版本来阐述Cookie认证方式的
1.ASP.NET CORE 框架中启用Cookie身份认证功能
要在ASP.NET CORE中使用Cookie身份认证,第一步就是在项目中的Startup.cs中启用Cookie身份认证中间件。Startup.cs 中的 ConfigureServices 方法中使用AddAuthentication注册cookie认证服务。具体代码如下:
public void ConfigureServices(IServiceCollection services)
{
......
//注册Cookie认证服务
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie();
}
services.AddMvc(options =>
{
options.Filters.Add(typeof(UserAuthorizeAttribute));//全局注册用户授权认证
});
然后在Startup.cs中的Configure方法中使用app. 启用cookie认证中间件(注意:app.UseAuthentication();要放在 app.UseMvc()前面)
/// <summary>
/// 用于定义请求管道中的中间件 http
/// </summary>
/// <param name="app"></param>
/// <param name="env"></param>
// 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, IApplicationLifetime appLifetime)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
//2019-5-26 add 注册用户身份验证
//app.UseAuthentication会启用Authentication中间件,该中间件会根据当前Http请求中的Cookie信息来设置HttpContext.User属性(后面会用到),所以只有在app.UseAuthentication方法之后注册的中间件才能够从HttpContext.User中读取到值,这也是为什么上面强调app.UseAuthentication方法一定要放在下面的app.UseMvc方法前面,因为只有这样ASP.NET Core的MVC中间件中才能读取到HttpContext.User的值。
app.UseAuthentication();
//mvc路由中间件
app.UseMvc(routes =>
{
//默认路由
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
2.用户登录
// 登录处理
public IActionResult Login(string username, string password)
{
ApiResult apiResult = new ApiResult();
if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password))
{
apiResult.Code = 1;
apiResult.Message = "请输入用户名或密码";
return OutputJson(apiResult);
}
//Member member = UserService.Login(username, password);
t_user member = UserService.User_Login(username, password);
if (member != null)
{
apiResult.OnSuccess(apiResult);
//下面的变量claims是Claim类型的数组,Claim是string类型的键值对,所以claims数组中可以存储任意个和用户有关的信息,
//不过要注意这些信息都是加密后存储在客户端浏览器cookie中的,所以最好不要存储太多特别敏感的信息,这里我们只存储了用户名到claims数组,
//表示当前登录的用户是谁
//var claims = new[] { new Claim("username", username) };
var identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme);
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, member.Uid.ToString()));
identity.AddClaim(new Claim(ClaimTypes.Name, member.Screen_name));
ClaimsPrincipal principal = new ClaimsPrincipal(identity);
Task.Run(async () =>
{
//登录用户,相当于ASP.NET中的FormsAuthentication.SetAuthCookie
//await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, user);
//可以使用HttpContext.SignInAsync方法的重载来定义持久化cookie存储用户认证信息,例如下面的代码就定义了用户登录后60分钟内cookie都会保留在客户端计算机硬盘上,
//即便用户关闭了浏览器,60分钟内再次访问站点仍然是处于登录状态,除非调用Logout方法注销登录。
//注意其中的AllowRefresh属性,如果AllowRefresh为true,表示如果用户登录后在超过50%的ExpiresUtc时间间隔内又访问了站点,就延长用户的登录时间(其实就是延长cookie在客户端计算机硬盘上的保留时间),
//例如本例中我们下面设置了ExpiresUtc属性为60分钟后,那么当用户登录后在大于30分钟且小于60分钟内访问了站点,那么就将用户登录状态再延长到当前时间后的60分钟。但是用户在登录后的30分钟内访问站点是不会延长登录时间的,
//因为ASP.NET Core有个硬性要求,是用户在超过50%的ExpiresUtc时间间隔内又访问了站点,才延长用户的登录时间。
//如果AllowRefresh为false,表示用户登录后60分钟内不管有没有访问站点,只要60分钟到了,立马就处于非登录状态(不延长cookie在客户端计算机硬盘上的保留时间,60分钟到了客户端计算机就自动删除cookie)
await HttpContext.SignInAsync(
CookieAuthenticationDefaults.AuthenticationScheme,//这里要注意的是HttpContext.SignInAsync(AuthenticationType,…) 所设置的Scheme一定要与前面的配置一样,这样对应的登录授权才会生效。
principal,
new AuthenticationProperties()
{
IsPersistent = true,
ExpiresUtc = DateTimeOffset.UtcNow.AddHours(1),//有效时间
AllowRefresh = true
});
}).Wait();
}
return OutputJson(apiResult);
}
3.读取cookie
/// <summary>
/// 实现cookie认证授权校验
/// 参考地址:https://www.cnblogs.com/OpenCoder/p/8341843.html
/// </summary>
public class UserAuthorizeAttribute : ActionFilterAttribute
{
static Logger logger = LogManager.GetCurrentClassLogger();
public override void OnActionExecuting(ActionExecutingContext context)
{
bool IsAuthenticated = false;
var requestURL = context.HttpContext.Request.Path;
//如果HttpContext.User.Identity.IsAuthenticated为true,
//或者HttpContext.User.Claims.Count()大于0表示用户已经登录
if (context.HttpContext.User.Identity.IsAuthenticated)
{
IsAuthenticated = true;
}
if (IsAuthenticated)
{
//这里通过 HttpContext.User.Claims 可以将我们在Login这个Action中存储到cookie中的所有
//claims键值对都读出来,比如我们刚才定义的UserName的值admin就在这里读取出来了
var userName = context.HttpContext.User.FindFirst(ClaimTypes.Name).Value;
logger.Info($"[{userName}]用户登录校验成功,请求地址[{requestURL}]");
}
else
{
logger.Info($"没登录访问地址[{requestURL}]");
context.Result = new RedirectResult("/Home/Login?returnURL=" + requestURL);
return;
}
base.OnActionExecuting(context);
}
}
/// <summary>
///因为我使用的是全局注册UserAuthorizeAttribute ,所以这里创建一个不需要授权认证的方法
/// 不需要验证
/// </summary>
public class SkipUserAuthorizeAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
base.OnActionExecuting(context);
}
}
4.注销
/// <summary>
/// 退出登录
/// 如果当前Http请求本来就没有登录用户,那么调用HttpContext.SignOutAsync方法时也不会报错
/// </summary>
/// <returns></returns>
public IActionResult LoginOut()
{
Task.Run(async () =>
{
//注销登录的用户,相当于ASP.NET中的FormsAuthentication.SignOut
await HttpContext.SignOutAsync();
}).Wait();
return View("Index");
}