From 33076971fe449fdf4c0b6f6d37c2004d000baf21 Mon Sep 17 00:00:00 2001 From: "yadong.zhang" Date: Tue, 30 Jul 2019 09:12:28 +0800 Subject: [PATCH] =?UTF-8?q?:bookmark:=20v1.9.3,=E8=AF=A6=E7=BB=86=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E5=86=85=E5=AE=B9=E5=8F=82=E8=80=83update.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 17 ++- pom.xml | 2 +- .../java/me/zhyd/oauth/cache/AuthCache.java | 50 ++++++ .../zhyd/oauth/cache/AuthCacheScheduler.java | 39 +++++ .../me/zhyd/oauth/cache/AuthDefaultCache.java | 144 ++++++++++++++++++ .../me/zhyd/oauth/cache/AuthStateCache.java | 51 +++++++ .../me/zhyd/oauth/model/AuthCallback.java | 11 ++ .../zhyd/oauth/request/AuthAlipayRequest.java | 2 +- .../zhyd/oauth/request/AuthBaiduRequest.java | 2 +- .../zhyd/oauth/request/AuthCodingRequest.java | 2 +- .../oauth/request/AuthDefaultRequest.java | 13 +- .../oauth/request/AuthDingTalkRequest.java | 2 +- .../zhyd/oauth/request/AuthDouyinRequest.java | 2 +- .../zhyd/oauth/request/AuthGoogleRequest.java | 2 +- .../oauth/request/AuthLinkedinRequest.java | 2 +- .../me/zhyd/oauth/request/AuthMiRequest.java | 2 +- .../oauth/request/AuthMicrosoftRequest.java | 2 +- .../oauth/request/AuthPinterestRequest.java | 2 +- .../me/zhyd/oauth/request/AuthRequest.java | 4 +- .../request/AuthStackOverflowRequest.java | 2 +- .../zhyd/oauth/request/AuthTaobaoRequest.java | 2 +- .../request/AuthTencentCloudRequest.java | 2 +- .../oauth/request/AuthToutiaoRequest.java | 2 +- .../zhyd/oauth/request/AuthWeChatRequest.java | 2 +- .../me/zhyd/oauth/utils/AuthStateUtils.java | 19 +++ .../java/me/zhyd/oauth/utils/StringUtils.java | 26 +++- .../java/me/zhyd/oauth/utils/UrlBuilder.java | 2 +- .../java/me/zhyd/oauth/utils/UuidUtils.java | 65 ++++++++ .../java/me/zhyd/oauth/AuthRequestTest.java | 38 +++++ .../zhyd/oauth/cache/AuthStateCacheTest.java | 32 ++++ .../me/zhyd/oauth/utils/UrlBuilderTest.java | 27 ++++ .../me/zhyd/oauth/utils/UuidUtilsTest.java | 13 ++ update.md | 8 + 33 files changed, 561 insertions(+), 30 deletions(-) create mode 100644 src/main/java/me/zhyd/oauth/cache/AuthCache.java create mode 100644 src/main/java/me/zhyd/oauth/cache/AuthCacheScheduler.java create mode 100644 src/main/java/me/zhyd/oauth/cache/AuthDefaultCache.java create mode 100644 src/main/java/me/zhyd/oauth/cache/AuthStateCache.java create mode 100644 src/main/java/me/zhyd/oauth/utils/AuthStateUtils.java create mode 100644 src/main/java/me/zhyd/oauth/utils/UuidUtils.java create mode 100644 src/test/java/me/zhyd/oauth/cache/AuthStateCacheTest.java create mode 100644 src/test/java/me/zhyd/oauth/utils/UuidUtilsTest.java diff --git a/README.md b/README.md index 8e51b9f..9e563f8 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@

- + @@ -15,7 +15,7 @@ - +

@@ -76,7 +76,7 @@ JustAuth,如你所见,它仅仅是一个**第三方授权登录**的**工具 me.zhyd.oauth JustAuth - 1.9.2 + 1.9.3 ``` - 调用api @@ -91,14 +91,19 @@ AuthRequest authRequest = new AuthGiteeRequest(AuthConfig.builder() // 生成授权页面 authRequest.authorize(); // 授权登录后会返回code(auth_code(仅限支付宝))、state,1.8.0版本后,可以用AuthCallback类作为回调接口的参数 +// 1.9.3版本后 如果需要验证state,可以在login之前调用{@see AuthCallback#checkState}方法校验state合法性 +// 注:JustAuth默认保存state的时效为3分钟,3分钟内未使用则会自动清除过期的state authRequest.login(callback); ``` -注:`1.8.0`版本后,增加了`state`参数校验,用于防止[CSRF](https://zh.wikipedia.org/wiki/%E8%B7%A8%E7%AB%99%E8%AF%B7%E6%B1%82%E4%BC%AA%E9%80%A0)。强烈建议,保证单次流程内`state`的唯一性,且每个`state`只可用一次。 - **配套Demo**: - [Springboot版](https://gitee.com/yadong.zhang/JustAuth-demo) -- [jFinal版](https://github.com/zhangyd-c/jfinal-justauth-demo) +- [jFinal版](https://github.com/xkcoding/jfinal-justauth-demo) +- [ActFramework版](https://github.com/xkcoding/act-justauth-demo) + +**扩展工具** + +- [justauth-spring-boot-starter](https://github.com/xkcoding/justauth-spring-boot-starter): Spring Boot 集成 JustAuth 的最佳实践 具体的例子可以参考: diff --git a/pom.xml b/pom.xml index 9752151..e466e7d 100644 --- a/pom.xml +++ b/pom.xml @@ -54,7 +54,7 @@ 2.2.1 3.7.0 true - 4.5.15 + 4.6.0 1.18.4 4.11 1.2.58 diff --git a/src/main/java/me/zhyd/oauth/cache/AuthCache.java b/src/main/java/me/zhyd/oauth/cache/AuthCache.java new file mode 100644 index 0000000..73de599 --- /dev/null +++ b/src/main/java/me/zhyd/oauth/cache/AuthCache.java @@ -0,0 +1,50 @@ +package me.zhyd.oauth.cache; + +/** + * JustAuth缓存,用来缓存State + * + * @author yadong.zhang (yadong.zhang0415(a)gmail.com) + * @since 1.9.3 + */ +public interface AuthCache { + + /** + * 设置缓存 + * + * @param key 缓存KEY + * @param value 缓存内容 + */ + void set(String key, String value); + + /** + * 设置缓存,指定过期时间 + * + * @param key 缓存KEY + * @param value 缓存内容 + * @param timeout 指定缓存过期时间(毫秒) + */ + void set(String key, String value, long timeout); + + /** + * 获取缓存 + * + * @param key 缓存KEY + * @return 缓存内容 + */ + String get(String key); + + /** + * 是否存在key,如果对应key的value值已过期,也返回false + * + * @param key 缓存KEY + * @return true:存在key,并且value没过期;false:key不存在或者已过期 + */ + boolean containsKey(String key); + + /** + * 清理过期的缓存 + */ + default void pruneCache() { + } + +} diff --git a/src/main/java/me/zhyd/oauth/cache/AuthCacheScheduler.java b/src/main/java/me/zhyd/oauth/cache/AuthCacheScheduler.java new file mode 100644 index 0000000..fbdfa88 --- /dev/null +++ b/src/main/java/me/zhyd/oauth/cache/AuthCacheScheduler.java @@ -0,0 +1,39 @@ +package me.zhyd.oauth.cache; + +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * 缓存调度器 + * + * @author yadong.zhang (yadong.zhang0415(a)gmail.com) + * @since 1.9.3 + */ +public enum AuthCacheScheduler { + + INSTANCE; + + private AtomicInteger cacheTaskNumber = new AtomicInteger(1); + private ScheduledExecutorService scheduler; + + AuthCacheScheduler() { + create(); + } + + private void create() { + this.shutdown(); + this.scheduler = new ScheduledThreadPoolExecutor(10, r -> new Thread(r, String.format("JustAuth-Task-%s", cacheTaskNumber.getAndIncrement()))); + } + + private void shutdown() { + if (null != scheduler) { + this.scheduler.shutdown(); + } + } + + public void schedule(Runnable task, long delay) { + this.scheduler.scheduleAtFixedRate(task, delay, delay, TimeUnit.MILLISECONDS); + } +} diff --git a/src/main/java/me/zhyd/oauth/cache/AuthDefaultCache.java b/src/main/java/me/zhyd/oauth/cache/AuthDefaultCache.java new file mode 100644 index 0000000..6da6695 --- /dev/null +++ b/src/main/java/me/zhyd/oauth/cache/AuthDefaultCache.java @@ -0,0 +1,144 @@ +package me.zhyd.oauth.cache; + +import lombok.Getter; +import lombok.Setter; + +import java.io.Serializable; +import java.util.Iterator; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +/** + * 默认的缓存实现 + * + * @author yadong.zhang (yadong.zhang0415(a)gmail.com) + * @since 1.9.3 + */ +public class AuthDefaultCache implements AuthCache { + + /** + * 默认缓存过期时间:3分钟 + * 鉴于授权过程中,根据个人的操作习惯,或者授权平台的不同(google等),每个授权流程的耗时也有差异,不过单个授权流程一般不会太长 + * 本缓存工具默认的过期时间设置为3分钟,即程序默认认为3分钟内的授权有效,超过3分钟则默认失效,失效后删除 + */ + private static final long DEF_TIMEOUT = 3 * 60 * 1000; + /** + * state cache + */ + private static Map stateCache = new ConcurrentHashMap<>(); + private final ReentrantReadWriteLock cacheLock = new ReentrantReadWriteLock(true); + private final Lock writeLock = cacheLock.writeLock(); + private final Lock readLock = cacheLock.readLock(); + + public AuthDefaultCache() { + this.schedulePrune(DEF_TIMEOUT); + } + + /** + * 设置缓存 + * + * @param key 缓存KEY + * @param value 缓存内容 + */ + @Override + public void set(String key, String value) { + set(key, value, DEF_TIMEOUT); + } + + /** + * 设置缓存 + * + * @param key 缓存KEY + * @param value 缓存内容 + * @param timeout 指定缓存过期时间(毫秒) + */ + @Override + public void set(String key, String value, long timeout) { + writeLock.lock(); + try { + stateCache.put(key, new CacheState(value, timeout)); + } finally { + writeLock.unlock(); + } + } + + /** + * 获取缓存 + * + * @param key 缓存KEY + * @return 缓存内容 + */ + @Override + public String get(String key) { + readLock.lock(); + try { + CacheState cacheState = stateCache.get(key); + if (null == cacheState || cacheState.isExpired()) { + return null; + } + return cacheState.getState(); + } finally { + readLock.unlock(); + } + } + + /** + * 是否存在key,如果对应key的value值已过期,也返回false + * + * @param key 缓存KEY + * @return true:存在key,并且value没过期;false:key不存在或者已过期 + */ + @Override + public boolean containsKey(String key) { + readLock.lock(); + try { + CacheState cacheState = stateCache.get(key); + return null != cacheState && !cacheState.isExpired(); + } finally { + readLock.unlock(); + } + } + + /** + * 清理过期的缓存 + */ + @Override + public void pruneCache() { + Iterator values = stateCache.values().iterator(); + CacheState cacheState; + while (values.hasNext()) { + cacheState = values.next(); + if (cacheState.isExpired()) { + values.remove(); + } + } + } + + /** + * 定时清理 + * + * @param delay 间隔时长,单位毫秒 + */ + public void schedulePrune(long delay) { + AuthCacheScheduler.INSTANCE.schedule(this::pruneCache, delay); + } + + @Getter + @Setter + private class CacheState implements Serializable { + private String state; + private long expire; + + CacheState(String state, long expire) { + this.state = state; + // 实际过期时间等于当前时间加上有效期 + this.expire = System.currentTimeMillis() + expire; + } + + boolean isExpired() { + return System.currentTimeMillis() > this.expire; + } + } +} diff --git a/src/main/java/me/zhyd/oauth/cache/AuthStateCache.java b/src/main/java/me/zhyd/oauth/cache/AuthStateCache.java new file mode 100644 index 0000000..e667829 --- /dev/null +++ b/src/main/java/me/zhyd/oauth/cache/AuthStateCache.java @@ -0,0 +1,51 @@ +package me.zhyd.oauth.cache; + +/** + * @author yadong.zhang (yadong.zhang0415(a)gmail.com) + * @version 1.0 + * @since 1.8 + */ +public class AuthStateCache { + private static AuthCache authCache = new AuthDefaultCache(); + + /** + * 存入缓存 + * + * @param key 缓存key + * @param value 缓存内容 + */ + public static void cache(String key, String value) { + authCache.set(key, value); + } + + /** + * 存入缓存 + * + * @param key 缓存key + * @param value 缓存内容 + * @param timeout 指定缓存过期时间(毫秒) + */ + public static void cache(String key, String value, long timeout) { + authCache.set(key, value, timeout); + } + + /** + * 获取缓存内容 + * + * @param key 缓存key + * @return 缓存内容 + */ + public static String get(String key) { + return authCache.get(key); + } + + /** + * 是否存在key,如果对应key的value值已过期,也返回false + * + * @param key 缓存key + * @return true:存在key,并且value没过期;false:key不存在或者已过期 + */ + public static boolean containsKey(String key) { + return authCache.containsKey(key); + } +} diff --git a/src/main/java/me/zhyd/oauth/model/AuthCallback.java b/src/main/java/me/zhyd/oauth/model/AuthCallback.java index 810ebea..4a6fbee 100644 --- a/src/main/java/me/zhyd/oauth/model/AuthCallback.java +++ b/src/main/java/me/zhyd/oauth/model/AuthCallback.java @@ -2,6 +2,7 @@ package me.zhyd.oauth.model; import lombok.Getter; import lombok.Setter; +import me.zhyd.oauth.cache.AuthStateCache; /** * 授权回调时的参数类 @@ -27,4 +28,14 @@ public class AuthCallback { * 访问AuthorizeUrl后回调时带的参数state,用于和请求AuthorizeUrl前的state比较,防止CSRF攻击 */ private String state; + + /** + * 内置的检验state合法性的方法 + * + * @return true: state正常;false:state不正常,可能授权时间过长导致state失效 + * @since 1.9.3 + */ + public boolean checkState() { + return AuthStateCache.containsKey(this.state); + } } diff --git a/src/main/java/me/zhyd/oauth/request/AuthAlipayRequest.java b/src/main/java/me/zhyd/oauth/request/AuthAlipayRequest.java index f3bc002..5f8fbbf 100644 --- a/src/main/java/me/zhyd/oauth/request/AuthAlipayRequest.java +++ b/src/main/java/me/zhyd/oauth/request/AuthAlipayRequest.java @@ -85,7 +85,7 @@ public class AuthAlipayRequest extends AuthDefaultRequest { } /** - * 返回带{@code state}参数的认证url,授权回调时会带上这个{@code state} + * 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state} * * @param state state 验证授权流程的参数,可以防止csrf * @return 返回授权地址 diff --git a/src/main/java/me/zhyd/oauth/request/AuthBaiduRequest.java b/src/main/java/me/zhyd/oauth/request/AuthBaiduRequest.java index 49bc962..43796fe 100644 --- a/src/main/java/me/zhyd/oauth/request/AuthBaiduRequest.java +++ b/src/main/java/me/zhyd/oauth/request/AuthBaiduRequest.java @@ -78,7 +78,7 @@ public class AuthBaiduRequest extends AuthDefaultRequest { } /** - * 返回带{@code state}参数的认证url,授权回调时会带上这个{@code state} + * 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state} * * @param state state 验证授权流程的参数,可以防止csrf * @return 返回授权地址 diff --git a/src/main/java/me/zhyd/oauth/request/AuthCodingRequest.java b/src/main/java/me/zhyd/oauth/request/AuthCodingRequest.java index cea24f9..0ff5241 100644 --- a/src/main/java/me/zhyd/oauth/request/AuthCodingRequest.java +++ b/src/main/java/me/zhyd/oauth/request/AuthCodingRequest.java @@ -70,7 +70,7 @@ public class AuthCodingRequest extends AuthDefaultRequest { } /** - * 返回带{@code state}参数的认证url,授权回调时会带上这个{@code state} + * 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state} * * @param state state 验证授权流程的参数,可以防止csrf * @return 返回授权地址 diff --git a/src/main/java/me/zhyd/oauth/request/AuthDefaultRequest.java b/src/main/java/me/zhyd/oauth/request/AuthDefaultRequest.java index 8663c20..354e3c5 100644 --- a/src/main/java/me/zhyd/oauth/request/AuthDefaultRequest.java +++ b/src/main/java/me/zhyd/oauth/request/AuthDefaultRequest.java @@ -3,6 +3,7 @@ package me.zhyd.oauth.request; import cn.hutool.http.HttpRequest; import cn.hutool.http.HttpResponse; import lombok.extern.slf4j.Slf4j; +import me.zhyd.oauth.cache.AuthStateCache; import me.zhyd.oauth.config.AuthConfig; import me.zhyd.oauth.config.AuthSource; import me.zhyd.oauth.exception.AuthException; @@ -10,6 +11,7 @@ import me.zhyd.oauth.model.*; import me.zhyd.oauth.utils.AuthChecker; import me.zhyd.oauth.utils.StringUtils; import me.zhyd.oauth.utils.UrlBuilder; +import me.zhyd.oauth.utils.UuidUtils; /** * 默认的request处理类 @@ -60,7 +62,7 @@ public abstract class AuthDefaultRequest implements AuthRequest { } /** - * 返回认证url,可自行跳转页面 + * 返回授权url,可自行跳转页面 *

* 不建议使用该方式获取授权地址,不带{@code state}的授权地址,容易受到csrf攻击。 * 建议使用{@link AuthDefaultRequest#authorize(String)}方法生成授权地址,在回调方法中对{@code state}进行校验 @@ -75,7 +77,7 @@ public abstract class AuthDefaultRequest implements AuthRequest { } /** - * 返回带{@code state}参数的认证url,授权回调时会带上这个{@code state} + * 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state} * * @param state state 验证授权流程的参数,可以防止csrf * @return 返回授权地址 @@ -150,7 +152,12 @@ public abstract class AuthDefaultRequest implements AuthRequest { * @return 返回不为null的state */ protected String getRealState(String state) { - return StringUtils.isEmpty(state) ? String.valueOf(System.currentTimeMillis()) : state; + if (StringUtils.isEmpty(state)) { + state = UuidUtils.getUUID(); + } + // 缓存state + AuthStateCache.cache(state, state); + return state; } /** diff --git a/src/main/java/me/zhyd/oauth/request/AuthDingTalkRequest.java b/src/main/java/me/zhyd/oauth/request/AuthDingTalkRequest.java index 6f93286..b01eea5 100644 --- a/src/main/java/me/zhyd/oauth/request/AuthDingTalkRequest.java +++ b/src/main/java/me/zhyd/oauth/request/AuthDingTalkRequest.java @@ -57,7 +57,7 @@ public class AuthDingTalkRequest extends AuthDefaultRequest { } /** - * 返回带{@code state}参数的认证url,授权回调时会带上这个{@code state} + * 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state} * * @param state state 验证授权流程的参数,可以防止csrf * @return 返回授权地址 diff --git a/src/main/java/me/zhyd/oauth/request/AuthDouyinRequest.java b/src/main/java/me/zhyd/oauth/request/AuthDouyinRequest.java index 618e683..cdab6ae 100644 --- a/src/main/java/me/zhyd/oauth/request/AuthDouyinRequest.java +++ b/src/main/java/me/zhyd/oauth/request/AuthDouyinRequest.java @@ -88,7 +88,7 @@ public class AuthDouyinRequest extends AuthDefaultRequest { } /** - * 返回带{@code state}参数的认证url,授权回调时会带上这个{@code state} + * 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state} * * @param state state 验证授权流程的参数,可以防止csrf * @return 返回授权地址 diff --git a/src/main/java/me/zhyd/oauth/request/AuthGoogleRequest.java b/src/main/java/me/zhyd/oauth/request/AuthGoogleRequest.java index f5e8941..8af2c41 100644 --- a/src/main/java/me/zhyd/oauth/request/AuthGoogleRequest.java +++ b/src/main/java/me/zhyd/oauth/request/AuthGoogleRequest.java @@ -60,7 +60,7 @@ public class AuthGoogleRequest extends AuthDefaultRequest { } /** - * 返回带{@code state}参数的认证url,授权回调时会带上这个{@code state} + * 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state} * * @param state state 验证授权流程的参数,可以防止csrf * @return 返回授权地址 diff --git a/src/main/java/me/zhyd/oauth/request/AuthLinkedinRequest.java b/src/main/java/me/zhyd/oauth/request/AuthLinkedinRequest.java index 933e2f3..adbb7e6 100644 --- a/src/main/java/me/zhyd/oauth/request/AuthLinkedinRequest.java +++ b/src/main/java/me/zhyd/oauth/request/AuthLinkedinRequest.java @@ -181,7 +181,7 @@ public class AuthLinkedinRequest extends AuthDefaultRequest { } /** - * 返回带{@code state}参数的认证url,授权回调时会带上这个{@code state} + * 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state} * * @param state state 验证授权流程的参数,可以防止csrf * @return 返回授权地址 diff --git a/src/main/java/me/zhyd/oauth/request/AuthMiRequest.java b/src/main/java/me/zhyd/oauth/request/AuthMiRequest.java index 1d8c60b..3b241e9 100644 --- a/src/main/java/me/zhyd/oauth/request/AuthMiRequest.java +++ b/src/main/java/me/zhyd/oauth/request/AuthMiRequest.java @@ -108,7 +108,7 @@ public class AuthMiRequest extends AuthDefaultRequest { } /** - * 返回带{@code state}参数的认证url,授权回调时会带上这个{@code state} + * 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state} * * @param state state 验证授权流程的参数,可以防止csrf * @return 返回授权地址 diff --git a/src/main/java/me/zhyd/oauth/request/AuthMicrosoftRequest.java b/src/main/java/me/zhyd/oauth/request/AuthMicrosoftRequest.java index 03b8fe7..55227af 100644 --- a/src/main/java/me/zhyd/oauth/request/AuthMicrosoftRequest.java +++ b/src/main/java/me/zhyd/oauth/request/AuthMicrosoftRequest.java @@ -101,7 +101,7 @@ public class AuthMicrosoftRequest extends AuthDefaultRequest { } /** - * 返回带{@code state}参数的认证url,授权回调时会带上这个{@code state} + * 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state} * * @param state state 验证授权流程的参数,可以防止csrf * @return 返回授权地址 diff --git a/src/main/java/me/zhyd/oauth/request/AuthPinterestRequest.java b/src/main/java/me/zhyd/oauth/request/AuthPinterestRequest.java index be73c7d..31151d5 100644 --- a/src/main/java/me/zhyd/oauth/request/AuthPinterestRequest.java +++ b/src/main/java/me/zhyd/oauth/request/AuthPinterestRequest.java @@ -69,7 +69,7 @@ public class AuthPinterestRequest extends AuthDefaultRequest { } /** - * 返回带{@code state}参数的认证url,授权回调时会带上这个{@code state} + * 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state} * * @param state state 验证授权流程的参数,可以防止csrf * @return 返回授权地址 diff --git a/src/main/java/me/zhyd/oauth/request/AuthRequest.java b/src/main/java/me/zhyd/oauth/request/AuthRequest.java index 4445389..6ceca1e 100644 --- a/src/main/java/me/zhyd/oauth/request/AuthRequest.java +++ b/src/main/java/me/zhyd/oauth/request/AuthRequest.java @@ -13,7 +13,7 @@ import me.zhyd.oauth.model.AuthToken; public interface AuthRequest { /** - * 返回认证url,可自行跳转页面 + * 返回授权url,可自行跳转页面 *

* 不建议使用该方式获取授权地址,不带{@code state}的授权地址,容易受到csrf攻击。 * 建议使用{@link AuthDefaultRequest#authorize(String)}方法生成授权地址,在回调方法中对{@code state}进行校验 @@ -26,7 +26,7 @@ public interface AuthRequest { } /** - * 返回带{@code state}参数的认证url,授权回调时会带上这个{@code state} + * 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state} * * @param state state 验证授权流程的参数,可以防止csrf * @return 返回授权地址 diff --git a/src/main/java/me/zhyd/oauth/request/AuthStackOverflowRequest.java b/src/main/java/me/zhyd/oauth/request/AuthStackOverflowRequest.java index a2791c9..ab48453 100644 --- a/src/main/java/me/zhyd/oauth/request/AuthStackOverflowRequest.java +++ b/src/main/java/me/zhyd/oauth/request/AuthStackOverflowRequest.java @@ -67,7 +67,7 @@ public class AuthStackOverflowRequest extends AuthDefaultRequest { } /** - * 返回带{@code state}参数的认证url,授权回调时会带上这个{@code state} + * 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state} * * @param state state 验证授权流程的参数,可以防止csrf * @return 返回授权地址 diff --git a/src/main/java/me/zhyd/oauth/request/AuthTaobaoRequest.java b/src/main/java/me/zhyd/oauth/request/AuthTaobaoRequest.java index 6468920..3fcdfdf 100644 --- a/src/main/java/me/zhyd/oauth/request/AuthTaobaoRequest.java +++ b/src/main/java/me/zhyd/oauth/request/AuthTaobaoRequest.java @@ -54,7 +54,7 @@ public class AuthTaobaoRequest extends AuthDefaultRequest { } /** - * 返回带{@code state}参数的认证url,授权回调时会带上这个{@code state} + * 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state} * * @param state state 验证授权流程的参数,可以防止csrf * @return 返回授权地址 diff --git a/src/main/java/me/zhyd/oauth/request/AuthTencentCloudRequest.java b/src/main/java/me/zhyd/oauth/request/AuthTencentCloudRequest.java index bedd8a3..7401df8 100644 --- a/src/main/java/me/zhyd/oauth/request/AuthTencentCloudRequest.java +++ b/src/main/java/me/zhyd/oauth/request/AuthTencentCloudRequest.java @@ -70,7 +70,7 @@ public class AuthTencentCloudRequest extends AuthDefaultRequest { } /** - * 返回带{@code state}参数的认证url,授权回调时会带上这个{@code state} + * 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state} * * @param state state 验证授权流程的参数,可以防止csrf * @return 返回授权地址 diff --git a/src/main/java/me/zhyd/oauth/request/AuthToutiaoRequest.java b/src/main/java/me/zhyd/oauth/request/AuthToutiaoRequest.java index 3aea926..89926f3 100644 --- a/src/main/java/me/zhyd/oauth/request/AuthToutiaoRequest.java +++ b/src/main/java/me/zhyd/oauth/request/AuthToutiaoRequest.java @@ -64,7 +64,7 @@ public class AuthToutiaoRequest extends AuthDefaultRequest { } /** - * 返回带{@code state}参数的认证url,授权回调时会带上这个{@code state} + * 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state} * * @param state state 验证授权流程的参数,可以防止csrf * @return 返回授权地址 diff --git a/src/main/java/me/zhyd/oauth/request/AuthWeChatRequest.java b/src/main/java/me/zhyd/oauth/request/AuthWeChatRequest.java index 913fd64..cf87013 100644 --- a/src/main/java/me/zhyd/oauth/request/AuthWeChatRequest.java +++ b/src/main/java/me/zhyd/oauth/request/AuthWeChatRequest.java @@ -99,7 +99,7 @@ public class AuthWeChatRequest extends AuthDefaultRequest { } /** - * 返回带{@code state}参数的认证url,授权回调时会带上这个{@code state} + * 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state} * * @param state state 验证授权流程的参数,可以防止csrf * @return 返回授权地址 diff --git a/src/main/java/me/zhyd/oauth/utils/AuthStateUtils.java b/src/main/java/me/zhyd/oauth/utils/AuthStateUtils.java new file mode 100644 index 0000000..4570d7b --- /dev/null +++ b/src/main/java/me/zhyd/oauth/utils/AuthStateUtils.java @@ -0,0 +1,19 @@ +package me.zhyd.oauth.utils; + +/** + * AuthState工具类,默认只提供一个创建随机uuid的方法 + * + * @author yadong.zhang (yadong.zhang0415(a)gmail.com) + * @since 1.9.3 + */ +public class AuthStateUtils { + + /** + * 生成随机state,采用{@see https://github.com/lets-mica/mica}的UUID工具 + * + * @return 随机的state字符串 + */ + public static String createState() { + return UuidUtils.getUUID(); + } +} diff --git a/src/main/java/me/zhyd/oauth/utils/StringUtils.java b/src/main/java/me/zhyd/oauth/utils/StringUtils.java index de22a43..b144bdb 100644 --- a/src/main/java/me/zhyd/oauth/utils/StringUtils.java +++ b/src/main/java/me/zhyd/oauth/utils/StringUtils.java @@ -1,9 +1,11 @@ package me.zhyd.oauth.utils; +import java.nio.charset.StandardCharsets; +import java.util.concurrent.ThreadLocalRandom; + /** * @author yadong.zhang (yadong.zhang0415(a)gmail.com) - * @version 1.0 - * @since 1.8 + * @since 1.0.0 */ public class StringUtils { @@ -14,4 +16,24 @@ public class StringUtils { public static boolean isNotEmpty(String str) { return !isEmpty(str); } + + /** + * 如果给定字符串{@code str}中不包含{@code appendStr},则在{@code str}后追加{@code appendStr}; + * 如果已包含{@code appendStr},则在{@code str}后追加{@code otherwise} + * + * @param str 给定的字符串 + * @param appendStr 需要追加的内容 + * @param otherwise 当{@code appendStr}不满足时追加到{@code str}后的内容 + * @return 追加后的字符串 + */ + public static String appendIfNotContain(String str, String appendStr, String otherwise) { + if (isEmpty(str) || isEmpty(appendStr)) { + return str; + } + if (str.contains(appendStr)) { + return str.concat(otherwise); + } + return str.concat(appendStr); + } + } diff --git a/src/main/java/me/zhyd/oauth/utils/UrlBuilder.java b/src/main/java/me/zhyd/oauth/utils/UrlBuilder.java index 54faead..c16ea0c 100644 --- a/src/main/java/me/zhyd/oauth/utils/UrlBuilder.java +++ b/src/main/java/me/zhyd/oauth/utils/UrlBuilder.java @@ -71,7 +71,7 @@ public class UrlBuilder { if (MapUtil.isEmpty(this.params)) { return this.baseUrl; } - String baseUrl = StrUtil.addSuffixIfNot(this.baseUrl, "?"); + String baseUrl = StringUtils.appendIfNotContain(this.baseUrl, "?", "&"); String paramString = GlobalAuthUtil.parseMapToString(this.params, encode); return baseUrl + paramString; } diff --git a/src/main/java/me/zhyd/oauth/utils/UuidUtils.java b/src/main/java/me/zhyd/oauth/utils/UuidUtils.java new file mode 100644 index 0000000..2782750 --- /dev/null +++ b/src/main/java/me/zhyd/oauth/utils/UuidUtils.java @@ -0,0 +1,65 @@ +package me.zhyd.oauth.utils; + +import java.nio.charset.StandardCharsets; +import java.util.concurrent.ThreadLocalRandom; + +/** + * 高性能的创建UUID的工具类,{@see https://github.com/lets-mica/mica} + * + * @author yadong.zhang (yadong.zhang0415(a)gmail.com) + * @since 1.9.3 + */ +public class UuidUtils { + + /** + * All possible chars for representing a number as a String + * copy from mica:https://github.com/lets-mica/mica/blob/master/mica-core/src/main/java/net/dreamlu/mica/core/utils/NumberUtil.java#L113 + */ + private final static byte[] DIGITS = { + '0', '1', '2', '3', '4', '5', + '6', '7', '8', '9', 'a', 'b', + 'c', 'd', 'e', 'f', 'g', 'h', + 'i', 'j', 'k', 'l', 'm', 'n', + 'o', 'p', 'q', 'r', 's', 't', + 'u', 'v', 'w', 'x', 'y', 'z', + 'A', 'B', 'C', 'D', 'E', 'F', + 'G', 'H', 'I', 'J', 'K', 'L', + 'M', 'N', 'O', 'P', 'Q', 'R', + 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z' + }; + + /** + * 生成uuid,采用 jdk 9 的形式,优化性能 + * copy from mica:https://github.com/lets-mica/mica/blob/master/mica-core/src/main/java/net/dreamlu/mica/core/utils/StringUtil.java#L335 + *

+ * 关于mica uuid生成方式的压测结果,可以参考:https://github.com/lets-mica/mica-jmh/wiki/uuid + * + * @return UUID + */ + public static String getUUID() { + ThreadLocalRandom random = ThreadLocalRandom.current(); + long lsb = random.nextLong(); + long msb = random.nextLong(); + byte[] buf = new byte[32]; + formatUnsignedLong(lsb, buf, 20, 12); + formatUnsignedLong(lsb >>> 48, buf, 16, 4); + formatUnsignedLong(msb, buf, 12, 4); + formatUnsignedLong(msb >>> 16, buf, 8, 4); + formatUnsignedLong(msb >>> 32, buf, 0, 8); + return new String(buf, StandardCharsets.UTF_8); + } + + /** + * copy from mica:https://github.com/lets-mica/mica/blob/master/mica-core/src/main/java/net/dreamlu/mica/core/utils/StringUtil.java#L348 + */ + private static void formatUnsignedLong(long val, byte[] buf, int offset, int len) { + int charPos = offset + len; + int radix = 1 << 4; + int mask = radix - 1; + do { + buf[--charPos] = DIGITS[((int) val) & mask]; + val >>>= 4; + } while (charPos > offset); + } +} diff --git a/src/test/java/me/zhyd/oauth/AuthRequestTest.java b/src/test/java/me/zhyd/oauth/AuthRequestTest.java index f5d26ed..9b50f53 100644 --- a/src/test/java/me/zhyd/oauth/AuthRequestTest.java +++ b/src/test/java/me/zhyd/oauth/AuthRequestTest.java @@ -21,6 +21,8 @@ public class AuthRequestTest { // 返回授权页面,可自行跳转 authRequest.authorize("state"); // 授权登录后会返回code(auth_code(仅限支付宝))、state,1.8.0版本后,可以用AuthCallback类作为回调接口的入参 + // 1.9.3版本后 如果需要验证state,可以在login之前调用{@see AuthCallback#checkState}方法校验state合法性 + // 注:JustAuth默认保存state的时效为3分钟,3分钟内未使用则会自动清除过期的state authRequest.login(new AuthCallback()); } @@ -34,6 +36,8 @@ public class AuthRequestTest { // 返回授权页面,可自行跳转 authRequest.authorize("state"); // 授权登录后会返回code(auth_code(仅限支付宝))、state,1.8.0版本后,可以用AuthCallback类作为回调接口的入参 + // 1.9.3版本后 如果需要验证state,可以在login之前调用{@see AuthCallback#checkState}方法校验state合法性 + // 注:JustAuth默认保存state的时效为3分钟,3分钟内未使用则会自动清除过期的state authRequest.login(new AuthCallback()); } @@ -47,6 +51,8 @@ public class AuthRequestTest { // 返回授权页面,可自行跳转 authRequest.authorize("state"); // 授权登录后会返回code(auth_code(仅限支付宝))、state,1.8.0版本后,可以用AuthCallback类作为回调接口的入参 + // 1.9.3版本后 如果需要验证state,可以在login之前调用{@see AuthCallback#checkState}方法校验state合法性 + // 注:JustAuth默认保存state的时效为3分钟,3分钟内未使用则会自动清除过期的state authRequest.login(new AuthCallback()); } @@ -60,6 +66,8 @@ public class AuthRequestTest { // 返回授权页面,可自行跳转 authRequest.authorize("state"); // 授权登录后会返回code(auth_code(仅限支付宝))、state,1.8.0版本后,可以用AuthCallback类作为回调接口的入参 + // 1.9.3版本后 如果需要验证state,可以在login之前调用{@see AuthCallback#checkState}方法校验state合法性 + // 注:JustAuth默认保存state的时效为3分钟,3分钟内未使用则会自动清除过期的state authRequest.login(new AuthCallback()); } @@ -73,6 +81,8 @@ public class AuthRequestTest { // 返回授权页面,可自行跳转 authRequest.authorize("state"); // 授权登录后会返回code(auth_code(仅限支付宝))、state,1.8.0版本后,可以用AuthCallback类作为回调接口的入参 + // 1.9.3版本后 如果需要验证state,可以在login之前调用{@see AuthCallback#checkState}方法校验state合法性 + // 注:JustAuth默认保存state的时效为3分钟,3分钟内未使用则会自动清除过期的state authRequest.login(new AuthCallback()); } @@ -86,6 +96,8 @@ public class AuthRequestTest { // 返回授权页面,可自行跳转 authRequest.authorize("state"); // 授权登录后会返回code(auth_code(仅限支付宝))、state,1.8.0版本后,可以用AuthCallback类作为回调接口的入参 + // 1.9.3版本后 如果需要验证state,可以在login之前调用{@see AuthCallback#checkState}方法校验state合法性 + // 注:JustAuth默认保存state的时效为3分钟,3分钟内未使用则会自动清除过期的state authRequest.login(new AuthCallback()); } @@ -99,6 +111,8 @@ public class AuthRequestTest { // 返回授权页面,可自行跳转 authRequest.authorize("state"); // 授权登录后会返回code(auth_code(仅限支付宝))、state,1.8.0版本后,可以用AuthCallback类作为回调接口的入参 + // 1.9.3版本后 如果需要验证state,可以在login之前调用{@see AuthCallback#checkState}方法校验state合法性 + // 注:JustAuth默认保存state的时效为3分钟,3分钟内未使用则会自动清除过期的state authRequest.login(new AuthCallback()); } @@ -112,6 +126,8 @@ public class AuthRequestTest { // 返回授权页面,可自行跳转 authRequest.authorize("state"); // 授权登录后会返回code(auth_code(仅限支付宝))、state,1.8.0版本后,可以用AuthCallback类作为回调接口的入参 + // 1.9.3版本后 如果需要验证state,可以在login之前调用{@see AuthCallback#checkState}方法校验state合法性 + // 注:JustAuth默认保存state的时效为3分钟,3分钟内未使用则会自动清除过期的state authRequest.login(new AuthCallback()); } @@ -126,6 +142,8 @@ public class AuthRequestTest { // 返回授权页面,可自行跳转 authRequest.authorize("state"); // 授权登录后会返回code(auth_code(仅限支付宝))、state,1.8.0版本后,可以用AuthCallback类作为回调接口的入参 + // 1.9.3版本后 如果需要验证state,可以在login之前调用{@see AuthCallback#checkState}方法校验state合法性 + // 注:JustAuth默认保存state的时效为3分钟,3分钟内未使用则会自动清除过期的state AuthResponse login = authRequest.login(new AuthCallback()); } @@ -139,6 +157,8 @@ public class AuthRequestTest { // 返回授权页面,可自行跳转 authRequest.authorize("state"); // 授权登录后会返回code(auth_code(仅限支付宝))、state,1.8.0版本后,可以用AuthCallback类作为回调接口的入参 + // 1.9.3版本后 如果需要验证state,可以在login之前调用{@see AuthCallback#checkState}方法校验state合法性 + // 注:JustAuth默认保存state的时效为3分钟,3分钟内未使用则会自动清除过期的state AuthResponse login = authRequest.login(new AuthCallback()); } @@ -152,6 +172,8 @@ public class AuthRequestTest { // 返回授权页面,可自行跳转 authRequest.authorize("state"); // 授权登录后会返回code(auth_code(仅限支付宝))、state,1.8.0版本后,可以用AuthCallback类作为回调接口的入参 + // 1.9.3版本后 如果需要验证state,可以在login之前调用{@see AuthCallback#checkState}方法校验state合法性 + // 注:JustAuth默认保存state的时效为3分钟,3分钟内未使用则会自动清除过期的state AuthResponse login = authRequest.login(new AuthCallback()); } @@ -165,6 +187,8 @@ public class AuthRequestTest { // 返回授权页面,可自行跳转 authRequest.authorize("state"); // 授权登录后会返回code(auth_code(仅限支付宝))、state,1.8.0版本后,可以用AuthCallback类作为回调接口的入参 + // 1.9.3版本后 如果需要验证state,可以在login之前调用{@see AuthCallback#checkState}方法校验state合法性 + // 注:JustAuth默认保存state的时效为3分钟,3分钟内未使用则会自动清除过期的state AuthResponse login = authRequest.login(new AuthCallback()); } @@ -178,6 +202,8 @@ public class AuthRequestTest { // 返回授权页面,可自行跳转 authRequest.authorize("state"); // 授权登录后会返回code(auth_code(仅限支付宝))、state,1.8.0版本后,可以用AuthCallback类作为回调接口的入参 + // 1.9.3版本后 如果需要验证state,可以在login之前调用{@see AuthCallback#checkState}方法校验state合法性 + // 注:JustAuth默认保存state的时效为3分钟,3分钟内未使用则会自动清除过期的state AuthResponse login = authRequest.login(new AuthCallback()); } @@ -191,6 +217,8 @@ public class AuthRequestTest { // 返回授权页面,可自行跳转 authRequest.authorize("state"); // 授权登录后会返回code(auth_code(仅限支付宝))、state,1.8.0版本后,可以用AuthCallback类作为回调接口的入参 + // 1.9.3版本后 如果需要验证state,可以在login之前调用{@see AuthCallback#checkState}方法校验state合法性 + // 注:JustAuth默认保存state的时效为3分钟,3分钟内未使用则会自动清除过期的state AuthResponse login = authRequest.login(new AuthCallback()); } @@ -204,6 +232,8 @@ public class AuthRequestTest { // 返回授权页面,可自行跳转 authRequest.authorize("state"); // 授权登录后会返回code(auth_code(仅限支付宝))、state,1.8.0版本后,可以用AuthCallback类作为回调接口的入参 + // 1.9.3版本后 如果需要验证state,可以在login之前调用{@see AuthCallback#checkState}方法校验state合法性 + // 注:JustAuth默认保存state的时效为3分钟,3分钟内未使用则会自动清除过期的state AuthResponse login = authRequest.login(new AuthCallback()); } @@ -217,6 +247,8 @@ public class AuthRequestTest { // 返回授权页面,可自行跳转 authRequest.authorize("state"); // 授权登录后会返回code(auth_code(仅限支付宝))、state,1.8.0版本后,可以用AuthCallback类作为回调接口的入参 + // 1.9.3版本后 如果需要验证state,可以在login之前调用{@see AuthCallback#checkState}方法校验state合法性 + // 注:JustAuth默认保存state的时效为3分钟,3分钟内未使用则会自动清除过期的state AuthResponse login = authRequest.login(new AuthCallback()); } @@ -230,6 +262,8 @@ public class AuthRequestTest { // 返回授权页面,可自行跳转 authRequest.authorize("state"); // 授权登录后会返回code(auth_code(仅限支付宝))、state,1.8.0版本后,可以用AuthCallback类作为回调接口的入参 + // 1.9.3版本后 如果需要验证state,可以在login之前调用{@see AuthCallback#checkState}方法校验state合法性 + // 注:JustAuth默认保存state的时效为3分钟,3分钟内未使用则会自动清除过期的state AuthResponse login = authRequest.login(new AuthCallback()); } @@ -243,6 +277,8 @@ public class AuthRequestTest { // 返回授权页面,可自行跳转 authRequest.authorize("state"); // 授权登录后会返回code(auth_code(仅限支付宝))、state,1.8.0版本后,可以用AuthCallback类作为回调接口的入参 + // 1.9.3版本后 如果需要验证state,可以在login之前调用{@see AuthCallback#checkState}方法校验state合法性 + // 注:JustAuth默认保存state的时效为3分钟,3分钟内未使用则会自动清除过期的state AuthResponse login = authRequest.login(new AuthCallback()); } @@ -256,6 +292,8 @@ public class AuthRequestTest { // 返回授权页面,可自行跳转 authRequest.authorize("state"); // 授权登录后会返回code(auth_code(仅限支付宝))、state,1.8.0版本后,可以用AuthCallback类作为回调接口的入参 + // 1.9.3版本后 如果需要验证state,可以在login之前调用{@see AuthCallback#checkState}方法校验state合法性 + // 注:JustAuth默认保存state的时效为3分钟,3分钟内未使用则会自动清除过期的state AuthResponse login = authRequest.login(new AuthCallback()); } } diff --git a/src/test/java/me/zhyd/oauth/cache/AuthStateCacheTest.java b/src/test/java/me/zhyd/oauth/cache/AuthStateCacheTest.java new file mode 100644 index 0000000..9c6e1e0 --- /dev/null +++ b/src/test/java/me/zhyd/oauth/cache/AuthStateCacheTest.java @@ -0,0 +1,32 @@ +package me.zhyd.oauth.cache; + +import org.junit.Assert; +import org.junit.Test; + +import java.util.concurrent.TimeUnit; + +public class AuthStateCacheTest { + + @Test + public void cache1() throws InterruptedException { + AuthStateCache.cache("key", "value"); + Assert.assertEquals(AuthStateCache.get("key"), "value"); + + TimeUnit.MILLISECONDS.sleep(4); + Assert.assertEquals(AuthStateCache.get("key"), "value"); + } + + @Test + public void cache2() throws InterruptedException { + AuthStateCache.cache("key", "value", 10); + Assert.assertEquals(AuthStateCache.get("key"), "value"); + + // 没过期 + TimeUnit.MILLISECONDS.sleep(5); + Assert.assertEquals(AuthStateCache.get("key"), "value"); + + // 过期 + TimeUnit.MILLISECONDS.sleep(6); + Assert.assertNull(AuthStateCache.get("key")); + } +} diff --git a/src/test/java/me/zhyd/oauth/utils/UrlBuilderTest.java b/src/test/java/me/zhyd/oauth/utils/UrlBuilderTest.java index 065c59a..62a4239 100644 --- a/src/test/java/me/zhyd/oauth/utils/UrlBuilderTest.java +++ b/src/test/java/me/zhyd/oauth/utils/UrlBuilderTest.java @@ -3,6 +3,7 @@ package me.zhyd.oauth.utils; import me.zhyd.oauth.config.AuthConfig; import me.zhyd.oauth.config.AuthSource; import me.zhyd.oauth.request.AuthWeChatRequest; +import org.junit.Assert; import org.junit.Test; /** @@ -33,4 +34,30 @@ public class UrlBuilderTest { String authorize = request.authorize("state"); System.out.println(authorize); } + + @Test + public void build() { + String url = UrlBuilder.fromBaseUrl("https://www.zhyd.me") + .queryParam("name", "yadong.zhang") + .build(); + Assert.assertEquals(url, "https://www.zhyd.me?name=yadong.zhang"); + + url = UrlBuilder.fromBaseUrl(url) + .queryParam("github", "https://github.com/zhangyd-c") + .build(); + Assert.assertEquals(url, "https://www.zhyd.me?name=yadong.zhang&github=https://github.com/zhangyd-c"); + } + + @Test + public void build1() { + String url = UrlBuilder.fromBaseUrl("https://www.zhyd.me") + .queryParam("name", "yadong.zhang") + .build(true); + Assert.assertEquals(url, "https://www.zhyd.me?name=yadong.zhang"); + + url = UrlBuilder.fromBaseUrl(url) + .queryParam("github", "https://github.com/zhangyd-c") + .build(true); + Assert.assertEquals(url, "https://www.zhyd.me?name=yadong.zhang&github=https%3A%2F%2Fgithub.com%2Fzhangyd-c"); + } } diff --git a/src/test/java/me/zhyd/oauth/utils/UuidUtilsTest.java b/src/test/java/me/zhyd/oauth/utils/UuidUtilsTest.java new file mode 100644 index 0000000..27b8664 --- /dev/null +++ b/src/test/java/me/zhyd/oauth/utils/UuidUtilsTest.java @@ -0,0 +1,13 @@ +package me.zhyd.oauth.utils; + +import org.junit.Test; + +public class UuidUtilsTest { + + @Test + public void getUUID() { + + String uuid = UuidUtils.getUUID(); + System.out.println(uuid); + } +} diff --git a/update.md b/update.md index cc70316..bffbea0 100644 --- a/update.md +++ b/update.md @@ -1,3 +1,11 @@ +### 2019/07/30 ([v1.9.3](https://gitee.com/yadong.zhang/JustAuth/releases/v1.9.3)) + +1. 规范注释 +2. 增加State缓存,`AuthCallback`中增加默认的校验state的方法 +3. 增加默认的state生成方法,参考`AuthStateUtils.java`和`UuidUtils.java` +4. 升级`hutool-http`版本到`v4.6.0` +5. 修复其他一些问题 + ### 2019/07/27 1. `IpUtils.getIp`改名为`IpUtils.getLocalIp` -- GitLab