Spring Security Oauth2學習(二)

Spring Security Oauth2(二)

認識JWT令牌

在介紹JWT之前先看一下傳統校驗令牌的方法,如下圖:
在這裏插入圖片描述
問題:
傳統授權方法的問題是用戶每次訪問資源服務,資源服務都須攜帶令牌去認證服務中心請求驗證令牌合法性,並根據令牌獲取用戶相關信息,性能低下。
這個時候的令牌只是作爲唯一標識,並沒有其他用途。

解決:
使用JWT的思路,用戶認證通過會得到一個JWT令牌,JWT令牌中已經包含了用戶相關信息,客戶端只需要攜帶JWT訪問服務資源,資源服務根據事先約定的算法自行完成令牌效驗,這個就不用每次請求認證服務完成授權。
在這裏插入圖片描述
什麼是jwt?

JSON Web Token(JWT)是一個開放的行業標(RFC 7519),它定義了一種簡介的、自包含的協議格式,用於在通信雙方傳遞json對象,傳遞的信息經過數字簽名可以被驗證和信任。JWT可以使用HMAC算法或使用RSA的公鑰/私鑰對來簽名,防止被篡改。

官網:https://jwt.io/
標準:https://tools.ietf.org/html/rfc7519

jwt令牌優點:

  1. jwt令牌採用json格式,便於解析
  2. 可以在令牌中自定義豐富的內容,易於擴展
  3. 通過非對稱加密算法和數字簽名技術,jwt防止篡改,安全性高
  4. 資源服務使用jwt可不用依賴認證服務即可完成授權。

缺點:

  1. jwt令牌較長,佔存儲空間

JWT令牌結構

JWT令牌由三部分組成,每部分中間使用點(.)分隔,比如:xxxxx.yyyyy.zzzzz

  • Header
    頭部包括令牌的類型(即JWT)及使用的哈希算法(如HMAC SHA256或RSA)一個例子如下:
    下邊是Header部分的內容
	{
		"alg": "HS256",
		"typ": "JWT"
	}

將上邊的內容使用Base64Url編碼,得到一個字符串就是JWT令牌的第一部分。

  • Payload

第二部分是負載,內容也是一個json對象,它是存放有效信息的地方,它可以存放jwt提供的現成字段,比
如:iss(簽發者),exp(過期時間戳), sub(面向的用戶)等,也可自定義字段。
此部分不建議存放敏感信息,因爲此部分可以解碼還原原始內容。
最後將第二部分負載使用Base64Url編碼,得到一個字符串就是JWT令牌的第二部分。
案例:

{
"sub": "1234567890",
"name": "456",
"admin": true
}
  • Signature
    第三部分是簽名,此部分用於防止jwt內容被篡改
    這個部分使用base64url將前兩部分進行編碼,編碼後使用點(.)連接組成字符串,最後使用header中聲明
    簽名算法進行簽名。
    一個例子:
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)

base64UrlEncode(header):jwt令牌的第一部分。
base64UrlEncode(payload):jwt令牌的第二部分。
secret:簽名所使用的密鑰。

生成私鑰和公鑰

JWT令牌生成採用非對稱加密算法
1、生成密鑰證書
下邊命令生成密鑰證書,採用RSA 算法每個證書包含公鑰和私鑰。

keytool -genkeypair -alias xckey -keyalg RSA -keypass xuecheng -keystore xc.keystore -storepass xuechengkeystore

Keytool 是一個java提供的證書管理工具
-alias:密鑰的別名
-keyalg:使用的hash算法
-keypass:密鑰的訪問密碼
-keystore:密鑰庫文件名,xc.keystore保存了生成的證書
-storepass:密鑰庫的訪問密碼

查詢證書信息:

keytool -list -keystore xc.keystore

刪除別名

keytool -delete -alias xckey -keystore xc.keystore

2.導出公鑰
openssl是一個加解密工具包,這裏使用openssl來導出公鑰信息。
安裝 openssl:

https://pan.baidu.com/s/133OZPVr4jlTu9vIkPuDN4g

安裝資料目錄下的Win64OpenSSL-1_1_0g.exe
配置openssl的path環境變量,本教程配置在D:\OpenSSL-Win64\bin
cmd進入xc.keystore文件所在目錄執行如下命令:

keytool -list -rfc --keystore xc.keystore | openssl x509 -inform pem -pubkey

輸入密鑰庫密碼:

xuechengkeystore

在這裏插入圖片描述
下邊這一段就是公鑰內容:

-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAijyxMdq4S6L1Af1rtB8SjCZHNgsQG8JTfGy55eYvzG0B
/E4AudR2prSRBvF7NYPL47scRCNPgLnvbQczBHbBug6uOr78qnWsYxHlW6Aa5dI5NsmOD4DLtSw8eX0hFyK5F
j6ScYOSFBz9cd1nNTvx2+oIv0lJDcpQdQhsfgsEr1ntvWterZt/8r7xNN83gHYuZ6TM5MYvjQNBc5qC7Krs9wM7U
oQuL+s0X6RlOib7/mcLn/lFLsLDdYQAZkSDx/6+t+1oHdMarChIPYT1sx9Dwj2j2mvFNDTKKKKAq0cv14Vrhz67Vj
mz2yMJePDqUi0JYS2r0iIo7n8vN7s83v5uOQIDAQAB
-----END PUBLIC KEY-----

將上邊的公鑰拷貝到文本文件中。

生成jwt令牌

創建測試類,測試jwt令牌的生成與驗證。

  //創建jwt令牌
    @Test
    public void testCreateJwt(){
        //密鑰庫文件,之前生成的私鑰文件
        String keystore = "xc.keystore";
        //密鑰庫的密碼
        String keystore_password = "xuechengkeystore";

        //密鑰庫文件路徑
        ClassPathResource classPathResource = new ClassPathResource(keystore);
        //密鑰別名
        String alias  = "xckey";
        //密鑰的訪問密碼
        String key_password = "xuecheng";
        //密鑰工廠
        KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(classPathResource,keystore_password.toCharArray());
        //密鑰對(公鑰和私鑰)
        KeyPair keyPair = keyStoreKeyFactory.getKeyPair(alias, key_password.toCharArray());
        //獲取私鑰
        RSAPrivateKey aPrivate = (RSAPrivateKey) keyPair.getPrivate();
        //jwt令牌的內容
        Map<String,String> body = new HashMap<>();
        body.put("name","itcast");
        String bodyString = JSON.toJSONString(body);
        //生成jwt令牌
        Jwt jwt = JwtHelper.encode(bodyString, new RsaSigner(aPrivate));
        //生成jwt令牌編碼
        String encoded = jwt.getEncoded();
        System.out.println(encoded);

    }

驗證JWT令牌

    //校驗jwt令牌
    @Test
    public void testVerify(){
        //公鑰
        String publickey = "-----BEGIN PUBLIC KEY-----MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnASXh9oSvLRLxk901HANYM6KcYMzX8vFPnH/To2R+SrUVw1O9rEX6m1+rIaMzrEKPm12qPjVq3HMXDbRdUaJEXsB7NgGrAhepYAdJnYMizdltLdGsbfyjITUCOvzZ/QgM1M4INPMD+Ce859xse06jnOkCUzinZmasxrmgNV3Db1GtpyHIiGVUY0lSO1Frr9m5dpemylaT0BV3UwTQWVW9ljm6yR3dBncOdDENumT5tGbaDVyClV0FEB1XdSKd7VjiDCDbUAUbDTG1fm3K9sx7kO1uMGElbXLgMfboJ963HEJcU01km7BmFntqI5liyKheX+HBUCD4zbYNPw236U+7QIDAQAB-----END PUBLIC KEY-----";
        //jwt令牌
        String jwtString = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiaXRjYXN0In0.lQOqL1s4DpDHROUAibkz6EMf6hcM7HmTPgmg-SlkacVoQAV7y3XQ7LXxiua6SJlN_uNX_EFjzIshEg_kyy972DtymtRMc2NIO5HzIF5I4oQCxNPsJdhu6qQni6sTas3q0JbAarMZSajDX7HhzVSYWPQJCussA4e1r9oFxDcoAo6TEAXOW8gRHzNIygQz1yCj6mdf4UOHI070kRy7f3BdhmrUJdOuDIMoRBYS4WsEOibAU1UCNPaJAXpZC0ihrtdY7SCg1N43fimeFOHrfpLb6OmRF7v7uvGMgrhg9JIYDbJ6nbode5OJkNceRx8QUICre2yKAe0ctlvXO0REf6OpRA";
        //校驗jwt令牌
        Jwt jwt = JwtHelper.decodeAndVerify(jwtString, new RsaVerifier(publickey));
        //拿到jwt令牌中自定義的內容
        String claims = jwt.getClaims();
        System.out.println(claims);
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章