Vue開(kāi)發(fā)中Jwt的使用詳解
一.Jwt的簡(jiǎn)介
JWT(JSON Web Token)是一種用于身份驗(yàn)證和授權(quán)的開(kāi)放標(biāo)準(zhǔn),它定義了一種緊湊且自包含的方式來(lái)在各方之間安全地傳輸信息。JWT通常由三部分組成:頭部、負(fù)載和簽名。其中,頭部描述了JWT的元數(shù)據(jù),如加密算法、類(lèi)型等,負(fù)載包含了需要傳遞的信息,比如用戶(hù)ID、權(quán)限等,簽名則用于保證數(shù)據(jù)的完整性和安全性。
JWT的工作流程通常如下:
- 用戶(hù)通過(guò)用戶(hù)名和密碼進(jìn)行校驗(yàn),服務(wù)器驗(yàn)證通過(guò)后生成JWT;
- 瀏覽器將JWT存儲(chǔ)在客戶(hù)端,可以使用cookie或localStorage等機(jī)制;
- 客戶(hù)端每次向服務(wù)器發(fā)送請(qǐng)求時(shí),都會(huì)附加JWT到HTTP請(qǐng)求頭中;
- 服務(wù)器收到請(qǐng)求后,會(huì)驗(yàn)證JWT的合法性、解析出負(fù)載中的信息,并對(duì)請(qǐng)求作出相應(yīng)的處理。
使用JWT的優(yōu)點(diǎn)包括:
- 無(wú)狀態(tài):每個(gè)請(qǐng)求都包含了足夠的信息,無(wú)需在服務(wù)端保存任何狀態(tài);
- 可擴(kuò)展性:可以添加額外的信息到負(fù)載中,如用戶(hù)角色、過(guò)期時(shí)間等;
- 自包含:負(fù)載中包含了信息和簽名,可以驗(yàn)證數(shù)據(jù)的完整性和來(lái)源;
- 通用性:JWT是一種通用的協(xié)議,可以被不同的應(yīng)用和語(yǔ)言使用。
需要注意的是,在使用JWT進(jìn)行身份驗(yàn)證時(shí),為保證數(shù)據(jù)的安全性,需要遵循一些最佳實(shí)踐,如使用HTTPS協(xié)議傳輸、設(shè)置合適的過(guò)期時(shí)間等。此外,JWT僅適用于傳遞數(shù)據(jù),而不適用于加密數(shù)據(jù),如果需要加密數(shù)據(jù),需要添加額外的加密層級(jí)。
總之,JWT是一種輕量級(jí)的身份驗(yàn)證和授權(quán)協(xié)議,具有無(wú)狀態(tài)、可擴(kuò)展、自包含和通用等優(yōu)點(diǎn),被廣泛應(yīng)用于Web和移動(dòng)應(yīng)用程序中。
傳統(tǒng)開(kāi)發(fā)的弊端,以及解決方法
二.Jwt工具類(lèi)的使用
1.定義工具類(lèi)
package com.zking.ssm.jwt; import java.util.Date; import java.util.Map; import java.util.UUID; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; import org.apache.commons.codec.binary.Base64; import io.jsonwebtoken.Claims; import io.jsonwebtoken.JwtBuilder; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; /** * JWT驗(yàn)證過(guò)濾器:配置順序 CorsFilte->JwtUtilsr-->StrutsPrepareAndExecuteFilter * */ public class JwtUtils { /** * JWT_WEB_TTL:WEBAPP應(yīng)用中token的有效時(shí)間,默認(rèn)30分鐘 */ public static final long JWT_WEB_TTL = 30 * 60 * 1000; /** * 將jwt令牌保存到header中的key */ public static final String JWT_HEADER_KEY = "jwt"; // 指定簽名的時(shí)候使用的簽名算法,也就是header那部分,jwt已經(jīng)將這部分內(nèi)容封裝好了。 private static final SignatureAlgorithm SIGNATURE_ALGORITHM = SignatureAlgorithm.HS256; private static final String JWT_SECRET = "f356cdce935c42328ad2001d7e9552a3";// JWT密匙 private static final SecretKey JWT_KEY;// 使用JWT密匙生成的加密key static { byte[] encodedKey = Base64.decodeBase64(JWT_SECRET); JWT_KEY = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES"); } private JwtUtils() { } /** * 解密jwt,獲得所有聲明(包括標(biāo)準(zhǔn)和私有聲明) * * @param jwt * @return * @throws Exception */ public static Claims parseJwt(String jwt) { Claims claims = Jwts.parser() .setSigningKey(JWT_KEY) .parseClaimsJws(jwt) .getBody(); return claims; } /** * 創(chuàng)建JWT令牌,簽發(fā)時(shí)間為當(dāng)前時(shí)間 * * @param claims * 創(chuàng)建payload的私有聲明(根據(jù)特定的業(yè)務(wù)需要添加,如果要拿這個(gè)做驗(yàn)證,一般是需要和jwt的接收方提前溝通好驗(yàn)證方式的) * @param ttlMillis * JWT的有效時(shí)間(單位毫秒),當(dāng)前時(shí)間+有效時(shí)間=過(guò)期時(shí)間 * @return jwt令牌 */ public static String createJwt(Map<String, Object> claims, long ttlMillis) { // 生成JWT的時(shí)間,即簽發(fā)時(shí)間 2021-10-30 10:02:00 -> 30 10:32:00 long nowMillis = System.currentTimeMillis(); //鏈?zhǔn)秸Z(yǔ)法: // 下面就是在為payload添加各種標(biāo)準(zhǔn)聲明和私有聲明了 // 這里其實(shí)就是new一個(gè)JwtBuilder,設(shè)置jwt的body JwtBuilder builder = Jwts.builder() // 如果有私有聲明,一定要先設(shè)置這個(gè)自己創(chuàng)建的私有的聲明,這個(gè)是給builder的claim賦值,一旦寫(xiě)在標(biāo)準(zhǔn)的聲明賦值之后,就是覆蓋了那些標(biāo)準(zhǔn)的聲明的 .setClaims(claims) // 設(shè)置jti(JWT ID):是JWT的唯一標(biāo)識(shí),根據(jù)業(yè)務(wù)需要,這個(gè)可以設(shè)置為一個(gè)不重復(fù)的值,主要用來(lái)作為一次性token,從而回避重放攻擊。 // 可以在未登陸前作為身份標(biāo)識(shí)使用 .setId(UUID.randomUUID().toString().replace("-", "")) // iss(Issuser)簽發(fā)者,寫(xiě)死 .setIssuer("zking") // iat: jwt的簽發(fā)時(shí)間 .setIssuedAt(new Date(nowMillis)) // 代表這個(gè)JWT的主體,即它的所有人,這個(gè)是一個(gè)json格式的字符串,可放數(shù)據(jù){"uid":"zs"}。此處沒(méi)放 // .setSubject("{}") // 設(shè)置簽名使用的簽名算法和簽名使用的秘鑰 .signWith(SIGNATURE_ALGORITHM, JWT_KEY) // 設(shè)置JWT的過(guò)期時(shí)間 .setExpiration(new Date(nowMillis + ttlMillis)); return builder.compact(); } /** * 復(fù)制jwt,并重新設(shè)置簽發(fā)時(shí)間(為當(dāng)前時(shí)間)和失效時(shí)間 * * @param jwt * 被復(fù)制的jwt令牌 * @param ttlMillis * jwt的有效時(shí)間(單位毫秒),當(dāng)前時(shí)間+有效時(shí)間=過(guò)期時(shí)間 * @return */ public static String copyJwt(String jwt, Long ttlMillis) { //解密JWT,獲取所有的聲明(私有和標(biāo)準(zhǔn)) //old Claims claims = parseJwt(jwt); // 生成JWT的時(shí)間,即簽發(fā)時(shí)間 long nowMillis = System.currentTimeMillis(); // 下面就是在為payload添加各種標(biāo)準(zhǔn)聲明和私有聲明了 // 這里其實(shí)就是new一個(gè)JwtBuilder,設(shè)置jwt的body JwtBuilder builder = Jwts.builder() // 如果有私有聲明,一定要先設(shè)置這個(gè)自己創(chuàng)建的私有的聲明,這個(gè)是給builder的claim賦值,一旦寫(xiě)在標(biāo)準(zhǔn)的聲明賦值之后,就是覆蓋了那些標(biāo)準(zhǔn)的聲明的 .setClaims(claims) // 設(shè)置jti(JWT ID):是JWT的唯一標(biāo)識(shí),根據(jù)業(yè)務(wù)需要,這個(gè)可以設(shè)置為一個(gè)不重復(fù)的值,主要用來(lái)作為一次性token,從而回避重放攻擊。 // 可以在未登陸前作為身份標(biāo)識(shí)使用 //.setId(UUID.randomUUID().toString().replace("-", "")) // iss(Issuser)簽發(fā)者,寫(xiě)死 // .setIssuer("zking") // iat: jwt的簽發(fā)時(shí)間 .setIssuedAt(new Date(nowMillis)) // 代表這個(gè)JWT的主體,即它的所有人,這個(gè)是一個(gè)json格式的字符串,可放數(shù)據(jù){"uid":"zs"}。此處沒(méi)放 // .setSubject("{}") // 設(shè)置簽名使用的簽名算法和簽名使用的秘鑰 .signWith(SIGNATURE_ALGORITHM, JWT_KEY) // 設(shè)置JWT的過(guò)期時(shí)間 .setExpiration(new Date(nowMillis + ttlMillis)); return builder.compact(); } }
2.通過(guò)工具類(lèi)進(jìn)行測(cè)試
2.1生成jwt
public void test1() {// 生成JWT //JWT Token=Header.Payload.Signature //頭部.載荷.簽名 //Payload=標(biāo)準(zhǔn)聲明+私有聲明+公有聲明 //定義私有聲明 Map<String, Object> claims = new HashMap<String, Object>(); claims.put("username", "lb"); claims.put("age", 18); //TTL:Time To Live String jwt = JwtUtils.createJwt(claims, JwtUtils.JWT_WEB_TTL); System.out.println(jwt); //獲取Payload(包含標(biāo)準(zhǔn)和私有聲明) Claims parseJwt = JwtUtils.parseJwt(jwt); for (Map.Entry<String, Object> entry : parseJwt.entrySet()) { System.out.println(entry.getKey() + "=" + entry.getValue()); } Date d1 = parseJwt.getIssuedAt(); Date d2 = parseJwt.getExpiration(); System.out.println("令牌簽發(fā)時(shí)間:" + sdf.format(d1)); System.out.println("令牌過(guò)期時(shí)間:" + sdf.format(d2)); }
2.2解析舊的Jwt
@Test public void test2() {// 解析oldJwt //io.jsonwebtoken.ExpiredJwtException:JWT過(guò)期異常 //io.jsonwebtoken.SignatureException:簽名異常 //String oldJwt="eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1OTA3MTg2NzcsImlhdCI6MTU5MDcxNjg3NywiYWdlIjoxOCwianRpIjoiNDFmZjFiZGFkYzkxNDA3OGE4ZGUyNGRkZDEwYjU4N2IiLCJ1c2VybmFtZSI6InpzcyJ9.DdPvioX6kuhV6lEfD9QAN2eQSk_mO3dYkmDmTQsqa78"; //eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2MzU1NjE3MjcsImlhdCI6MTYzNTU1OTkyNywiYWdlIjoxOCwianRpIjoiN2RlYmIzM2JiZTg3NDBmODgzNDI5Njk0ZWE4NzcyMTgiLCJ1c2VybmFtZSI6InpzcyJ9.dUR-9JUlyRdoYx-506SxXQ3gbHFCv0g5Zm8ZGzK1fzw String newJwt="eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ6a2luZyIsImV4cCI6MTY2MjM0Njg3MSwiaWF0IjoxNjYyMzQ1MDcxLCJhZ2UiOjE4LCJqdGkiOiI4YjllNzc3YzFlMDM0MjViYThmMDVjNTFlMTU3NDQ1MiIsInVzZXJuYW1lIjoienNzIn0.UWpJxPxwJ09PKxE2SY5ME41W1Kv3jP5bZGKK-oNUDuM"; String oldJwt = "eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2MzU1NjE3MjcsImlhdCI6MTYzNTU1OTkyNywiYWdlIjoxOCwianRpIjoiN2RlYmIzM2JiZTg3NDBmODgzNDI5Njk0ZWE4NzcyMTgiLCJ1c2VybmFtZSI6InpzcyJ9.dUR-9JUlyRdoYx-506SxXQ3gbHFCv0g5Zm8ZGzK1fzw"; Claims parseJwt = JwtUtils.parseJwt(newJwt); for (Map.Entry<String, Object> entry : parseJwt.entrySet()) { System.out.println(entry.getKey() + "=" + entry.getValue()); } Date d1 = parseJwt.getIssuedAt(); Date d2 = parseJwt.getExpiration(); System.out.println("令牌簽發(fā)時(shí)間:" + sdf.format(d1)); System.out.println("令牌過(guò)期時(shí)間:" + sdf.format(d2)); }
2.3測(cè)試JWT的有效時(shí)間
@Test public void test4() {// 測(cè)試JWT的有效時(shí)間 Map<String, Object> claims = new HashMap<String, Object>(); claims.put("username", "zss"); String jwt = JwtUtils.createJwt(claims, 3 * 1000L); System.out.println(jwt); Claims parseJwt = JwtUtils.parseJwt(jwt); Date d1 = parseJwt.getIssuedAt(); Date d2 = parseJwt.getExpiration(); System.out.println("令牌簽發(fā)時(shí)間:" + sdf.format(d1)); System.out.println("令牌過(guò)期時(shí)間:" + sdf.format(d2)); }
2.4復(fù)制Jwt
@Test public void test3() {// 復(fù)制jwt,并延時(shí)30分鐘 String oldJwt = "eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ6a2luZyIsImV4cCI6MTY2MjM0Njg3MSwiaWF0IjoxNjYyMzQ1MDcxLCJhZ2UiOjE4LCJqdGkiOiI4YjllNzc3YzFlMDM0MjViYThmMDVjNTFlMTU3NDQ1MiIsInVzZXJuYW1lIjoienNzIn0.UWpJxPxwJ09PKxE2SY5ME41W1Kv3jP5bZGKK-oNUDuM"; //String newJwt = "eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2MDU3NTM2NTUsImlhdCI6MTYwNTc1MTg1NSwiYWdlIjoxOCwianRpIjoiYmNmN2Q1MzQ2YjE3NGU2MDk1MmIxYzQ3ZTlmMzQyZjgiLCJ1c2VybmFtZSI6InpzcyJ9.m1Qn84RxgbKCnsvrdbbAnj8l_5Jwovry8En0j4kCxhc"; //String oldJwt = "eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1NjI5MDMzNjAsImlhdCI6MTU2MjkwMTU2MCwiYWdlIjoxOCwianRpIjoiZDVjMzE4Njg0MDcyNDgyZDg1MDE5ODVmMDY3OGQ4NjkiLCJ1c2VybmFtZSI6InpzcyJ9.XDDDRRq5jYq5EdEBHtPm7GcuBz4S0VhDTS1amRCdf48"; String newJwt = JwtUtils.copyJwt(oldJwt, JwtUtils.JWT_WEB_TTL); System.out.println(newJwt); Claims parseJwt = JwtUtils.parseJwt(newJwt); for (Map.Entry<String, Object> entry : parseJwt.entrySet()) { System.out.println(entry.getKey() + "=" + entry.getValue()); } Date d1 = parseJwt.getIssuedAt(); Date d2 = parseJwt.getExpiration(); System.out.println("令牌簽發(fā)時(shí)間:" + sdf.format(d1)); System.out.println("令牌過(guò)期時(shí)間:" + sdf.format(d2)); }
3.實(shí)例
3.1打開(kāi)Jwt校驗(yàn)
3.2 實(shí)現(xiàn)效果
定義jwt變量(state.js)
設(shè)置值(muntionis.js)
setJwt:(state,payload)=>{ //state 就是 state.js文件導(dǎo)出的參數(shù) // payload vue 傳遞的參數(shù) state.jwt=payload.jwt; }
getters.js取值
getJwt:(state)=>{ return state.jwt; }
在Http.js設(shè)置攔截請(qǐng)求
// 請(qǐng)求攔截器 axios.interceptors.request.use(function(config) { let jwt= window.vm.$store.getters.getJwt; if(jwt){ config.headers['jwt'] = jwt; } return config; }, function(error) { return Promise.reject(error); }); // 響應(yīng)攔截器 axios.interceptors.response.use(function(response) { let jwt = response.headers['jwt']; if(jwt){ window.vm.$store.commit('setJwt', { jwt:jwt }); } return response; }, function(error) { return Promise.reject(error); });
3.3效果展示
到此這篇關(guān)于Vue開(kāi)發(fā)中Jwt的使用詳解的文章就介紹到這了,更多相關(guān)Vue Jwt內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Vue入門(mén)之a(chǎn)nimate過(guò)渡動(dòng)畫(huà)效果
這篇文章主要介紹了Vue入門(mén)之a(chǎn)nimate過(guò)渡動(dòng)畫(huà)效果的相關(guān)資料,需要的朋友可以參考下2018-04-04一個(gè)vue組件庫(kù)發(fā)布到npm的完整實(shí)現(xiàn)過(guò)程
工作的時(shí)候總是使用別人的npm包,然而我有時(shí)心底會(huì)好奇自己如何發(fā)布一個(gè)npm包呢,什么時(shí)候自己的包能夠被很多人喜歡并使用呢,下面這篇文章主要給大家介紹了關(guān)于一個(gè)vue組件庫(kù)發(fā)布到npm的相關(guān)資料,需要的朋友可以參考下2022-03-03vue-cli使用stimulsoft.reports.js的詳細(xì)教程
Stimulsoft?Reports.JS是一個(gè)使用JavaScript和HTML5生成報(bào)表的平臺(tái)。它擁有所有擁來(lái)設(shè)計(jì),編輯和查看報(bào)表的必需組件。該報(bào)表工具根據(jù)開(kāi)發(fā)人員數(shù)量授權(quán)而不是根據(jù)應(yīng)用程序的用戶(hù)數(shù)量。接下來(lái)通過(guò)本文給大家介紹vue-cli使用stimulsoft.reports.js的方法,一起看看吧2021-12-12Vue文件下載進(jìn)度條的實(shí)現(xiàn)過(guò)程
這篇文章主要介紹了Vue文件下載進(jìn)度條的實(shí)現(xiàn)原理,通過(guò)使用onDownloadProgress方法API獲取進(jìn)度及文件大小等數(shù)據(jù),本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-07-07Vue3+Element?Plus實(shí)現(xiàn)el-table跨行顯示(非腳手架)
這篇文章主要介紹了Vue3+Element Plus實(shí)現(xiàn)el-table跨行顯示(非腳手架),本文通過(guò)示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-09-09vue項(xiàng)目打包之后生成一個(gè)可修改IP地址的文件(具體操作)
這篇文章主要介紹了vue項(xiàng)目打包之后生成一個(gè)可修改IP地址的文件(具體操作),本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-03-03