登入和cookie緩衝
對於用來登入的cookie,有兩種常見的方法可以將登入資訊儲存在cookie裡面:一種是簽名(signed)cookie,另一種是令牌(token)cookie。
簽名cookie通常會儲存使用者名稱,可能還有使用者ID、使用者最後一次成功登陸的時間,以及網站覺得有用的其他任何資訊。除了使用者的相關資訊外,簽名cookie還包含一個簽名,伺服器可以使用這個簽名來驗證發送的資訊是否未經改動(比如將cookie中的登入使用者名稱改成另一個客戶)。
令牌cookie會在cookie裡面儲存一串隨機位元組作為令牌,伺服器可以根據令牌在資料庫中尋找令牌的擁有者。下表展示了簽名cookie和令牌cookie的優點與缺點。
cookie類型 |
優點 |
缺點 |
簽名cookie |
驗證cookie所需的一切資訊都儲存在cookie裡面。cookie可以包含額外的資訊(additional information),並且對這些資訊進行簽名也很容易 |
正確地處理簽名很難。很容易忘記對資料進行簽名,或者忘記驗證資料的簽名,從而造成安全性漏洞 |
令牌cookie |
添加資訊非常容易。cookie的體積非常小,因此移動端和速度較慢的用戶端可以更快地發送請求 |
需要在伺服器中儲存更多資訊。如果使用的是關聯式資料庫,那麼載入和儲存cookie的代價可能會更高 |
下面是用java寫的一個例子
import java.util.ArrayList;import java.util.Set;import redis.clients.jedis.Jedis;public class Login { public String checkToken(Jedis conn,String token){ return conn.hget("login:", token); } public void updateToken(Jedis conn,String token,String user,String item){ long time=System.currentTimeMillis()/1000; conn.hset("login:", token, user);//維持令牌與使用者之間的映射 conn.zadd("recent:", time, token);//儲存令牌最後一次出現的時間 if(item!=null){ conn.zadd("viewd:"+token, time, item);//根據這個令牌來設定該使用者在這個時間戳記訪問的商品名字 conn.zremrangeByRank("viewd:"+token, 0, -26);//移除就的使用者記錄,只保留使用者瀏覽過的25個商品。 conn.zincrby("viewd:", -1, item); } } public class CleanSessionsThread extends Thread{ private Jedis conn; private int limit; private boolean quit; public CleanSessionsThread(int limit) { // TODO Auto-generated constructor stub this.conn=new Jedis("localhost"); conn.select(15); this.limit=limit; } public void quit(){ quit=true; } @Override public void run() { // TODO Auto-generated method stub while(!quit){ long size=conn.zcard("recent:");//依據登入時間確定線上人數 if(size<=limit){ try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } }else{ long endIndex=Math.min(size-limit,100); Set<String> tokensSet=conn.zrange("recent:", 0, endIndex-1); String[] tokens=tokensSet.toArray(new String[tokensSet.size()]); ArrayList<String> sessionKeys=new ArrayList<>(); for(String token:tokens){ sessionKeys.add("viewd:"+token); } conn.del(sessionKeys.toArray(new String[sessionKeys.size()])); conn.hdel("login:", tokens); conn.zrem("recent:", tokens); } } } }}