應該怎么做一個登錄功能?


版權是我的,轉載沒有通過我的同意的爬蟲都是傻逼.

假設有user表.里面有id,acount(賬戶,nvarchar(50)),pwd(密碼,nvarchar(50)).

最簡單的實現

渣渣說做個登錄功能,那還不簡單.

select * from user where  acount=XX AND PWD =YY;

然而,某天,我們需要在登錄的時候更新最后登錄時間,也就是在user表里面加個lastLoginTime.

這時渣渣說

select id from user wher acount=XX AND PWD =YY;

update user set XXXX where id=@id;

然后就他就被打了.實際上,可以合為一句:

update user set XXXX where acount=XX AND PWD =YY;

受影響行數大於0表示登錄成功.

密碼的加密

某一天,渣渣看到某某數據庫被下載了.想到自己的數據庫如果泄露了怎么辦,上面全是明文.

於是他就開始想加密這個事.

1MD5(同一個密碼,MD5還是一樣的,所以否決了)

2des(用於可逆密碼加密解密,然而有些安全系數要求高的密碼是不能被解密的,所以有適用范圍)

3隨機加鹽HASH.

第三種方案,要加在原來的表加多一個salt列.

每一次的登錄的話,

應該是select *  from user wher acount=XX 

然后把里面的鹽加上輸入的密碼進行hash然后與數據庫的加密后pwd進行比較.比如string.equal(MD(pwd+salt),pwd)

true當然就是說輸入正確啦.

其實加密的方案我舉的都只是最簡單的例子.對於鹽的把握,大家開腦洞自己想吧.

(比如可以對原密碼進行md5,然后對於md5的每一個偶數位字符插入鹽的一個字符,從而最后組成一個不明所以的字符串)

"無驗證碼登錄"

我們為什么需要驗證碼呢?是為了防止被密碼爆破.

那么是否可以無驗證碼登錄呢?當然可以.

遍地開花的開放平台接口

比如支付寶,旺旺,qq,微博....

以qq為例.他的步驟無非就是,跳到一個指向自己應用的騰訊鏈接.用戶在那上面授權給這個應用.授權后返回token和openid給網站.

做過微信第三方服務器的人就知道,這個token是用來以授權者的名義做各種操作的,實際上,對於登錄,只要有這個openid,去數據庫檢索成功了,就應該視為登錄有效.所以token其實沒啥用.

可信狀態下不需要驗證碼

固定的上網行為

我們應該怎么定義什么樣的狀態算作穩定?比如,我單個用戶,連續10次的登錄有9次都是在同一個ip上登錄,那么這個ip的實際來源就應該視為可信的狀態.

固定的上網行為其實還有很多種,比如登錄的時間段眾數,登錄的頻率,習慣使用的瀏覽器.只要你想的周全了,其實有很多用戶細節可以捕捉.

不頻繁的操作

我們相信這個世界的人大體都是美好的,所以你一看到我這個網站,要登錄的時候我就需要你輸入驗證碼,但如果你連續數次密碼錯誤,那我就要懷疑你的人品,給你下套.具體的可以看新浪微博.

場景的變換

不明公共wifi

我們知道http 下,明文post上去的明文被截取了就能分分鍾拿到信息.所以https就登場了.我post的密文就算給你看了,你也解密不了,耶~

然而這並沒有卵用.https證書根據不同的級別收不同的錢.於是12306自己做了個證書,哈哈.如果你們有魄力教導用戶安裝自己做的證書,那就走這條路吧....

移動端(Android,iPhone)

這就是我最近遇到的一個比較蛋疼的問題.我們知道url有長度限制,並且參數還得(utf-8)編碼.所以合適的做法應該是可解密的json密文作為http請求報文的body傳上去.

然而.net環境的des默認工作模式是cbc,所以其他客戶端也應該用cbc進行加密/解析.java的默認實現不是cbc,而且它的偏移向量每次都隨機.所以加密的結果和c#不一樣.

這里引用s站的說法,中文意思就是java的des是ecb而.NET的是cdc.工作模式不一樣.並且,我發現一些java 的des實現是用隨機向量的,所以變量不一致的話結果也會和.net的不一樣.

SunJCE provider uses ECB as the default mode, and PKCS5Padding as the default padding scheme for DES, DES-EDE and Blowfish ciphers. (JCA Doc)

In .Net, The default operation mode for the symmetric algorithm is CipherMode.CBC and default padding is PaddingMode.PKCS7. (msdn..SymmetricAlgorithm)

 

        /// <summary>
        ///  DES加密字符串
        /// </summary>
        /// <param name="encryptString">待加密的字符串</param>
        /// <param name="key"></param>
        /// <returns>加密成功返回加密后的字符串,失敗返回源串</returns>
        public static string DESEncrypt(this string encryptString, string key)
        {
            try
            {
                if (key.Length < 8)
                    throw new ArgumentException("密鑰和向量必須為8位,否則加密解密都不成功", "key");
                byte[] rgbKey = Encoding.UTF8.GetBytes(key.Substring(0, 8));
                byte[] inputByteArray = Encoding.UTF8.GetBytes(encryptString);
                using (var dCSP = new DESCryptoServiceProvider())
                {
                    using (MemoryStream mStream = new MemoryStream())
                    {
                        using (var cStream = new CryptoStream(mStream, dCSP.CreateEncryptor(rgbKey, rgbKey), CryptoStreamMode.Write))
                        {
                            cStream.Write(inputByteArray, 0, inputByteArray.Length);
                            cStream.FlushFinalBlock();
                            cStream.Close();
                            return Convert.ToBase64String(mStream.ToArray());
                        }
                    }
                }
            }
            catch
            {
                return encryptString;
            }
        }

  這下面是哥用了2015年7月3日用了0.7個下午的時間研(抄)究(襲)出來的java加密代碼

import java.util.HashMap;
import java.util.Map;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.SecretKeySpec;

import java.security.InvalidAlgorithmParameterException;
import java.security.Key;
import java.security.Security;

import javax.crypto.spec.DESKeySpec;
import javax.crypto.spec.IvParameterSpec;

import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.KeySpec;

import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

    public static void main(String[] args) throws Exception {
  
           
           Map map = new HashMap();  
           map.put( "Msg", "哦" );  
           map.put( "Url", "oooo");                       
           JSONObject jsonObject = JSONObject.fromObject( map );  
           String shit=jsonObject.toString();
           System.out.println(shit);  
              System.out.println(encrypt(shit,"UTF8"));
               }
    public static String encrypt(String message,String encoding,String myKey) throws Exception {  
        Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");  
         DESKeySpec desKeySpec = new DESKeySpec(myKey.getBytes(encoding));  
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");  
        SecretKey secretKey = keyFactory.generateSecret(desKeySpec);  
        IvParameterSpec iv = new IvParameterSpec(myKey.getBytes(encoding));  
        cipher.init(Cipher.ENCRYPT_MODE, secretKey, iv);  
        byte[] buf = cipher.doFinal(message.getBytes(encoding));        
        sun.misc.BASE64Encoder encoder = new sun.misc.BASE64Encoder();
        String a =   encoder.encode(buf);
         
      return a;  
    }  

這是哥移植到安卓的代碼,只是最后的base64用了Android的類,其余無區別

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import javax.crypto.spec.IvParameterSpec;
public static String encrypt(String key, String message, String encoding) throws Exception {
    Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
    DESKeySpec desKeySpec = new DESKeySpec(key.getBytes(encoding));
    SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
    SecretKey secretKey = keyFactory.generateSecret(desKeySpec);
    IvParameterSpec iv = new IvParameterSpec(key.getBytes(encoding));
    cipher.init(Cipher.ENCRYPT_MODE, secretKey, iv);
    byte[] buf = cipher.doFinal(message.getBytes(encoding));
    return android.util.Base64.encodeToString(buf, android.util.Base64.DEFAULT);
}

  

ios(object c)的我不知道,但是同事告訴我,轉成的base64除了大小寫有細微的差別外,沒有其他問題(他們請求我的web api接口有正確的響應).

Android的,也許還能參考我的第一個參考鏈接.不過it does not work for me.

參考鏈接:

Android平台和java平台 DES加密解密互通程序及其不能互通的原因 .

C# and Java DES Encryption value are not identical

 

版權是我的,轉載沒有通過我的同意的爬蟲都是傻逼.

 


免責聲明!

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



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