家园志改版--登录验证加密处理

上一篇关于登录模块遗留下了加密问题。

开始的时候我认为即使要加密也没有多大意义,因为开始判断用户是否登录的方法只判断客户端是否有名为"UserName"和"UserID"的Cookie,如下代码:

public static LoginState CheckLogin() {
    HttpCookieCollection CookieCollection = GetSavedCookieCollection();
        if (CookieCollection.AllKeys.Length < keys.Length)
        {
            return LoginState.UnLogin;
        }
        else
        {
            return LoginState.Success;
        }
    }
}

所以,即使对Cookie加密也没有半点意义。但是我知道这样很不安全,只要客户端添加两个Cookie就可以登录了,岂不是很不安全。penpen的提议给了我思路:将同一个字段的用两种不同方法加密,判断时再各自解密并判断是否相等。具体流程如下图示意:

登录流程

改进后的代码:

保存Cookie的时候就要将UserID以两个不同密钥加密保存,UserName也是如此。现在保存到Cookie的值如下:

private static string[] keys = { "UserID","UserEID", "UserName","UserEName" }; //需要存放在Cookie里面的信息

保存Cookie的方法如下:

public static void SaveCookie(bool saveLong, string username) {
    int userid = GetUserIDByUserName(username);
    if (userid != -1)
    {
        Hashtable CookieValues = new Hashtable();
        CookieValues["UserID"] = Encryptor.Encrypt(userid.ToString());
        CookieValues["UserEID"] = Encryptor.Encrypt(userid.ToString(), "7E52PDK9");
        CookieValues["UserName"] =Encryptor.Encrypt( username);
        CookieValues["UserEName"] = Encryptor.Encrypt(username, "7E52PDK9");
        HttpCookieCollection cookieCollection = GetCookieCollection();
        foreach (string key in keys)
        {
            cookieCollection[key].Value = CookieValues[key].ToString();
            if (saveLong)
            {    //如果用户要求记住密码,则让Cookie在14天后过期
                cookieCollection[key].Expires = DateTime.Now.AddDays(14d);
            }
            HttpContext.Current.Response.Cookies.Add(cookieCollection[key]);
        }
    }
}

这样保存到客户端的Cookie是加过密的,很小的概率用户添加的Cookie值经过解密后会相等(但是还是有概率的)。

现在需要改进判断用户是否已登录的方法,

public static LoginState CheckLogin() {
    HttpCookieCollection CookieCollection = GetSavedCookieCollection(); //7E52PDK9
    try
    {
        if (CookieCollection.AllKeys.Length < keys.Length || Encryptor.Decrypt(CookieCollection["UserID"].Value) != Encryptor.Decrypt(CookieCollection["UserEID"].Value, "7E52PDK9") || Encryptor.Decrypt(CookieCollection["UserName"].Value) != Encryptor.Decrypt(CookieCollection["UserEName"].Value, "7E52PDK9"))
        {
            return LoginState.UnLogin;
        }
        else
        {
            return LoginState.Success;
        }
    }
    catch (Exception)
    {
        return LoginState.UnLogin;
    }
}

这里增加了三个条件说明用户未登录:解密后的UserName和UserEName不相等;解密后的UserID和UserEID不相等;客户端的Cookie少于必须的Cookie。然而在我测试的时候发现如果我将Cookie值改变成长度大于8的时候,程序就会报错,所以加上Try{}Catch{},当然此时用户肯定未登录。

下面是对字符串加密的方法,引用金色海洋--自然框架里面的:

public static string Encrypt(string sourceData) {  //加密,固定密钥
    //set key and initialization vector values
    //Byte[] key = new byte[] {0x21, 2, 0x88, 4, 5, 0x56, 7, 0x99};
    //Byte[] iv = new byte[] {0x21, 2, 0x88, 4, 5, 0x56, 7, 0x99};
    Byte[] key = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };
    Byte[] iv = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };
    try
    {
        //convert data to byte array
        Byte[] sourceDataBytes = System.Text.ASCIIEncoding.UTF8.GetBytes(sourceData);
        //get target memory stream
        MemoryStream tempStream = new MemoryStream();
        //get encryptor and encryption stream
        DESCryptoServiceProvider encryptor = new DESCryptoServiceProvider();
        CryptoStream encryptionStream = new CryptoStream(tempStream, encryptor.CreateEncryptor(key, iv), CryptoStreamMode.Write);

        //encrypt data
        encryptionStream.Write(sourceDataBytes, 0, sourceDataBytes.Length);
        encryptionStream.FlushFinalBlock();

        //put data into byte array
        Byte[] encryptedDataBytes = tempStream.GetBuffer();
        //convert encrypted data into string
        return System.Convert.ToBase64String(encryptedDataBytes, 0, (int)tempStream.Length);
    }
    catch (Exception ex)
    {
        throw (ex);
    }
}
由于篇幅的问题,这就不一一写出来了。可以下载源程序。
posted @ 2009-12-24 15:44  吕飞  阅读(507)  评论(2)    收藏  举报