Session跨域及單點登入解決方案
cookie機制
關於cookie和seesion的聯絡
cookie中會包含那些資訊
名字,值,過期時間,路徑,域
cookie會帶到http請求頭中傳送給服務端
如果cookie沒有設定過期時間的話,那麼cookie的預設生命週期是瀏覽器的會話
session機制
1,session是容器物件,客戶端在請求服務端的時候,服務端會根據客戶端的請求判斷是否包含了jsessionId的標識
2,如果已經包含了,說明客戶端之前已經建立了會話。sessionId是一個唯一的值
3,如果sessionid不存在,那麼服務端為這個客戶端生成一個sessionid. JESSIONID
session cookie 儲存的是JSESSIONID
session儲存在伺服器端 cookie儲存在瀏覽器端
伺服器端(Tomcat) 會生成一個唯一的sessionId號儲存在cookie中 叫 jessionid
在伺服器端(tomcat)中儲存serssion 使用concurrentMap (ConcurrentMap key JSESSIONID values session)
瀏覽器端下次請求伺服器端是將jsessionId帶過來 找到對應的session 獲取session中儲存的資訊(使用者資訊)
解決session跨域共享問題
1. session sticky :會話儲存在單機上 保證會話請求落在同一臺伺服器上
2. session replication:session 複製 每一臺伺服器上都保持一份相同的session (造成額外的儲存開銷和網路開銷)
3. session 集中儲存 :儲存在db、 儲存在快取伺服器 (redis)
4. 基於cookie (主流)
a)
access_token(userid/token/timestamp(過期時間) 加密)
將access_token儲存在客戶端的cookie中 每次 客戶端過來訪問 伺服器端攔截其中 獲取cookie中的access_token 根據 userid和
b)基於JWT的解決方案
json web Token 客戶端和服務端資訊保安傳遞,身份認證的一種解決方案。用在登陸上
jwt由三個組成:header,payload 載荷,signature
header{
typ:"jwt" //型別
alg:"HS256" //加密演算法
}
payload :jwt本身規範提供的格式 claims
{
iss:“簽發者”
iat:“簽發時間”
exp:“過期時間”
sub:
}
可以自己定一些claims,放入自定義的資訊如 uid 等
signature: 將 header+ payload 組合成為一個字串
Base64(header).Base64(payload) + head中定義的演算法 +金鑰 生成一個字串 str.簽名字串 就是 JWT的token
<!-- https://mvnrepository.com/artifact/com.auth0/java-jwt -->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.3.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/joda-time/joda-time -->
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.9.9</version>
</dependency>
/**
* @Auther: tengxiao
* @Date: 2018/9/13 16:47
* @Description:
*/
public class JWTTokenUtil {
private static final String JWT_KEY_USER_ID="JWT_KEY_USER_ID";
private static final int EXPIRED_TIME=6000;
private static final String SECRET_KEY="tengvincent_user";
public static String generatorToken(Long userId)throws Exception{
//header Map
Map<String,Object> headerMap=new HashMap<>();
headerMap.put("typ","JWT");
headerMap.put("alg","HS256");
String token=JWT.create()
.withHeader(headerMap)
.withClaim("iss","Service")//簽發者
.withClaim("aud","APP")
.withClaim(JWT_KEY_USER_ID,userId)
.withIssuedAt(DateTime.now().toDate())//sign time
.withExpiresAt(DateTime.now().plusMinutes(EXPIRED_TIME).toDate())//expired time
.sign(Algorithm.HMAC256(SECRET_KEY));
return token;
}
public static Map<String,Claim> varifyToken(String token){
DecodedJWT jwt=null;
try{
JWTVerifier verifer= JWT.require(Algorithm.HMAC256(SECRET_KEY)).build();
jwt=verifer.verify(token);
}catch (Exception e){
// e.printStackTrace();
// token 校驗失敗, 丟擲Token驗證非法異常
}
return jwt.getClaims();
}
public static Long getTokenInfo(String token){
Map<String, Claim> claims = varifyToken(token);
Claim user_id_claim = claims.get("user_id");
if (null == user_id_claim || StringUtils.isEmpty(user_id_claim.asString())) {
// token 校驗失敗, 丟擲Token驗證非法異常
}
return Long.valueOf(user_id_claim.asString());
}
}
1.token+redis與jwt的區別
(1)簡單的說,token只是一個標識,以token加redis為例,服務端將token儲存在redis中,客服端訪問時帶上token,如果在redis中能夠查到這個token,說明身份有效。
(2)jwt不需要查庫,本身已經包含了使用者的相關資訊,可以直接通過服務端解析出相關的資訊,與session,token的最大區別就是服務端不儲存任何資訊。
2.如何實現jwt續期
在jwt中儲存過期時間,解析時進行判定,如果即將超時則重新設定過期時間返回一個新的jwt給客戶端。
3.jwt登出失效
登出時將相關的資訊比如使用者名稱儲存在redis中,並設定過期時間。當再次訪問時,從jwt中解析出使用者名稱去redis中查詢,如果存在則表示此jwt已登出失效。這裡需要注意的是,如果用此方法,則驗證jwt是否登出應該放在第一位。思考一個場景,如果redis中儲存的是使用者名稱,那麼當用戶登出後,redis中已經有了相應的使用者名稱,當用戶再次登入時,解析jwt發現此使用者已登出,則jwt失效,所以在登入時要清空相關的登出快取。
參考文件