.Net Core MVC 基於Cookie進行用戶認證


在打代碼之前先說一下思路。

登錄的的時候服務端生成加密的字符串(用戶名、id、當前時間)並且存入客戶端cookie中,服務端的緩存中。對客戶端的每次請求進行攔截,解密保存在cookie中的加密字符串。查看是否已過期,如果已過期跳轉到登錄頁,並且刪除cookie與緩存中的數據。如未過期修改緩存中的時間,並進行下一步操作。

加密解密的代碼

引入包:Microsoft.Extensions.Configuration(讀配置文件的時候會用到,加密key與解密key最好存在配置文件中,從配置文件中讀取)

public class Encryption
{
    private IConfiguration _configuration;
    public Encryption(IConfiguration configuration)
    {
        _configuration = configuration;
    }

    public byte[] key()
    {
        return ASCIIEncoding.ASCII.GetBytes(_configuration["DES:rgbKey"]);
    }
    public byte[] vector()
    {
        return ASCIIEncoding.ASCII.GetBytes(_configuration["DES:rgbIV"]);
    }

    /// <summary>
    /// DES加密
    /// </summary>
    /// <param name="str"></param>
    /// <returns></returns>
    public async Task<string> Encrypt(string str)
    {
        MemoryStream ms = null;
        CryptoStream cs = null;
        StreamWriter sw = null;

        DESCryptoServiceProvider des = new DESCryptoServiceProvider();
        try
        {
            ms = new MemoryStream();
            cs = new CryptoStream(ms, des.CreateEncryptor(this.key(), this.vector()), CryptoStreamMode.Write);
            sw = new StreamWriter(cs);
            sw.Write(str);
            sw.Flush();
            cs.FlushFinalBlock();
            return Convert.ToBase64String(ms.GetBuffer(), 0, (int)ms.Length);
        }
        finally
        {
            if (sw != null) sw.Close();
            if (cs != null) cs.Close();
            if (ms != null) ms.Close();
        }
    }

    /// <summary>
    /// DES解密
    /// </summary>
    /// <param name="str"></param>
    /// <returns></returns>
    public async Task<string> Decrypt(string str)
    {
        MemoryStream ms = null;
        CryptoStream cs = null;
        StreamReader sr = null;

        DESCryptoServiceProvider des = new DESCryptoServiceProvider();
        try
        {
            ms = new MemoryStream(Convert.FromBase64String(str));
            cs = new CryptoStream(ms, des.CreateDecryptor(this.key(), this.vector()), CryptoStreamMode.Read);
            sr = new StreamReader(cs);
            return sr.ReadToEnd();
        }
        finally
        {
            if (sr != null) sr.Close();
            if (cs != null) cs.Close();
            if (ms != null) ms.Close();
        }
    }
}

操作Redis

引入包:StackExchange.Redis 

連接字符串最好也存入配置文件中

  public class RedisContent
    {
        public StackExchange.Redis.ConnectionMultiplexer Redis = null;
        public StackExchange.Redis.IDatabase db = null;

        public RedisContent(IConfiguration configuration)
        {
            _configuration = configuration;
            this.Redis = ConnectionMultiplexer.Connect($"{_configuration["Redis:dbconn"]}");
            this.db = this.Redis.GetDatabase();
        }
        private IConfiguration _configuration;
    }

定義一個特性類,對於一些不需要認證的接口,加上這個特性即可。相當於微軟的[AllowAnonymous]認證中間件。這里的話我們自己寫一個。

 public class AllowAuthAttribute : Attribute
 {
 }

添加過濾器AuthorizeFilter。上面封裝的一些方法,全部以注入的形式進行使用。

public class AuthorizeFilter : Attribute, IAuthorizationFilter
    {
        private readonly ILogger<AuthorizeFilter> _logger;
        private readonly RedisContent _content;
        private readonly Encryption _encryption;
        public AuthorizeFilter(RedisContent content,
            Encryption encryption,
            ILogger<AuthorizeFilter> logger)
        {
            _content = content;
            _encryption = encryption;
            _logger = logger;
        }

        public void OnAuthorization(AuthorizationFilterContext context)
        {
            var isDefined = false;
            var controllerActionDescriptor = context.ActionDescriptor as ControllerActionDescriptor;
            if (controllerActionDescriptor != null)
            {
                  //判斷請求的控制器和方法有沒有加上AllowAuthAttribute(不需要認證)
                  //反射得到控制器的type
                  Type typeController = Type.GetType($"{controllerActionDescriptor.ControllerTypeInfo.FullName}");
                  //先判斷控制器上是否有這個特性
                  isDefined = typeController.IsDefined(typeof(AllowAuthAttribute), true);
                  //判斷方法上是否有這個特性
                  isDefined = controllerActionDescriptor.MethodInfo.GetCustomAttributes(inherit: true).Any(a => a.GetType().Equals(typeof(AllowAuthAttribute))) ? true : isDefined;
            }
            if (isDefined) return;
            if (context.HttpContext.Request.Path == "/")
                return;
            string value = string.Empty;
            context.HttpContext.Request.Cookies.TryGetValue("userinfo", out value);
            if (string.IsNullOrEmpty(value))
                context.HttpContext.Response.Redirect("https://localhost:44300/");
            else
            {
                //解密cookie
                var decryptValueArray = _encryption.Decrypt(value).Result.Split("|");
                string user = _content.db.StringGet($"{decryptValueArray[0]}-{decryptValueArray[2]}");
                if (string.IsNullOrEmpty(user) || !user.Equals(value))
                {
                    _logger.LogError($"Token已過期/有誤! Url:{context.HttpContext.Request.Path}");
                    context.HttpContext.Response.Cookies.Delete("userinfo");
                    context.HttpContext.Response.Redirect("https://localhost:44300/");
                }
                else
                {
                    //重新設置key的時間
                    _content.db.KeyExpire($"{decryptValueArray[0]}-{decryptValueArray[2]}", TimeSpan.FromMinutes(30));
                    return;
                }
            }
        }
    }

編寫控制器中的代碼

        /// <summary>
        /// 驗證登錄 頒發Token 存入cookie
        /// </summary>
        /// <param name="userName"></param>
        /// <param name="password"></param>
        /// <returns></returns>
        [AllowAuth]
        public async Task<IActionResult> LoginUser(string userName, string password)
        {
            if (string.IsNullOrEmpty(userName) && string.IsNullOrEmpty(password))
                return Json(new { success = false, msg = "用戶名或密碼不能為空" });
            //帶參查詢
            var data = await _userInfoServices.QueryUserInfo(userName, password);
            if (data.Any())
            {
                //得到uid,將uid也帶進token中加密
                var uid = data.ToList()[0].id;
                //加密 30分鍾的有效期
                var token = await _encryption.Encrypt(userName + "|" + DateTime.Now + "|" + uid + "|" + 30);
                //存入redis中
                _content.db.StringSet($"{userName}" + "-" + uid, token, TimeSpan.FromMinutes(30));
                //存入cookie中
                Response.Cookies.Append("userinfo", token);
                return Json(new { success = true, msg = "登陸成功" });
            }
            else
            {
                return Json(new { success = false, msg = "用戶名或密碼輸入錯誤" });
            }
        }

如有不足,還望見諒!😉


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM