1. 程式人生 > >網站記住賬號和自動登入功能實現

網站記住賬號和自動登入功能實現

剛開始做這個功能的時候一頭霧水,本來打算直接用cookie實現,但發現在控制檯用document.cookie,就會完全暴露出使用者名稱和密碼,於是在網上找了各種辦法,但沒有找到一個很好的解決方案,最後想到在服務端設定cookie,並且經過加密來實現。

因為公司後端是php,所以以下用php為例。

前端部分:

<!DOCTYPE html>
<html lang="zh-cn">
<head>
    <meta charset="UTF-8">
    <title>記住密碼和自動登入</title>
</head>
<body>
    <div>賬號:<input type="text" id="account"></div><br>
    <div>密碼:<input type="password" id="password"></div><br>
    <div>
    <label for="remPassword"><input type="checkbox" id="remPassword">記住密碼</label>
    <label for="autoLogin"><input type="checkbox" id="autoLogin">自動登入</label>
    </div><br>
    
    <div><button type="button">登入</button></div>

    <script src="http://libs.baidu.com/jquery/2.0.3/jquery.min.js"></script>
    <!-- 
        引入jquery.cookie是為了演示方便,可以通過後端語言讀取cookie,並設定html
     -->
    <script src="https://cdn.bootcss.com/jquery-cookie/1.4.1/jquery.cookie.min.js"></script>

    <script>
        /*
        *存在cookie則展示使用者名稱和密碼,
        *並選中記住密碼,注意使用者和密碼需要後端解密,並設定到頁面,
        *因為不方便演示,所以略過。
        */
        if($.cookie('account') && $.cookie('password')){
            $('#remPassword').prop('checked',true);
        }
        $('button').bind('click',function(){
            var account = $('#account').val(),
                password = $('#password').val();
            if(!account || !password){alert('請輸入使用者名稱和密碼');return false;}
            var d = {account:account,password:password};

            if($("#remPassword").is(":checked")){//記住密碼
                d.remPassword = true;
            }
            if($("#autoLogin").is(":checked")){//自動登入
                d.autoLogin = true;
            }

            $.post('login.php',d,function(data){
                alert(data);
            },'text')
        })
        //自動登入
        if($.cookie('autoLogin')){
            $('#autoLogin').prop('checked',true);
            //觸發登入
            $('button').click();
        }
    </script>
</body>
</html>

php部分(login.php):

<?php
	/*
	*該方法只是一個加解密的舉例
	*/
	//$string:字串,明文或密文;$operation:DECODE表示解密,其它表示加密;$key:密匙;$expiry:密文有效期
	function encrypt($string, $operation = 'DECODE', $key = 'key', $expiry = 0) {   
		// 動態密匙長度,相同的明文會生成不同密文就是依靠動態密匙   
		$ckey_length = 4;   

		// 密匙   
		$key = md5($key ? $key : $GLOBALS['discuz_auth_key']);   

		// 密匙a會參與加解密   
		$keya = md5(substr($key, 0, 16));   
		// 密匙b會用來做資料完整性驗證   
		$keyb = md5(substr($key, 16, 16));   
		// 密匙c用於變化生成的密文   
		$keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length): 
			substr(md5(microtime()), -$ckey_length)) : '';   
		// 參與運算的密匙   
		$cryptkey = $keya.md5($keya.$keyc);   
		$key_length = strlen($cryptkey);   
		// 明文,前10位用來儲存時間戳,解密時驗證資料有效性,10到26位用來儲存$keyb(密匙b), 
		//解密時會通過這個密匙驗證資料完整性   
		// 如果是解碼的話,會從第$ckey_length位開始,因為密文前$ckey_length位儲存 動態密匙,以保證解密正確   
		$string = $operation == 'DECODE' ? base64_decode(substr($string, $ckey_length)) :  
		sprintf('%010d', $expiry ? $expiry + time() : 0).substr(md5($string.$keyb), 0, 16).$string;   
		$string_length = strlen($string);   
		$result = '';   
		$box = range(0, 255);   
		$rndkey = array();   
		// 產生密匙簿   
		for($i = 0; $i <= 255; $i++) {   
			$rndkey[$i] = ord($cryptkey[$i % $key_length]);   
		}   
		// 用固定的演算法,打亂密匙簿,增加隨機性,好像很複雜,實際上對並不會增加密文的強度   
		for($j = $i = 0; $i < 256; $i++) {   
			$j = ($j + $box[$i] + $rndkey[$i]) % 256;   
			$tmp = $box[$i];   
			$box[$i] = $box[$j];   
			$box[$j] = $tmp;   
		}   
		// 核心加解密部分   
		for($a = $j = $i = 0; $i < $string_length; $i++) {   
			$a = ($a + 1) % 256;   
			$j = ($j + $box[$a]) % 256;   
			$tmp = $box[$a];   
			$box[$a] = $box[$j];   
			$box[$j] = $tmp;   
		// 從密匙簿得出密匙進行異或,再轉成字元   
			$result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));   
		}   
		if($operation == 'DECODE') {  
		// 驗證資料有效性,請看未加密明文的格式   
			if((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) &&  
				substr($result, 10, 16) == substr(md5(substr($result, 26).$keyb), 0, 16)) {   
				return substr($result, 26);   
		} else {   
			return '';   
		}   
		} else {   
		// 把動態密匙儲存在密文裡,這也是為什麼同樣的明文,生產不同密文後能解密的原因   
		// 因為加密後的密文可能是一些特殊字元,複製過程可能會丟失,所以用base64編碼   
		return $keyc.str_replace('=', '', base64_encode($result));   
		}   
	}
	
	
	//登入操作
	$account   = $_POST['account'];
	$password   = $_POST['password'];
	
	//如果設定記住賬號 設定cookie
	if(isset($_POST['remPassword'])){
	  $a = encrypt($account,'ENCODE');
	  $p = encrypt($password,'ENCODE');
	  
	  setcookie("account",$a, time()+3600*24*7);
	  setcookie("password",$p, time()+3600*24*7);
	}else{
		//刪除cookie
		if(isset($_COOKIE['account']) && isset($_COOKIE['password'])){
			setcookie ("account", "", time() - 3600);
			setcookie ("password", "", time() - 3600);
		}
	}
	
	//如果設定自動登入 設定cookie
	if(isset($_POST['autoLogin'])){
	  setcookie("autoLogin",$_POST['autoLogin'], time()+3600*24*7);
	}else{
		//刪除cookie
		if(isset($_COOKIE['autoLogin'])){
			setcookie ("autoLogin", "", time() - 3600);
		}
	}
	/*
	* 解密cookie,頁面初始化時展示出來
	if(isset($_COOKIE["account"]) && isset($_COOKIE["password"])){
		$account   = encrypt($_COOKIE["account"],'DECODE');
	    $password  = encrypt($_COOKIE["password"],'DECODE');
	}
	*/
	
	echo '登入成功'.$account.'--'.$password;
?>

加解密方法是我從網上搜到的一個方法,也可以使用其他方法,只要能混淆cookie並防止他人解密就好,以上的cookie還可以把使用者名稱和密碼的部分相互替換(解密前先替換好),增加密文的複雜度,通過這些加密和混淆,即使別人獲取了cookie,也是難以破解的。

此外,還有一個安全問題,他人在獲取了電腦/手機的情況下,是可以通過dom元素獲取密碼的,所以頁面可以不展示解密後的密碼,只需寫入一串特定的符號,如果使用者進行登入時沒有改變,表示是要通過cookie中的密碼進行登入,後端進行解密再登入,這樣,只需關注cookie的安全性就好。