功能:登錄驗證+過期驗證+注銷清除cookie+未注銷下關閉或刷新瀏覽器仍可直接訪問action
概述:token只存在客戶端cookie,后端AES加密+解密+驗證,每一次成功訪問action都會刷新token包括過期時間
1.過濾器
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using System.Web;
using System.Web.Mvc;
namespace TokenTest.MyActionFilter
{
/// <summary>
/// <param name="Die_Time">Die_Time//設置token過期時間小於7days</param>
/// <param name="Jump_Page">Jump_Page//設置token驗證失敗后跳轉頁面如/Login/Index;可攜帶參數/Login/Index?******</param>
/// </summary>
public class Token_Filter:ActionFilterAttribute//繼承ActionFilterAttribute類
{
public int Die_Time { get; set;}//設置token過期時間<7days
public string Jump_Page { get; set; }//設置token驗證失敗后跳轉頁面如/Login/Index;可攜帶參數/Login/Index?******
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var token = HttpContext.Current.Request.Cookies["Token"];
if (token==null)
{
HttpContext.Current.Response.Write("<script language='javascript'>alert('您未登陸,請先登陸!');location.href='" + Jump_Page + "' ;</script>");
}
else
{
string U_ID = AESDecrypt(token.Value.ToString(), "jjq").Split('_')[0];
DateTime Token_Time = Convert.ToDateTime(AESDecrypt(token.Value.ToString(), "jjq").Split('_')[1]);
//檢驗過期時間;token理論上驗證有效期:Die_Time<7天/////每次加載過濾action都會重新設置時間(可改)
if (Token_Time.AddDays(Die_Time) < DateTime.Now)
{
HttpContext.Current.Response.Write("<script language='javascript'>alert('登陸過期,請重新登錄!');location.href='" + Jump_Page + "' ;</script>");
}
else
{
//完全驗證通過后重新改寫token並覆寫在cookie里
Set_Token(U_ID);
}
}
}
public static void Set_Token(string user_name)
{
var Token = AESEncrypt(user_name + "_" + DateTime.Now.ToString() + "_" + Guid.NewGuid(), "jjq");//Token加密;;jjq加密密匙
HttpContext.Current.Response.Cookies["Token"].Value = Token;
HttpContext.Current.Response.Cookies["Token"].Expires = DateTime.Now.AddDays(7);//設置Token客戶端保留時間7天
}
#region
/// <summary>
/// 加密解密功能代碼片段
/// </summary>
//AES密鑰向量
private static readonly byte[] _aeskeys = { 0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF, 0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF };
/// <summary>
/// AES加密
/// </summary>
/// <param name="encryptStr">加密字符串</param>
/// <param name="encryptKey">密鑰</param>
/// <returns></returns>
public static string AESEncrypt(string encryptStr, string encryptKey)
{
if (string.IsNullOrWhiteSpace(encryptStr))
return string.Empty;
encryptKey = SubString(encryptKey, 0, 32);
encryptKey = encryptKey.PadRight(32, ' ');
//分組加密算法
SymmetricAlgorithm des = Rijndael.Create();
byte[] inputByteArray = Encoding.UTF8.GetBytes(encryptStr);//得到需要加密的字節數組
//設置密鑰及密鑰向量
des.Key = Encoding.UTF8.GetBytes(encryptKey);
des.IV = _aeskeys;
byte[] cipherBytes = null;
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, des.CreateEncryptor(), CryptoStreamMode.Write))
{
cs.Write(inputByteArray, 0, inputByteArray.Length);
cs.FlushFinalBlock();
cipherBytes = ms.ToArray();//得到加密后的字節數組
cs.Close();
ms.Close();
}
}
return Convert.ToBase64String(cipherBytes);
}
/// <summary>
/// AES解密
/// </summary>
/// <param name="decryptStr">解密字符串</param>
/// <param name="decryptKey">密鑰</param>
/// <returns></returns>
public static string AESDecrypt(string decryptStr, string decryptKey)
{
if (string.IsNullOrWhiteSpace(decryptStr))
return string.Empty;
decryptKey = SubString(decryptKey, 0, 32);
decryptKey = decryptKey.PadRight(32, ' ');
byte[] cipherText = Convert.FromBase64String(decryptStr);
SymmetricAlgorithm des = Rijndael.Create();
des.Key = Encoding.UTF8.GetBytes(decryptKey);
des.IV = _aeskeys;
byte[] decryptBytes = new byte[cipherText.Length];
using (MemoryStream ms = new MemoryStream(cipherText))
{
using (CryptoStream cs = new CryptoStream(ms, des.CreateDecryptor(), CryptoStreamMode.Read))
{
cs.Read(decryptBytes, 0, decryptBytes.Length);
cs.Close();
ms.Close();
}
}
return Encoding.UTF8.GetString(decryptBytes).Replace("\0", "");//將字符串后尾的'\0'去掉
}
public static string SubString(string sourceStr, int startIndex, int length)
{
if (!string.IsNullOrEmpty(sourceStr))
{
if (sourceStr.Length >= (startIndex + length))
return sourceStr.Substring(startIndex, length);
else
return sourceStr.Substring(startIndex);
}
return "";
}
#endregion
}
}
2.使用方法
1.需要過濾的Controller中引用(注意給參數)
using System.Web.Mvc;
using TokenTest.MyActionFilter;
namespace TokenTest.Controllers
{
public class adminController : Controller
{
// GET: admin
[Token_Filter(Die_Time=3,Jump_Page = "/Login/Index")]
public ActionResult Index()
{
return View();
}
}
}
2.LoginController中引用Token_Filter.Set_Token()方法;激活設置token+注銷按鈕(清除cookie)
using System;
using System.Web.Mvc;
using TokenTest.MyActionFilter;
namespace TokenTest.Controllers
{/// <summary>
/// ////////////////////////////////////////////////
/// </summary>
public class LoginController : Controller
{
// GET: Login
public ActionResult Index()
{
return View();
}
public ActionResult Logout()
{
//注銷按鈕---清除cookie
Response.Cookies["Token"].Expires = DateTime.Now.AddDays(-1);
return View("Index");
}
public ActionResult Login()
{
var get_name = Request["name"];
var get_pwd = Request["pwd"];
JJQPractice visitor = JJQPractice.SingleOrDefault(new { name = get_name });//得到對應輸入的賬號的集
if (visitor != null)//驗證輸入的賬號是否存在
{
if (visitor.pwd == get_pwd)//驗證密碼是否正確
{
Token_Filter.Set_Token(get_name);
return RedirectToAction("Index", "admin"); //密碼正確后跳轉到查詢視圖
}
else
{
Response.Write("登錄失敗!密碼錯誤.");
return View("Index");
}
}
else
{
Response.Write("登錄失敗,用戶名不存在.");
return View("Index");
}
}
}
}
<h2>Logn-Index</h2>
<br><br>
<form action="/Login/Login" method="post">
姓名:<input type="text" name="name"/><br><br>
密碼:<input type="text" name="pwd"/><br><br>
<input type="submit" value="提交"/><br><br>
</form>
<h2>Index</h2>
<form action="/admin/Index" method="post">
<input name="h" value="6666"/>
<input type="submit" value="按鈕"/><br /><br />
</form>
<form action="/Login/Logout" method="post">
<input type="submit" value="注銷_清除cookie" />
</form>