Asp.Net Core 3.x基于Claim的登录验证 【超详细】【有代码】

Asp.Net Core 3.x MVC中基于Claim的登录验证实现过程

一、首先导入Nuget包
Microsoft.AspNetcore.Authentication.Cookies
在这里插入图片描述
二、修改Startup.cs

  1. 注册Authentication服务。 在 ConfigureServices 方法中加入以下代码。(直接复制下面的代码粘贴。注意修改登录页面的路径 比如我的是 AccountController里的Login方法 我就写 /Account/Login
 services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
                  .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
                  {
                      options.LoginPath = new PathString("/Account/Login"); // 登录页面的url
                      options.AccessDeniedPath = new PathString("/Account/Login");//没有授权跳转的页面
                      options.ExpireTimeSpan = TimeSpan.FromHours(0.5); // cookies的过期时间
                  });

在这里插入图片描述

2.添加中间件。在 Configure 方法中加入 app.UseAuthentication();

在这里插入图片描述

三 、准备工作做好后,写一些简单的代码来实现演示一下流程(代码简单 但是我想尽可能的把过程讲的通俗易懂些 所以可能篇幅冗长)
1.用到的类
LogOnModel:登录时用的类对象 用户名和密码即可
UserLogOnStatus:登录状态

(可能下面的代码中会出现Account类 因为我这里把模型又分为视图模型和实体类所以不必惊慌 ,Account 就是一个用户类有 Id,UserName,Pasword,Email等字段 自己写一下应该没问题)

 public class LogOnModel
    {
        [Required]
        public string UserName { get; set; }

        [Required]
        [Display(Name = "Password")]
        public string Password { get; set; }

    }
    public enum UserLogOnStatus
    {
        Failed,
        User,
        DontExist
    }

在这里插入图片描述
2.登录页面的表单
只需要用户名和密码即可

 <form method="post" class="layui-form">
            <div class="layui-form-item">
                <input name="UserName" placeholder="用户名" type="text" lay-verify="required" class="layui-input" value="@Model.UserName">
            </div>
            <p> @Html.ValidationMessageFor(m => m.UserName, "", new { @class = "text-danger" })</p>
            <div class="layui-form-item">
                <input name="Password" lay-verify="required" placeholder="密码" type="password" class="layui-input">
            </div>
            <p> @Html.ValidationMessageFor(m => m.Password, "", new { @class = "text-danger" })</p>
            <div class="layui-form-item">
                <input class="loginin" value="登录" lay-submit lay-filter="login" style="width:100%;" type="submit">
            </div>
        </form>

在这里插入图片描述
3.控制器
如果直接将验证写在控制器里的话 必须为异步 代码如下:
过程: 登录页面将用户名信息 (==我这里用的是模型绑定 也可以直接写 string userName,string passWord ==) 提交过来后 对用户名和密码进行验证 (这步自己实现)如果判别用户名密码正确的话 生成 Claim ;根据 Claim 生成 ClaimsIdentity 在根据 ClaimsIdentity 生成 ClaimsPrincipal 然后SingInAsync

扩展:解释一下这些东西是什么 (可能语言组织不好 可以百度进行更深入的理解)

Claim:是对被验证主体的一些特征信息 比如我这里ClaimTypes.Sid代表当前登录用户的Id和 ClaimTypes.Name代表当前用户名;比如身份证上的 姓名 出生年月等的键值形式的信息。

ClaimsIdentity: 一些claims可以构成了一个identity,具有这些claims的identity就是生成的ClaimsIdentity。ClaimsIdentity可以看作一张完整的身份证了。 可以理解为一张令牌通行证之类的。。

ClaimsPrincipal :可以理解为令牌的持有者 按我上面举的身份证的例子的话 ClaimsPrincipal代表你自己。

代码:



public async Task<IActionResult> Login(LogOnModel model)
        {
            if (ModelState.IsValid)
            {
                UserSession userSession;
                Account user;
                var logOnStatus = _accountService.VerifyUserLogon(model, out userSession,out user);
                if (logOnStatus == UserLogOnStatus.User)
                {
                    var claims = new List<Claim>
                    {
                        new Claim(ClaimTypes.Sid,user.Id.ToString()),
                        new Claim(ClaimTypes.Name,user.UserName)
                     };
                    var identity = new ClaimsIdentity(claims,loginClaim);
                    var userPrincipal = new ClaimsPrincipal(identity);
                    await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, userPrincipal, new AuthenticationProperties
                    {
                        ExpiresUtc = DateTime.UtcNow.AddMinutes(20),
                        IsPersistent = false,
                        AllowRefresh = false
                    });
                   
                    HttpContext.Session.SetString("CurrentUser", userSession.ToString());
                    return Redirect("/Home/Index");
                }
                if (logOnStatus == UserLogOnStatus.Failed)
                {
                    ModelState.AddModelError("Password", "密码错误!");
                }
                else if (logOnStatus == UserLogOnStatus.DontExist)
                {
                    ModelState.AddModelError("UserName", "账户不存在!");
                }
            }

扩展 。==可能到这里有的人说 这样直接写在控制器里是不是有点生硬,能不能把他封装出来? 可以 但是有点细节 下面仔细看 ==

我这里新建了一个类 代码如下

细节1 这里有个关键点是这样 我们封装出来的话 直接写HttpContext.SignInAsync是不行的,原因是你这里写HttpContext并不是当前请求的HttpContext 。所以现在有个问题就要解决,就是怎样得到当前的 HttpContext。
所以这里要用到 IHttpContextAccessor 至于这是个什么 大家可以去百度了解 这里我只是告诉大家可以用它获取当前 HttpContext .
我这里的做法是在Startup.cs里通过把IHttpContextAccessor注入(我这里因为用的是Autofac 所以写在了 ConfigureContainer

 builder.RegisterType(typeof(HttpContextAccessor)).As(typeof(IHttpContextAccessor)).SingleInstance();

如果没用Autofac的话可以直接写在 ConfigureServices

 services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();

在这里插入图片描述


using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.Security.Claims;


namespace PersonalWeb.UI
{
    public interface IAuthenticationService
    {
        public  void SignIn(string userId, string userName);
        void SignOut();
    }
    public class AuthenticationService : IAuthenticationService
    {
        private readonly IHttpContextAccessor _httpContextAccessor;
        public AuthenticationService(IHttpContextAccessor httpContextAccessor)
        {
            _httpContextAccessor = httpContextAccessor;
        }
        public async void SignIn(string userId, string userName)
        {
            var claims = new List<Claim>
            {
                new Claim(ClaimTypes.Sid,userId),
                new Claim(ClaimTypes.Name,userName)
             };
            var identity = new ClaimsIdentity(claims, "loginClaim");
            var userPrincipal = new ClaimsPrincipal(identity);
            await _httpContextAccessor.HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, userPrincipal, new AuthenticationProperties
            {
                ExpiresUtc = DateTime.UtcNow.AddMinutes(20),
                IsPersistent = false,
                AllowRefresh = false
            });
        }

        public async void SignOut()
        {
            await _httpContextAccessor.HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
        }
    }
}

细节2 所以现在封装好了之后怎样在控制器中使用呢 两种办法
第一种就是 注释掉的那样 在控制器初始化的时候 判断_authenticationService为空 就给他生成一个

第二种 就是在控制器的构造函数里直接注入(注入方式和上面注入 IHttpContextAccessor 的方式一样)

代码:

  private IAuthenticationService _authenticationService { get; set; }
        private readonly IAccountService _accountService;

        //protected override void Initialize(RequestContext requestContext)
        //{
        //    if (AuthenticationService == null) { AuthenticationService = new AuthenticationService(); }

        //    base.Initialize(requestContext);
        //}

        public AccountController(IAccountService accountService, IAuthenticationService authenticationService)
        {
            _accountService = accountService;
            _authenticationService = authenticationService;
        }

在这里插入图片描述
在控制器中直接调用就可以了

 _authenticationService.SignIn(user.Id.ToString(), user.UserName);

4.使用方法
在需要验证的控制器或方法上面加 [Authorize] 就可以了 这样如果在浏览器地址栏直接访问HomeController的话会先进行验证 如果验证不通过就会自动跳到Login页面
在这里插入图片描述
不需要验证的地方 加 [AllowAnonymous]
在这里插入图片描述

------------------------------------------- 【结束】 --------------------------------------
终于完了。。。 篇幅有点长 见谅

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值