diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md index f6f6c08d..79c3586d 100755 --- a/docs/SUMMARY.md +++ b/docs/SUMMARY.md @@ -233,5 +233,8 @@ * [AIO封装-AioServer和AioClient](socket/AIO封装-AioServer和AioClient.md) * 🍩JWT(Hutool-jwt) * [概述](jwt/概述.md) + * [JWT工具-JWTUtil](jwt/JWT工具-JWTUtil.md) + * [JWT签名工具-JWTSignerUtil](jwt/JWT签名工具-JWTSignerUtil.md) + * [JWT验证-JWTValidator](jwt/JWT验证-JWTValidator.md) * 😄[Hutool的朋友们](Hutool的朋友们.md) * 💳[捐赠使用公开](捐赠使用公开.md) \ No newline at end of file diff --git a/docs/json/JSONArray.md b/docs/json/JSONArray.md index 83be476d..1e194762 100755 --- a/docs/json/JSONArray.md +++ b/docs/json/JSONArray.md @@ -1,14 +1,14 @@ -JSONArray -=== - ## 介绍 + 在JSON中,JSONArray代表一个数组,使用中括号包围,每个元素使用逗号隔开。一个JSONArray类似于这样: ```json ["value1","value2","value3"] ``` ## 使用 + ### 创建 + ```java //方法1 JSONArray array = JSONUtil.createArray(); @@ -23,9 +23,95 @@ array.add("value3"); array.toString(); ``` -### 转换 +### 从Bean列表解析 + +先定义bean: + +```java +@Data +public class KeyBean{ + private String akey; + private String bkey; +} +``` + +```java +KeyBean b1 = new KeyBean(); +b1.setAkey("aValue1"); +b1.setBkey("bValue1"); +KeyBean b2 = new KeyBean(); +b2.setAkey("aValue2"); +b2.setBkey("bValue2"); + +ArrayList list = CollUtil.newArrayList(b1, b2); + +// [{"akey":"aValue1","bkey":"bValue1"},{"akey":"aValue2","bkey":"bValue2"}] +JSONArray jsonArray = JSONUtil.parseArray(list); + +// aValue1 +jsonArray.getJSONObject(0).getStr("akey"); +``` + +### 从JSON字符串解析 + ```java String jsonStr = "[\"value1\", \"value2\", \"value3\"]"; JSONArray array = JSONUtil.parseArray(jsonStr); ``` +### 转换为bean的List + +先定义一个Bean + +```java +@Data +static class User { + private Integer id; + private String name; +} +``` + +```java +String jsonArr = "[{\"id\":111,\"name\":\"test1\"},{\"id\":112,\"name\":\"test2\"}]"; +JSONArray array = JSONUtil.parseArray(jsonArr); + +List userList = JSONUtil.toList(array, User.class); + +// 111 +userList.get(0).getId(); +``` + +### 转换为Dict的List + +Dict是Hutool定义的特殊Map,提供了以字符串为key的Map功能,并提供getXXX方法,转换也类似: + +```java +String jsonArr = "[{\"id\":111,\"name\":\"test1\"},{\"id\":112,\"name\":\"test2\"}]"; +JSONArray array = JSONUtil.parseArray(jsonArr); + +List list = JSONUtil.toList(array, Dict.class); + +// 111 +list.get(0).getInt("id"); +``` + +### 转换为数组 + +```java +String jsonArr = "[{\"id\":111,\"name\":\"test1\"},{\"id\":112,\"name\":\"test2\"}]"; +JSONArray array = JSONUtil.parseArray(jsonArr); + +User[] list = array.toArray(new User[0]); +``` + +### JSON路径 + +如果JSON的层级特别深,那么获取某个值就变得非常麻烦,代码也很臃肿,Hutool提供了`getByPath`方法可以通过表达式获取JSON中的值。 + +```java +String jsonStr = "[{\"id\": \"1\",\"name\": \"a\"},{\"id\": \"2\",\"name\": \"b\"}]"; +final JSONArray jsonArray = JSONUtil.parseArray(jsonStr); + +// b +jsonArray.getByPath("[1].name"); +``` \ No newline at end of file diff --git a/docs/json/JSONObject.md b/docs/json/JSONObject.md index 0eb27f99..8ca6e9fe 100755 --- a/docs/json/JSONObject.md +++ b/docs/json/JSONObject.md @@ -134,18 +134,4 @@ json.setDateFormat("yyyy-MM-dd HH:mm:ss"); } ] } -``` - -同样,`JSONUtil`还可以支持以下对象转为JSONObject对象: -- String对象 -- Java Bean对象 -- Map对象 -- XML字符串(使用`JSONUtil.parseFromXml`方法) -- ResourceBundle(使用`JSONUtil.parseFromResourceBundle`) - -`JSONUtil`还提供了JSONObject对象转换为其它对象的方法: -- toJsonStr 转换为JSON字符串 -- toXmlStr 转换为XML字符串 -- toBean 转换为JavaBean -- - +``` \ No newline at end of file diff --git a/docs/json/JSONUtil.md b/docs/json/JSONUtil.md index 945059d4..7f765142 100755 --- a/docs/json/JSONUtil.md +++ b/docs/json/JSONUtil.md @@ -6,9 +6,99 @@ JSONUtil `JSONUtil`是针对JSONObject和JSONArray的静态快捷方法集合,在之前的章节我们已经介绍了一些工具方法,在本章节我们将做一些补充。 ## 使用 -### parseXXX和toXXX -这两种方法主要是针对JSON和其它对象之间的转换。 +### JSON字符串创建 + +`JSONUtil.toJsonStr`可以将任意对象(Bean、Map、集合等)直接转换为JSON字符串。 +如果对象是有序的Map等对象,则转换后的JSON字符串也是有序的。 + +```java +SortedMap sortedMap = new TreeMap() { + private static final long serialVersionUID = 1L; + { + put("attributes", "a"); + put("b", "b"); + put("c", "c"); +}}; + +JSONUtil.toJsonStr(sortedMap); +``` + +结果: + +```json +{"attributes":"a","b":"b","c":"c"} +``` + +如果我们想获得格式化后的JSON,则: + +```java +JSONUtil.toJsonPrettyStr(sortedMap); +``` + +结果: + +```json +{ + "attributes": "a", + "b": "b", + "c": "c" +} +``` + +### JSON字符串解析 + +```java +String html = "{\"name\":\"Something must have been changed since you leave\"}"; +JSONObject jsonObject = JSONUtil.parseObj(html); +jsonObject.getStr("name"); +``` + +### XML字符串转换为JSON + +```java +String s = "123456aa1"; +JSONObject json = JSONUtil.parseFromXml(s); + +json.get("sfzh"); +json.get("name"); +``` + +### JSON转换为XML + +```java +final JSONObject put = JSONUtil.createObj() + .set("aaa", "你好") + .set("键2", "test"); + +// 你好<键2>test +final String s = JSONUtil.toXmlStr(put); +``` + +### JSON转Bean + +我们先定义两个较为复杂的Bean(包含泛型) + +```java +@Data +public class ADT { + private List BookingCode; +} + +@Data +public class Price { + private List> ADT; +} +``` + +```java +String json = "{\"ADT\":[[{\"BookingCode\":[\"N\",\"N\"]}]]}"; + +Price price = JSONUtil.toBean(json, Price.class); + +// +price.getADT().get(0).get(0).getBookingCode().get(0); +``` ### readXXX @@ -18,6 +108,7 @@ JSONUtil - readJSONArray ### 其它方法 + 除了上面中常用的一些方法,JSONUtil还提供了一些JSON辅助方法: - quote 对所有双引号做转义处理(使用双反斜杠做转义) - wrap 包装对象,可以将普通任意对象转为JSON对象 diff --git "a/docs/json/\346\246\202\350\277\260.md" "b/docs/json/\346\246\202\350\277\260.md" index bac97bf5..bc878655 100755 --- "a/docs/json/\346\246\202\350\277\260.md" +++ "b/docs/json/\346\246\202\350\277\260.md" @@ -1,9 +1,3 @@ -概述 -=== - -## Hutool-json ------------------------- - ## 为何集成 JSON在现在的开发中做为跨平台的数据交换格式已经慢慢有替代XML的趋势(比如RestFul规范),我想大家在开发中对外提供接口也越来越多的使用JSON格式。 diff --git "a/docs/jwt/JWT\345\267\245\345\205\267-JWTUtil.md" "b/docs/jwt/JWT\345\267\245\345\205\267-JWTUtil.md" new file mode 100644 index 00000000..e07d029e --- /dev/null +++ "b/docs/jwt/JWT\345\267\245\345\205\267-JWTUtil.md" @@ -0,0 +1,46 @@ +## 介绍 + +我们可以通过`JWT`实现链式创建JWT对象或JWT字符串,Hutool同样提供了一些快捷方法封装在`JWTUtil`中。主要包括: + +- JWT创建 +- JWT解析 +- JWT验证 + +## 使用 + +- JWT创建 + +```java +Map map = new HashMap() { + private static final long serialVersionUID = 1L; + { + put("uid", Integer.parseInt("123")); + put("expire_time", System.currentTimeMillis() + 1000 * 60 * 60 * 24 * 15); + } +}; + +JWTUtil.createToken(map, "1234".getBytes()); +``` + +- JWT解析 + +```java +String rightToken = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9." + + "eyJzdWIiOiIxMjM0NTY3ODkwIiwiYWRtaW4iOnRydWUsIm5hbWUiOiJsb29seSJ9." + + "U2aQkC2THYV9L0fTN-yBBI7gmo5xhmvMhATtu8v0zEA"; + +final JWT jwt = JWTUtil.parseToken(rightToken); + +jwt.getHeader(JWTHeader.TYPE); +jwt.getPayload("sub"); +``` + +- JWT验证 + +```java +String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9." + + "eyJ1c2VyX25hbWUiOiJhZG1pbiIsInNjb3BlIjpbImFsbCJdLCJleHAiOjE2MjQwMDQ4MjIsInVzZXJJZCI6MSwiYXV0aG9yaXRpZXMiOlsiUk9MRV_op5LoibLkuozlj7ciLCJzeXNfbWVudV8xIiwiUk9MRV_op5LoibLkuIDlj7ciLCJzeXNfbWVudV8yIl0sImp0aSI6ImQ0YzVlYjgwLTA5ZTctNGU0ZC1hZTg3LTVkNGI5M2FhNmFiNiIsImNsaWVudF9pZCI6ImhhbmR5LXNob3AifQ." + + "aixF1eKlAKS_k3ynFnStE7-IRGiD5YaqznvK2xEjBew"; + +JWTUtil.verify(token, "123456".getBytes()); +``` \ No newline at end of file diff --git "a/docs/jwt/JWT\347\255\276\345\220\215\345\267\245\345\205\267-JWTSignerUtil.md" "b/docs/jwt/JWT\347\255\276\345\220\215\345\267\245\345\205\267-JWTSignerUtil.md" new file mode 100644 index 00000000..b00fdab4 --- /dev/null +++ "b/docs/jwt/JWT\347\255\276\345\220\215\345\267\245\345\205\267-JWTSignerUtil.md" @@ -0,0 +1,61 @@ +## 介绍 + +JWT签名算法比较多,主要分为非对称算法和对称算法,支持的算法定义在`SignAlgorithm`中。 + +### 对称签名 + +- HS256(HmacSHA256) +- HS384(HmacSHA384) +- HS512(HmacSHA512) + +### 非对称签名 + +- RS256(SHA256withRSA) +- RS384(SHA384withRSA) +- RS512(SHA512withRSA) + +- ES256(SHA256withECDSA) +- ES384(SHA384withECDSA) +- ES512(SHA512withECDSA) + +### 依赖于BounyCastle的算法 + +- PS256(SHA256WithRSA/PSS) +- PS384(SHA384WithRSA/PSS) +- PS512(SHA512WithRSA/PSS) + +## 使用 + +### 创建预定义算法签名器 + +`JWTSignerUtil`中预定义了一些算法的签名器的创建方法,如创建HS256的签名器: + +```java +final JWTSigner signer = JWTSignerUtil.hs256("123456".getBytes()); +JWT jwt = JWT.create().setSigner(signer); +``` + +### 创建自定义算法签名器 + +通过`JWTSignerUtil.createSigner`即可通过动态传入`algorithmId`创建对应的签名器,如我们如果需要实现`ps256`算法,则首先引入`bcprov-jdk15to18`包: + +```xml + + org.bouncycastle + bcprov-jdk15to18 + 1.69 + +``` + +再创建对应签名器即可: + +```java +String id = "ps256"; +final JWTSigner signer = JWTSignerUtil.createSigner(id, KeyUtil.generateKeyPair(AlgorithmUtil.getAlgorithm(id))); + +JWT jwt = JWT.create().setSigner(signer); +``` + +### 自行实现算法签名器 + +`JWTSigner`接口是一个通用的签名器接口,如果想实现自定义算法,实现此接口即可。 \ No newline at end of file diff --git "a/docs/jwt/JWT\351\252\214\350\257\201-JWTValidator.md" "b/docs/jwt/JWT\351\252\214\350\257\201-JWTValidator.md" new file mode 100644 index 00000000..f8598fc7 --- /dev/null +++ "b/docs/jwt/JWT\351\252\214\350\257\201-JWTValidator.md" @@ -0,0 +1,48 @@ +## 介绍 + +由于`JWT.verify`,只能验证JWT Token的签名是否有效,其他payload字段验证都可以使用`JWTValidator`完成。 + +## 使用 + +### 验证算法 + +算法的验证包括两个方面 + +1. 验证header中算法ID和提供的算法ID是否一致 +2. 调用`JWT.verify`验证token是否正确 + +```java +// 创建JWT Token +final String token = JWT.create() + .setNotBefore(DateUtil.date()) + .setKey("123456".getBytes()) + .sign(); + +// 验证算法 +JWTValidator.of(token).validateAlgorithm(JWTSignerUtil.hs256("123456".getBytes())); +``` + +### 验证时间 + +对于时间类载荷,有单独的验证方法,主要包括: + +- 生效时间(`JWTPayload#NOT_BEFORE`)不能晚于当前时间 +- 失效时间(`JWTPayload#EXPIRES_AT`)不能早于当前时间 +- 签发时间(`JWTPayload#ISSUED_AT`)不能晚于当前时间 + +一般时间线是: + +(签发时间)---------(生效时间)---------(**当前时间**)---------(失效时间) + +> 签发时间和生效时间一般没有前后要求,都早于当前时间即可。 + +```java +final String token = JWT.create() + // 设置签发时间 + .setIssuedAt(DateUtil.date()) + .setKey("123456".getBytes()) + .sign(); + +// 由于只定义了签发时间,因此只检查签发时间 +JWTValidator.of(token).validateDate(DateUtil.date()); +``` \ No newline at end of file diff --git "a/docs/jwt/\346\246\202\350\277\260.md" "b/docs/jwt/\346\246\202\350\277\260.md" index 1b007514..6f73eabe 100644 --- "a/docs/jwt/\346\246\202\350\277\260.md" +++ "b/docs/jwt/\346\246\202\350\277\260.md" @@ -133,4 +133,6 @@ String token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJNb0xpIiwiZXhwIjo byte[] key = "1234567890".getBytes(); boolean validate = JWT.of(token).setKey(key).validate(0); -``` \ No newline at end of file +``` + +其他自定义详细验证见`JWT验证-JWTValidator`章节。 \ No newline at end of file