1. 程式人生 > >Session跨域及單點登入解決方案

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和

timestamp(過期時間) 判斷是否有效

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失效,所以在登入時要清空相關的登出快取。

參考文件