需解决的问题:
网站登录往往需要用户名+密码验证,为避免重复验证,一般采用Cookie 、Session等技术来保持用户的登录状态:
Session是在服务端保存的一个数据结构,用来跟踪用户的状态,这个数据可以保存在集群、数据库、文件中;
Cookie是客户端保存用户信息的一种机制,用来记录用户的一些信息,也是实现Session的一种方式
从上解释可见,Cookie 比 Session 更简单,也不占用服务器的资源。
ActionFilterAttribute + Cookie 可以解决 以上问题:
首先,在工程中新建一个空目录(名称自取):
然后,在目录中添加一个类 UserCookie 负责对用户登录信息的Cookie相应操作:
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Web;
namespace ManageSysetm.Tools
{
public class UserCookie
{
public static T JsonDeserialize(string jsonString)
{
return JsonConvert.DeserializeObject(jsonString);
}
public static string ToJson(object obj)
{
JsonSerializerSettings settings = new JsonSerializerSettings();
settings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
return JsonConvert.SerializeObject(obj);
}
public static void WriteCookie(Maticsoft.Model.user mod)
{
FormsAuthentication.SetAuthCookie(ToJson(mod), true, FormsAuthentication.FormsCookiePath);
//把用户对象保存在票据里
FormsAuthenticationTicket Ticket = new FormsAuthenticationTicket(1, mod.userName, DateTime.Now, DateTime.Now.AddTicks(FormsAuthentication.Timeout.Ticks), false, ToJson(mod));
//加密票据
string hashTicket = FormsAuthentication.Encrypt(Ticket);
HttpCookie userCookie = new HttpCookie(FormsAuthentication.FormsCookieName, hashTicket);
userCookie.Expires.AddHours(3);
System.Web.HttpContext.Current.Response.Cookies.Add(userCookie);
}
public static Maticsoft.Model.user ReadCookie()
{
try
{
var cookie = System.Web.HttpContext.Current.Request.Cookies[FormsAuthentication.FormsCookieName]; //的到Cookie
if (cookie == null)
return null;
var ticket = FormsAuthentication.Decrypt(cookie.Value); //解密票据
string str = ticket.UserData;
return JsonDeserialize(str);
}
catch
{
return null;
}
}
public static void ReMoveCookie()
{
FormsAuthentication.SignOut();
}
}
}
说明:
1、添加了序列化的Json处理函数,关于JSON可以参考前面的介绍;
2、读写Cookie:对输入的Model 序列化 (ToJson) 成字符串,然后写入票据,读的过程正好相反
再添加筛选器类:IsLogin 继承于 ActionFilterAttribute
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace ManageSysetm.Tools
{
public class IsLogin : ActionFilterAttribute
{
//当方法执行时
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
try
{
var isLogin = UserCookie.ReadCookie();
if (isLogin == null)
{
filterContext.Result = new RedirectResult("/Home/login");
}
}
catch
{
filterContext.Result = new RedirectResult("/Home/login");
}
base.OnActionExecuting(filterContext);
}
//当方法执行完毕
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
base.OnActionExecuted(filterContext);
}
}
}
逻辑比较简单:判断是否存在Cookie,不存在的时候或者异常时跳转到登录页面,否则正常通过。
可以用“帽子”的方法来实现筛选:
在默认的路由 Index上添加 筛选器 IsLogin ,则在该Action执行前先进行筛选器判断:取Cookie,没有则返回登录页面,否则正常进入Index 。
拓展:
既然可以添加 筛选器 IsLogin ,那么是否也可以添加 筛选器 IsAdmin (名称自取) ? ——这样可以根据不同用户角色筛选进入不同的页面。
当然是可以的,解决思路无非就是加入用户Cookie中身份的判断逻辑!
数据表中用明文记录用户的密码是不可取的,一旦数据表被曝光后,用户的角色就能够被人使用。比如:数据库管理人员可以利用数据表中的用户名和密码直接进行用户角色登录!
解决方法是加密:(贴几个实用函数,可以放入某个工具类中)
private static string Key
{
get { return @")O[NB]6,YF}+efcaj{+oESb9d8>Z'e9M"; }
}
private static string IV
{
get { return @"L+\~f4,Ir)b$=pkf"; }
}
public static string Encrypt(string pwd)//加密
{
byte[] bKey = Encoding.UTF8.GetBytes(Key);
byte[] bIV = Encoding.UTF8.GetBytes(IV);
byte[] byteArray = Encoding.UTF8.GetBytes(pwd);//将字符串转换为字节序列
string encrypt = null;
Rijndael aes = Rijndael.Create();
using (MemoryStream mStream = new MemoryStream())//创建内存流对象
{
using (CryptoStream cStream = new CryptoStream(mStream, aes.CreateEncryptor(bKey, bIV), CryptoStreamMode.Write))//创建加密流对象
{
cStream.Write(byteArray, 0, byteArray.Length);//向加密流中写入字节序列
cStream.FlushFinalBlock();//将数据压入基础流,用缓冲区的当前状态更新基础数据源或储存库,随后清除缓冲区。
encrypt = Convert.ToBase64String(mStream.ToArray());//从内存流中获取字节序列,并返回加密后的字符串
}
}
aes.Clear();
return encrypt;
}
public static string AESDecrypt(string encryptStr)
{
byte[] bKey = Encoding.UTF8.GetBytes(Key);
byte[] bIV = Encoding.UTF8.GetBytes(IV);
byte[] byteArray = Convert.FromBase64String(encryptStr);
string decrypt = null;
Rijndael aes = Rijndael.Create();
using (MemoryStream mStream = new MemoryStream())
{
using (CryptoStream cStream = new CryptoStream(mStream, aes.CreateDecryptor(bKey, bIV), CryptoStreamMode.Write))
{
cStream.Write(byteArray, 0, byteArray.Length);
cStream.FlushFinalBlock();
decrypt = Encoding.UTF8.GetString(mStream.ToArray());
}
}
aes.Clear();
return decrypt;
}
使用方法比较简单:string s= UserCookie.Encrypt(password); //对密码加密处理
加密字符串,由此解决用户表密码信息的保护:
PS:
验证的时候不需要解密,用户输入的密码加密 与 数据表中字段匹配一下即可。