shiro(13)-JWT(Token的生成)

shiro安全控制目錄

shiro有狀態(tài)認(rèn)證是利用session保存登錄狀態(tài)的授權(quán)認(rèn)證方式锹杈。但是當(dāng)前后端分離迈着,web服務(wù)無狀態(tài)化之后,那么JWT身份認(rèn)證便是趨勢咬清。

1. JWT簡介

1.1 什么叫做JWT

JWT(json web token)是為了在網(wǎng)絡(luò)應(yīng)用環(huán)境之間傳遞聲明而執(zhí)行的一種基于JSON的開放標(biāo)準(zhǔn)奴潘。
JWT的聲明一般被用來在身份提供者服務(wù)提供者間傳遞被認(rèn)證的用戶身份信息,以便從資源服務(wù)器獲取資源粪滤。比如用于登錄雀扶。

shiro(9)-有狀態(tài)身份認(rèn)證和無狀態(tài)身份認(rèn)證的區(qū)別

1.2 JWT的構(gòu)成

JWT由三部分組成:頭部(header)、載荷(payload)予权、簽名(signature)。頭部定義類型和加密方式岗照;載荷部分放的不是很重要的數(shù)據(jù)笆环;簽名使用定義的加密方式加密base64后的header和payload和一段自己加密key。最后的token由base64(header).base64(payload).base64(signature)組成迫吐。

JWT生成Token后是這個樣子的:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJvcmciOiLku4rml6XlpLTmnaEiLCJuYW1lIjoiRnJlZeeggeWGnCIsImV4cCI6MTUxNDM1NjEwMywiaWF0IjoxNTE0MzU2MDQzLCJhZ2UiOiIyOCJ9.49UF72vSkj-sA4aHHiYN5eoZ9Nb4w5Vb45PsLF7x_NY

JWT的結(jié)構(gòu).png

1.2.1. header

JWT頭部分是一個描述JWT元數(shù)據(jù)的JSON對象志膀。

  • 聲明類型鳖擒,這里是jwt。
  • 聲明加密的算法戳稽,通常直接使用HMAC SHA256期升。

完整的頭部就像下面這樣的json。

{"typ": "JWT","alg": "HS256"}

然后將頭部進(jìn)行base64加密赊时,構(gòu)成第一部分:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

1.2.2. payload

載荷是存放有效信息的地方祖秒,這些有效部分包含三個部分舟奠。

  • 標(biāo)準(zhǔn)中注冊的聲明;
  • 公共的聲明抬纸;
  • 私有的聲明耿戚;

標(biāo)準(zhǔn)中注冊的聲明:

類型 作用
iss 發(fā)行人
sub 主題
aud 接收Token的用戶
exp Token過期時間
iat Token簽發(fā)時間
nbf 在該時間之前Token不可用
jti Token的唯一標(biāo)識

公共的聲明:

公共的聲明可以添加任何的信息,一般添加用戶的相關(guān)信息或其他業(yè)務(wù)需要的必要信息坛猪,但不建議添加敏感的信息,因為這部分在客戶端可解密命黔。

私有的聲明:

私有聲明是提供者和消費者所共同定義的聲明就斤,一般不建議存放敏感信息,因為base64是對稱解密的搜立,意味著該部分信息可以歸類為明文信息槐秧。

定義一個payload:

{"name":"Free碼農(nóng)","age":"28","org":"今日頭條"}

然后將其進(jìn)行base64加密刁标,得到第二部分

eyJvcmciOiLku4rml6XlpLTmnaEiLCJuYW1lIjoiRnJlZeeggeWGnCIsImV4cCI6MTUxNDM1NjEwMywiaWF0IjoxNTE0MzU2MDQzLCJhZ2UiOiIyOCJ9

1.2.3. signuature

jwt的第三部分是一個簽證信息址晕,這個簽證信息由三部分組成:

  • header(base64后的)
  • payload(base64后的)
  • secret(密鑰)

這個部分需要base64加密后的header和base64加密后的payload使用.連接組成的字符串谨垃,然后通過header中聲明的加密方式進(jìn)行加鹽secret組合加密,就構(gòu)成了jwt的第三部分:

49UF72vSkj-sA4aHHiYN5eoZ9Nb4w5Vb45PsLF7x_NY

注:密鑰secret是保存在服務(wù)端的胳赌,服務(wù)端會根據(jù)這個密鑰進(jìn)行生成token和驗證匙隔,所以要保護(hù)好。

1.3 JWT優(yōu)點

  1. 因為json的通用性捍掺,所以JWT是可以跨語言支持的再膳。
  2. payload部分,JWT可以在自身存儲一些其他業(yè)務(wù)邏輯所必要的非敏感信息不瓶。
  3. 便于傳輸,JWT構(gòu)成簡單湃番,字節(jié)占用很小,所以它是非常便于傳輸?shù)淖鸲瑁恍枰诜?wù)端保存會話信息泥兰,所以利于應(yīng)用的擴(kuò)展。

2. JAVA代碼生成JWT

1.maven依賴

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.0</version>
</dependency>

2. 生成jwt

public class JwtUtils {


    private static final String secret = "66diangezanbageixioapang12oc";


       /**
     * (默認(rèn)的超時時間)token存活時間膀捷,2小時
     */
    private static final long liveMills = 3600 * 2 * 1000;


    /**
     * 獲取secret
     */
    public static SecretKey obtainKey() {
        //對key進(jìn)行解碼
        byte[] secretBytes = secret.getBytes();
        return new SecretKeySpec(secretBytes, 0, secretBytes.length, "AES");
    }

    /**
     * 獲取token全庸,使用默認(rèn)的超時時間融痛,即2個小時。
     *
     * @param sub 主題(需要加密的字符串)
     * @return token字符串
     */
    public static String createJWT(String sub) {
        return createJWT(sub, liveMills);
    }

    /**
     * 會自動對主題對象序列化(使用fastJson進(jìn)行序列化)得到字符串
     *
     * @param subObj 主題對象
     * @return token字符串
     */
    public static <T> String createJWT(T subObj) {
        return createJWT(JSONObject.toJSONString(subObj), liveMills);
    }

    /**
     * 會自動對主題對象序列化(使用fastJson進(jìn)行序列化)得到字符串
     *
     * @param subObj    主題對象
     * @param liveMills 失效時間覆劈,單位毫秒
     * @return token字符串
     */
    public static <T> String createJWT(T subObj, Long liveMills) {
        return createJWT(JSONObject.toJSONString(subObj), liveMills);
    }

    /**
     * @param sub       需要加密的主題
     * @param liveMills 失效時間责语,單位毫秒
     * @return 經(jīng)過jwt加密的token字符串目派,失效時間即當(dāng)前時間+liveMills毫秒數(shù)
     */
    public static String createJWT(String sub, Long liveMills) {
        //加密模式
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
        long currentTimeMillis = System.currentTimeMillis();
        Date now = new Date(currentTimeMillis);  //iat token簽發(fā)時間
        SecretKey secretKey = obtainKey();
        //jti表示該token的唯一id,不推薦使用相同值|isa 下發(fā)時間
        JwtBuilder jwtBuilder = Jwts.builder().setId("jti-xp").
                setIssuedAt(now).
                setSubject(sub).
                signWith(signatureAlgorithm, secretKey);
        if (liveMills > 0) {
            long expMills = currentTimeMillis + liveMills;
            Date expDate = new Date(expMills);  //失效時間
            jwtBuilder.setExpiration(expDate);
        }
        return jwtBuilder.compact();
    }

    /**
     * 解密token,返回Claims對象
     * <p>
     * 注铐拐,若token失效练对,會拋出{@link ExpiredJwtException}的異常
     **/
    public static Claims parseJWT(String token) {
        SecretKey key = obtainKey();
        return Jwts.parser().setSigningKey(key).parseClaimsJws(token).getBody();  // Claims [kle?mz] 聲明
    }

    /**
     * 解析token獲取到sub(主題)螟凭。
     * <p>
     * 注,若token失效螺男,會拋出{@link ExpiredJwtException}的異常
     */
    public static String parseJWT2Sub(String token) {
        SecretKey key = obtainKey();
        Claims body = Jwts.parser().setSigningKey(key).parseClaimsJws(token).getBody();
        return body.getSubject();
    }

    /**
     * 解密token獲取sub纵穿,并反序列化為對象谓媒。
     *
     * @param token 需要解密的token字符串
     * @param clazz sub反序列化的對象類型
     */
    public static <T> T parseJWT2Sub(String token, Class<T> clazz) {
        SecretKey key = obtainKey();
        Claims body = Jwts.parser().setSigningKey(key).parseClaimsJws(token).getBody();
        return JSON.parseObject(body.getSubject(), clazz);
    }


    public static void main(String[] args) {
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("username", "xiaopang");
        jsonObject.put("host", "127.0.0.1");
        String token = JwtUtils.createJWT(jsonObject.toJSONString());
        System.out.println(token);
        //解析token
        Claims claims = parseJWT(token);
        String subject = claims.getSubject();
        System.out.println(subject);
        //base64 payload解析
        String payload="eyJqdGkiOiJqdGkteHAiLCJpYXQiOjE1NjM5NDcxMTAsInN1YiI6IntcImhvc3RcIjpcIjEyNy4wLjAuMVwiLFwidXNlcm5hbWVcIjpcInhpYW9wYW5nXCJ9IiwiZXhwIjoxNTYzOTU0MzEwfQ";
        //org.apache.shiro.codec.Base64
        System.out.println("payload的base64解密:"+new String(Base64.decode(payload)));
        //base64 header解析
        String header="eyJhbGciOiJIUzI1NiJ9";
        System.out.println("header的base64解密:"+new String(Base64.decode(header)));
    }
}

解析結(jié)果

eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiJqdGkteHAiLCJpYXQiOjE1NjM5NDc2OTQsInN1YiI6IntcImhvc3RcIjpcIjEyNy4wLjAuMVwiLFwidXNlcm5hbWVcIjpcInhpYW9wYW5nXCJ9IiwiZXhwIjoxNTYzOTU0ODk0fQ.QibTsAVmwHnnl5D-o6HuvPRhJPgTmgxivSZj36lyEnU
{"host":"127.0.0.1","username":"xiaopang"}
payload的base64解密:{"jti":"jti-xp","iat":1563947110,"sub":"{\"host\":\"127.0.0.1\",\"username\":\"xiaopang\"}","exp":1563954310  
header的base64解密:{"alg":"HS256"}

附錄:

1. 重放攻擊

重放攻擊是攻擊者獲取客戶端發(fā)送給服務(wù)器端的包句惯,不做修改抢野,原封不動的發(fā)送給服務(wù)器用來實現(xiàn)某些功能各墨。比如說客戶端發(fā)送給服務(wù)器端一個包的功能是查詢某個信息,攻擊者攔截到這個包恃轩,然后想要查詢這個信息的時候黎做,把這個包發(fā)送給服務(wù)器,服務(wù)器就會做相應(yīng)的操作引几,返回查詢的信息伟桅。

文章參考:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末溃列,一起剝皮案震驚了整個濱河市膛薛,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌雅任,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,807評論 6 518
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件沪么,死亡現(xiàn)場離奇詭異,居然都是意外死亡寇漫,警方通過查閱死者的電腦和手機(jī)哭当,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,284評論 3 399
  • 文/潘曉璐 我一進(jìn)店門钦勘,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人彻采,你說我怎么就攤上這事×朐粒” “怎么了特笋?”我有些...
    開封第一講書人閱讀 169,589評論 0 363
  • 文/不壞的土叔 我叫張陵猎物,是天一觀的道長。 經(jīng)常有香客問我蔫磨,道長,這世上最難降的妖魔是什么蒲列? 我笑而不...
    開封第一講書人閱讀 60,188評論 1 300
  • 正文 為了忘掉前任蝗岖,我火速辦了婚禮榔至,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己杰标,他們只是感情好彩匕,可當(dāng)我...
    茶點故事閱讀 69,185評論 6 398
  • 文/花漫 我一把揭開白布驼仪。 她就那樣靜靜地躺著,像睡著了一般绪爸。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上介褥,一...
    開封第一講書人閱讀 52,785評論 1 314
  • 那天柔滔,我揣著相機(jī)與錄音,去河邊找鬼睛廊。 笑死杉编,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的嘶朱。 我是一名探鬼主播,決...
    沈念sama閱讀 41,220評論 3 423
  • 文/蒼蘭香墨 我猛地睜開眼见咒,長吁一口氣:“原來是場噩夢啊……” “哼挂疆!你這毒婦竟也來了缤言?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,167評論 0 277
  • 序言:老撾萬榮一對情侶失蹤胆萧,失蹤者是張志新(化名)和其女友劉穎跌穗,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體锈拨,經(jīng)...
    沈念sama閱讀 46,698評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡羹唠,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,767評論 3 343
  • 正文 我和宋清朗相戀三年佩微,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片哺眯。...
    茶點故事閱讀 40,912評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡族购,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出寝杖,到底是詐尸還是另有隱情,我是刑警寧澤磕蒲,帶...
    沈念sama閱讀 36,572評論 5 351
  • 正文 年R本政府宣布只盹,位于F島的核電站,受9級特大地震影響站削,放射性物質(zhì)發(fā)生泄漏孵稽。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,254評論 3 336
  • 文/蒙蒙 一园细、第九天 我趴在偏房一處隱蔽的房頂上張望接校。 院中可真熱鬧,春花似錦、人聲如沸睦柴。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,746評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽恬试。三九已至,卻和暖如春训柴,著一層夾襖步出監(jiān)牢的瞬間妇拯,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,859評論 1 274
  • 我被黑心中介騙來泰國打工仗嗦, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留甘凭,地道東北人。 一個月前我還...
    沈念sama閱讀 49,359評論 3 379
  • 正文 我出身青樓德撬,卻偏偏與公主長得像躲胳,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子坯苹,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,922評論 2 361

推薦閱讀更多精彩內(nèi)容