JWT工具模块
文章目錄
- JWT工具模塊
- 測試
JWT工具模塊
如果要想在項目之中去使用JWT技術,那么就必須結合到已有的模塊之中,最佳的做法就是將JWT的相關的處理
操作做為一個自動的starter組件進行接入
1、【microcloud項目】既然要開發一個starter組件,最佳的做法就是開發一個新的模塊,模塊名稱:“yootk-starter.jwt ”
2、【microcloud 項目】需要為“yootk-starter-jwt”模塊配置所需要的依賴庫,這些依賴庫包括
implementation group: 'org.springframework.boot', name: 'spring-boot-configuration-processor', version: '2.5.5' compileOnly group: 'javax.servlet', name: 'javax.servlet-api', version: '4.0.1' implementation group: 'commons-codec', name: 'commons-codec', version: '1.15' implementation group: 'io.jsonwebtoken', name: 'jjwt', version: '0.9.1' implementation group: 'javax.xml.bind', name: 'jaxb-api', version: '2.3.1' implementation group: 'com.sun.xml.bind', name: 'jaxb-impl', version: '2.3.0' implementation group: 'com.sun.xml.bind', name: 'jaxb-core', version: '2.3.0'3、【microcloud項目】既然已經確定了所需要的項目依賴庫,隨后就可以修改“dependencies.gradle”配置文件,定義所依賴模塊的配置。
ext.versions = [ // 定義全部的依賴庫版本號servlet : '4.0.1', // Servlet的依賴庫commonsCodec : '1.15', // codec依賴庫jjwt : '0.9.1', // jwt依賴庫jaxb : '2.3.0', // JAXB依賴庫 JDK11需要加的 ] ext.libraries = [// 以下的配置為JWT的服務整合'servlet-api' : "javax.servlet:javax.servlet-api:${versions.servlet}",'commons-codec' : "commons-codec:commons-codec:${versions.commonsCodec}",'jjwt' : "io.jsonwebtoken:jjwt:${versions.jjwt}",'jaxb-api' : "javax.xml.bind:jaxb-api:${versions.jaxb}",'jaxb-impl' : "com.sun.xml.bind:jaxb-impl:${versions.jaxb}",'jaxb-core' : "com.sun.xml.bind:jaxb-core:${versions.jaxb}", ]4、【microcloud項目】修改build.gradle配置文件,添加相關的依賴
project(":yootk-starter-jwt") { // JWT的實現組件dependencies {annotationProcessor('org.springframework.boot:spring-boot-configuration-processor')implementation(libraries.'servlet-api')implementation(libraries.'commons-codec')// 以下的組件會被其他的模塊繼續引用,所以必須將其的編譯范圍配置為compilecompile(libraries.'jjwt')compile(libraries.'jaxb-api')compile(libraries.'jaxb-impl')compile(libraries.'jaxb-core')} }5、【yootk-starter-jwt子模塊】由于該模塊最終需要進行編譯處理,所以此時要修改build.gradle配置文件,進行任務配置。
jar { enabled = true} // 允許打包為jar文件 bootJar { enabled = false } // 不允許打包為Boot執行文件 javadocJar { enabled = false } // 不需要打包為jar文件 javadocTask { enabled = false } // 不需要打包為doc文件6、【yootk-starter-jwt子模塊】為了便于用戶的信息的相應,創建一個JWT響應代碼枚舉類。
package com.yootk.jwt.code;import javax.servlet.http.HttpServletResponse;public enum JWTResponseCode { // 定義為一個枚舉類SUCCESS_CODE(HttpServletResponse.SC_OK, "Token數據正確,服務正常訪問!"),TOKEN_TIMEOUT_CODE(HttpServletResponse.SC_BAD_REQUEST, "Token信息已經失效,需要重新申請!"),NO_AUTH_CODE(HttpServletResponse.SC_NOT_FOUND, "沒有找到匹配的Token信息,無法進行服務訪問!");private int code; // 響應的代碼private String message; // 響應信息private JWTResponseCode(int code, String message) {this.code = code;this.message = message;}public String toString() { // 直接將數據以JSON的形式返回return "{\"code\":" + this.code + ",\"message\":" + this.message + "}";} }7、 【yootk-starter-jwt】此時的yootk-starter-jwt模塊最終是一個自動裝配的組件,那么既然是組件就需要通過一個配置類來讀取引用該模塊時所添加的配置信息,那么創建一個JWTConfigProperties 配置類。
package com.yootk.jwt.config;import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties;@Data // Lombok直接生成的所有代碼 @ConfigurationProperties(prefix = "yootk.security.config.jwt") // 配置項的前綴 public class JWTConfigProperties { // JWT配置類private String sign; // 保存簽名信息private String issuer; // 證書簽發者private String secret; // 加密的密鑰private long expire; // 失效時間 }8、【yootk-starter-jwt子模塊】創建ITokenService服務處理接口,專門實現JWT數據的相關處理。
package com.yootk.jwt.service;import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jws; import io.jsonwebtoken.JwtException;import javax.crypto.SecretKey; import java.util.Map;public interface ITokenService { // 創建一個JWT的操作接口public SecretKey generalKey(); // 獲取當前JWT數據的加密KEY// 創建Token的數據內容,同時要求保存用戶的id以及所需要的附加數據public String createToken(String id, Map<String, Object> subject);public Jws<Claims> parseToken(String token) throws JwtException; // 解析Token數據public boolean verifyToken(String token); // 驗證Token有效性public String refreshToken(String token); // 刷新Token內容 }9.【yootk-starter-jwt子模塊】創建TokenServicelmpl實現子類,很多的數據需要通過JSON實現傳遞。
package com.yootk.jwt.service.impl;import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.yootk.jwt.config.JWTConfigProperties; import com.yootk.jwt.service.ITokenService; import io.jsonwebtoken.*; import org.apache.commons.codec.binary.Base64; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value;import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; import java.util.Date; import java.util.HashMap; import java.util.Map; // 此時的組件中的代碼需要被其他的模塊去引用,所以未必會與掃描包相同 public class TokenServiceImpl implements ITokenService {@Autowired // SpringBoot容器啟動時會自動提供Jackson 實例private ObjectMapper objectMapper; // Jackson的數據處理類對象@Autowiredprivate JWTConfigProperties jwtConfigProperties; // 獲取JWT的相關配置屬性@Value("${spring.application.name}") // 通過SpEL進行配置注入private String applicationName; // 應用名稱private SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256; // 簽名算法@Overridepublic SecretKey generalKey() {byte [] encodeKey = Base64.decodeBase64(Base64.encodeBase64(this.jwtConfigProperties.getSecret().getBytes()));SecretKey key = new SecretKeySpec(encodeKey, 0, encodeKey.length, "AES"); // 獲取加密KEYreturn key;}@Overridepublic String createToken(String id, Map<String, Object> subject) {// 使用JWT數據結構進行開發,目的之一就是不需要進行JWT數據的分布式存儲,所以所謂的緩存組件、數據庫都用不到// 所有的Token都存在有保存時效的問題,所以就需要通過當前時間來進行計算Date nowDate = new Date(); // 獲取當前的日期時間Date expireDate = new Date(nowDate.getTime() + this.jwtConfigProperties.getExpire() * 1000); // 證書過期時間Map<String, Object> cliams = new HashMap<>(); // 保存所有附加數據cliams.put("site", "www.yootk.com"); // 視頻下載地址,頂部有一個下載資源cliams.put("msg", "世界上爆可愛的老師 —— 爆可愛的小李老師"); // 隨便添加內容cliams.put("nice", "Good Good Good");Map<String, Object> headers = new HashMap<>(); // 保存頭信息headers.put("author", "李興華"); // 作者,也可以通過配置處理// 后續由于很多的模塊都會引用此組件,所以為了后續的安全,最佳的做法就是設置一個模塊名稱的信息headers.put("module", this.applicationName);JwtBuilder builder = null;try {builder = Jwts.builder() // 進行JWTBuilder對象實例化.setClaims(cliams) // 保存附加的數據內容.setHeader(headers) // 保存頭信息.setId(id)// 保存ID信息.setIssuedAt(nowDate) // 簽發時間.setIssuer(this.jwtConfigProperties.getIssuer()) // 設置簽發者.setSubject(this.objectMapper.writeValueAsString(subject)) // 所要傳遞的數據轉為JSON.signWith(this.signatureAlgorithm, this.generalKey()) // 獲取簽名算法.setExpiration(expireDate); // 配置失效時間} catch (JsonProcessingException e) {e.printStackTrace();}return builder.compact(); // 創建Token}@Overridepublic Jws<Claims> parseToken(String token) throws JwtException {if (this.verifyToken(token)) { // 只有正確的時候再進行Token解析Jws<Claims> claims = Jwts.parser().setSigningKey(this.generalKey()).parseClaimsJws(token);return claims;}return null; // 解析失敗返回null}@Overridepublic boolean verifyToken(String token) {try {Jwts.parser().setSigningKey(this.generalKey()).parseClaimsJws(token).getBody();return true; // 沒有異常就返回true} catch (Exception e) {}return false;}@Overridepublic String refreshToken(String token) {if (this.verifyToken(token)) {Jws<Claims> jws = this.parseToken(token); // 解析Token數據return this.createToken(jws.getBody().getId(), this.objectMapper.readValue(jws.getBody().getSubject(), Map.class));}return null;} }10、【yootk-starter-jwt子模塊】定義一個加密的屬性配置
package com.yootk.jwt.config;import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties;@Data @ConfigurationProperties(prefix = "yootk.security.config.password.encrypt") // 配置前綴 public class EncryptConfigProperties { // 加密配置屬性private Integer repeat; // 定義重復的次數private String salt; // 加密的鹽值 }11、【yootk-starter-jwt子模塊】既然所有的用戶的信息都要保存在數據表里面,那么就需要進行密碼的加密處理。
package com.yootk.jwt.service;public interface IEncryptService { // 密碼加密public String getEncryptPassword(String password); // 得到一個加密后的密碼 }12、【yootk-starter-jwt子模塊】定義具體的實現子類
package com.yootk.jwt.service.impl;import com.yootk.jwt.config.EncryptConfigProperties; import com.yootk.jwt.service.IEncryptService; import org.springframework.beans.factory.annotation.Autowired;import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Base64;public class EncryptServiceImpl implements IEncryptService {@Autowiredprivate EncryptConfigProperties encryptConfigProperties; // 屬性配置private static MessageDigest MD5_DIGEST; // MD5加密處理private static final Base64.Encoder BASE64_ENCODER = Base64.getEncoder(); // 加密器static { // 初始化操作try {MD5_DIGEST = MessageDigest.getInstance("MD5");} catch (NoSuchAlgorithmException e) {e.printStackTrace();}}@Overridepublic String getEncryptPassword(String password) {String saltPassword = "{" + this.encryptConfigProperties.getSalt() + "}" + password;for (int x = 0 ; x < this.encryptConfigProperties.getRepeat(); x ++) {saltPassword = BASE64_ENCODER.encodeToString(MD5_DIGEST.digest(saltPassword.getBytes()));}return saltPassword;} }13、【yootk-starter-jwt子模塊】創建JWT自動配置類
package com.yootk.jwt.autoconfig;import com.yootk.jwt.config.EncryptConfigProperties; import com.yootk.jwt.config.JWTConfigProperties; import com.yootk.jwt.service.IEncryptService; import com.yootk.jwt.service.ITokenService; import com.yootk.jwt.service.impl.EncryptServiceImpl; import com.yootk.jwt.service.impl.TokenServiceImpl; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;@Configuration @EnableConfigurationProperties({JWTConfigProperties.class, EncryptConfigProperties.class}) // 配置注入屬性 public class JWTAutoConfiguration {@Bean("tokenService")public ITokenService getTokenServiceBean() {return new TokenServiceImpl();}@Bean("encryptService")public IEncryptService getEncryptServiceBean() {return new EncryptServiceImpl();} }14、【yootk-starter-jwt子模塊】在“src/main/resources”目錄之中創建“META-INF/spring.factories”配置文件
org.springframework.boot.autoconfigure.EnableAutoConfiguration= com.yootk.jwt.autoconfig.JWTAutoConfiguration15、【yootk-starter-jwt子模塊】模塊開發完成之后來進行編譯: gradle build
16、【yootk-starter-jwt子模塊】既然已經成功的實現了模塊的編譯處理,隨后就需要進行一些環境上的測試,創建SpringBoot的配置文件: application.yml
yootk:security:config:jwt:sign: muyanissuer: Muyansecret: yootkexpire: 100 # 單位:秒password:encrypt:repeat: 5salt: yootkspring:application:name: JWT-TEST測試
17、【yootk-starter-jwt子模塊】創建一個程序啟動的主類,主要是進行測試用的
package com.yootk.jwt;import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication public class StartJWTConfiguration {public static void main(String[] args) {SpringApplication.run(StartJWTConfiguration.class, args);} }18、【yootk-starter-jwt子模塊】編寫測試程序進行TokenService測試
package com.yootk.test;import com.yootk.jwt.StartJWTConfiguration; import com.yootk.jwt.service.ITokenService; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jws; import io.jsonwebtoken.JwsHeader; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.test.context.web.WebAppConfiguration;import java.util.HashMap; import java.util.Map; import java.util.UUID;@ExtendWith(SpringExtension.class) @WebAppConfiguration @SpringBootTest(classes = StartJWTConfiguration.class) // 隨便寫的測試類 public class TestTokenService { // 代碼測試@Autowiredprivate ITokenService tokenService;private String jwt = "eyJhdXRob3IiOiLmnY7lhbTljY4iLCJtb2R1bGUiOiJKV1QtVEVTVCIsImFsZyI6IkhTMjU2In0.eyJtc2ciOiLkuJbnlYzkuIrniIblj6_niLHnmoTogIHluIgg4oCU4oCUIOeIhuWPr-eIseeahOWwj-adjuiAgeW4iCIsInN1YiI6IntcInJpZHNcIjpcIlVTRVI7QURNSU47REVQVDtFTVA7Uk9MRVwiLFwibmFtZVwiOlwi5rKQ6KiA56eR5oqAIOKAlOKAlCDmnY7lhbTljY5cIixcIm1pZFwiOlwibXV5YW5cIn0iLCJzaXRlIjoid3d3Lnlvb3RrLmNvbSIsImlzcyI6Ik11eWFuWW9vdGsiLCJleHAiOjE2MzM2NzE3NjcsImlhdCI6MTYzMzU3MTc2NywibmljZSI6Ikdvb2QgR29vZCBHb29kIiwianRpIjoieW9vdGstMDgwNGI3NDQtNTBjZC00NjI2LTgzNmEtNjA1MmFiZWMyYzQ4In0.O71QGGPtWYwL7Tyhx8iOLQFAWc1DmVlAS4i0N99OJJk"; // 測試解析使用的@Testpublic void testCreateToken() {Map<String, Object> map = new HashMap<>(); // 測試生成map.put("mid", "muyan");map.put("name", "沐言科技 —— 李興華");map.put("rids", "USER;ADMIN;DEPT;EMP;ROLE"); // 用戶角色信息String id = "yootk-" + UUID.randomUUID(); // 隨意生成一個JWT-ID數據System.out.println(this.tokenService.createToken(id, map));}@Testpublic void testParseToken() { // 解析Token數據內容Jws<Claims> jws = this.tokenService.parseToken(jwt);System.out.println("JWT簽名數據:" + jws.getSignature()); // 獲取簽名數據JwsHeader headers = jws.getHeader(); // 獲取頭信息headers.forEach((headerName, headerValue) -> {System.out.println("【JWT頭信息】" + headerName + " = " + headerValue);});Claims claims = jws.getBody();claims.forEach((bodyName, bodyValue) -> {System.out.println("【JWT數據】" + bodyName + " = " + bodyValue);});} @Testpublic void testVerifyJWT() {System.out.println("【JWT數據驗證】" + this.tokenService.verifyToken(jwt));}@Testpublic void testRefreshJWT() {System.out.println("【JWT數據刷新】" + this.tokenService.refreshToken(jwt));} }19、【yootk-starter-jwt子模塊】隨后進行密碼加密的測試
package com.yootk.test;import com.yootk.jwt.StartJWTConfiguration; import com.yootk.jwt.service.IEncryptService; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.test.context.web.WebAppConfiguration;@ExtendWith(SpringExtension.class) @WebAppConfiguration @SpringBootTest(classes = StartJWTConfiguration.class) // 隨便寫的測試類 public class TestEncryptService {@Autowiredprivate IEncryptService encryptService;@Testpublic void testCreatePassword() {System.out.println(this.encryptService.getEncryptPassword("hello"));} }此時已經成功的開發出了一套完整的與JWT相關的應用組件模塊,使用的時候直接導入依賴庫即可應用。
總結
- 上一篇: 外观够靓功能够多,AOC的卢839一体机
- 下一篇: 电脑无线网无法使用电脑连接无线网无法使用