Compare commits
104 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fdc78212ff | |||
| 51087658a4 | |||
| 6567f17513 | |||
| 6a3e061921 | |||
| 3a5a312b68 | |||
| 3ff0b36b49 | |||
| faae8816be | |||
| 6cf6a7d7dc | |||
| c424f9c9c0 | |||
| 726018031f | |||
| 80f2dbdad7 | |||
| 02f9f833e6 | |||
| 05374e7a1f | |||
| 1eacc41959 | |||
| 66c7455d30 | |||
| 055807d75f | |||
| 9b4e149953 | |||
| 2e16556968 | |||
| c94b123a4b | |||
| ee1b6c8c5f | |||
| 2be048ef0e | |||
| 55fd335511 | |||
| bb975f8eb3 | |||
| 13a388fb0f | |||
| ff48e107c8 | |||
| de273d0482 | |||
| 16f77ec770 | |||
| 10df9f05f3 | |||
| fd183afd4a | |||
| d48a62d5cb | |||
| b466dd8de3 | |||
| 4d8411e978 | |||
| cf77e4d0e0 | |||
| 4f303705d9 | |||
| 1fb8be6c82 | |||
| 48a368b516 | |||
| 6594d90a71 | |||
| 31c983234f | |||
| 52682debfb | |||
| d5e161eaef | |||
| 1ab1cc124a | |||
| 050686d85f | |||
| 05f95b86b4 | |||
| 665daa37b2 | |||
| 576402eec3 | |||
| ec6c7a92b6 | |||
| d44cbd6e2b | |||
| 15caef01d3 | |||
| ad2e7c8231 | |||
| 57a7b7ff17 | |||
| 7a40159d15 | |||
| bcb27e4118 | |||
| 0b9cb95103 | |||
| d61bb58b93 | |||
| d034722232 | |||
| 197f15dc4f | |||
| fcb6fd9937 | |||
| ce5c437289 | |||
| fabfff60c9 | |||
| d4296d160e | |||
| d9967b2814 | |||
| 267b74bed7 | |||
| 3e8c475d3f | |||
| c1f9e96a92 | |||
| bed01eef6d | |||
| 909702e4da | |||
| 57cb7fb0d1 | |||
| b9c29c7534 | |||
| 6474c46505 | |||
| 864665f1b8 | |||
| 83d23302f8 | |||
| ca1522adfd | |||
| 55459652b9 | |||
| 92bc4ab34a | |||
| 4fab7561a4 | |||
| 16918ea13f | |||
| eec26580c9 | |||
| 421cabb176 | |||
| f71fce6858 | |||
| 3199334ddc | |||
| 79417395a1 | |||
| 06934b5242 | |||
| fcaef297ff | |||
| e590077701 | |||
| 6ce26d4ce1 | |||
| 43d9ec8631 | |||
| 8e99e54519 | |||
| 33076971fe | |||
| a2d6dfe707 | |||
| 64aa1940e4 | |||
| 56c1e4ea35 | |||
| ea1e1ba665 | |||
| 55c4b391bc | |||
| 094c297920 | |||
| 7f725b579b | |||
| 66a68c4ead | |||
| 504bf1cc7c | |||
| 97e7018795 | |||
| 993dc687fc | |||
| 38264616b4 | |||
| a75bac2cf7 | |||
| 666d50b461 | |||
| 7515301f50 | |||
| cab222059c |
@@ -1,13 +1,12 @@
|
||||
### 哪个平台?
|
||||
为更快的帮您定位问题,推荐您用以下模板反馈问题:
|
||||
|
||||
### 1. 出现问题时,您做了哪些操作?
|
||||
|
||||
### 2. 在哪个步骤出现了问题?
|
||||
|
||||
### 重现步骤
|
||||
|
||||
|
||||
|
||||
### 报错信息
|
||||
|
||||
### 3. 您希望得到什么结果?
|
||||
|
||||
### 4. 您实际得到什么结果?
|
||||
|
||||
### 5. 请附上您出现问题的整屏截图或者整个异常堆栈信息
|
||||
|
||||
|
||||
@@ -1,15 +1,26 @@
|
||||
### 该Pull Request关联的Issue
|
||||
- [ ] 是否为解决Issue?
|
||||
|
||||
|
||||
### 修改描述
|
||||
|
||||
|
||||
|
||||
### 测试用例
|
||||
|
||||
|
||||
|
||||
### 修复效果的截屏
|
||||
### 您做了哪些更新?
|
||||
|
||||
- 新增
|
||||
|
||||
- 修改
|
||||
|
||||
- 修复
|
||||
|
||||
- 其他
|
||||
|
||||
|
||||
### 是否做了充分测试?
|
||||
|
||||
- [ ] 是,已经做过测试,并且测试通过
|
||||
- [ ] 否,还没做测试,需要作者自测
|
||||
|
||||
注:测试demo可以使用:
|
||||
- [Springboot版](https://gitee.com/yadong.zhang/JustAuth-demo)
|
||||
- [jFinal版](https://github.com/xkcoding/jfinal-justauth-demo)
|
||||
- [ActFramework版](https://github.com/xkcoding/act-justauth-demo)
|
||||
|
||||
|
||||
|
||||
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
language: java
|
||||
|
||||
sudo: false # faster builds
|
||||
|
||||
install: true
|
||||
|
||||
jdk:
|
||||
- openjdk8
|
||||
|
||||
notifications:
|
||||
email: false
|
||||
|
||||
script:
|
||||
- export TZ=Asia/Shanghai
|
||||
- mvn install -DskipTests=true -Dmaven.javadoc.skip=true -B -V
|
||||
- mvn cobertura:cobertura -Dcobertura.report.format=xml -Dmaven.javadoc.skip.true
|
||||
|
||||
after_success:
|
||||
- bash <(curl -s https://codecov.io/bash)
|
||||
+188
@@ -0,0 +1,188 @@
|
||||
<p align="center">
|
||||
<a href="https://docs.justauth.whnb.wang"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/logo.png" width="400"></a>
|
||||
</p>
|
||||
<p align="center">
|
||||
<strong>Login, so easy.</strong>
|
||||
</p>
|
||||
<p align="center">
|
||||
<a target="_blank" href="https://search.maven.org/search?q=JustAuth">
|
||||
<img src="https://img.shields.io/badge/Maven Central-1.12.0-blue.svg" ></img>
|
||||
</a>
|
||||
<a target="_blank" href="https://gitee.com/yadong.zhang/JustAuth/blob/master/LICENSE">
|
||||
<img src="https://img.shields.io/apm/l/vim-mode.svg?color=yellow" ></img>
|
||||
</a>
|
||||
<a target="_blank" href="https://www.oracle.com/technetwork/java/javase/downloads/index.html">
|
||||
<img src="https://img.shields.io/badge/JDK-1.8+-green.svg" ></img>
|
||||
</a>
|
||||
<a target="_blank" href="https://apidoc.gitee.com/yadong.zhang/JustAuth/" title="API文档">
|
||||
<img src="https://img.shields.io/badge/Api Docs-1.12.0-orange.svg" ></img>
|
||||
</a>
|
||||
<a target="_blank" href="https://docs.justauth.whnb.wang" title="参考文档">
|
||||
<img src="https://img.shields.io/badge/Docs-latest-blueviolet.svg" ></img>
|
||||
</a>
|
||||
<a href="https://codecov.io/gh/zhangyd-c/JustAuth">
|
||||
<img src="https://codecov.io/gh/zhangyd-c/JustAuth/branch/master/graph/badge.svg" />
|
||||
</a>
|
||||
<a href='https://gitee.com/yadong.zhang/JustAuth/stargazers'>
|
||||
<img src='https://gitee.com/yadong.zhang/JustAuth/badge/star.svg?theme=white' alt='star'></img>
|
||||
</a>
|
||||
<a target="_blank" href='https://github.com/zhangyd-c/JustAuth'>
|
||||
<img src="https://img.shields.io/github/stars/zhangyd-c/JustAuth.svg?style=social" alt="github star"></img>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<center>
|
||||
<table>
|
||||
<tr>
|
||||
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/gitee.png" width="20"></td>
|
||||
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/github.png" width="20"></td>
|
||||
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/weibo.png" width="20"></td>
|
||||
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/dingtalk.png" width="20"></td>
|
||||
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/baidu.png" width="20"></td>
|
||||
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/coding.png" width="20"></td>
|
||||
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/tencentCloud.png" width="20"></td>
|
||||
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/oschina.png" width="20"></td>
|
||||
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/alipay.png" width="20"></td>
|
||||
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/qq.png" width="20"></td>
|
||||
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/wechat.png" width="20"></td>
|
||||
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/taobao.png" width="20"></td>
|
||||
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/google.png" width="20"></td>
|
||||
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/facebook.png" width="20"></td>
|
||||
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/douyin.png" width="20"></td>
|
||||
</tr>
|
||||
</table>
|
||||
<table>
|
||||
<tr>
|
||||
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/linkedin.png" width="20"></td>
|
||||
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/microsoft.png" width="20"></td>
|
||||
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/mi.png" width="20"></td>
|
||||
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/toutiao.png" width="20"></td>
|
||||
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/teambition.png" width="20"></td>
|
||||
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/renren.png" width="20"></td>
|
||||
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/pinterest.png" width="20"></td>
|
||||
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/stackoverflow.png" width="20"></td>
|
||||
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/huawei.png" width="20"></td>
|
||||
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/wechat.png" width="20" title="微信企业版"></td>
|
||||
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/csdn.png" width="20"></td>
|
||||
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/kujiale.png" width="20"></td>
|
||||
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/gitlab.png" width="20"></td>
|
||||
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/meituan.png" width="20"></td>
|
||||
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/eleme.png" width="20"></td>
|
||||
</tr>
|
||||
</table>
|
||||
</center>
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
`JustAuth`, as you see, It is just a Java library of third-party authorized login, It's smaller and easier to use. JustAuth is the best third-party login tool written in JAVA.
|
||||
|
||||
Source Code:[gitee](https://gitee.com/yadong.zhang/JustAuth) | [github](https://github.com/zhangyd-c/JustAuth)
|
||||
Docs:[Reference Doc](https://docs.justauth.whnb.wang)
|
||||
|
||||
## Features
|
||||
|
||||
1. **Multiple platform**: Has integrated more than a dozen third-party platforms.
|
||||
2. **Minimalist**: The minimalist design is very simple to use.
|
||||
|
||||
## Quick start
|
||||
|
||||
- Add maven dependency
|
||||
|
||||
These artifacts are available from Maven Central:
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>me.zhyd.oauth</groupId>
|
||||
<artifactId>JustAuth</artifactId>
|
||||
<version>${latest.version}</version>
|
||||
</dependency>
|
||||
```
|
||||
- Using JustAuth
|
||||
```java
|
||||
// Create authorization request
|
||||
AuthRequest authRequest = new AuthGiteeRequest(AuthConfig.builder()
|
||||
.clientId("clientId")
|
||||
.clientSecret("clientSecret")
|
||||
.redirectUri("redirectUri")
|
||||
.build());
|
||||
// Generate authorization url
|
||||
authRequest.authorize("state");
|
||||
// After authorization to login, it will return: code(auth_code(Alipay only)),state, After version 1.8.0, you can use the AuthCallback as a parameter to the callback interface
|
||||
// Note: JustAuth saves state for 3 minutes by default. If it is not used within 3 minutes, the expired state will be cleared automatically.
|
||||
authRequest.login(callback);
|
||||
```
|
||||
|
||||
**Examples**:
|
||||
- [Springboot Example](https://gitee.com/yadong.zhang/JustAuth-demo)
|
||||
- [jFinal Example](https://github.com/xkcoding/jfinal-justauth-demo): by [xkcoding](https://github.com/xkcoding)
|
||||
- [ActFramework Example](https://github.com/xkcoding/act-justauth-demo): by [xkcoding](https://github.com/xkcoding)
|
||||
- [Nutzboot版](https://github.com/EggsBlue/nutzboot-justauth-demo): by [蛋蛋](https://github.com/EggsBlue)
|
||||
|
||||
**Springboot Starter**
|
||||
|
||||
- [justauth-spring-boot-starter](https://github.com/xkcoding/justauth-spring-boot-starter): Spring Boot integrates best practices with JustAuth by [xkcoding](https://github.com/xkcoding)
|
||||
|
||||
#### API
|
||||
| :computer: platform | :coffee: API | :page_facing_up: Official document |
|
||||
|:------:|:-------:|:-------:|
|
||||
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/gitee.png" width="20"> | [AuthGiteeRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthGiteeRequest.java) | <a href="https://gitee.com/api/v5/oauth_doc#list_1" target="_blank">参考文档</a> |
|
||||
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/github.png" width="20"> | [AuthGithubRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthGiteeRequest.java) | <a href="https://developer.github.com/apps/building-oauth-apps/authorizing-oauth-apps/" target="_blank">参考文档</a> |
|
||||
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/weibo.png" width="20"> | [AuthWeiboRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthGiteeRequest.java) | <a href="https://open.weibo.com/wiki/%E6%8E%88%E6%9D%83%E6%9C%BA%E5%88%B6%E8%AF%B4%E6%98%8E" target="_blank">参考文档</a> |
|
||||
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/dingtalk.png" width="20"> | [AuthDingTalkRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthDingTalkRequest.java) | <a href="https://open-doc.dingtalk.com/microapp/serverapi2/kymkv6" target="_blank">参考文档</a> |
|
||||
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/baidu.png" width="20"> | [AuthBaiduRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthBaiduRequest.java) | <a href="http://developer.baidu.com/wiki/index.php?title=docs/oauth" target="_blank">参考文档</a> |
|
||||
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/coding.png" width="25"> | [AuthCodingRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthCodingRequest.java) | <a href="https://open.coding.net/references/oauth/" target="_blank">参考文档</a> |
|
||||
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/tencentCloud.png" width="25"> | [AuthTencentCloudRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthTencentCloudRequest.java) | <a href="https://dev.tencent.com/help/doc/faq/b4e5b7aee786/oauth" target="_blank">参考文档</a> |
|
||||
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/oschina.png" width="20"> | [AuthOschinaRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthOschinaRequest.java) | <a href="https://www.oschina.net/openapi/docs/oauth2_authorize" target="_blank">参考文档</a> |
|
||||
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/alipay.png" width="20"> | [AuthAlipayRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthAlipayRequest.java) | <a href="https://alipay.open.taobao.com/docs/doc.htm?spm=a219a.7629140.0.0.336d4b70GUKXOl&treeId=193&articleId=105809&docType=1" target="_blank">参考文档</a> |
|
||||
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/qq.png" width="20"> | [AuthQqRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthQqRequest.java) | <a href="https://wiki.connect.qq.com/%E4%BD%BF%E7%94%A8authorization_code%E8%8E%B7%E5%8F%96access_token" target="_blank">参考文档</a> |
|
||||
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/wechat.png" width="20"> | [AuthWeChatRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthWeChatRequest.java) | <a href="https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1419316505&token=&lang=zh_CN" target="_blank">参考文档</a> |
|
||||
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/taobao.png" width="20"> | [AuthTaobaoRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthTaobaoRequest.java) | <a href="https://open.taobao.com/doc.htm?spm=a219a.7386797.0.0.4e00669acnkQy6&source=search&docId=105590&docType=1" target="_blank">参考文档</a> |
|
||||
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/google.png" width="20"> | [AuthGoogleRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthGoogleRequest.java) | <a href="https://developers.google.com/identity/protocols/OpenIDConnect" target="_blank">参考文档</a> |
|
||||
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/facebook.png" width="20"> | [AuthFacebookRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthFacebookRequest.java) | <a href="https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow" target="_blank">参考文档</a> |
|
||||
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/douyin.png" width="20"> | [AuthDouyinRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthDouyinRequest.java) | <a href="https://www.douyin.com/platform/doc/m-2-1-1" target="_blank">参考文档</a> |
|
||||
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/linkedin.png" width="20"> | [AuthLinkedinRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthLinkedinRequest.java) | <a href="https://docs.microsoft.com/zh-cn/linkedin/shared/authentication/authorization-code-flow?context=linkedin/context" target="_blank">参考文档</a> |
|
||||
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/microsoft.png" width="20"> | [AuthMicrosoftRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthMicrosoftRequest.java) | <a href="https://docs.microsoft.com/zh-cn/graph/auth/" target="_blank">参考文档</a> |
|
||||
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/mi.png" width="20"> | [AuthMiRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthMiRequest.java) | <a href="https://dev.mi.com/console/doc/detail?pId=711" target="_blank">参考文档</a> |
|
||||
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/toutiao.png" width="20"> | [AuthToutiaoRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthToutiaoRequest.java) | <a href="https://open.mp.toutiao.com/#/resource?_k=y7mfgk" target="_blank">参考文档</a> |
|
||||
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/teambition.png" width="20"> | [AuthTeambitionRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthTeambitionRequest.java) | <a href="https://docs.teambition.com/" target="_blank">参考文档</a> |
|
||||
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/renren.png" width="20"> | [AuthRenrenRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthRenrenRequest.java) | <a href="http://open.renren.com/wiki/OAuth2.0" target="_blank">参考文档</a> |
|
||||
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/pinterest.png" width="20"> | [AuthPinterestRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthPinterestRequest.java) | <a href="https://developers.pinterest.com/docs/api/overview/?" target="_blank">参考文档</a> |
|
||||
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/stackoverflow.png" width="20"> | [AuthStackOverflowRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthStackOverflowRequest.java) | <a href="https://api.stackexchange.com/docs/authentication" target="_blank">参考文档</a> |
|
||||
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/huawei.png" width="20"> | [AuthHuaweiRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthHuaweiRequest.java) | <a href="https://developer.huawei.com/consumer/cn/devservice/doc/30101" target="_blank">参考文档</a> |
|
||||
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/wechat.png" width="20"> | [AuthWeChatEnterpriseRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthWeChatEnterpriseRequest.java) | <a href="https://open.work.weixin.qq.com/api/doc#90000/90135/90664" target="_blank">参考文档</a> |
|
||||
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/kujiale.png" width="20"> | [AuthKujialeRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthKujialeRequest.java) | <a href="https://open.kujiale.com/open/apps/2/docs?doc_id=95" target="_blank">参考文档</a> |
|
||||
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/gitlab.png" width="20"> | [AuthGitlabRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthGitlabRequest.java) | <a href="https://docs.gitlab.com/ee/api/oauth2.html" target="_blank">参考文档</a> |
|
||||
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/meituan.png" width="20"> | [AuthMeituanRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthMeituanRequest.java) | <a href="http://open.waimai.meituan.com/openapi_docs/oauth/" target="_blank">参考文档</a> |
|
||||
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/eleme.png" width="20"> | [AuthElemeRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthElemeRequest.java) | <a href="https://open.shop.ele.me/openapi/documents/khd001" target="_blank">参考文档</a> |
|
||||
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/csdn.png" width="20"> | [AuthCsdnRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthCsdnRequest.java) | 无 |
|
||||
|
||||
|
||||
|
||||
## Contributions
|
||||
|
||||
1. Fork this project to your repository
|
||||
2. Clone the project after fork.
|
||||
3. Modify the code (either to fix issue, or to add new features)
|
||||
4. Commit and push code to a remote repository
|
||||
5. Create a new PR (pull request), and select `dev` branch
|
||||
6. Waiting for author to merge
|
||||
|
||||
I look forward to your joining us.
|
||||
|
||||
|
||||
## Contributors
|
||||
|
||||
[contributors](https://docs.justauth.whnb.wang/#/contributors)
|
||||
|
||||
## Recommend
|
||||
|
||||
- `spring-boot-demo` In-depth study and actual combat of spring boot projects: [https://github.com/xkcoding/spring-boot-demo](https://github.com/xkcoding/spring-boot-demo)
|
||||
- `mica` Efficient Development of scaffolding by Spring Cloud: [https://github.com/lets-mica/mica](https://github.com/lets-mica/mica)
|
||||
- `pig` Cosmic strongest Micro Services Certified authorized scaffolding (essential for Architects): [https://gitee.com/log4j/pig](https://gitee.com/log4j/pig)
|
||||
- `SpringBlade` Complete online solution (necessary for enterprise development): https://gitee.com/smallc/SpringBlade
|
||||
|
||||
## References
|
||||
|
||||
- [The OAuth 2.0 Authorization Framework](https://tools.ietf.org/html/rfc6749)
|
||||
- [OAuth 2.0](https://oauth.net/2/)
|
||||
@@ -1,12 +1,12 @@
|
||||
<p align="center">
|
||||
<a href="https://www.justauth.cn/"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/logo.png" width="400"></a>
|
||||
<a href="https://docs.justauth.whnb.wang"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/logo.png" width="400"></a>
|
||||
</p>
|
||||
<p align="center">
|
||||
<strong>Login, so easy.</strong>
|
||||
</p>
|
||||
<p align="center">
|
||||
<a target="_blank" href="https://search.maven.org/search?q=JustAuth">
|
||||
<img src="https://img.shields.io/badge/Maven Central-1.9.1-blue.svg" ></img>
|
||||
<img src="https://img.shields.io/badge/Maven Central-1.12.0-blue.svg" ></img>
|
||||
</a>
|
||||
<a target="_blank" href="https://gitee.com/yadong.zhang/JustAuth/blob/master/LICENSE">
|
||||
<img src="https://img.shields.io/apm/l/vim-mode.svg?color=yellow" ></img>
|
||||
@@ -14,42 +14,60 @@
|
||||
<a target="_blank" href="https://www.oracle.com/technetwork/java/javase/downloads/index.html">
|
||||
<img src="https://img.shields.io/badge/JDK-1.8+-green.svg" ></img>
|
||||
</a>
|
||||
<a target="_blank" href="https://apidoc.gitee.com/yadong.zhang/JustAuth/">
|
||||
<img src="https://img.shields.io/badge/Docs-1.9.1-orange.svg" ></img>
|
||||
<a target="_blank" href="https://apidoc.gitee.com/yadong.zhang/JustAuth/" title="API文档">
|
||||
<img src="https://img.shields.io/badge/Api Docs-1.12.0-orange.svg" ></img>
|
||||
</a>
|
||||
<a target="_blank" href="https://docs.justauth.whnb.wang" title="参考文档">
|
||||
<img src="https://img.shields.io/badge/Docs-latest-blueviolet.svg" ></img>
|
||||
</a>
|
||||
<a href="https://codecov.io/gh/zhangyd-c/JustAuth">
|
||||
<img src="https://codecov.io/gh/zhangyd-c/JustAuth/branch/master/graph/badge.svg" />
|
||||
</a>
|
||||
<a href='https://gitee.com/yadong.zhang/JustAuth/stargazers'>
|
||||
<img src='https://gitee.com/yadong.zhang/JustAuth/badge/star.svg?theme=white' alt='star'></img>
|
||||
</a>
|
||||
<a target="_blank" href='https://github.com/zhangyd-c/JustAuth'>
|
||||
<img src="https://img.shields.io/github/stars/zhangyd-c/JustAuth.svg?style=social" alt="github star"></img>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<center>
|
||||
<table>
|
||||
<tr>
|
||||
<td align="center" width="200"><a href="#授权gitee"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/gitee.png" width="20"></a></td>
|
||||
<td align="center" width="200"><a href="#授权github"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/github.png" width="20"></a></td>
|
||||
<td align="center" width="200"><a href="#授权weibo"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/weibo.png" width="20"></a></td>
|
||||
<td align="center" width="200"><a href="#授权钉钉"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/dingtalk.png" width="20"></a></td>
|
||||
<td align="center" width="200"><a href="#授权百度"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/baidu.png" width="20"></a></td>
|
||||
<td align="center" width="200"><a href="#授权coding"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/coding.png" width="20"></a></td>
|
||||
<td align="center" width="200"><a href="#授权腾讯云开发者平台"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/tencentCloud.png" width="20"></a></td>
|
||||
<td align="center" width="200"><a href="#授权oschina"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/oschina.png" width="20"></a></td>
|
||||
<td align="center" width="200"><a href="#授权支付宝"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/alipay.png" width="20"></a></td>
|
||||
<td align="center" width="200"><a href="#授权qq"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/qq.png" width="20"></a></td>
|
||||
<td align="center" width="200"><a href="#授权微信"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/wechat.png" width="20"></a></td>
|
||||
<td align="center" width="200"><a href="#授权淘宝"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/taobao.png" width="20"></a></td>
|
||||
<td align="center" width="200"><a href="#授权google"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/google.png" width="20"></a></td>
|
||||
<td align="center" width="200"><a href="#授权facebook"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/facebook.png" width="20"></a></td>
|
||||
<td align="center" width="200"><a href="#授权抖音"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/douyin.png" width="20"></a></td>
|
||||
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/gitee.png" width="20"></td>
|
||||
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/github.png" width="20"></td>
|
||||
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/weibo.png" width="20"></td>
|
||||
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/dingtalk.png" width="20"></td>
|
||||
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/baidu.png" width="20"></td>
|
||||
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/coding.png" width="20"></td>
|
||||
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/tencentCloud.png" width="20"></td>
|
||||
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/oschina.png" width="20"></td>
|
||||
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/alipay.png" width="20"></td>
|
||||
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/qq.png" width="20"></td>
|
||||
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/wechat.png" width="20"></td>
|
||||
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/taobao.png" width="20"></td>
|
||||
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/google.png" width="20"></td>
|
||||
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/facebook.png" width="20"></td>
|
||||
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/douyin.png" width="20"></td>
|
||||
</tr>
|
||||
</table>
|
||||
<table>
|
||||
<tr>
|
||||
<td align="center" width="200"><a href="#授权领英"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/linkedin.png" width="20"></a></td>
|
||||
<td align="center" width="200"><a href="#授权微软"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/microsoft.png" width="20"></a></td>
|
||||
<td align="center" width="200"><a href="#授权小米"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/mi.png" width="20"></a></td>
|
||||
<td align="center" width="200"><a href="#授权今日头条"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/toutiao.png" width="20"></a></td>
|
||||
<td align="center" width="200"><a href="#授权Teambition"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/teambition.png" width="20"></a></td>
|
||||
<td align="center" width="200"><a href="#授权人人"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/renren.png" width="20"></a></td>
|
||||
<td align="center" width="200"><a href="#授权Pinterest"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/pinterest.png" width="20"></a></td>
|
||||
<td align="center" width="200"><a href="#授权Stack Overflow"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/stackoverflow.png" width="20"></a></td>
|
||||
<td align="center" width="200"><a href="#授权csdn"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/csdn.png" width="20"></a></td>
|
||||
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/linkedin.png" width="20"></td>
|
||||
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/microsoft.png" width="20"></td>
|
||||
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/mi.png" width="20"></td>
|
||||
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/toutiao.png" width="20"></td>
|
||||
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/teambition.png" width="20"></td>
|
||||
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/renren.png" width="20"></td>
|
||||
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/pinterest.png" width="20"></td>
|
||||
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/stackoverflow.png" width="20"></td>
|
||||
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/huawei.png" width="20"></td>
|
||||
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/wechat.png" width="20" title="微信企业版"></td>
|
||||
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/csdn.png" width="20"></td>
|
||||
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/kujiale.png" width="20"></td>
|
||||
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/gitlab.png" width="20"></td>
|
||||
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/meituan.png" width="20"></td>
|
||||
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/eleme.png" width="20"></td>
|
||||
</tr>
|
||||
</table>
|
||||
</center>
|
||||
@@ -60,7 +78,8 @@
|
||||
|
||||
JustAuth,如你所见,它仅仅是一个**第三方授权登录**的**工具类库**,它可以让我们脱离繁琐的第三方登录SDK,让登录变得**So easy!**
|
||||
|
||||
项目开源地址:[gitee](https://gitee.com/yadong.zhang/JustAuth) | [github](https://github.com/zhangyd-c/JustAuth)
|
||||
项目开源地址:[gitee](https://gitee.com/yadong.zhang/JustAuth) | [github](https://github.com/zhangyd-c/JustAuth)
|
||||
项目文档:[参考文档](https://docs.justauth.whnb.wang)
|
||||
|
||||
## 特点
|
||||
|
||||
@@ -76,7 +95,7 @@ JustAuth,如你所见,它仅仅是一个**第三方授权登录**的**工具
|
||||
<dependency>
|
||||
<groupId>me.zhyd.oauth</groupId>
|
||||
<artifactId>JustAuth</artifactId>
|
||||
<version>1.9.1</version>
|
||||
<version>1.12.0</version>
|
||||
</dependency>
|
||||
```
|
||||
- 调用api
|
||||
@@ -86,19 +105,22 @@ AuthRequest authRequest = new AuthGiteeRequest(AuthConfig.builder()
|
||||
.clientId("clientId")
|
||||
.clientSecret("clientSecret")
|
||||
.redirectUri("redirectUri")
|
||||
.state("state")
|
||||
.build());
|
||||
// 生成授权页面
|
||||
authRequest.authorize();
|
||||
authRequest.authorize("state");
|
||||
// 授权登录后会返回code(auth_code(仅限支付宝))、state,1.8.0版本后,可以用AuthCallback类作为回调接口的参数
|
||||
// 注: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): Jfinal集成JustAuth的demo by [xkcoding](https://github.com/xkcoding)
|
||||
- [ActFramework版](https://github.com/xkcoding/act-justauth-demo): ActFramework 集成 JustAuth 的 demo by [xkcoding](https://github.com/xkcoding)
|
||||
- [Nutzboot版](https://github.com/EggsBlue/nutzboot-justauth-demo): NutzBoot集成JustAuth的demo by [蛋蛋](https://github.com/EggsBlue)
|
||||
|
||||
## 插件
|
||||
- [justauth-spring-boot-starter](https://github.com/xkcoding/justauth-spring-boot-starter): Spring Boot 集成 JustAuth 的最佳实践 by [xkcoding](https://github.com/xkcoding)
|
||||
|
||||
具体的例子可以参考:
|
||||
|
||||
@@ -132,6 +154,12 @@ authRequest.login(callback);
|
||||
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/renren.png" width="20"> | [AuthRenrenRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthRenrenRequest.java) | <a href="http://open.renren.com/wiki/OAuth2.0" target="_blank">参考文档</a> |
|
||||
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/pinterest.png" width="20"> | [AuthPinterestRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthPinterestRequest.java) | <a href="https://developers.pinterest.com/docs/api/overview/?" target="_blank">参考文档</a> |
|
||||
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/stackoverflow.png" width="20"> | [AuthStackOverflowRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthStackOverflowRequest.java) | <a href="https://api.stackexchange.com/docs/authentication" target="_blank">参考文档</a> |
|
||||
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/huawei.png" width="20"> | [AuthHuaweiRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthHuaweiRequest.java) | <a href="https://developer.huawei.com/consumer/cn/devservice/doc/30101" target="_blank">参考文档</a> |
|
||||
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/wechat.png" width="20"> | [AuthWeChatEnterpriseRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthWeChatEnterpriseRequest.java) | <a href="https://open.work.weixin.qq.com/api/doc#90000/90135/90664" target="_blank">参考文档</a> |
|
||||
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/kujiale.png" width="20"> | [AuthKujialeRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthKujialeRequest.java) | <a href="https://open.kujiale.com/open/apps/2/docs?doc_id=95" target="_blank">参考文档</a> |
|
||||
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/gitlab.png" width="20"> | [AuthGitlabRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthGitlabRequest.java) | <a href="https://docs.gitlab.com/ee/api/oauth2.html" target="_blank">参考文档</a> |
|
||||
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/meituan.png" width="20"> | [AuthMeituanRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthMeituanRequest.java) | <a href="http://open.waimai.meituan.com/openapi_docs/oauth/" target="_blank">参考文档</a> |
|
||||
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/eleme.png" width="20"> | [AuthElemeRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthElemeRequest.java) | <a href="https://open.shop.ele.me/openapi/documents/khd001" target="_blank">参考文档</a> |
|
||||
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/csdn.png" width="20"> | [AuthCsdnRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthCsdnRequest.java) | 无 |
|
||||
|
||||
_请知悉:经咨询CSDN官方客服得知,CSDN的授权开放平台已经下线。如果以前申请过的应用,可以继续使用,但是不再支持申请新的应用。so, 本项目中的CSDN登录只能针对少部分用户使用了_
|
||||
@@ -151,17 +179,30 @@ _请知悉:经咨询CSDN官方客服得知,CSDN的授权开放平台已经
|
||||
5. 发起PR(pull request) 请求,提交到`dev`分支
|
||||
6. 等待作者合并
|
||||
|
||||
## 贡献者名单
|
||||
|
||||
[contributors](https://docs.justauth.whnb.wang/#/contributors)
|
||||
|
||||
## 致谢
|
||||
|
||||
在项目立项初期,也对当前开源圈的一些相同类型的项目作过调研,同时本项目也参考过这些项目,再次感谢开源圈内的朋友。
|
||||
|
||||
[YurunOAuthLogin](https://gitee.com/yurunsoft/YurunOAuthLogin): PHP 第三方登录授权 SDK
|
||||
- [YurunOAuthLogin](https://gitee.com/yurunsoft/YurunOAuthLogin): PHP 第三方登录授权 SDK
|
||||
- [阿里妈妈MUX倾力打造的矢量图标库-iconfont](https://www.iconfont.cn/search/index): 本文档中的图标大部分取自该平台
|
||||
- [mica](https://github.com/lets-mica/mica):Spring Cloud 微服务开发核心包,支持 `web `和 `webflux`。注:JustAuth项目中的[UuidUtils](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/utils/UuidUtils.java)就是直接使用的mica提供的高性能的uuid创建工具类源码[StringUtil.java](https://github.com/lets-mica/mica/blob/master/mica-core/src/main/java/net/dreamlu/mica/core/utils/StringUtil.java#L335)
|
||||
- 感谢 JetBrains 提供的免费开源 License:
|
||||
<img src="https://github.com/lets-mica/mica/raw/c251e176b81518a6a570bf4eb21f525c4f582a81/docs/img/jetbrains.png" alt="图片引用自lets-mica" style="float:left;">
|
||||
|
||||
[阿里妈妈MUX倾力打造的矢量图标库-iconfont](https://www.iconfont.cn/search/index): 本文档中的图标大部分取自该平台
|
||||
## 开源推荐
|
||||
- `spring-boot-demo` 深度学习并实战 spring boot 的项目: [https://github.com/xkcoding/spring-boot-demo](https://github.com/xkcoding/spring-boot-demo)
|
||||
- `mica` SpringBoot 微服务高效开发工具集: [https://github.com/lets-mica/mica](https://github.com/lets-mica/mica)
|
||||
- `pig` 宇宙最强微服务认证授权脚手架(架构师必备): [https://gitee.com/log4j/pig](https://gitee.com/log4j/pig)
|
||||
- `SpringBlade` 完整的线上解决方案(企业开发必备): https://gitee.com/smallc/SpringBlade
|
||||
|
||||
## 关于OAuth
|
||||
|
||||
[The OAuth 2.0 Authorization Framework](https://tools.ietf.org/html/rfc6749)
|
||||
- [The OAuth 2.0 Authorization Framework](https://tools.ietf.org/html/rfc6749)
|
||||
- [OAuth 2.0](https://oauth.net/2/)
|
||||
|
||||
## 关注&交流
|
||||
|
||||
@@ -173,8 +214,7 @@ _请知悉:经咨询CSDN官方客服得知,CSDN的授权开放平台已经
|
||||
|
||||
- JustAuth交流群 (230017570):专业交流该项目
|
||||
|
||||
- 开源总群 (190886500):各个开源项目的都有,也有博客建设等方面的朋友。(注意,该群需付费进入,防止发垃圾广告、垃圾推广等人士)
|
||||
|
||||
- 开源总群 (190886500):各个开源项目的都有,也有博客建设等方面的朋友。
|
||||
|
||||
## 请喝咖啡
|
||||
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
# 项目贡献者名单
|
||||
|
||||
- <img src="https://avatar.gitee.com/uploads/99/784199_yadong.zhang.png!avatar100?1462325358" width="20"> · yadong.zhang : <a href="https://github.com/zhangyd-c" target="_blank">[Github]</a> | <a href="https://gitee.com/yadong.zhang" target="_blank">[Gitee]</a> | <a href="https://www.zhyd.me" target="_blank">[个人网站]</a>
|
||||
- <img src="https://avatars0.githubusercontent.com/u/10429917?s=460&v=4" width="20"> · yangkai.shen : <a href="https://github.com/xkcoding" target="_blank">[Github]</a> | <a href="https://xkcoding.com" target="_blank">[个人网站]</a>
|
||||
- <img src="https://avatar.gitee.com/uploads/51/1651_dolphinboy.png!avatar100?1479346570" width="20"> · skqing : <a href="https://gitee.com/skqing" target="_blank">[Gitee]</a> | <a href="https://my.oschina.net/dolphinboy" target="_blank">[个人网站]</a>
|
||||
- <img src="https://avatars2.githubusercontent.com/u/2988765?s=115&v=4" width="20"> · pengisgood : <a href="https://github.com/pengisgood" target="_blank">[Github]</a> | <a href="https://pengisgood.github.io" target="_blank">[个人网站]</a>
|
||||
- 千年等一回,我只为等你...
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
## ~~升级到1.8.0后如何启用state?~~
|
||||
|
||||
~~在原api使用方法的基础上,为config追加一个state即可。~~
|
||||
```
|
||||
AuthRequest authRequest = new AuthGiteeRequest(AuthConfig.builder()
|
||||
.clientId("clientId")
|
||||
.clientSecret("clientSecret")
|
||||
.redirectUri("redirectUri")
|
||||
.state("state") // 就是这儿
|
||||
.build());
|
||||
```
|
||||
|
||||
## ~~升级到1.8.0后login方法报错?~~
|
||||
|
||||
~~这是因为1.8.0版本中新增了[AuthCallback](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/model/AuthCallback.java)类,这个类封装了所有可能的回调参数。目前包含以下三个参数:~~
|
||||
- ~~`code`: 访问AuthorizeUrl后回调时带的参数code,用来换取token~~
|
||||
- ~~`auth_code`: 支付宝授权登陆时不会返回code而是返回`auth_code`参数~~
|
||||
- ~~`state`: 访问AuthorizeUrl后回调时带的参数state,用于和请求AuthorizeUrl前的state比较,防止CSRF攻击~~
|
||||
|
||||
~~1.8.0版本之后的api,可以直接用AuthCallback类作为回调方法的入参,比如:~~
|
||||
```
|
||||
@RequestMapping("/callback/{source}")
|
||||
public Object login(@PathVariable("source") String source, AuthCallback callback) {
|
||||
System.out.println("进入callback:" + source + " callback params:" + JSONObject.toJSONString(callback));
|
||||
AuthRequest authRequest = getAuthRequest(source);
|
||||
AuthResponse response = authRequest.login(callback);
|
||||
System.out.println(JSONObject.toJSONString(response));
|
||||
return response;
|
||||
}
|
||||
```
|
||||
~~_代码截取自_ :https://gitee.com/yadong.zhang/JustAuth-demo~~
|
||||
|
||||
## ~~升级到1.8.0后对于state参数有什么特殊要求吗?~~
|
||||
|
||||
~~理论上没有,stata只是用来保持会话状态,因为http协议的无状态性,从授权到回调,无法感知具体是哪个用户触发的。所以可以使用state作为校验。注:state参数每次完整的授权链中只可用一次!(也是为了防止不必要的危险)~~
|
||||
|
||||
~~作者建议state命名格式如下:~~
|
||||
- ~~授权登录:`{source}_{ip}_{random}`~~
|
||||
- ~~账号绑定:`{source}_{userId}_{ip}_{random}`~~
|
||||
|
||||
~~其中`source`表示授权平台,可以直接去JustAuth中的source,`ip`为当前用户的ip(部分情况可能不适用),`random`为随机字符串,`userId`为当前登录用户的id。~~
|
||||
|
||||
~~注:`authorize`和`login`(不是指回调传回的`state`,而是声明`request`时传入的`state`)中传的`state`务必保证一致~~
|
||||
|
||||
## 升级到1.9.3+版本后编译失败
|
||||
|
||||
主要明显的就是`IpUtils.getIp`和request的`.state`报错。
|
||||
|
||||
这是因为从`v1.9.3`版本开始,对项目进行了一些优化,具体优化内容参考:[v1.9.3](https://gitee.com/yadong.zhang/JustAuth/releases/v1.9.3)和[v1.9.4](https://gitee.com/yadong.zhang/JustAuth/releases/v1.9.4)。
|
||||
|
||||
新版本的使用方式,参考[JustAuth-demo](https://gitee.com/yadong.zhang/JustAuth-demo/blob/master/src/main/java/me/zhyd/justauth/RestAuthController.java)
|
||||
```
|
||||
@RequestMapping("/render/{source}")
|
||||
public void renderAuth(@PathVariable("source") String source, HttpServletResponse response) throws IOException {
|
||||
AuthRequest authRequest = getAuthRequest(source);
|
||||
String authorizeUrl = authRequest.authorize(AuthStateUtils.createState());
|
||||
response.sendRedirect(authorizeUrl);
|
||||
}
|
||||
@RequestMapping("/callback/{source}")
|
||||
public Object login(@PathVariable("source") String source, AuthCallback callback) {
|
||||
AuthRequest authRequest = getAuthRequest(source);
|
||||
AuthResponse response = authRequest.login(callback);
|
||||
return response;
|
||||
}
|
||||
```
|
||||
|
||||
## 升级到最新版本后为什么支付宝登录不能用了?
|
||||
|
||||
在升级到新版后,使用支付宝登录会提示`ClassNotFoundExcption`异常,这是因为从`1.9.4`版本开始,JustAuth将不在强依赖`alipay-sdk-java`,如果你需要用到Alipay的授权登陆,那么你还需要添加以下依赖:
|
||||
|
||||
```
|
||||
<dependency>
|
||||
<groupId>com.alipay.sdk</groupId>
|
||||
<artifactId>alipay-sdk-java</artifactId>
|
||||
<version>3.7.4.ALL</version>
|
||||
</dependency>
|
||||
```
|
||||
+169
@@ -0,0 +1,169 @@
|
||||
<p align="center">
|
||||
<a href="https://docs.justauth.whnb.wang"><img src="./_media/cover.png" width="400"></a>
|
||||
</p>
|
||||
<p align="center">
|
||||
<strong>Login, so easy!</strong>
|
||||
</p>
|
||||
<p align="center">
|
||||
<strong>史上最全的整合第三方登录的开源库</strong>
|
||||
</p>
|
||||
<p align="center">
|
||||
<a target="_blank" href="https://search.maven.org/search?q=JustAuth">
|
||||
<img src="https://img.shields.io/badge/Maven Central-1.12.0-blue.svg" ></img>
|
||||
</a>
|
||||
<a target="_blank" href="https://gitee.com/yadong.zhang/JustAuth/blob/master/LICENSE">
|
||||
<img src="https://img.shields.io/apm/l/vim-mode.svg?color=yellow" ></img>
|
||||
</a>
|
||||
<a target="_blank" href="https://www.oracle.com/technetwork/java/javase/downloads/index.html">
|
||||
<img src="https://img.shields.io/badge/JDK-1.8+-green.svg" ></img>
|
||||
</a>
|
||||
<a target="_blank" href="https://apidoc.gitee.com/yadong.zhang/JustAuth/" title="API文档">
|
||||
<img src="https://img.shields.io/badge/Api Docs-1.12.0-orange.svg" ></img>
|
||||
</a>
|
||||
<a target="_blank" href="https://docs.justauth.whnb.wang" title="参考文档">
|
||||
<img src="https://img.shields.io/badge/Docs-latest-blueviolet.svg" ></img>
|
||||
</a>
|
||||
<a href="https://codecov.io/gh/zhangyd-c/JustAuth">
|
||||
<img src="https://codecov.io/gh/zhangyd-c/JustAuth/branch/master/graph/badge.svg" />
|
||||
</a>
|
||||
<a href='https://gitee.com/yadong.zhang/JustAuth/stargazers'>
|
||||
<img src='https://gitee.com/yadong.zhang/JustAuth/badge/star.svg?theme=white' alt='star'></img>
|
||||
</a>
|
||||
<a target="_blank" href='https://github.com/zhangyd-c/JustAuth'>
|
||||
<img src="https://img.shields.io/github/stars/zhangyd-c/JustAuth.svg?style=social" alt="github star"></img>
|
||||
</a>
|
||||
</p>
|
||||
<p align="center">
|
||||
<strong>开源地址:</strong> <a target="_blank" href='https://gitee.com/yadong.zhang/JustAuth'>Gitee</a> | <a target="_blank" href='https://github.com/zhangyd-c/JustAuth'>Github</a>
|
||||
</p>
|
||||
<p align="center">
|
||||
<strong>QQ群:</strong>230017570
|
||||
</p>
|
||||
<p align="center">
|
||||
<strong>文档更新日期:</strong> {docsify-updated}
|
||||
</p>
|
||||
|
||||
## 简介
|
||||
|
||||
JustAuth,如你所见,它仅仅是一个**第三方授权登录**的**工具类库**,它可以让我们脱离繁琐的第三方登录SDK,让登录变得**So easy!**
|
||||
|
||||
## 特点
|
||||
|
||||
废话不多说,就俩字:
|
||||
|
||||
1. **全**:已集成十多家第三方平台(国内外常用的基本都已包含),后续依然还有扩展计划!
|
||||
2. **简**:API就是奔着最简单去设计的,尽量让您用起来没有障碍感!
|
||||
|
||||
## 项目关注度趋势
|
||||
|
||||
[](https://starchart.cc/justauth/JustAuth)
|
||||
|
||||
## 已集成的平台
|
||||
|
||||
| :computer: 平台 | :coffee: API类 | :page_facing_up: SDK |
|
||||
|:------:|:-------:|:-------:|
|
||||
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/gitee.png" width="20"> | [AuthGiteeRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthGiteeRequest.java) | <a href="https://gitee.com/api/v5/oauth_doc#list_1" target="_blank">参考文档</a> |
|
||||
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/github.png" width="20"> | [AuthGithubRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthGiteeRequest.java) | <a href="https://developer.github.com/apps/building-oauth-apps/authorizing-oauth-apps/" target="_blank">参考文档</a> |
|
||||
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/weibo.png" width="20"> | [AuthWeiboRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthGiteeRequest.java) | <a href="https://open.weibo.com/wiki/%E6%8E%88%E6%9D%83%E6%9C%BA%E5%88%B6%E8%AF%B4%E6%98%8E" target="_blank">参考文档</a> |
|
||||
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/dingtalk.png" width="20"> | [AuthDingTalkRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthDingTalkRequest.java) | <a href="https://open-doc.dingtalk.com/microapp/serverapi2/kymkv6" target="_blank">参考文档</a> |
|
||||
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/baidu.png" width="20"> | [AuthBaiduRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthBaiduRequest.java) | <a href="http://developer.baidu.com/wiki/index.php?title=docs/oauth" target="_blank">参考文档</a> |
|
||||
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/coding.png" width="25"> | [AuthCodingRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthCodingRequest.java) | <a href="https://open.coding.net/references/oauth/" target="_blank">参考文档</a> |
|
||||
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/tencentCloud.png" width="25"> | [AuthTencentCloudRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthTencentCloudRequest.java) | <a href="https://dev.tencent.com/help/doc/faq/b4e5b7aee786/oauth" target="_blank">参考文档</a> |
|
||||
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/oschina.png" width="20"> | [AuthOschinaRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthOschinaRequest.java) | <a href="https://www.oschina.net/openapi/docs/oauth2_authorize" target="_blank">参考文档</a> |
|
||||
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/alipay.png" width="20"> | [AuthAlipayRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthAlipayRequest.java) | <a href="https://alipay.open.taobao.com/docs/doc.htm?spm=a219a.7629140.0.0.336d4b70GUKXOl&treeId=193&articleId=105809&docType=1" target="_blank">参考文档</a> |
|
||||
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/qq.png" width="20"> | [AuthQqRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthQqRequest.java) | <a href="https://wiki.connect.qq.com/%E4%BD%BF%E7%94%A8authorization_code%E8%8E%B7%E5%8F%96access_token" target="_blank">参考文档</a> |
|
||||
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/wechat.png" width="20"> | [AuthWeChatRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthWeChatRequest.java) | <a href="https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1419316505&token=&lang=zh_CN" target="_blank">参考文档</a> |
|
||||
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/taobao.png" width="20"> | [AuthTaobaoRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthTaobaoRequest.java) | <a href="https://open.taobao.com/doc.htm?spm=a219a.7386797.0.0.4e00669acnkQy6&source=search&docId=105590&docType=1" target="_blank">参考文档</a> |
|
||||
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/google.png" width="20"> | [AuthGoogleRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthGoogleRequest.java) | <a href="https://developers.google.com/identity/protocols/OpenIDConnect" target="_blank">参考文档</a> |
|
||||
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/facebook.png" width="20"> | [AuthFacebookRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthFacebookRequest.java) | <a href="https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow" target="_blank">参考文档</a> |
|
||||
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/douyin.png" width="20"> | [AuthDouyinRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthDouyinRequest.java) | <a href="https://www.douyin.com/platform/doc/m-2-1-1" target="_blank">参考文档</a> |
|
||||
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/linkedin.png" width="20"> | [AuthLinkedinRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthLinkedinRequest.java) | <a href="https://docs.microsoft.com/zh-cn/linkedin/shared/authentication/authorization-code-flow?context=linkedin/context" target="_blank">参考文档</a> |
|
||||
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/microsoft.png" width="20"> | [AuthMicrosoftRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthMicrosoftRequest.java) | <a href="https://docs.microsoft.com/zh-cn/graph/auth/" target="_blank">参考文档</a> |
|
||||
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/mi.png" width="20"> | [AuthMiRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthMiRequest.java) | <a href="https://dev.mi.com/console/doc/detail?pId=711" target="_blank">参考文档</a> |
|
||||
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/toutiao.png" width="20"> | [AuthToutiaoRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthToutiaoRequest.java) | <a href="https://open.mp.toutiao.com/#/resource?_k=y7mfgk" target="_blank">参考文档</a> |
|
||||
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/teambition.png" width="20"> | [AuthTeambitionRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthTeambitionRequest.java) | <a href="https://docs.teambition.com/" target="_blank">参考文档</a> |
|
||||
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/renren.png" width="20"> | [AuthRenrenRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthRenrenRequest.java) | <a href="http://open.renren.com/wiki/OAuth2.0" target="_blank">参考文档</a> |
|
||||
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/pinterest.png" width="20"> | [AuthPinterestRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthPinterestRequest.java) | <a href="https://developers.pinterest.com/docs/api/overview/?" target="_blank">参考文档</a> |
|
||||
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/stackoverflow.png" width="20"> | [AuthStackOverflowRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthStackOverflowRequest.java) | <a href="https://api.stackexchange.com/docs/authentication" target="_blank">参考文档</a> |
|
||||
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/huawei.png" width="20"> | [AuthHuaweiRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthHuaweiRequest.java) | <a href="https://developer.huawei.com/consumer/cn/devservice/doc/30101" target="_blank">参考文档</a> |
|
||||
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/wechat.png" width="20"> | [AuthWeChatEnterpriseRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthWeChatEnterpriseRequest.java) | <a href="https://open.work.weixin.qq.com/api/doc#90000/90135/90664" target="_blank">参考文档</a> |
|
||||
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/kujiale.png" width="20"> | [AuthKujialeRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthKujialeRequest.java) | <a href="https://open.kujiale.com/open/apps/2/docs?doc_id=95" target="_blank">参考文档</a> |
|
||||
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/gitlab.png" width="20"> | [AuthGitlabRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthGitlabRequest.java) | <a href="https://docs.gitlab.com/ee/api/oauth2.html" target="_blank">参考文档</a> |
|
||||
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/meituan.png" width="20"> | [AuthMeituanRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthMeituanRequest.java) | <a href="http://open.waimai.meituan.com/openapi_docs/oauth/" target="_blank">参考文档</a> |
|
||||
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/eleme.png" width="20"> | [AuthElemeRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthElemeRequest.java) | <a href="https://open.shop.ele.me/openapi/documents/khd001" target="_blank">参考文档</a> |
|
||||
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/csdn.png" width="20"> | [AuthCsdnRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthCsdnRequest.java) | 无 |
|
||||
|
||||
|
||||
## 快速开始
|
||||
|
||||
- 引入依赖
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>me.zhyd.oauth</groupId>
|
||||
<artifactId>JustAuth</artifactId>
|
||||
<version>${latest.version}</version>
|
||||
</dependency>
|
||||
```
|
||||
- 调用api
|
||||
```java
|
||||
// 创建授权request
|
||||
AuthRequest authRequest = new AuthGiteeRequest(AuthConfig.builder()
|
||||
.clientId("clientId")
|
||||
.clientSecret("clientSecret")
|
||||
.redirectUri("redirectUri")
|
||||
.build());
|
||||
// 生成授权页面
|
||||
authRequest.authorize("state");
|
||||
// 授权登录后会返回code(auth_code(仅限支付宝))、state,1.8.0版本后,可以用AuthCallback类作为回调接口的参数
|
||||
// 注:JustAuth默认保存state的时效为3分钟,3分钟内未使用则会自动清除过期的state
|
||||
authRequest.login(callback);
|
||||
```
|
||||
|
||||
## 参与&贡献
|
||||
|
||||
JustAuth的发展离不开朋友们的支持,时至今日,JustAuth已渐趋完善,但仍有很大的改善空间。欢迎各位朋友为JustAuth贡献一份力量。
|
||||
|
||||
### 提供bug或建议
|
||||
|
||||
- [Gitee](https://gitee.com/yadong.zhang/JustAuth/issues)
|
||||
- [Github](https://github.com/justauth/JustAuth/issues)
|
||||
|
||||
如果你正在使用JustAuth,可以在这儿留下你的足迹,获得优先推送、曝光
|
||||
|
||||
- [Gitee](https://gitee.com/yadong.zhang/JustAuth/issues/IZ2T7)
|
||||
- [Github](https://github.com/justauth/JustAuth/issues/17)
|
||||
|
||||
### 贡献代码的步骤
|
||||
|
||||
1. fork本项目到自己的repo
|
||||
2. 把fork过去的项目也就是你仓库中的项目clone到你本地
|
||||
3. 修改代码(`dev`分支)
|
||||
4. commit后push到自己的仓库
|
||||
5. 发起PR(pull request) 请求,提交到`dev`分支
|
||||
6. 等待合并
|
||||
|
||||
### 注意事项
|
||||
|
||||
1. JustAuth只接受集成**OAuth2.0**的平台
|
||||
2. 建议安装“**阿里编码规约**”插件,然后进行开发
|
||||
3. 提交PR前请格式化好自己的代码
|
||||
4. 注释规范,自定义的方法一定要加上:方法说明、参数说明、返回值说明等
|
||||
|
||||
## 功能尝鲜
|
||||
|
||||
JustAuth一共有两个主要分支:
|
||||
- 线上版分支(master):稳定版,发布版就是这个分支的代码
|
||||
- 开发版分支(dev):不保证稳定,新功能都会优先推送到该分支,对于想尝鲜的朋友,可以直接下载代码,然后源码编译dev分支
|
||||
|
||||
## 开源推荐
|
||||
- `spring-boot-demo` 深度学习并实战 spring boot 的项目: [https://github.com/xkcoding/spring-boot-demo](https://github.com/xkcoding/spring-boot-demo)
|
||||
- `mica` SpringBoot 微服务高效开发工具集: [https://github.com/lets-mica/mica](https://github.com/lets-mica/mica)
|
||||
- `pig` 宇宙最强微服务认证授权脚手架(架构师必备): [https://gitee.com/log4j/pig](https://gitee.com/log4j/pig)
|
||||
- `SpringBlade` 完整的线上解决方案(企业开发必备): https://gitee.com/smallc/SpringBlade
|
||||
|
||||
## 捐赠
|
||||
|
||||
| 支付宝 | 微信 |
|
||||
| :------------: | :------------: |
|
||||
| <img src="https://gitee.com/yadong.zhang/static/raw/master/qrcode/zfb_code.png" width="200"/> | <img src="https://gitee.com/yadong.zhang/static/raw/master/qrcode/wx_code.png" width="200" /> |
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
|
||||

|
||||
# JustAuth <small>1.11.0</small>
|
||||
|
||||
<strong>史上最全的整合第三方登录的开源库</strong>
|
||||
|
||||
<strong>Login, so easy</strong>
|
||||
|
||||
<p>已集成国内外十多家平台</p>
|
||||
<p>极简的API设计</p>
|
||||
|
||||
|
||||
[Gitee](https://gitee.com/yadong.zhang/JustAuth)
|
||||
[Github](https://github.com/zhangyd-c/JustAuth)
|
||||
[Get Started](#简介)
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 19 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 6.0 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
@@ -0,0 +1,14 @@
|
||||
- [入门和使用](README.md)
|
||||
- [贡献者名单](contributors.md)
|
||||
- 快速开始
|
||||
- [名词解释](explain.md)
|
||||
- [OAuth流程](oauth.md)
|
||||
- [如何使用](how-to-use.md)
|
||||
- 其他特性
|
||||
- [使用State](using-state.md)
|
||||
- [自定义state缓存](customize-the-state-cache.md)
|
||||
- [配套项目](supporting.md)
|
||||
- [Q&A](Q&A.md)
|
||||
- [Who is using](users.md)
|
||||
- [致谢](thx.md)
|
||||
- [更新记录](update.md)
|
||||
@@ -0,0 +1,31 @@
|
||||
# 项目贡献者名单
|
||||
|
||||
> 排序不分先后
|
||||
|
||||
- <img src="https://avatar.gitee.com/uploads/99/784199_yadong.zhang.png!avatar100?1462325358" width="20"> · yadong.zhang : <a href="https://github.com/zhangyd-c" target="_blank">[Github]</a> | <a href="https://gitee.com/yadong.zhang" target="_blank">[Gitee]</a> | <a href="https://www.zhyd.me" target="_blank">[个人网站]</a>
|
||||
- <img src="https://avatars0.githubusercontent.com/u/10429917?s=460&v=4" width="20"> · yangkai.shen : <a href="https://github.com/xkcoding" target="_blank">[Github]</a> | <a href="https://xkcoding.com" target="_blank">[个人网站]</a>
|
||||
- 集成微信登录、QQ登录、Google登录、微软登录、小米登录、企业微信登录
|
||||
- 优化代码、架构,增加自定义缓存
|
||||
- 提供jFinal版demo
|
||||
- 提供ActFramework版demo
|
||||
- 提供SpringBoot快速集成的justauth-spring-boot-starter
|
||||
- <img src="https://avatars2.githubusercontent.com/u/2988765?s=115&v=4" width="20"> · pengisgood : <a href="https://github.com/pengisgood" target="_blank">[Github]</a> | <a href="https://pengisgood.github.io" target="_blank">[个人网站]</a>
|
||||
- 集成人人登录、Pinterest登录、StackOverflow登录
|
||||
- <img src="https://avatar.gitee.com/uploads/51/1651_dolphinboy.png!avatar100?1479346570" width="20"> · skqing : <a href="https://gitee.com/skqing" target="_blank">[Gitee]</a> | <a href="https://my.oschina.net/dolphinboy" target="_blank">[个人网站]</a>
|
||||
- 修复钉钉登录的部分问题
|
||||
- 优化微博登录
|
||||
- <img src="https://avatars1.githubusercontent.com/u/47110161?s=88&v=4" width="20"> · dyc12ii : <a href="https://github.com/dyc12ii" target="_blank">[Gitee]</a>
|
||||
- 升级fastjson版本至1.2.58
|
||||
- <img src="https://gitee.com/uploads/22/4981222_harryleexyz.png?1556524275" width="20"> · harrylee : <a href="https://gitee.com/harryleexyz" target="_blank">[Gitee]</a>
|
||||
- 升级fastjson依赖到1.2.60
|
||||
- <img src="https://avatars3.githubusercontent.com/u/32814990?s=460&v=4" width="20"> · Veigar : <a href="https://github.com/wuweiqi1993" target="_blank">[Github]</a>
|
||||
- 集成酷家乐登录
|
||||
- <img src="https://avatar.gitee.com/uploads/24/1280924_TopCoderMyDream.png!avatar200?1523763232" width="20"> · 蛋蛋 : <a href="https://gitee.com/TopCoderMyDream" target="_blank">[Gitee]</a> | <a href="https://github.com/EggsBlue" target="_blank">[Github]</a>
|
||||
- 提供NutzBoot版的demo项目
|
||||
- <img src="https://avatars0.githubusercontent.com/u/35978114?s=180&v=4" width="20"> · Braavos96 : <a href="https://github.com/Braavos96" target="_blank">[Github]</a>
|
||||
- 添加测试用例:UrlBuilder 、GlobalAuthUtil
|
||||
- <img src="https://avatars0.githubusercontent.com/u/283483?s=180&v=4" width="20"> · Chris Smowton : <a href="https://github.com/smowton" target="_blank">[Github]</a>
|
||||
- 添加测试用例:StringUtils
|
||||
- 千年等一回,我只为等你...
|
||||
|
||||
ps: 如有遗漏,请告知
|
||||
@@ -0,0 +1,3 @@
|
||||
# 自定义state缓存
|
||||
|
||||
待补充
|
||||
@@ -0,0 +1,29 @@
|
||||
本文将就JustAuth中涉及到的一些配置、关键词做一下简单说明,方便使用者理解、使用。
|
||||
|
||||
## 本文相关名词
|
||||
|
||||
- `开发者` 指使用`JustAuth`的开发者
|
||||
- `第三方` 指开发者对接的第三方网站,比如:QQ平台、微信平台、微博平台
|
||||
- `用户` 指最终服务的真实用户
|
||||
|
||||
## JustAuth中的关键词
|
||||
|
||||
以下内容了解后,将会使你更容易地上手JustAuth。
|
||||
|
||||
- `clientId` 客户端身份标识符(应用id),一般在申请完Oauth应用后,由**第三方平台颁发**,唯一
|
||||
- `clientSecret` 客户端密钥,一般在申请完Oauth应用后,由**第三方平台颁发**
|
||||
- `redirectUri` **开发者项目中的有效api地址**。用户在确认第三方平台授权(登录)后,第三方平台会重定向到该地址,并携带code等参数
|
||||
- `state` 用来保持授权会话流程完整性,防止CSRF攻击的安全的随机的参数,由**开发者生成**
|
||||
- `alipayPublicKey` 支付宝公钥。当选择支付宝登录时,必传该值,由**开发者生成**
|
||||
- `unionId` 是否需要申请unionid,目前只针对**qq登录**。注:qq授权登录时,获取unionid需要单独发送邮件申请权限。如果个人开发者账号中申请了该权限,可以将该值置为true,在获取openId时就会同步获取unionId。参考链接:[UnionID介绍](http://wiki.connect.qq.com/unionid%E4%BB%8B%E7%BB%8D)
|
||||
- `stackOverflowKey` Stack Overflow 登陆时需单独提供的key,由**第三方平台颁发**
|
||||
- `agentId` 企业微信登陆时需单独提供该值,由**第三方平台颁发**,为授权方的网页应用ID
|
||||
- `source` JustAuth支持的第三方平台,比如:GITHUB、GITEE等
|
||||
|
||||
## 参考资料
|
||||
|
||||
关于OAuth2相关的内容、原理可以自行参阅以下资料:
|
||||
|
||||
- [The OAuth 2.0 Authorization Framework](https://tools.ietf.org/html/rfc6749)
|
||||
- [OAuth 2.0](https://oauth.net/2/)
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1021 B |
@@ -0,0 +1,148 @@
|
||||
# 如何使用
|
||||
|
||||
在前面有介绍到,JustAuth的特点之一就是**简**,极简主义,不给使用者造成不必要的障碍。
|
||||
|
||||
既然牛皮吹下了, 那么如何才能用JustAuth实现第三方登录呢?
|
||||
|
||||
## 使用步骤
|
||||
|
||||
使用JustAuth总共分三步(**这三步也适合于JustAuth支持的任何一个平台**):
|
||||
|
||||
1. 申请注册第三方平台的开发者账号
|
||||
2. 创建第三方平台的应用,获取配置信息(`accessKey`, `secretKey`, `redirectUri`)
|
||||
3. 使用该工具实现授权登陆
|
||||
|
||||
## 使用方式
|
||||
|
||||
- 引入依赖
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>me.zhyd.oauth</groupId>
|
||||
<artifactId>JustAuth</artifactId>
|
||||
<version>${latest.version}</version>
|
||||
</dependency>
|
||||
```
|
||||
- 调用api
|
||||
```java
|
||||
// 创建授权request
|
||||
AuthRequest authRequest = new AuthGiteeRequest(AuthConfig.builder()
|
||||
.clientId("clientId")
|
||||
.clientSecret("clientSecret")
|
||||
.redirectUri("redirectUri")
|
||||
.build());
|
||||
// 生成授权页面
|
||||
authRequest.authorize("state");
|
||||
// 授权登录后会返回code(auth_code(仅限支付宝))、state,1.8.0版本后,可以用AuthCallback类作为回调接口的参数
|
||||
// 注:JustAuth默认保存state的时效为3分钟,3分钟内未使用则会自动清除过期的state
|
||||
authRequest.login(callback);
|
||||
```
|
||||
|
||||
## API分解
|
||||
|
||||
**JustAuth**的核心就是一个个的`request`,每个平台都对应一个具体的`request`类,所以在使用之前,需要就具体的授权平台创建响应的`request`
|
||||
|
||||
```java
|
||||
// 创建授权request
|
||||
AuthRequest authRequest = new AuthGiteeRequest(AuthConfig.builder()
|
||||
.clientId("clientId")
|
||||
.clientSecret("clientSecret")
|
||||
.redirectUri("redirectUri")
|
||||
.build());
|
||||
```
|
||||
|
||||
所有可用的`Request`列表请参考:[已集成的平台](https://docs.justauth.whnb.wang/#/README?id=已集成的平台)
|
||||
|
||||
### 获取授权链接
|
||||
|
||||
```java
|
||||
String authorizeUrl = authRequest.authorize("state");
|
||||
```
|
||||
获取到`authorizeUrl`后,可以手动实现redirect到`authorizeUrl`上
|
||||
|
||||
**伪代码**
|
||||
|
||||
```java
|
||||
/**
|
||||
*
|
||||
* @param source 第三方授权平台,以本例为参考,该值为gitee(因为上面声明的AuthGiteeRequest)
|
||||
*/
|
||||
@RequestMapping("/render/{source}")
|
||||
public void renderAuth(@PathVariable("source") String source, HttpServletResponse response) throws IOException {
|
||||
AuthRequest authRequest = getAuthRequest(source);
|
||||
String authorizeUrl = authRequest.authorize(AuthStateUtils.createState());
|
||||
response.sendRedirect(authorizeUrl);
|
||||
}
|
||||
```
|
||||
|
||||
注:`state`建议必传!`state`在`OAuth`的流程中的主要作用就是保证请求完整性,防止**CSRF**风险,此处传的`state`将在回调时传回
|
||||
|
||||
### 登录(获取用户信息)
|
||||
|
||||
```java
|
||||
AuthResponse response = authRequest.login(callback);
|
||||
```
|
||||
|
||||
授权登录后会返回code(auth_code(仅限支付宝)、authorization_code(仅限华为))、state,1.8.0版本后,用`AuthCallback`类作为回调接口的入参
|
||||
|
||||
**伪代码**
|
||||
|
||||
```java
|
||||
/**
|
||||
*
|
||||
* @param source 第三方授权平台,以本例为参考,该值为gitee(因为上面声明的AuthGiteeRequest)
|
||||
*/
|
||||
@RequestMapping("/callback/{source}")
|
||||
public Object login(@PathVariable("source") String source, AuthCallback callback) {
|
||||
AuthRequest authRequest = getAuthRequest(source);
|
||||
AuthResponse response = authRequest.login(callback);
|
||||
return response;
|
||||
}
|
||||
```
|
||||
|
||||
**注:第三方平台中配置的授权回调地址,以本文为例,在创建授权应用时的回调地址应为:`[host]/callback/gitee`**
|
||||
|
||||
### 刷新token
|
||||
|
||||
注:`refresh`功能,并不是每个平台都支持
|
||||
|
||||
```java
|
||||
AuthResponse response = authRequest.refresh(AuthToken.builder().refreshToken(token).build());
|
||||
```
|
||||
|
||||
**伪代码**
|
||||
|
||||
```java
|
||||
/**
|
||||
*
|
||||
* @param source 第三方授权平台,以本例为参考,该值为gitee(因为上面声明的AuthGiteeRequest)
|
||||
* @param token login成功后返回的refreshToken
|
||||
*/
|
||||
@RequestMapping("/refresh/{source}")
|
||||
public Object refreshAuth(@PathVariable("source") String source, String token){
|
||||
AuthRequest authRequest = getAuthRequest(source);
|
||||
return authRequest.refresh(AuthToken.builder().refreshToken(token).build());
|
||||
}
|
||||
```
|
||||
|
||||
### 取消授权
|
||||
|
||||
注:`revoke`功能,并不是每个平台都支持
|
||||
|
||||
```java
|
||||
AuthResponse response = authRequest.revoke(AuthToken.builder().accessToken(token).build());
|
||||
```
|
||||
|
||||
**伪代码**
|
||||
|
||||
```java
|
||||
/**
|
||||
*
|
||||
* @param source 第三方授权平台,以本例为参考,该值为gitee(因为上面声明的AuthGiteeRequest)
|
||||
* @param token login成功后返回的accessToken
|
||||
*/
|
||||
@RequestMapping("/revoke/{source}/{token}")
|
||||
public Object revokeAuth(@PathVariable("source") String source, @PathVariable("token") String token) throws IOException {
|
||||
AuthRequest authRequest = getAuthRequest(source);
|
||||
return authRequest.revoke(AuthToken.builder().accessToken(token).build());
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,78 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>JustAuth - 史上最全的整合第三方登录的开源库</title>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
|
||||
|
||||
<meta name="description" content="JustAuth,史上最全的整合第三方登录的开源库" />
|
||||
<meta name="keywords" content="JustAuth,第三方授权登录,OAuth" />
|
||||
|
||||
<meta itemprop="name" content="JustAuth,史上最全的整合第三方登录的开源库" />
|
||||
<meta itemprop="description" content="JustAuth,如你所见,它仅仅是一个第三方授权登录的工具类库,它可以让我们脱离繁琐的第三方登录SDK,让登录变得So easy!" />
|
||||
<meta itemprop="image" content="./_media/cover.png" />
|
||||
|
||||
<link rel="shortcut icon" href="favicon.ico">
|
||||
<link rel="stylesheet" href="//unpkg.com/docsify/lib/themes/vue.css">
|
||||
<!-- 百度统计 -->
|
||||
<script>
|
||||
var _hmt = _hmt || [];
|
||||
(function() {
|
||||
var hm = document.createElement("script");
|
||||
hm.src = "https://hm.baidu.com/hm.js?4c7a1a462477545b480a3dd1ed95f0a9";
|
||||
var s = document.getElementsByTagName("script")[0];
|
||||
s.parentNode.insertBefore(hm, s);
|
||||
})();
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app">Please wait...</div>
|
||||
<script>
|
||||
window.$docsify = {
|
||||
name: '<img src="./_media/cover.png" width="200"><br>Login, so easy',
|
||||
search: {
|
||||
maxAge: 86400000,
|
||||
noData: {
|
||||
'/': '找不到结果'
|
||||
},
|
||||
paths: 'auto',
|
||||
placeholder: {
|
||||
'/': '搜索'
|
||||
}
|
||||
},
|
||||
repo: 'https://gitee.com/yadong.zhang/JustAuth',
|
||||
loadSidebar: true,
|
||||
maxLevel: 4,
|
||||
subMaxLevel: 2,
|
||||
auto2top: true,
|
||||
coverpage: true,
|
||||
formatUpdated: '{YYYY}/{MM}/{DD} {HH}:{mm}:{ss}',
|
||||
plugins: [
|
||||
function (hook, vm) {
|
||||
var footer = [
|
||||
'<hr/>',
|
||||
'<footer>',
|
||||
'<span>JustAuth: <a href="https://github.com/justauth/JustAuth">Github</a> | <a href="https://gitee.com/yadong.zhang/JustAuth">Gitee</a> ©2019.</span>',
|
||||
'<span>Proudly published with <a href="https://github.com/docsifyjs/docsify" target="_blank">docsify</a>.</span>',
|
||||
'</footer>'
|
||||
].join('');
|
||||
hook.afterEach(function (html) {
|
||||
return html + footer
|
||||
});
|
||||
hook.beforeEach(function (html) {
|
||||
var url = 'https://gitee.com/yadong.zhang/JustAuth/tree/master/docs/' + vm.route.file;
|
||||
var editHtml = '[📝 编辑该文档](' + url + ')\n';
|
||||
|
||||
return html
|
||||
+ '\n----\n'
|
||||
+ 'Last modified {docsify-updated} '
|
||||
+ editHtml
|
||||
})
|
||||
}
|
||||
]
|
||||
}
|
||||
</script>
|
||||
<script src="//unpkg.com/docsify/lib/docsify.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,61 @@
|
||||
# 关于OAuth
|
||||
|
||||
请先查阅以下资料:
|
||||
|
||||
- [The OAuth 2.0 Authorization Framework](https://tools.ietf.org/html/rfc6749)
|
||||
- [OAuth 2.0](https://oauth.net/2/)
|
||||
|
||||
## OAuth 2的授权流程
|
||||
|
||||
### 参与的角色
|
||||
|
||||
- `Resource Owner` 资源所有者,即代表授权客户端访问本身资源信息的用户(User),也就是应用场景中的“**开发者A**”
|
||||
- `Resource Server` 资源服务器,托管受保护的**用户账号信息**,比如Github
|
||||
- `Authorization Server` 授权服务器,**验证用户身份**然后为客户端派发资源访问令牌,比如Github
|
||||
- `Resource Server`和`Authorization Server` 可以是同一台服务器,也可以是不同的服务器,视具体的授权平台而有所差异
|
||||
- `Client` 客户端,即代表意图访问受限资源的**第三方应用**
|
||||
|
||||
### 授权流程
|
||||
```html
|
||||
+--------+ +---------------+
|
||||
| |--(A)- Authorization Request ->| Resource |
|
||||
| | | Owner |
|
||||
| |<-(B)-- Authorization Grant ---| |
|
||||
| | +---------------+
|
||||
| |
|
||||
| | +---------------+
|
||||
| |--(C)-- Authorization Grant -->| Authorization |
|
||||
| Client | | Server |
|
||||
| |<-(D)----- Access Token -------| |
|
||||
| | +---------------+
|
||||
| |
|
||||
| | +---------------+
|
||||
| |--(E)----- Access Token ------>| Resource |
|
||||
| | | Server |
|
||||
| |<-(F)--- Protected Resource ---| |
|
||||
+--------+ +---------------+
|
||||
```
|
||||
|
||||
上面的流程图取自[The OAuth 2.0 Authorization Framework#1.2](https://tools.ietf.org/html/rfc6749#section-1.2)
|
||||
|
||||
- (A) 用户打开**客户端**以后,**客户端**要求**用户**给予授权。
|
||||
- (B) **用户**同意给予**客户端**授权。
|
||||
- (C) **客户端**使用上一步获得的授权,向**认证服务器**申请令牌。
|
||||
- (D) **认证服务器**对**客户端**进行认证以后,确认无误,同意发放令牌
|
||||
- (E) **客户端**使用令牌,向**资源服务器**申请获取资源。
|
||||
- (F) **资源服务器**确认令牌无误,同意向**客户端**开放资源。
|
||||
|
||||
### 授权许可 `Authorization Grant`
|
||||
|
||||
- Authorization Code
|
||||
- 结合普通服务器端应用使用(**web**端常用的授权方式)
|
||||
- Implicit
|
||||
- 结合移动应用或 Web App 使用
|
||||
- Resource Owner Password Credentials
|
||||
- 适用于受信任客户端应用,例如同个组织的内部或外部应用
|
||||
- Client Credentials
|
||||
- 适用于客户端调用主服务API型应用(比如百度API Store)
|
||||
|
||||
在`JustAuth`中是使用的`Authorization Code`授权方式,下面将主要讲解`Authorization Code`的授权流程
|
||||
|
||||
(未完待续)
|
||||
@@ -0,0 +1,8 @@
|
||||
**配套Demo**:
|
||||
- [Springboot版](https://gitee.com/yadong.zhang/JustAuth-demo)
|
||||
- [jFinal版](https://github.com/xkcoding/jfinal-justauth-demo): Jfinal集成JustAuth的demo by [xkcoding](https://github.com/xkcoding)
|
||||
- [ActFramework版](https://github.com/xkcoding/act-justauth-demo): ActFramework 集成 JustAuth 的 demo by [xkcoding](https://github.com/xkcoding)
|
||||
- [Nutzboot版](https://github.com/EggsBlue/nutzboot-justauth-demo): NutzBoot集成JustAuth的demo by [蛋蛋](https://github.com/EggsBlue)
|
||||
|
||||
## 插件
|
||||
- [justauth-spring-boot-starter](https://github.com/xkcoding/justauth-spring-boot-starter): Spring Boot 集成 JustAuth 的最佳实践 by [xkcoding](https://github.com/xkcoding)
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
# 致谢
|
||||
|
||||
在项目立项初期,也对当前开源圈的一些相同类型的项目作过调研,同时本项目也参考过这些项目,再次感谢开源圈内的朋友。
|
||||
|
||||
- [YurunOAuthLogin](https://gitee.com/yurunsoft/YurunOAuthLogin): PHP 第三方登录授权 SDK
|
||||
- [阿里妈妈MUX倾力打造的矢量图标库-iconfont](https://www.iconfont.cn/search/index): 本文档中的图标大部分取自该平台
|
||||
- [mica](https://github.com/lets-mica/mica):Spring Cloud 微服务开发核心包,支持 `web `和 `webflux`。注:JustAuth项目中的[UuidUtils](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/utils/UuidUtils.java)就是直接使用的mica提供的高性能的uuid创建工具类源码[StringUtil.java](https://github.com/lets-mica/mica/blob/master/mica-core/src/main/java/net/dreamlu/mica/core/utils/StringUtil.java#L335)
|
||||
|
||||
|
||||
**感谢 JetBrains 提供的免费开源 License**
|
||||
|
||||
<img src="https://github.com/lets-mica/mica/raw/c251e176b81518a6a570bf4eb21f525c4f582a81/docs/img/jetbrains.png" alt="图片引用自lets-mica" style="float:left;">
|
||||
|
||||
<div style="clear: both;"></div>
|
||||
|
||||
+129
-20
@@ -1,10 +1,96 @@
|
||||
### 2019/07/19
|
||||
## v1.12.0
|
||||
### 2019/09/06
|
||||
|
||||
- 集成“美团”授权登录
|
||||
- 集成“饿了么”授权登录
|
||||
- 升级Fastjson依赖到1.2.60,预防[“Fastjson < 1.2.60 远程拒绝服务漏洞预警”](https://card.weibo.com/article/m/show/id/2309404413257925394542)
|
||||
|
||||
## v1.11.0
|
||||
### 2019/09/03
|
||||
|
||||
- 集成“Gitlab”授权登录
|
||||
|
||||
### 2019/09/02
|
||||
|
||||
- 集成“酷家乐”授权登录
|
||||
|
||||
## v1.10.1
|
||||
### 2019/08/17
|
||||
|
||||
- AuthUser添加构造函数,支持反序列化
|
||||
|
||||
### 2019/08/08
|
||||
|
||||
- 项目迁移到组织[justauth](https://github.com/justauth)
|
||||
|
||||
## v1.10.0
|
||||
### 2019/08/06
|
||||
|
||||
- 合并[PR-34](https://github.com/zhangyd-c/JustAuth/pull/34),添加StringUtil单元测试,修复bug
|
||||
- 合并[PR-35](https://github.com/zhangyd-c/JustAuth/pull/35),集成企业微信
|
||||
|
||||
### 2019/08/05
|
||||
|
||||
- 集成华为登录
|
||||
- 修改`AuthChecker#checkCode`方法,对于不同平台使用不同参数接受code的情况统一做处理
|
||||
|
||||
### 2019/08/03
|
||||
|
||||
合并github上[xkcoding](https://github.com/xkcoding) 的[pr#32](https://github.com/zhangyd-c/JustAuth/pull/32),抽取 cache 接口,方便用户自行集成 cache
|
||||
|
||||
### 2019/08/02
|
||||
|
||||
- 增加`AuthCache`配置类`AuthCacheConfig.java`,可以自定义缓存有效期以及是否开启定时任务
|
||||
- 去掉`slf4j`依赖,封装`Log.java`工具类
|
||||
- 规范测试类
|
||||
|
||||
## v1.9.5
|
||||
### 2019/07/31
|
||||
|
||||
`v1.9.4`版本发布失败,请升级到`1.9.5`版本!
|
||||
|
||||
由此给您带来的不便,敬请谅解!
|
||||
|
||||
## v1.9.4
|
||||
### 2019/07/30
|
||||
|
||||
1. 升级`hutool-http`版本到`v4.6.1`
|
||||
2. 去除`AuthCallback`中增加的默认的校验state的方法,挪到`AuthDefaultRequest`中做统一处理
|
||||
3. `alipay-sdk-java`依赖改为`provided`,如果需要使用支付宝登录,需要使用方手动引入相关依赖,具体操作方式,见项目WIKI;
|
||||
4. 规范注释
|
||||
|
||||
## v1.9.3
|
||||
### 2019/07/30
|
||||
|
||||
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`
|
||||
2. 规范注释
|
||||
|
||||
### 2019/07/25
|
||||
|
||||
1. `AuthConfig`类中去掉state参数
|
||||
2. 删除`AuthState`类
|
||||
3. 增加`authorize(String)`方法,并且使用`@Deprecated`标记`authorize()`方法
|
||||
|
||||
## v1.9.2
|
||||
### 2019/07/22
|
||||
1. 合并github上[xkcoding](https://github.com/xkcoding) 的[pr#26](https://github.com/zhangyd-c/JustAuth/pull/26),AuthConfig类添加lombok注解,方便 [justauth-spring-boot-starter](https://github.com/xkcoding/justauth-spring-boot-starter) 直接使用
|
||||
|
||||
## v1.9.1
|
||||
### 2019/07/22
|
||||
1. 增加`stackoverflow`参数校验
|
||||
2. 解决`Pinterest`获取用户失败的问题
|
||||
3. 添加注释
|
||||
|
||||
### 2019/07/19
|
||||
## v1.9.0
|
||||
### 2019/07/19
|
||||
|
||||
1. 合并github上[@dyc12ii](https://github.com/dyc12ii) 的[pr#25](https://github.com/zhangyd-c/JustAuth/pull/25),升级fastjson版本至1.2.58,避免安全漏洞
|
||||
2. `AuthUserGender`枚举类挪到`enums`包下
|
||||
@@ -32,31 +118,40 @@
|
||||
2. 将CSDN相关的类置为`Deprecated`,后续可能会删除,也可能一直保留。毕竟CSDN的openAPI已经不对外开放了。
|
||||
3. `BaseAuthRequest` 改名为 `AuthDefaultRequest`
|
||||
4. `ResponseStatus` 改名为 `AuthResponseStatus` 并且移动到 `me.zhyd.oauth.model`
|
||||
5. 合并github上[@xkcoding](https://github.com/xkcoding) 的[pr#18](https://github.com/zhangyd-c/JustAuth/pull/18),修复小米回调错误问题 同时 支持微信获取
|
||||
5. 合并github上[@xkcoding](https://github.com/xkcoding) 的[pr#18](https://github.com/zhangyd-c/JustAuth/pull/18),修复小米回调错误问题 同时 支持微信获取unionId
|
||||
|
||||
### 2019/07/15
|
||||
## v1.8.1
|
||||
### 2019/07/15
|
||||
1. 新增 `AuthState` 类,内置默认的state生成规则和校验规则
|
||||
|
||||
### 2019/07/12
|
||||
1. 合并[Braavos96](https://github.com/Braavos96)提交的[PR#16](https://github.com/zhangyd-c/JustAuth/pull/16)
|
||||
|
||||
### 2019/06/28
|
||||
## v1.8.0
|
||||
### 2019/06/28
|
||||
1. 修复百度登录获取不到token失效时间的问题
|
||||
2. 增加state参数校验,预防CSRF。**强烈建议启用state**!
|
||||
|
||||
### 2019/06/27
|
||||
1. 修改login方法的参数为AuthCallback,封装回调返回的参数
|
||||
2. 支持state参数
|
||||
3. 增加code和state参数校验
|
||||
1. 修复百度登录获取不到token失效时间的问题
|
||||
2. 增加state参数校验,预防CSRF。**强烈建议启用state**!
|
||||
3. 修改login方法的参数为AuthCallback,封装回调返回的参数
|
||||
4. 支持state参数
|
||||
5. 增加code和state参数校验
|
||||
|
||||
### 2019/06/25
|
||||
由于state安全问题,1.8.0以前的版本都有隐藏的CSRF漏洞问题,所以强烈建议正在使用JustAuth的朋友升级到1.8.0版本!
|
||||
|
||||
## v1.7.1
|
||||
### 2019/06/25
|
||||
qq授权登录时,需要获取`openId`作为`uuid`,在`1.6.1-beta`和`1.7.0`版本中,引入了`unionId`这一属性。获取`unionid`需要单独向qq团队**发送邮件**申请权限,鉴于这一申请权限的步骤比较麻烦(需要填写的内容比较多),所以在`AuthConfig`中增加了一个`unionId`属性,当为**true**时才会获取unionid,当为false时只获取openId。如果你需要该功能, 则在自行申请了相关权限后,将该属性置为true即可。关于unionId的参考链接:[UnionID介绍](http://wiki.connect.qq.com/unionid%E4%BB%8B%E7%BB%8D)
|
||||
|
||||
### 2019/06/19
|
||||
## v1.7.0
|
||||
### 2019/06/19
|
||||
1. 合并[xkcoding](https://github.com/xkcoding)提交的[PR](https://github.com/zhangyd-c/JustAuth/pull/14),重构了部分代码,jar包由原来的`130+kb`优化到现在的`110+kb`
|
||||
2. 合并[skqing](https://gitee.com/skqing)提交的[PR](https://gitee.com/yadong.zhang/JustAuth/pulls/3), 解决抖音登录失败问题
|
||||
|
||||
### 2019/06/18
|
||||
## v1.6.1-beta
|
||||
### 2019/06/18
|
||||
1. 解决Issue [#IY2HW](https://gitee.com/yadong.zhang/JustAuth/issues/IY2HW)
|
||||
2. 解决Issue [#IY2OH](https://gitee.com/yadong.zhang/JustAuth/issues/IY2OH)
|
||||
3. 解决Issue [#IY2FV](https://gitee.com/yadong.zhang/JustAuth/issues/IY2FV)
|
||||
@@ -64,38 +159,44 @@ qq授权登录时,需要获取`openId`作为`uuid`,在`1.6.1-beta`和`1.7.0`
|
||||
5. 解决Issue [#IY1QR](https://gitee.com/yadong.zhang/JustAuth/issues/IY1QR) 增加对Config属性的校验功能,主要校验redirect uri的合法性
|
||||
6. 合并[skqing](https://gitee.com/skqing)提交的[PR](https://gitee.com/yadong.zhang/JustAuth/pulls/2),解决一些BUG
|
||||
|
||||
### 2019/06/06
|
||||
## v1.6.0-beta
|
||||
### 2019/06/06
|
||||
1. 增加今日头条的授权登陆
|
||||
2. 发布1.6.0-beta版本,今日头条开发者暂时不能认证, 所以无法做测试,等测试通过后,正式发布release版本
|
||||
|
||||
### 2019/05/28
|
||||
## v1.5.0
|
||||
### 2019/05/28
|
||||
1. 增加小米账号和微软的授权登陆
|
||||
2. 发布1.5.0版本
|
||||
|
||||
### 2019/05/26
|
||||
## v1.4.0
|
||||
### 2019/05/26
|
||||
1. 增加抖音和Linkedin的授权登陆
|
||||
2. 修改部分图片命名
|
||||
3. 优化部分代码
|
||||
4. 修复`AuthSource`中腾讯云开发平台的拼写错误:`TENCEN_CLOUD`->`TENCENT_CLOUD`
|
||||
5. 修复支付宝登陆时用户名为空的问题
|
||||
|
||||
|
||||
### 2019/05/24
|
||||
## v1.3.3
|
||||
### 2019/05/24
|
||||
1. 修复一些问题
|
||||
2. 升级api,在AuthUser中增加`uuid`属性,可以通过`uuid` + `source`唯一确定一个用户,此举解决了用户身份归属的问题。
|
||||
3. 发布1.3.3版本的jar包到公开仓库(1.3.2忘记发布了,( ╯□╰ ))
|
||||
4. 重要:经咨询官方客服得知,CSDN的授权开放平台已经下线,如果以前申请过的应用,可以继续使用,但是不再支持申请新的应用。so, 本项目中的CSDN登录只能针对少部分用户使用了
|
||||
|
||||
### 2019/05/23
|
||||
## v1.3.1
|
||||
### 2019/05/23
|
||||
1. 修复QQ登录的问题
|
||||
2. 发布1.3.1版本的jar包到公开仓库
|
||||
|
||||
### 2019/05/21
|
||||
## v1.3.0
|
||||
### 2019/05/21
|
||||
1. 新增google授权登录
|
||||
2. 新增facebook授权登录
|
||||
3. 发布1.3.0版本的jar包到公开仓库
|
||||
|
||||
### 2019/05/18
|
||||
## v1.1.0
|
||||
### 2019/05/18
|
||||
1. 发布1.1.0版本的jar包到公开仓库(支持qq和微信登录)
|
||||
2. 支持淘宝登录
|
||||
3. 修改`AuthUser.java`类中的`accessToken`属性,由原本的~~accessToken (String)~~改为`token (AuthToken)`
|
||||
@@ -104,4 +205,12 @@ qq授权登录时,需要获取`openId`作为`uuid`,在`1.6.1-beta`和`1.7.0`
|
||||
|
||||
### 2019/05/17
|
||||
1. 增加qq和微信的授权登录
|
||||
2. 修改getAccessToken方法的返回值
|
||||
2. 修改getAccessToken方法的返回值
|
||||
|
||||
## v1.0.1
|
||||
### 2019/03/27
|
||||
集成 支付宝授权登录
|
||||
|
||||
## v1.0.0
|
||||
### 2019/03/25
|
||||
史上最全的整合第三方登录的工具,目前已支持Github、Gitee、微博、钉钉和百度、Coding、腾讯云开发者平台和OSChina登录。 Login, so easy!
|
||||
@@ -0,0 +1,11 @@
|
||||
# Who is using?
|
||||
|
||||
- [spring-boot-demo](https://github.com/xkcoding/spring-boot-demo): spring boot demo 是一个用来学习 spring boot 的项目,总共包含 55 个集成demo,已经完成 46 个。(注:[spring-boot-demo-social](https://github.com/xkcoding/spring-boot-demo/tree/master/spring-boot-demo-social)模块中集成了JustAuth)注:该作者是JustAuth的开发者之一,负责开发QQ登录、微信登录、小米登录、微软登录、谷歌登录。
|
||||
- [Guns](https://gitee.com/stylefeng/guns): Guns基于Spring Boot2,致力于做更简洁的后台管理系统。(注:Guns企业版中使用到了JustAuth实现第三方OAuth登录)
|
||||
|
||||
- [Shiro-Action](https://github.com/zhaojun1998/Shiro-Action): 基于 Shiro 的权限管理系统,支持 restful url 授权
|
||||
- [project-template](https://github.com/HobbyBear/project-template): 作为前后端分离项目的后端模板整个项目基于springboot2.1.3,集jsr303框架做参数校验,spring security 做权限管理,并实现限制同一账号登陆会话数量功能,spring mail 发送邮件,justAuth做的第三方登陆, spring session做session共享,orm框架采用jpa,集成七牛云实现图片上传,redis实现分布式锁
|
||||
- [mica](https://github.com/lets-mica/mica/tree/master/mica-social):mica是Spring Cloud 微服务开发核心包。采用源码形式(保留了作者名)另加改了一些代码。
|
||||
...
|
||||
|
||||
我在这儿等你 >>> :alien:
|
||||
@@ -0,0 +1,53 @@
|
||||
# 使用State
|
||||
|
||||
## state使用的流程
|
||||
|
||||
在JustAuth中`state`参数的使用流程如下:
|
||||
|
||||
1. 获取`authorizeUrl`时创建`state`(开发者创建,如果不创建则系统默认生成)
|
||||
2. 缓存`state`(JustAuth执行)
|
||||
3. 内置的缓存调度器自动清除已过期的`state`(JustAuth执行)
|
||||
|
||||
## 创建state(开发者)
|
||||
`state`在OAuth授权流程中是一个**非必要但很重要**的参数,就如[名词解释](https://docs.justauth.whnb.wang/#/explain?id=justauth中的关键词)中描述的:`state`是用来保持授权会话流程完整性,防止CSRF攻击的安全的随机的参数,**由开发者生成**。
|
||||
|
||||
在JustAuth中提供了一个默认的创建state的方法,使用方式:
|
||||
|
||||
```java
|
||||
String state = AuthStateUtils.createState()
|
||||
```
|
||||
|
||||
`createState`的内部实现其实就是生成了一个UUID(采用 jdk 9 的形式,优化性能),该工具是直接copy自[mica](https://github.com/lets-mica/mica/blob/master/mica-core/src/main/java/net/dreamlu/mica/core/utils/StringUtil.java)(`mica`是一个SpringBoot微服务高效开发工具集,开源地址:[https://github.com/lets-mica/mica](https://github.com/lets-mica/mica)),关于mica uuid生成方式的压测结果,可以参考:https://github.com/lets-mica/mica-jmh/wiki/uuid。
|
||||
|
||||
除此之外,开发者还可以自己生成特定的`state`参数。
|
||||
|
||||
## 缓存state(JustAuth)
|
||||
|
||||
在JustAuth中,内置了一个基于map的state缓存器,默认缓存有效期为3分钟(缓存配置见`AuthCacheConfig.java`)。`AuthCacheConfig`中包含两个配置参数:
|
||||
|
||||
- `timeout` 缓存过期时间,默认3分钟
|
||||
- `schedulePrune` 是否开启定时清理过期state的任务,默认开启。如果不开启,则需要开发者自己对state做处理,防止map存入过多内容
|
||||
|
||||
缓存state的操作是在`getRealState`中触发的,不需要开发者自己处理
|
||||
```java
|
||||
/**
|
||||
* 获取state,如果为空, 则默认取当前日期的时间戳
|
||||
*
|
||||
* @param state 原始的state
|
||||
* @return 返回不为null的state
|
||||
*/
|
||||
protected String getRealState(String state) {
|
||||
if (StringUtils.isEmpty(state)) {
|
||||
state = UuidUtils.getUUID();
|
||||
}
|
||||
// 缓存state
|
||||
authStateCache.cache(state, state);
|
||||
return state;
|
||||
}
|
||||
```
|
||||
|
||||
注:关于自定义缓存,请参考下节内容。
|
||||
|
||||
## 清理state(JustAuth)
|
||||
|
||||
JustAuth内置了一个缓存调度器,默认3分钟清理一次过期的`state`,缓存清理时间可以通过`AuthCacheConfig.timeout`进行修改,不建议修改太大。
|
||||
@@ -2,192 +2,265 @@
|
||||
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>me.zhyd.oauth</groupId>
|
||||
<artifactId>JustAuth</artifactId>
|
||||
<version>1.9.1</version>
|
||||
<groupId>me.zhyd.oauth</groupId>
|
||||
<artifactId>JustAuth</artifactId>
|
||||
<version>1.12.0</version>
|
||||
|
||||
<name>JustAuth</name>
|
||||
<name>JustAuth</name>
|
||||
<url>https://gitee.com/yadong.zhang/JustAuth</url>
|
||||
<description>
|
||||
史上最全的整合第三方登录的开源库。目前已支持Github、Gitee、微博、钉钉、百度、Coding、腾讯云开发者平台、OSChina、支付宝、
|
||||
QQ、微信、淘宝、Google、Facebook、抖音、领英、小米、微软、今日头条、Teambition、StackOverflow、Pinterest、人人、华为、企业微信、酷家乐和Gitlab等第三方平台的授权登录。
|
||||
Login, so easy!
|
||||
</description>
|
||||
|
||||
<licenses>
|
||||
<license>
|
||||
<name>MIT</name>
|
||||
<url>https://gitee.com/yadong.zhang/JustAuth/blob/master/LICENSE</url>
|
||||
</license>
|
||||
</licenses>
|
||||
|
||||
<scm>
|
||||
<connection>scm:git:https://gitee.com/yadong.zhang/JustAuth.git</connection>
|
||||
<developerConnection>scm:git:https://gitee.com/yadong.zhang/JustAuth.git</developerConnection>
|
||||
<url>https://gitee.com/yadong.zhang/JustAuth</url>
|
||||
<description>
|
||||
史上最全的整合第三方登录的工具,目前已支持Github、Gitee、微博、钉钉、百度、Coding、腾讯云开发者平台、OSChina、支付宝、QQ、微信、淘宝、Google、Facebook、抖音、领英、小米、微软和今日头条等第三方平台的授权登录。
|
||||
Login, so easy!
|
||||
</description>
|
||||
</scm>
|
||||
|
||||
<licenses>
|
||||
<license>
|
||||
<name>MIT</name>
|
||||
<url>https://gitee.com/yadong.zhang/JustAuth/blob/master/LICENSE</url>
|
||||
</license>
|
||||
</licenses>
|
||||
<developers>
|
||||
<developer>
|
||||
<name>Yadong.Zhang</name>
|
||||
<email>yadong.zhang0415@gmail.com</email>
|
||||
<url>https://www.zhyd.me</url>
|
||||
</developer>
|
||||
<developer>
|
||||
<name>Yangkai.Shen</name>
|
||||
<email>shenyangkai1994@gmail.com</email>
|
||||
<url>https://xkcoding.com</url>
|
||||
</developer>
|
||||
<developer>
|
||||
<name>Hongwei.Peng</name>
|
||||
<email>pengisgood@gmail.com</email>
|
||||
<url>https://github.com/pengisgood</url>
|
||||
</developer>
|
||||
</developers>
|
||||
|
||||
<scm>
|
||||
<connection>scm:git:https://gitee.com/yadong.zhang/JustAuth.git</connection>
|
||||
<developerConnection>scm:git:https://gitee.com/yadong.zhang/JustAuth.git</developerConnection>
|
||||
<url>https://gitee.com/yadong.zhang/JustAuth</url>
|
||||
</scm>
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<java.version>1.8</java.version>
|
||||
<maven.compiler.source>1.8</maven.compiler.source>
|
||||
<maven.compiler.target>1.8</maven.compiler.target>
|
||||
<maven-source.version>2.2.1</maven-source.version>
|
||||
<maven-compiler.version>3.8.1</maven-compiler.version>
|
||||
<maven-javadoc.version>3.1.0</maven-javadoc.version>
|
||||
<cobertura-version>2.7</cobertura-version>
|
||||
<maven-surefire-version>2.20</maven-surefire-version>
|
||||
<maven-gpg-version>1.6</maven-gpg-version>
|
||||
<maven.test.skip>false</maven.test.skip>
|
||||
<hutool-version>4.6.1</hutool-version>
|
||||
<lombok-version>1.18.4</lombok-version>
|
||||
<junit-version>4.11</junit-version>
|
||||
<fastjson-version>1.2.60</fastjson-version>
|
||||
<alipay-sdk-version>3.7.4.ALL</alipay-sdk-version>
|
||||
<jacoco-version>0.8.2</jacoco-version>
|
||||
</properties>
|
||||
|
||||
<developers>
|
||||
<developer>
|
||||
<name>Yadong.Zhang</name>
|
||||
<email>yadong.zhang0415@gmail.com</email>
|
||||
<url>https://www.zhyd.me</url>
|
||||
</developer>
|
||||
<developer>
|
||||
<name>Yangkai.Shen</name>
|
||||
<email>shenyangkai1994@gmail.com</email>
|
||||
<url>https://xkcoding.com</url>
|
||||
</developer>
|
||||
<developer>
|
||||
<name>Hongwei.Peng</name>
|
||||
<email>pengisgood@gmail.com</email>
|
||||
<url>https://github.com/pengisgood</url>
|
||||
</developer>
|
||||
</developers>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>${lombok-version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-http</artifactId>
|
||||
<version>${hutool-version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>${junit-version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>fastjson</artifactId>
|
||||
<version>${fastjson-version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alipay.sdk</groupId>
|
||||
<artifactId>alipay-sdk-java</artifactId>
|
||||
<version>${alipay-sdk-version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<java.version>1.8</java.version>
|
||||
<maven.compiler.source>1.8</maven.compiler.source>
|
||||
<maven.compiler.target>1.8</maven.compiler.target>
|
||||
<maven-source.version>2.2.1</maven-source.version>
|
||||
<maven-compiler.version>3.7.0</maven-compiler.version>
|
||||
<maven.test.skip>true</maven.test.skip>
|
||||
<hutool-version>4.5.15</hutool-version>
|
||||
<lombok-version>1.18.4</lombok-version>
|
||||
<junit-version>4.11</junit-version>
|
||||
<fastjson-version>1.2.58</fastjson-version>
|
||||
<alipay-sdk-version>3.7.4.ALL</alipay-sdk-version>
|
||||
<slf4j-version>1.7.25</slf4j-version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>${lombok-version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-http</artifactId>
|
||||
<version>${hutool-version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>${junit-version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>fastjson</artifactId>
|
||||
<version>${fastjson-version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alipay.sdk</groupId>
|
||||
<artifactId>alipay-sdk-java</artifactId>
|
||||
<version>${alipay-sdk-version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-simple</artifactId>
|
||||
<version>${slf4j-version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<finalName>${project.artifactId}-${project.version}</finalName>
|
||||
<build>
|
||||
<finalName>${project.artifactId}-${project.version}</finalName>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>${maven-compiler.version}</version>
|
||||
<configuration>
|
||||
<encoding>${project.build.sourceEncoding}</encoding>
|
||||
<source>${java.version}</source>
|
||||
<target>${java.version}</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<version>${maven-source.version}</version>
|
||||
<inherited>true</inherited>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>jar-no-fork</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<version>${maven-javadoc.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>${maven-surefire-version}</version>
|
||||
<configuration>
|
||||
<!-- 包含其他测试类 -->
|
||||
<includes>
|
||||
<include>**/*Test.java</include>
|
||||
</includes>
|
||||
<!-- 排除掉AuthRequestTest测试类,该类只做api演示用 -->
|
||||
<excludes>
|
||||
<exclude>**/AuthRequestTest.java</exclude>
|
||||
</excludes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>release</id>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>${maven-compiler.version}</version>
|
||||
<configuration>
|
||||
<encoding>${project.build.sourceEncoding}</encoding>
|
||||
<source>${java.version}</source>
|
||||
<target>${java.version}</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<version>${maven-source.version}</version>
|
||||
<inherited>true</inherited>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>jar-no-fork</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-gpg-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<version>${maven-source.version}</version>
|
||||
<inherited>true</inherited>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>jar-no-fork</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>cobertura-maven-plugin</artifactId>
|
||||
<version>${cobertura-version}</version>
|
||||
<configuration>
|
||||
<formats>
|
||||
<format>html</format>
|
||||
<format>xml</format>
|
||||
</formats>
|
||||
<check/>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.jacoco</groupId>
|
||||
<artifactId>jacoco-maven-plugin</artifactId>
|
||||
<version>${jacoco-version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>prepare-agent</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>report</id>
|
||||
<phase>test</phase>
|
||||
<goals>
|
||||
<goal>report</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-gpg-plugin</artifactId>
|
||||
<version>${maven-gpg-version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>verify</phase>
|
||||
<goals>
|
||||
<goal>sign</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>release</id>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<version>${maven-source.version}</version>
|
||||
<inherited>true</inherited>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>jar-no-fork</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-gpg-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>verify</phase>
|
||||
<goals>
|
||||
<goal>sign</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<distributionManagement>
|
||||
<snapshotRepository>
|
||||
<id>sonatype-nexus-snapshots</id>
|
||||
<url>https://oss.sonatype.org/content/repositories/snapshots/</url>
|
||||
</snapshotRepository>
|
||||
<repository>
|
||||
<id>sonatype-nexus-staging</id>
|
||||
<url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>
|
||||
</repository>
|
||||
</distributionManagement>
|
||||
</profile>
|
||||
</profiles>
|
||||
</build>
|
||||
<distributionManagement>
|
||||
<snapshotRepository>
|
||||
<id>sonatype-nexus-snapshots</id>
|
||||
<url>https://oss.sonatype.org/content/repositories/snapshots/</url>
|
||||
</snapshotRepository>
|
||||
<repository>
|
||||
<id>sonatype-nexus-staging</id>
|
||||
<url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>
|
||||
</repository>
|
||||
</distributionManagement>
|
||||
</profile>
|
||||
<!--私服-->
|
||||
<profile>
|
||||
<id>nexus</id>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-gpg-plugin</artifactId>
|
||||
<version>${maven-gpg-version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>verify</phase>
|
||||
<goals>
|
||||
<goal>sign</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<distributionManagement>
|
||||
<repository>
|
||||
<id>xkcoding-nexus</id>
|
||||
<url>https://nexus.xkcoding.com/repository/maven-releases/</url>
|
||||
</repository>
|
||||
<snapshotRepository>
|
||||
<id>xkcoding-nexus</id>
|
||||
<url>https://nexus.xkcoding.com/repository/maven-snapshots/</url>
|
||||
</snapshotRepository>
|
||||
</distributionManagement>
|
||||
</profile>
|
||||
</profiles>
|
||||
</project>
|
||||
|
||||
@@ -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() {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package me.zhyd.oauth.cache;
|
||||
|
||||
/**
|
||||
* AuthCache配置类
|
||||
*
|
||||
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
|
||||
* @since 1.10.0
|
||||
*/
|
||||
public class AuthCacheConfig {
|
||||
|
||||
/**
|
||||
* 默认缓存过期时间:3分钟
|
||||
* 鉴于授权过程中,根据个人的操作习惯,或者授权平台的不同(google等),每个授权流程的耗时也有差异,不过单个授权流程一般不会太长
|
||||
* 本缓存工具默认的过期时间设置为3分钟,即程序默认认为3分钟内的授权有效,超过3分钟则默认失效,失效后删除
|
||||
*/
|
||||
public static long timeout = 3 * 60 * 1000;
|
||||
|
||||
/**
|
||||
* 是否开启定时{@link AuthDefaultCache#pruneCache()}的任务
|
||||
*/
|
||||
public static boolean schedulePrune = true;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
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 {
|
||||
|
||||
/**
|
||||
* state cache
|
||||
*/
|
||||
private static Map<String, CacheState> stateCache = new ConcurrentHashMap<>();
|
||||
private final ReentrantReadWriteLock cacheLock = new ReentrantReadWriteLock(true);
|
||||
private final Lock writeLock = cacheLock.writeLock();
|
||||
private final Lock readLock = cacheLock.readLock();
|
||||
|
||||
public AuthDefaultCache() {
|
||||
if (AuthCacheConfig.schedulePrune) {
|
||||
this.schedulePrune(AuthCacheConfig.timeout);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置缓存
|
||||
*
|
||||
* @param key 缓存KEY
|
||||
* @param value 缓存内容
|
||||
*/
|
||||
@Override
|
||||
public void set(String key, String value) {
|
||||
set(key, value, AuthCacheConfig.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<CacheState> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package me.zhyd.oauth.cache;
|
||||
|
||||
/**
|
||||
* 默认的state缓存实现
|
||||
*
|
||||
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
|
||||
* @since 1.10.0
|
||||
*/
|
||||
public enum AuthDefaultStateCache implements AuthStateCache {
|
||||
|
||||
/**
|
||||
* 当前实例
|
||||
*/
|
||||
INSTANCE;
|
||||
|
||||
private AuthCache authCache;
|
||||
|
||||
AuthDefaultStateCache() {
|
||||
authCache = new AuthDefaultCache();
|
||||
}
|
||||
|
||||
/**
|
||||
* 存入缓存
|
||||
*
|
||||
* @param key 缓存key
|
||||
* @param value 缓存内容
|
||||
*/
|
||||
@Override
|
||||
public void cache(String key, String value) {
|
||||
authCache.set(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 存入缓存
|
||||
*
|
||||
* @param key 缓存key
|
||||
* @param value 缓存内容
|
||||
* @param timeout 指定缓存过期时间(毫秒)
|
||||
*/
|
||||
@Override
|
||||
public void cache(String key, String value, long timeout) {
|
||||
authCache.set(key, value, timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取缓存内容
|
||||
*
|
||||
* @param key 缓存key
|
||||
* @return 缓存内容
|
||||
*/
|
||||
@Override
|
||||
public String get(String key) {
|
||||
return authCache.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否存在key,如果对应key的value值已过期,也返回false
|
||||
*
|
||||
* @param key 缓存key
|
||||
* @return true:存在key,并且value没过期;false:key不存在或者已过期
|
||||
*/
|
||||
@Override
|
||||
public boolean containsKey(String key) {
|
||||
return authCache.containsKey(key);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package me.zhyd.oauth.cache;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* State缓存接口,方便用户扩展
|
||||
* </p>
|
||||
*
|
||||
* @author yangkai.shen
|
||||
* @since 1.10.0
|
||||
*/
|
||||
public interface AuthStateCache {
|
||||
/**
|
||||
* 存入缓存
|
||||
*
|
||||
* @param key 缓存key
|
||||
* @param value 缓存内容
|
||||
*/
|
||||
void cache(String key, String value);
|
||||
|
||||
/**
|
||||
* 存入缓存
|
||||
*
|
||||
* @param key 缓存key
|
||||
* @param value 缓存内容
|
||||
* @param timeout 指定缓存过期时间(毫秒)
|
||||
*/
|
||||
void cache(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);
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
/**
|
||||
* JustAuth 缓存实现, 提供基础的基于ConcurrentHashMap + ScheduledExecutorService 实现的定时缓存。
|
||||
* 同时对外暴露{@code AuthStateCache}接口,可进行对缓存实现的自定义。
|
||||
*/
|
||||
package me.zhyd.oauth.cache;
|
||||
@@ -1,26 +1,27 @@
|
||||
package me.zhyd.oauth.config;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.*;
|
||||
|
||||
/**
|
||||
* JustAuth配置类
|
||||
*
|
||||
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
|
||||
* @version 1.0
|
||||
* @since 1.8
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class AuthConfig {
|
||||
|
||||
/**
|
||||
* 客户端id:对应个平台的appKey
|
||||
* 客户端id:对应各平台的appKey
|
||||
*/
|
||||
private String clientId;
|
||||
|
||||
/**
|
||||
* 客户端Secret:对应个平台的appSecret
|
||||
* 客户端Secret:对应各平台的appSecret
|
||||
*/
|
||||
private String clientSecret;
|
||||
|
||||
@@ -43,17 +44,18 @@ public class AuthConfig {
|
||||
*/
|
||||
private boolean unionId;
|
||||
|
||||
/**
|
||||
* 一个神奇的参数,最好使用随机的不可测的内容,可以用来防止CSRF攻击
|
||||
* <p>
|
||||
* 1.8.0版本新增参数
|
||||
*/
|
||||
private String state;
|
||||
|
||||
/**
|
||||
* Stack Overflow Key
|
||||
* <p>
|
||||
* 1.9.0版本新增参数
|
||||
*
|
||||
* @since 1.9.0
|
||||
*/
|
||||
private String stackOverflowKey;
|
||||
|
||||
/**
|
||||
* 企业微信,授权方的网页应用ID
|
||||
*
|
||||
* @since 1.10.0
|
||||
*/
|
||||
private String agentId;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,677 @@
|
||||
package me.zhyd.oauth.config;
|
||||
|
||||
import me.zhyd.oauth.enums.AuthResponseStatus;
|
||||
import me.zhyd.oauth.exception.AuthException;
|
||||
|
||||
/**
|
||||
* JustAuth内置的各api需要的url, 用枚举类分平台类型管理
|
||||
*
|
||||
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
|
||||
* @since 1.0
|
||||
*/
|
||||
public enum AuthDefaultSource implements AuthSource {
|
||||
/**
|
||||
* Github
|
||||
*/
|
||||
GITHUB {
|
||||
@Override
|
||||
public String authorize() {
|
||||
return "https://github.com/login/oauth/authorize";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String accessToken() {
|
||||
return "https://github.com/login/oauth/access_token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String userInfo() {
|
||||
return "https://api.github.com/user";
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 新浪微博
|
||||
*/
|
||||
WEIBO {
|
||||
@Override
|
||||
public String authorize() {
|
||||
return "https://api.weibo.com/oauth2/authorize";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String accessToken() {
|
||||
return "https://api.weibo.com/oauth2/access_token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String userInfo() {
|
||||
return "https://api.weibo.com/2/users/show.json";
|
||||
}
|
||||
},
|
||||
/**
|
||||
* gitee
|
||||
*/
|
||||
GITEE {
|
||||
@Override
|
||||
public String authorize() {
|
||||
return "https://gitee.com/oauth/authorize";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String accessToken() {
|
||||
return "https://gitee.com/oauth/token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String userInfo() {
|
||||
return "https://gitee.com/api/v5/user";
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 钉钉
|
||||
*/
|
||||
DINGTALK {
|
||||
@Override
|
||||
public String authorize() {
|
||||
return "https://oapi.dingtalk.com/connect/qrconnect";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String accessToken() {
|
||||
throw new AuthException(AuthResponseStatus.UNSUPPORTED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String userInfo() {
|
||||
return "https://oapi.dingtalk.com/sns/getuserinfo_bycode";
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 百度
|
||||
*/
|
||||
BAIDU {
|
||||
@Override
|
||||
public String authorize() {
|
||||
return "https://openapi.baidu.com/oauth/2.0/authorize";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String accessToken() {
|
||||
return "https://openapi.baidu.com/oauth/2.0/token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String userInfo() {
|
||||
return "https://openapi.baidu.com/rest/2.0/passport/users/getInfo";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String revoke() {
|
||||
return "https://openapi.baidu.com/rest/2.0/passport/auth/revokeAuthorization";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String refresh() {
|
||||
return "https://openapi.baidu.com/oauth/2.0/token";
|
||||
}
|
||||
},
|
||||
/**
|
||||
* csdn
|
||||
*/
|
||||
CSDN {
|
||||
@Override
|
||||
public String authorize() {
|
||||
return "https://api.csdn.net/oauth2/authorize";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String accessToken() {
|
||||
return "https://api.csdn.net/oauth2/access_token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String userInfo() {
|
||||
return "https://api.csdn.net/user/getinfo";
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Coding
|
||||
*/
|
||||
CODING {
|
||||
@Override
|
||||
public String authorize() {
|
||||
return "https://coding.net/oauth_authorize.html";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String accessToken() {
|
||||
return "https://coding.net/api/oauth/access_token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String userInfo() {
|
||||
return "https://coding.net/api/account/current_user";
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 腾讯云开发者平台(coding升级后就变成腾讯云开发者平台了)
|
||||
*/
|
||||
TENCENT_CLOUD {
|
||||
@Override
|
||||
public String authorize() {
|
||||
return "https://dev.tencent.com/oauth_authorize.html";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String accessToken() {
|
||||
return "https://dev.tencent.com/api/oauth/access_token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String userInfo() {
|
||||
return "https://dev.tencent.com/api/account/current_user";
|
||||
}
|
||||
},
|
||||
/**
|
||||
* oschina 开源中国
|
||||
*/
|
||||
OSCHINA {
|
||||
@Override
|
||||
public String authorize() {
|
||||
return "https://www.oschina.net/action/oauth2/authorize";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String accessToken() {
|
||||
return "https://www.oschina.net/action/openapi/token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String userInfo() {
|
||||
return "https://www.oschina.net/action/openapi/user";
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 支付宝
|
||||
*/
|
||||
ALIPAY {
|
||||
@Override
|
||||
public String authorize() {
|
||||
return "https://openauth.alipay.com/oauth2/publicAppAuthorize.htm";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String accessToken() {
|
||||
return "https://openapi.alipay.com/gateway.do";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String userInfo() {
|
||||
return "https://openapi.alipay.com/gateway.do";
|
||||
}
|
||||
},
|
||||
/**
|
||||
* QQ
|
||||
*/
|
||||
QQ {
|
||||
@Override
|
||||
public String authorize() {
|
||||
return "https://graph.qq.com/oauth2.0/authorize";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String accessToken() {
|
||||
return "https://graph.qq.com/oauth2.0/token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String userInfo() {
|
||||
return "https://graph.qq.com/user/get_user_info";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String refresh() {
|
||||
return "https://graph.qq.com/oauth2.0/token";
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 微信
|
||||
*/
|
||||
WECHAT {
|
||||
@Override
|
||||
public String authorize() {
|
||||
return "https://open.weixin.qq.com/connect/qrconnect";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String accessToken() {
|
||||
return "https://api.weixin.qq.com/sns/oauth2/access_token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String userInfo() {
|
||||
return "https://api.weixin.qq.com/sns/userinfo";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String refresh() {
|
||||
return "https://api.weixin.qq.com/sns/oauth2/refresh_token";
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 淘宝
|
||||
*/
|
||||
TAOBAO {
|
||||
@Override
|
||||
public String authorize() {
|
||||
return "https://oauth.taobao.com/authorize";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String accessToken() {
|
||||
return "https://oauth.taobao.com/token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String userInfo() {
|
||||
throw new AuthException(AuthResponseStatus.UNSUPPORTED);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Google
|
||||
*/
|
||||
GOOGLE {
|
||||
@Override
|
||||
public String authorize() {
|
||||
return "https://accounts.google.com/o/oauth2/v2/auth";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String accessToken() {
|
||||
return "https://www.googleapis.com/oauth2/v4/token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String userInfo() {
|
||||
return "https://www.googleapis.com/oauth2/v3/userinfo";
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Facebook
|
||||
*/
|
||||
FACEBOOK {
|
||||
@Override
|
||||
public String authorize() {
|
||||
return "https://www.facebook.com/v3.3/dialog/oauth";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String accessToken() {
|
||||
return "https://graph.facebook.com/v3.3/oauth/access_token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String userInfo() {
|
||||
return "https://graph.facebook.com/v3.3/me";
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 抖音
|
||||
*/
|
||||
DOUYIN {
|
||||
@Override
|
||||
public String authorize() {
|
||||
return "https://open.douyin.com/platform/oauth/connect";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String accessToken() {
|
||||
return "https://open.douyin.com/oauth/access_token/";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String userInfo() {
|
||||
return "https://open.douyin.com/oauth/userinfo/";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String refresh() {
|
||||
return "https://open.douyin.com/oauth/refresh_token/";
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 领英
|
||||
*/
|
||||
LINKEDIN {
|
||||
@Override
|
||||
public String authorize() {
|
||||
return "https://www.linkedin.com/oauth/v2/authorization";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String accessToken() {
|
||||
return "https://www.linkedin.com/oauth/v2/accessToken";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String userInfo() {
|
||||
return "https://api.linkedin.com/v2/me";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String refresh() {
|
||||
return "https://www.linkedin.com/oauth/v2/accessToken";
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 微软
|
||||
*/
|
||||
MICROSOFT {
|
||||
@Override
|
||||
public String authorize() {
|
||||
return "https://login.microsoftonline.com/common/oauth2/v2.0/authorize";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String accessToken() {
|
||||
return "https://login.microsoftonline.com/common/oauth2/v2.0/token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String userInfo() {
|
||||
return "https://graph.microsoft.com/v1.0/me";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String refresh() {
|
||||
return "https://login.microsoftonline.com/common/oauth2/v2.0/token";
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 小米
|
||||
*/
|
||||
MI {
|
||||
@Override
|
||||
public String authorize() {
|
||||
return "https://account.xiaomi.com/oauth2/authorize";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String accessToken() {
|
||||
return "https://account.xiaomi.com/oauth2/token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String userInfo() {
|
||||
return "https://open.account.xiaomi.com/user/profile";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String refresh() {
|
||||
return "https://account.xiaomi.com/oauth2/token";
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 今日头条
|
||||
*/
|
||||
TOUTIAO {
|
||||
@Override
|
||||
public String authorize() {
|
||||
return "https://open.snssdk.com/auth/authorize";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String accessToken() {
|
||||
return "https://open.snssdk.com/auth/token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String userInfo() {
|
||||
return "https://open.snssdk.com/data/user_profile";
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Teambition
|
||||
*/
|
||||
TEAMBITION {
|
||||
@Override
|
||||
public String authorize() {
|
||||
return "https://account.teambition.com/oauth2/authorize";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String accessToken() {
|
||||
return "https://account.teambition.com/oauth2/access_token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String refresh() {
|
||||
return "https://account.teambition.com/oauth2/refresh_token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String userInfo() {
|
||||
return "https://api.teambition.com/users/me";
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 人人网
|
||||
*/
|
||||
RENREN {
|
||||
@Override
|
||||
public String authorize() {
|
||||
return "https://graph.renren.com/oauth/authorize";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String accessToken() {
|
||||
return "https://graph.renren.com/oauth/token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String refresh() {
|
||||
return "https://graph.renren.com/oauth/token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String userInfo() {
|
||||
return "https://api.renren.com/v2/user/get";
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Pinterest
|
||||
*/
|
||||
PINTEREST {
|
||||
@Override
|
||||
public String authorize() {
|
||||
return "https://api.pinterest.com/oauth";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String accessToken() {
|
||||
return "https://api.pinterest.com/v1/oauth/token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String userInfo() {
|
||||
return "https://api.pinterest.com/v1/me";
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Stack Overflow
|
||||
*/
|
||||
STACK_OVERFLOW {
|
||||
@Override
|
||||
public String authorize() {
|
||||
return "https://stackoverflow.com/oauth";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String accessToken() {
|
||||
return "https://stackoverflow.com/oauth/access_token/json";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String userInfo() {
|
||||
return "https://api.stackexchange.com/2.2/me";
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 华为
|
||||
*
|
||||
* @since 1.10.0
|
||||
*/
|
||||
HUAWEI {
|
||||
@Override
|
||||
public String authorize() {
|
||||
return "https://oauth-login.cloud.huawei.com/oauth2/v2/authorize";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String accessToken() {
|
||||
return "https://oauth-login.cloud.huawei.com/oauth2/v2/token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String userInfo() {
|
||||
return "https://api.vmall.com/rest.php";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String refresh() {
|
||||
return "https://oauth-login.cloud.huawei.com/oauth2/v2/token";
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 企业微信
|
||||
*
|
||||
* @since 1.10.0
|
||||
*/
|
||||
WECHAT_ENTERPRISE {
|
||||
@Override
|
||||
public String authorize() {
|
||||
return "https://open.work.weixin.qq.com/wwopen/sso/qrConnect";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String accessToken() {
|
||||
return "https://qyapi.weixin.qq.com/cgi-bin/gettoken";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String userInfo() {
|
||||
return "https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo";
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 酷家乐
|
||||
*
|
||||
* @since 1.11.0
|
||||
*/
|
||||
KUJIALE {
|
||||
@Override
|
||||
public String authorize() {
|
||||
return "https://oauth.kujiale.com/oauth2/show";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String accessToken() {
|
||||
return "https://oauth.kujiale.com/oauth2/auth/token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String userInfo() {
|
||||
return "https://oauth.kujiale.com/oauth2/openapi/user";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String refresh() {
|
||||
return "https://oauth.kujiale.com/oauth2/auth/token/refresh";
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Gitlab
|
||||
*
|
||||
* @since 1.11.0
|
||||
*/
|
||||
GITLAB {
|
||||
@Override
|
||||
public String authorize() {
|
||||
return "https://gitlab.com/oauth/authorize";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String accessToken() {
|
||||
return "https://gitlab.com/oauth/token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String userInfo() {
|
||||
return "https://gitlab.com/api/v4/user";
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 美团
|
||||
*
|
||||
* @since 1.12.0
|
||||
*/
|
||||
MEITUAN {
|
||||
@Override
|
||||
public String authorize() {
|
||||
return "https://openapi.waimai.meituan.com/oauth/authorize";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String accessToken() {
|
||||
return "https://openapi.waimai.meituan.com/oauth/access_token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String userInfo() {
|
||||
return "https://openapi.waimai.meituan.com/oauth/userinfo";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String refresh() {
|
||||
return "https://openapi.waimai.meituan.com/oauth/refresh_token";
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 饿了么
|
||||
* <p>
|
||||
* 注:集成的是正式环境,非沙箱环境
|
||||
*
|
||||
* @since 1.12.0
|
||||
*/
|
||||
ELEME {
|
||||
@Override
|
||||
public String authorize() {
|
||||
return "https://open-api.shop.ele.me/authorize";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String accessToken() {
|
||||
return "https://open-api.shop.ele.me/token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String userInfo() {
|
||||
return "https://open-api.shop.ele.me/api/v1/";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String refresh() {
|
||||
return "https://open-api.shop.ele.me/token";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,553 +1,55 @@
|
||||
package me.zhyd.oauth.config;
|
||||
|
||||
import me.zhyd.oauth.enums.AuthResponseStatus;
|
||||
import me.zhyd.oauth.exception.AuthException;
|
||||
import me.zhyd.oauth.model.AuthResponseStatus;
|
||||
import me.zhyd.oauth.model.AuthCallback;
|
||||
|
||||
/**
|
||||
* 各api需要的url, 用枚举类分平台类型管理
|
||||
* OAuth平台的API管理类的统一接口,提供以下接口:
|
||||
* 1) {@link AuthSource#authorize()}: 获取授权api. 必须实现
|
||||
* 2) {@link AuthSource#accessToken()}: 获取授权api. 必须实现
|
||||
* 3) {@link AuthSource#userInfo()}: 获取授权api. 必须实现
|
||||
* 4) {@link AuthSource#revoke()}: 获取授权api. 非必须实现接口(部分平台不支持)
|
||||
* 5) {@link AuthSource#refresh()} ()}: 获取授权api. 非必须实现接口(部分平台不支持)
|
||||
* <p>
|
||||
* 注:
|
||||
* ①、如需通过JustAuth扩展实现第三方授权,请参考{@link AuthDefaultSource}自行创建对应的枚举类并实现{@link AuthSource}接口
|
||||
* ②、如果不是使用的枚举类,那么在授权成功后获取用户信息时,需要单独处理sourcec字段的赋值
|
||||
* ③、如果扩展了对应枚举类时,在{@link me.zhyd.oauth.request.AuthRequest#login(AuthCallback)}中可以通过{@code xx.toString()}获取对应的source
|
||||
*
|
||||
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
|
||||
* @version 1.0
|
||||
* @since 1.0
|
||||
* @since 1.12.0
|
||||
*/
|
||||
public enum AuthSource {
|
||||
/**
|
||||
* Github
|
||||
*/
|
||||
GITHUB {
|
||||
@Override
|
||||
public String authorize() {
|
||||
return "https://github.com/login/oauth/authorize";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String accessToken() {
|
||||
return "https://github.com/login/oauth/access_token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String userInfo() {
|
||||
return "https://api.github.com/user";
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 新浪微博
|
||||
*/
|
||||
WEIBO {
|
||||
@Override
|
||||
public String authorize() {
|
||||
return "https://api.weibo.com/oauth2/authorize";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String accessToken() {
|
||||
return "https://api.weibo.com/oauth2/access_token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String userInfo() {
|
||||
return "https://api.weibo.com/2/users/show.json";
|
||||
}
|
||||
},
|
||||
/**
|
||||
* gitee
|
||||
*/
|
||||
GITEE {
|
||||
@Override
|
||||
public String authorize() {
|
||||
return "https://gitee.com/oauth/authorize";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String accessToken() {
|
||||
return "https://gitee.com/oauth/token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String userInfo() {
|
||||
return "https://gitee.com/api/v5/user";
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 钉钉
|
||||
*/
|
||||
DINGTALK {
|
||||
@Override
|
||||
public String authorize() {
|
||||
return "https://oapi.dingtalk.com/connect/qrconnect";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String accessToken() {
|
||||
throw new AuthException(AuthResponseStatus.UNSUPPORTED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String userInfo() {
|
||||
return "https://oapi.dingtalk.com/sns/getuserinfo_bycode";
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 百度
|
||||
*/
|
||||
BAIDU {
|
||||
@Override
|
||||
public String authorize() {
|
||||
return "https://openapi.baidu.com/oauth/2.0/authorize";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String accessToken() {
|
||||
return "https://openapi.baidu.com/oauth/2.0/token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String userInfo() {
|
||||
return "https://openapi.baidu.com/rest/2.0/passport/users/getInfo";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String revoke() {
|
||||
return "https://openapi.baidu.com/rest/2.0/passport/auth/revokeAuthorization";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String refresh() {
|
||||
return "https://openapi.baidu.com/oauth/2.0/token";
|
||||
}
|
||||
},
|
||||
/**
|
||||
* csdn
|
||||
*/
|
||||
CSDN {
|
||||
@Override
|
||||
public String authorize() {
|
||||
return "https://api.csdn.net/oauth2/authorize";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String accessToken() {
|
||||
return "https://api.csdn.net/oauth2/access_token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String userInfo() {
|
||||
return "https://api.csdn.net/user/getinfo";
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Coding
|
||||
*/
|
||||
CODING {
|
||||
@Override
|
||||
public String authorize() {
|
||||
return "https://coding.net/oauth_authorize.html";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String accessToken() {
|
||||
return "https://coding.net/api/oauth/access_token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String userInfo() {
|
||||
return "https://coding.net/api/account/current_user";
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 腾讯云开发者平台(coding升级后就变成腾讯云开发者平台了)
|
||||
*/
|
||||
TENCENT_CLOUD {
|
||||
@Override
|
||||
public String authorize() {
|
||||
return "https://dev.tencent.com/oauth_authorize.html";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String accessToken() {
|
||||
return "https://dev.tencent.com/api/oauth/access_token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String userInfo() {
|
||||
return "https://dev.tencent.com/api/account/current_user";
|
||||
}
|
||||
},
|
||||
/**
|
||||
* oschina 开源中国
|
||||
*/
|
||||
OSCHINA {
|
||||
@Override
|
||||
public String authorize() {
|
||||
return "https://www.oschina.net/action/oauth2/authorize";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String accessToken() {
|
||||
return "https://www.oschina.net/action/openapi/token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String userInfo() {
|
||||
return "https://www.oschina.net/action/openapi/user";
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 支付宝
|
||||
*/
|
||||
ALIPAY {
|
||||
@Override
|
||||
public String authorize() {
|
||||
return "https://openauth.alipay.com/oauth2/publicAppAuthorize.htm";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String accessToken() {
|
||||
return "https://openapi.alipay.com/gateway.do";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String userInfo() {
|
||||
return "https://openapi.alipay.com/gateway.do";
|
||||
}
|
||||
},
|
||||
/**
|
||||
* QQ
|
||||
*/
|
||||
QQ {
|
||||
@Override
|
||||
public String authorize() {
|
||||
return "https://graph.qq.com/oauth2.0/authorize";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String accessToken() {
|
||||
return "https://graph.qq.com/oauth2.0/token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String userInfo() {
|
||||
return "https://graph.qq.com/user/get_user_info";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String refresh() {
|
||||
return "https://graph.qq.com/oauth2.0/token";
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 微信
|
||||
*/
|
||||
WECHAT {
|
||||
@Override
|
||||
public String authorize() {
|
||||
return "https://open.weixin.qq.com/connect/qrconnect";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String accessToken() {
|
||||
return "https://api.weixin.qq.com/sns/oauth2/access_token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String userInfo() {
|
||||
return "https://api.weixin.qq.com/sns/userinfo";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String refresh() {
|
||||
return "https://api.weixin.qq.com/sns/oauth2/refresh_token";
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 淘宝
|
||||
*/
|
||||
TAOBAO {
|
||||
@Override
|
||||
public String authorize() {
|
||||
return "https://oauth.taobao.com/authorize";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String accessToken() {
|
||||
return "https://oauth.taobao.com/token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String userInfo() {
|
||||
throw new AuthException(AuthResponseStatus.UNSUPPORTED);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Google
|
||||
*/
|
||||
GOOGLE {
|
||||
@Override
|
||||
public String authorize() {
|
||||
return "https://accounts.google.com/o/oauth2/v2/auth";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String accessToken() {
|
||||
return "https://www.googleapis.com/oauth2/v4/token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String userInfo() {
|
||||
return "https://www.googleapis.com/oauth2/v3/userinfo";
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Facebook
|
||||
*/
|
||||
FACEBOOK {
|
||||
@Override
|
||||
public String authorize() {
|
||||
return "https://www.facebook.com/v3.3/dialog/oauth";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String accessToken() {
|
||||
return "https://graph.facebook.com/v3.3/oauth/access_token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String userInfo() {
|
||||
return "https://graph.facebook.com/v3.3/me";
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 抖音
|
||||
*/
|
||||
DOUYIN {
|
||||
@Override
|
||||
public String authorize() {
|
||||
return "https://open.douyin.com/platform/oauth/connect";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String accessToken() {
|
||||
return "https://open.douyin.com/oauth/access_token/";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String userInfo() {
|
||||
return "https://open.douyin.com/oauth/userinfo/";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String refresh() {
|
||||
return "https://open.douyin.com/oauth/refresh_token/";
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 领英
|
||||
*/
|
||||
LINKEDIN {
|
||||
@Override
|
||||
public String authorize() {
|
||||
return "https://www.linkedin.com/oauth/v2/authorization";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String accessToken() {
|
||||
return "https://www.linkedin.com/oauth/v2/accessToken";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String userInfo() {
|
||||
return "https://api.linkedin.com/v2/me";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String refresh() {
|
||||
return "https://www.linkedin.com/oauth/v2/accessToken";
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 微软
|
||||
*/
|
||||
MICROSOFT {
|
||||
@Override
|
||||
public String authorize() {
|
||||
return "https://login.microsoftonline.com/common/oauth2/v2.0/authorize";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String accessToken() {
|
||||
return "https://login.microsoftonline.com/common/oauth2/v2.0/token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String userInfo() {
|
||||
return "https://graph.microsoft.com/v1.0/me";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String refresh() {
|
||||
return "https://login.microsoftonline.com/common/oauth2/v2.0/token";
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 小米
|
||||
*/
|
||||
MI {
|
||||
@Override
|
||||
public String authorize() {
|
||||
return "https://account.xiaomi.com/oauth2/authorize";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String accessToken() {
|
||||
return "https://account.xiaomi.com/oauth2/token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String userInfo() {
|
||||
return "https://open.account.xiaomi.com/user/profile";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String refresh() {
|
||||
return "https://account.xiaomi.com/oauth2/token";
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 今日头条
|
||||
*/
|
||||
TOUTIAO {
|
||||
@Override
|
||||
public String authorize() {
|
||||
return "https://open.snssdk.com/auth/authorize";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String accessToken() {
|
||||
return "https://open.snssdk.com/auth/token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String userInfo() {
|
||||
return "https://open.snssdk.com/data/user_profile";
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Teambition
|
||||
*/
|
||||
TEAMBITION {
|
||||
@Override
|
||||
public String authorize() {
|
||||
return "https://account.teambition.com/oauth2/authorize";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String accessToken() {
|
||||
return "https://account.teambition.com/oauth2/access_token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String refresh() {
|
||||
return "https://account.teambition.com/oauth2/refresh_token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String userInfo() {
|
||||
return "https://api.teambition.com/users/me";
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 人人网
|
||||
*/
|
||||
RENREN {
|
||||
@Override
|
||||
public String authorize() {
|
||||
return "https://graph.renren.com/oauth/authorize";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String accessToken() {
|
||||
return "https://graph.renren.com/oauth/token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String refresh() {
|
||||
return "https://graph.renren.com/oauth/token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String userInfo() {
|
||||
return "https://api.renren.com/v2/user/get";
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Pinterest
|
||||
*/
|
||||
PINTEREST {
|
||||
@Override
|
||||
public String authorize() {
|
||||
return "https://api.pinterest.com/oauth";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String accessToken() {
|
||||
return "https://api.pinterest.com/v1/oauth/token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String userInfo() {
|
||||
return "https://api.pinterest.com/v1/me";
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Stack Overflow
|
||||
*/
|
||||
STACK_OVERFLOW {
|
||||
@Override
|
||||
public String authorize() {
|
||||
return "https://stackoverflow.com/oauth";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String accessToken() {
|
||||
return "https://stackoverflow.com/oauth/access_token/json";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String userInfo() {
|
||||
return "https://api.stackexchange.com/2.2/me";
|
||||
}
|
||||
};
|
||||
public interface AuthSource {
|
||||
|
||||
/**
|
||||
* 授权的api
|
||||
*
|
||||
* @return url
|
||||
*/
|
||||
public abstract String authorize();
|
||||
String authorize();
|
||||
|
||||
/**
|
||||
* 获取accessToken的api
|
||||
*
|
||||
* @return url
|
||||
*/
|
||||
public abstract String accessToken();
|
||||
String accessToken();
|
||||
|
||||
/**
|
||||
* 获取用户信息的api
|
||||
*
|
||||
* @return url
|
||||
*/
|
||||
public abstract String userInfo();
|
||||
String userInfo();
|
||||
|
||||
/**
|
||||
* 取消授权的api
|
||||
*
|
||||
* @return url
|
||||
*/
|
||||
public String revoke() {
|
||||
default String revoke() {
|
||||
throw new AuthException(AuthResponseStatus.UNSUPPORTED);
|
||||
}
|
||||
|
||||
@@ -556,8 +58,7 @@ public enum AuthSource {
|
||||
*
|
||||
* @return url
|
||||
*/
|
||||
public String refresh() {
|
||||
default String refresh() {
|
||||
throw new AuthException(AuthResponseStatus.UNSUPPORTED);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* JustAuth 核心配置相关,包括{@code AuthConfig}和{@code AuthSource}
|
||||
*/
|
||||
package me.zhyd.oauth.config;
|
||||
+8
-3
@@ -1,22 +1,27 @@
|
||||
package me.zhyd.oauth.model;
|
||||
package me.zhyd.oauth.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* JustAuth通用的状态码对照表
|
||||
*
|
||||
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
|
||||
* @version 1.0
|
||||
* @since 1.8
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum AuthResponseStatus {
|
||||
/**
|
||||
* 2000:正常;
|
||||
* other:调用异常,具体异常内容见{@code msg}
|
||||
*/
|
||||
SUCCESS(2000, "Success"),
|
||||
FAILURE(5000, "Failure"),
|
||||
NOT_IMPLEMENTED(5001, "Not Implemented"),
|
||||
PARAMETER_INCOMPLETE(5002, "Parameter incomplete"),
|
||||
UNSUPPORTED(5003, "Unsupported operation"),
|
||||
NO_AUTH_SOURCE(5004, "AuthSource cannot be null"),
|
||||
NO_AUTH_SOURCE(5004, "AuthDefaultSource cannot be null"),
|
||||
UNIDENTIFIED_PLATFORM(5005, "Unidentified platform"),
|
||||
ILLEGAL_REDIRECT_URI(5006, "Illegal redirect uri"),
|
||||
ILLEGAL_REQUEST(5007, "Illegal request"),
|
||||
@@ -7,12 +7,15 @@ import lombok.Getter;
|
||||
* 今日头条授权登录时的异常状态码
|
||||
*
|
||||
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
|
||||
* @version 1.0
|
||||
* @since 1.8
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum AuthToutiaoErrorCode {
|
||||
/**
|
||||
* 0:正常;
|
||||
* other:调用异常,具体异常内容见{@code desc}
|
||||
*/
|
||||
EC0(0, "接口调用成功"),
|
||||
EC1(1, "API配置错误,未传入Client Key"),
|
||||
EC2(2, "API配置错误,Client Key错误,请检查是否和开放平台的ClientKey一致"),
|
||||
|
||||
@@ -9,13 +9,19 @@ import java.util.Arrays;
|
||||
* 用户性别
|
||||
*
|
||||
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
|
||||
* @version 1.0
|
||||
* @since 1.8
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum AuthUserGender {
|
||||
MALE(1, "男"), FEMALE(0, "女"), UNKNOWN(-1, "未知");
|
||||
/**
|
||||
* MALE/FAMALE为正常值,通过{@link AuthUserGender#getRealGender(String)}方法获取真实的性别
|
||||
* UNKNOWN为容错值,部分平台不会返回用户性别,为了方便统一,使用UNKNOWN标记所有未知或不可测的用户性别信息
|
||||
*/
|
||||
MALE(1, "男"),
|
||||
FEMALE(0, "女"),
|
||||
UNKNOWN(-1, "未知");
|
||||
|
||||
private int code;
|
||||
private String desc;
|
||||
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* 提供一些必要的枚举类
|
||||
*/
|
||||
package me.zhyd.oauth.enums;
|
||||
@@ -1,10 +1,11 @@
|
||||
package me.zhyd.oauth.exception;
|
||||
|
||||
import me.zhyd.oauth.model.AuthResponseStatus;
|
||||
import me.zhyd.oauth.enums.AuthResponseStatus;
|
||||
|
||||
/**
|
||||
* JustAuth通用异常类
|
||||
*
|
||||
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
|
||||
* @version 1.0
|
||||
* @since 1.8
|
||||
*/
|
||||
public class AuthException extends RuntimeException {
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* JustAuth专用异常封装
|
||||
*/
|
||||
package me.zhyd.oauth.exception;
|
||||
@@ -0,0 +1,150 @@
|
||||
package me.zhyd.oauth.log;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.io.PrintStream;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
/**
|
||||
* 针对JustAuth提供的轻量级的日志打印工具
|
||||
*
|
||||
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
|
||||
* @see Log#debug(String)
|
||||
* @see Log#debug(String, Throwable)
|
||||
* @see Log#warn(String)
|
||||
* @see Log#warn(String, Throwable)
|
||||
* @see Log#error(String)
|
||||
* @see Log#error(String, Throwable)
|
||||
* @since 1.10.0
|
||||
*/
|
||||
public class Log {
|
||||
|
||||
public static void debug(String msg) {
|
||||
debug(msg, null);
|
||||
}
|
||||
|
||||
public static void warn(String msg) {
|
||||
warn(msg, null);
|
||||
}
|
||||
|
||||
public static void error(String msg) {
|
||||
error(msg, null);
|
||||
}
|
||||
|
||||
public static void debug(String msg, Throwable t) {
|
||||
print(Level.DEBUG, msg, t, System.out);
|
||||
}
|
||||
|
||||
public static void warn(String msg, Throwable t) {
|
||||
print(Level.WARN, msg, t, System.out);
|
||||
}
|
||||
|
||||
public static void error(String msg, Throwable t) {
|
||||
print(Level.ERROR, msg, t, System.err);
|
||||
}
|
||||
|
||||
/**
|
||||
* 打印日志内容,格式:2019-08-02 20:44:07 main me.zhyd.oauth.log.Log(debug:39) [DEBUG] - xxxx
|
||||
*
|
||||
* @param level 日志级别
|
||||
* @param msg 日志内容
|
||||
* @param t 异常信息
|
||||
* @param ps 实际执行打印的PrintStream
|
||||
*/
|
||||
private static void print(Level level, String msg, Throwable t, PrintStream ps) {
|
||||
if (Config.enable) {
|
||||
if (level.getLevelNum() >= Config.level.getLevelNum()) {
|
||||
ps.println(String.format("%s %s %s [%s] - %s", getDate(), Thread.currentThread().getName(), getCaller(), level, msg));
|
||||
writeThrowable(t, ps);
|
||||
ps.flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取调用方的信息
|
||||
*
|
||||
* @return 返回调用方的信息,格式:class(method:lineNumber)
|
||||
*/
|
||||
private static String getCaller() {
|
||||
int offset = 2;
|
||||
StackTraceElement[] stackTraceArr = (new Throwable()).getStackTrace();
|
||||
StackTraceElement stackTrace = null;
|
||||
if (offset >= stackTraceArr.length) {
|
||||
offset = offset - 1;
|
||||
}
|
||||
stackTrace = stackTraceArr[offset];
|
||||
return stackTrace.getClassName() +
|
||||
"(" +
|
||||
stackTrace.getMethodName() +
|
||||
':' +
|
||||
stackTrace.getLineNumber() +
|
||||
")";
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取格式化后的日期
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private static String getDate() {
|
||||
return LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
|
||||
}
|
||||
|
||||
/**
|
||||
* 打印异常信息
|
||||
*
|
||||
* @param t 异常
|
||||
* @param targetStream 实际执行打印的PrintStream
|
||||
*/
|
||||
private static void writeThrowable(Throwable t, PrintStream targetStream) {
|
||||
if (t != null) {
|
||||
t.printStackTrace(targetStream);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 日志级别
|
||||
*
|
||||
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
|
||||
* @since 1.10.0
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum Level {
|
||||
/**
|
||||
* DEBUG: 普通级别
|
||||
*/
|
||||
DEBUG(10),
|
||||
/**
|
||||
* WARN: 警告级别
|
||||
*/
|
||||
WARN(30),
|
||||
/**
|
||||
* ERROR: 异常级别
|
||||
*/
|
||||
ERROR(40);
|
||||
|
||||
private int levelNum;
|
||||
}
|
||||
|
||||
/**
|
||||
* 日志配置
|
||||
*
|
||||
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
|
||||
* @since 1.10.0
|
||||
*/
|
||||
static class Config {
|
||||
|
||||
/**
|
||||
* 需要打印的日志级别
|
||||
*/
|
||||
static Level level = Level.DEBUG;
|
||||
/**
|
||||
* 是否启用日志打印功能,默认启用
|
||||
*/
|
||||
static boolean enable = true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* 针对JustAuth简单封装的日志打印工具,可用过{@link me.zhyd.oauth.log.Log.Config}开关日志和指定日志级别
|
||||
*/
|
||||
package me.zhyd.oauth.log;
|
||||
@@ -7,8 +7,7 @@ import lombok.Setter;
|
||||
* 授权回调时的参数类
|
||||
*
|
||||
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
|
||||
* @version 1.0
|
||||
* @since 1.8
|
||||
* @since 1.8.0
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@@ -28,4 +27,11 @@ public class AuthCallback {
|
||||
* 访问AuthorizeUrl后回调时带的参数state,用于和请求AuthorizeUrl前的state比较,防止CSRF攻击
|
||||
*/
|
||||
private String state;
|
||||
|
||||
/**
|
||||
* 华为授权登录接受code的参数名
|
||||
*
|
||||
* @since 1.10.0
|
||||
*/
|
||||
private String authorization_code;
|
||||
}
|
||||
|
||||
@@ -2,13 +2,12 @@ package me.zhyd.oauth.model;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import me.zhyd.oauth.enums.AuthResponseStatus;
|
||||
|
||||
/**
|
||||
* JustAuth统一授权响应类
|
||||
*
|
||||
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
|
||||
* @version 1.0
|
||||
* @since 1.8
|
||||
*/
|
||||
@Getter
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package me.zhyd.oauth.model;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@@ -9,7 +8,6 @@ import lombok.Setter;
|
||||
* 授权所需的token
|
||||
*
|
||||
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
|
||||
* @version 1.0
|
||||
* @since 1.8
|
||||
*/
|
||||
@Getter
|
||||
@@ -37,4 +35,11 @@ public class AuthToken {
|
||||
private String macAlgorithm;
|
||||
private String macKey;
|
||||
|
||||
/**
|
||||
* 企业微信附带属性
|
||||
*
|
||||
* @since 1.10.0
|
||||
*/
|
||||
private String code;
|
||||
|
||||
}
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
package me.zhyd.oauth.model;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import me.zhyd.oauth.config.AuthSource;
|
||||
import lombok.*;
|
||||
import me.zhyd.oauth.enums.AuthUserGender;
|
||||
|
||||
/**
|
||||
* 授权成功后的用户信息,根据授权平台的不同,获取的数据完整性也不同
|
||||
*
|
||||
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
|
||||
* @version 1.0
|
||||
* @since 1.8
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class AuthUser {
|
||||
/**
|
||||
* 用户第三方系统的唯一id。在调用方集成改组件时,可以用uuid + source唯一确定一个用户
|
||||
*
|
||||
* @since 1.3.3
|
||||
*/
|
||||
private String uuid;
|
||||
/**
|
||||
@@ -60,9 +60,10 @@ public class AuthUser {
|
||||
/**
|
||||
* 用户来源
|
||||
*/
|
||||
private AuthSource source;
|
||||
private String source;
|
||||
/**
|
||||
* 用户授权的token信息
|
||||
*/
|
||||
private AuthToken token;
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* JustAuth核心模型类,封装了用户、token、响应和callback等实体类
|
||||
*/
|
||||
package me.zhyd.oauth.model;
|
||||
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* JustAuth,如你所见,它仅仅是一个第三方授权登录的工具类库,它可以让我们脱离繁琐的第三方登录SDK,让登录变得So easy!
|
||||
* <p>
|
||||
* 史上最全的整合第三方登录的开源库。目前已支持Github、Gitee、微博、钉钉、百度、Coding、腾讯云开发者平台、OSChina、
|
||||
* 支付宝、QQ、微信、淘宝、Google、Facebook、抖音、领英、小米、微软、今日头条、Teambition、StackOverflow、Pinterest、
|
||||
* 人人、华为和企业微信等第三方平台的授权登录。 Login, so easy!
|
||||
*/
|
||||
package me.zhyd.oauth;
|
||||
@@ -7,8 +7,9 @@ import com.alipay.api.request.AlipaySystemOauthTokenRequest;
|
||||
import com.alipay.api.request.AlipayUserInfoShareRequest;
|
||||
import com.alipay.api.response.AlipaySystemOauthTokenResponse;
|
||||
import com.alipay.api.response.AlipayUserInfoShareResponse;
|
||||
import me.zhyd.oauth.cache.AuthStateCache;
|
||||
import me.zhyd.oauth.config.AuthConfig;
|
||||
import me.zhyd.oauth.config.AuthSource;
|
||||
import me.zhyd.oauth.config.AuthDefaultSource;
|
||||
import me.zhyd.oauth.enums.AuthUserGender;
|
||||
import me.zhyd.oauth.exception.AuthException;
|
||||
import me.zhyd.oauth.model.AuthCallback;
|
||||
@@ -21,16 +22,21 @@ import me.zhyd.oauth.utils.UrlBuilder;
|
||||
* 支付宝登录
|
||||
*
|
||||
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
|
||||
* @version 1.0
|
||||
* @since 1.8
|
||||
* @since 1.0.1
|
||||
*/
|
||||
public class AuthAlipayRequest extends AuthDefaultRequest {
|
||||
|
||||
private AlipayClient alipayClient;
|
||||
|
||||
public AuthAlipayRequest(AuthConfig config) {
|
||||
super(config, AuthSource.ALIPAY);
|
||||
this.alipayClient = new DefaultAlipayClient(AuthSource.ALIPAY.accessToken(), config.getClientId(), config.getClientSecret(), "json", "UTF-8", config
|
||||
super(config, AuthDefaultSource.ALIPAY);
|
||||
this.alipayClient = new DefaultAlipayClient(AuthDefaultSource.ALIPAY.accessToken(), config.getClientId(), config.getClientSecret(), "json", "UTF-8", config
|
||||
.getAlipayPublicKey(), "RSA2");
|
||||
}
|
||||
|
||||
public AuthAlipayRequest(AuthConfig config, AuthStateCache authStateCache) {
|
||||
super(config, AuthDefaultSource.ALIPAY, authStateCache);
|
||||
this.alipayClient = new DefaultAlipayClient(AuthDefaultSource.ALIPAY.accessToken(), config.getClientId(), config.getClientSecret(), "json", "UTF-8", config
|
||||
.getAlipayPublicKey(), "RSA2");
|
||||
}
|
||||
|
||||
@@ -81,22 +87,24 @@ public class AuthAlipayRequest extends AuthDefaultRequest {
|
||||
.location(location)
|
||||
.gender(AuthUserGender.getRealGender(response.getGender()))
|
||||
.token(authToken)
|
||||
.source(source)
|
||||
.source(source.toString())
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回认证url,可自行跳转页面
|
||||
* 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state}
|
||||
*
|
||||
* @param state state 验证授权流程的参数,可以防止csrf
|
||||
* @return 返回授权地址
|
||||
* @since 1.9.3
|
||||
*/
|
||||
@Override
|
||||
public String authorize() {
|
||||
public String authorize(String state) {
|
||||
return UrlBuilder.fromBaseUrl(source.authorize())
|
||||
.queryParam("app_id", config.getClientId())
|
||||
.queryParam("scope", "auth_user")
|
||||
.queryParam("redirect_uri", config.getRedirectUri())
|
||||
.queryParam("state", getRealState(config.getState()))
|
||||
.queryParam("state", getRealState(state))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,11 +3,16 @@ package me.zhyd.oauth.request;
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import me.zhyd.oauth.cache.AuthStateCache;
|
||||
import me.zhyd.oauth.config.AuthConfig;
|
||||
import me.zhyd.oauth.config.AuthSource;
|
||||
import me.zhyd.oauth.config.AuthDefaultSource;
|
||||
import me.zhyd.oauth.enums.AuthResponseStatus;
|
||||
import me.zhyd.oauth.enums.AuthUserGender;
|
||||
import me.zhyd.oauth.exception.AuthException;
|
||||
import me.zhyd.oauth.model.*;
|
||||
import me.zhyd.oauth.model.AuthCallback;
|
||||
import me.zhyd.oauth.model.AuthResponse;
|
||||
import me.zhyd.oauth.model.AuthToken;
|
||||
import me.zhyd.oauth.model.AuthUser;
|
||||
import me.zhyd.oauth.utils.StringUtils;
|
||||
import me.zhyd.oauth.utils.UrlBuilder;
|
||||
|
||||
@@ -15,13 +20,16 @@ import me.zhyd.oauth.utils.UrlBuilder;
|
||||
* 百度账号登录
|
||||
*
|
||||
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
|
||||
* @version 1.0
|
||||
* @since 1.8
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class AuthBaiduRequest extends AuthDefaultRequest {
|
||||
|
||||
public AuthBaiduRequest(AuthConfig config) {
|
||||
super(config, AuthSource.BAIDU);
|
||||
super(config, AuthDefaultSource.BAIDU);
|
||||
}
|
||||
|
||||
public AuthBaiduRequest(AuthConfig config, AuthStateCache authStateCache) {
|
||||
super(config, AuthDefaultSource.BAIDU, authStateCache);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -44,7 +52,7 @@ public class AuthBaiduRequest extends AuthDefaultRequest {
|
||||
.remark(object.getString("userdetail"))
|
||||
.gender(AuthUserGender.getRealGender(object.getString("sex")))
|
||||
.token(authToken)
|
||||
.source(source)
|
||||
.source(source.toString())
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -79,18 +87,20 @@ public class AuthBaiduRequest extends AuthDefaultRequest {
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回认证url,可自行跳转页面
|
||||
* 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state}
|
||||
*
|
||||
* @param state state 验证授权流程的参数,可以防止csrf
|
||||
* @return 返回授权地址
|
||||
* @since 1.9.3
|
||||
*/
|
||||
@Override
|
||||
public String authorize() {
|
||||
public String authorize(String state) {
|
||||
return UrlBuilder.fromBaseUrl(source.authorize())
|
||||
.queryParam("response_type", "code")
|
||||
.queryParam("client_id", config.getClientId())
|
||||
.queryParam("redirect_uri", config.getRedirectUri())
|
||||
.queryParam("display", "popup")
|
||||
.queryParam("state", getRealState(config.getState()))
|
||||
.queryParam("state", getRealState(state))
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
@@ -2,26 +2,30 @@ package me.zhyd.oauth.request;
|
||||
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import me.zhyd.oauth.cache.AuthStateCache;
|
||||
import me.zhyd.oauth.config.AuthConfig;
|
||||
import me.zhyd.oauth.config.AuthSource;
|
||||
import me.zhyd.oauth.config.AuthDefaultSource;
|
||||
import me.zhyd.oauth.enums.AuthUserGender;
|
||||
import me.zhyd.oauth.exception.AuthException;
|
||||
import me.zhyd.oauth.model.AuthCallback;
|
||||
import me.zhyd.oauth.model.AuthToken;
|
||||
import me.zhyd.oauth.model.AuthUser;
|
||||
import me.zhyd.oauth.enums.AuthUserGender;
|
||||
import me.zhyd.oauth.utils.UrlBuilder;
|
||||
|
||||
/**
|
||||
* Cooding登录
|
||||
*
|
||||
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
|
||||
* @version 1.0
|
||||
* @since 1.8
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class AuthCodingRequest extends AuthDefaultRequest {
|
||||
|
||||
public AuthCodingRequest(AuthConfig config) {
|
||||
super(config, AuthSource.CODING);
|
||||
super(config, AuthDefaultSource.CODING);
|
||||
}
|
||||
|
||||
public AuthCodingRequest(AuthConfig config, AuthStateCache authStateCache) {
|
||||
super(config, AuthDefaultSource.CODING, authStateCache);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -55,7 +59,7 @@ public class AuthCodingRequest extends AuthDefaultRequest {
|
||||
.email(object.getString("email"))
|
||||
.remark(object.getString("slogan"))
|
||||
.token(authToken)
|
||||
.source(source)
|
||||
.source(source.toString())
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -71,18 +75,20 @@ public class AuthCodingRequest extends AuthDefaultRequest {
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回认证url,可自行跳转页面
|
||||
* 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state}
|
||||
*
|
||||
* @param state state 验证授权流程的参数,可以防止csrf
|
||||
* @return 返回授权地址
|
||||
* @since 1.9.3
|
||||
*/
|
||||
@Override
|
||||
public String authorize() {
|
||||
public String authorize(String state) {
|
||||
return UrlBuilder.fromBaseUrl(source.authorize())
|
||||
.queryParam("response_type", "code")
|
||||
.queryParam("client_id", config.getClientId())
|
||||
.queryParam("redirect_uri", config.getRedirectUri())
|
||||
.queryParam("scope", "user")
|
||||
.queryParam("state", getRealState(config.getState()))
|
||||
.queryParam("state", getRealState(state))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,9 @@ package me.zhyd.oauth.request;
|
||||
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import me.zhyd.oauth.cache.AuthStateCache;
|
||||
import me.zhyd.oauth.config.AuthConfig;
|
||||
import me.zhyd.oauth.config.AuthSource;
|
||||
import me.zhyd.oauth.config.AuthDefaultSource;
|
||||
import me.zhyd.oauth.enums.AuthUserGender;
|
||||
import me.zhyd.oauth.exception.AuthException;
|
||||
import me.zhyd.oauth.model.AuthCallback;
|
||||
@@ -14,14 +15,17 @@ import me.zhyd.oauth.model.AuthUser;
|
||||
* CSDN登录
|
||||
*
|
||||
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
|
||||
* @version 1.0
|
||||
* @since 1.8
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Deprecated
|
||||
public class AuthCsdnRequest extends AuthDefaultRequest {
|
||||
|
||||
public AuthCsdnRequest(AuthConfig config) {
|
||||
super(config, AuthSource.CSDN);
|
||||
super(config, AuthDefaultSource.CSDN);
|
||||
}
|
||||
|
||||
public AuthCsdnRequest(AuthConfig config, AuthStateCache authStateCache) {
|
||||
super(config, AuthDefaultSource.CSDN, authStateCache);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -44,7 +48,7 @@ public class AuthCsdnRequest extends AuthDefaultRequest {
|
||||
.blog(object.getString("website"))
|
||||
.gender(AuthUserGender.UNKNOWN)
|
||||
.token(authToken)
|
||||
.source(source)
|
||||
.source(source.toString())
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
@@ -2,32 +2,42 @@ package me.zhyd.oauth.request;
|
||||
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import me.zhyd.oauth.cache.AuthDefaultStateCache;
|
||||
import me.zhyd.oauth.cache.AuthStateCache;
|
||||
import me.zhyd.oauth.config.AuthConfig;
|
||||
import me.zhyd.oauth.config.AuthSource;
|
||||
import me.zhyd.oauth.enums.AuthResponseStatus;
|
||||
import me.zhyd.oauth.exception.AuthException;
|
||||
import me.zhyd.oauth.model.*;
|
||||
import me.zhyd.oauth.log.Log;
|
||||
import me.zhyd.oauth.model.AuthCallback;
|
||||
import me.zhyd.oauth.model.AuthResponse;
|
||||
import me.zhyd.oauth.model.AuthToken;
|
||||
import me.zhyd.oauth.model.AuthUser;
|
||||
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处理类
|
||||
*
|
||||
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
|
||||
* @author yangkai.shen (https://xkcoding.com)
|
||||
* @version 1.0
|
||||
* @since 1.8
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Slf4j
|
||||
public abstract class AuthDefaultRequest implements AuthRequest {
|
||||
protected AuthConfig config;
|
||||
protected AuthSource source;
|
||||
protected AuthStateCache authStateCache;
|
||||
|
||||
public AuthDefaultRequest(AuthConfig config, AuthSource source) {
|
||||
this(config, source, AuthDefaultStateCache.INSTANCE);
|
||||
}
|
||||
|
||||
public AuthDefaultRequest(AuthConfig config, AuthSource source, AuthStateCache authStateCache) {
|
||||
this.config = config;
|
||||
this.source = source;
|
||||
this.authStateCache = authStateCache;
|
||||
if (!AuthChecker.isSupportedAuth(config, source)) {
|
||||
throw new AuthException(AuthResponseStatus.PARAMETER_INCOMPLETE);
|
||||
}
|
||||
@@ -35,25 +45,53 @@ public abstract class AuthDefaultRequest implements AuthRequest {
|
||||
AuthChecker.checkConfig(config, source);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取access token
|
||||
*
|
||||
* @param authCallback 授权成功后的回调参数
|
||||
* @return token
|
||||
* @see AuthDefaultRequest#authorize()
|
||||
* @see AuthDefaultRequest#authorize(String)
|
||||
*/
|
||||
protected abstract AuthToken getAccessToken(AuthCallback authCallback);
|
||||
|
||||
/**
|
||||
* 使用token换取用户信息
|
||||
*
|
||||
* @param authToken token信息
|
||||
* @return 用户信息
|
||||
* @see AuthDefaultRequest#getAccessToken(AuthCallback)
|
||||
*/
|
||||
protected abstract AuthUser getUserInfo(AuthToken authToken);
|
||||
|
||||
/**
|
||||
* 统一的登录入口。当通过{@link AuthDefaultRequest#authorize(String)}授权成功后,会跳转到调用方的相关回调方法中
|
||||
* 方法的入参可以使用{@code AuthCallback},{@code AuthCallback}类中封装好了OAuth2授权回调所需要的参数
|
||||
*
|
||||
* @param authCallback 用于接收回调参数的实体
|
||||
* @return AuthResponse
|
||||
*/
|
||||
@Override
|
||||
public AuthResponse login(AuthCallback authCallback) {
|
||||
try {
|
||||
AuthChecker.checkCode(source == AuthSource.ALIPAY ? authCallback.getAuth_code() : authCallback.getCode());
|
||||
AuthChecker.checkState(authCallback.getState(), config.getState());
|
||||
AuthChecker.checkCode(source, authCallback);
|
||||
this.checkState(authCallback.getState());
|
||||
|
||||
AuthToken authToken = this.getAccessToken(authCallback);
|
||||
AuthUser user = this.getUserInfo(authToken);
|
||||
return AuthResponse.builder().code(AuthResponseStatus.SUCCESS.getCode()).data(user).build();
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to login with oauth authorization.", e);
|
||||
Log.error("Failed to login with oauth authorization.", e);
|
||||
return this.responseError(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理{@link AuthDefaultRequest#login(AuthCallback)} 发生异常的情况,统一响应参数
|
||||
*
|
||||
* @param e 具体的异常
|
||||
* @return AuthResponse
|
||||
*/
|
||||
private AuthResponse responseError(Exception e) {
|
||||
int errorCode = AuthResponseStatus.FAILURE.getCode();
|
||||
if (e instanceof AuthException) {
|
||||
@@ -63,17 +101,34 @@ public abstract class AuthDefaultRequest implements AuthRequest {
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回认证url,可自行跳转页面
|
||||
* 返回授权url,可自行跳转页面
|
||||
* <p>
|
||||
* 不建议使用该方式获取授权地址,不带{@code state}的授权地址,容易受到csrf攻击。
|
||||
* 建议使用{@link AuthDefaultRequest#authorize(String)}方法生成授权地址,在回调方法中对{@code state}进行校验
|
||||
*
|
||||
* @return 返回授权地址
|
||||
* @see AuthDefaultRequest#authorize(String)
|
||||
*/
|
||||
@Deprecated
|
||||
@Override
|
||||
public String authorize() {
|
||||
return this.authorize(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state}
|
||||
*
|
||||
* @param state state 验证授权流程的参数,可以防止csrf
|
||||
* @return 返回授权地址
|
||||
* @since 1.9.3
|
||||
*/
|
||||
@Override
|
||||
public String authorize(String state) {
|
||||
return UrlBuilder.fromBaseUrl(source.authorize())
|
||||
.queryParam("response_type", "code")
|
||||
.queryParam("client_id", config.getClientId())
|
||||
.queryParam("redirect_uri", config.getRedirectUri())
|
||||
.queryParam("state", getRealState(config.getState()))
|
||||
.queryParam("state", getRealState(state))
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -130,13 +185,18 @@ public abstract class AuthDefaultRequest implements AuthRequest {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取state,如果为空, 则默认去当前日期的时间戳
|
||||
* 获取state,如果为空, 则默认取当前日期的时间戳
|
||||
*
|
||||
* @param state 原始的state
|
||||
* @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;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -165,6 +225,7 @@ public abstract class AuthDefaultRequest implements AuthRequest {
|
||||
* @param authToken token封装
|
||||
* @return HttpResponse
|
||||
*/
|
||||
@Deprecated
|
||||
protected HttpResponse doPostUserInfo(AuthToken authToken) {
|
||||
return HttpRequest.post(userInfoUrl(authToken)).execute();
|
||||
}
|
||||
@@ -185,6 +246,7 @@ public abstract class AuthDefaultRequest implements AuthRequest {
|
||||
* @param authToken token封装
|
||||
* @return HttpResponse
|
||||
*/
|
||||
@Deprecated
|
||||
protected HttpResponse doPostRevoke(AuthToken authToken) {
|
||||
return HttpRequest.post(revokeUrl(authToken)).execute();
|
||||
}
|
||||
@@ -198,4 +260,16 @@ public abstract class AuthDefaultRequest implements AuthRequest {
|
||||
protected HttpResponse doGetRevoke(AuthToken authToken) {
|
||||
return HttpRequest.get(revokeUrl(authToken)).execute();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 校验回调传回的state
|
||||
*
|
||||
* @param state {@code state}一定不为空
|
||||
*/
|
||||
protected void checkState(String state) {
|
||||
if (StringUtils.isEmpty(state) || !authStateCache.containsKey(state)) {
|
||||
throw new AuthException(AuthResponseStatus.ILLEGAL_REQUEST);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,8 +4,9 @@ import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import me.zhyd.oauth.cache.AuthStateCache;
|
||||
import me.zhyd.oauth.config.AuthConfig;
|
||||
import me.zhyd.oauth.config.AuthSource;
|
||||
import me.zhyd.oauth.config.AuthDefaultSource;
|
||||
import me.zhyd.oauth.enums.AuthUserGender;
|
||||
import me.zhyd.oauth.exception.AuthException;
|
||||
import me.zhyd.oauth.model.AuthCallback;
|
||||
@@ -18,13 +19,16 @@ import me.zhyd.oauth.utils.UrlBuilder;
|
||||
* 钉钉登录
|
||||
*
|
||||
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
|
||||
* @version 1.0
|
||||
* @since 1.8
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class AuthDingTalkRequest extends AuthDefaultRequest {
|
||||
|
||||
public AuthDingTalkRequest(AuthConfig config) {
|
||||
super(config, AuthSource.DINGTALK);
|
||||
super(config, AuthDefaultSource.DINGTALK);
|
||||
}
|
||||
|
||||
public AuthDingTalkRequest(AuthConfig config, AuthStateCache authStateCache) {
|
||||
super(config, AuthDefaultSource.DINGTALK, authStateCache);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -52,24 +56,26 @@ public class AuthDingTalkRequest extends AuthDefaultRequest {
|
||||
.nickname(object.getString("nick"))
|
||||
.username(object.getString("nick"))
|
||||
.gender(AuthUserGender.UNKNOWN)
|
||||
.source(source)
|
||||
.source(source.toString())
|
||||
.token(token)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回认证url,可自行跳转页面
|
||||
* 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state}
|
||||
*
|
||||
* @param state state 验证授权流程的参数,可以防止csrf
|
||||
* @return 返回授权地址
|
||||
* @since 1.9.3
|
||||
*/
|
||||
@Override
|
||||
public String authorize() {
|
||||
public String authorize(String state) {
|
||||
return UrlBuilder.fromBaseUrl(source.authorize())
|
||||
.queryParam("response_type", "code")
|
||||
.queryParam("appid", config.getClientId())
|
||||
.queryParam("scope", "snsapi_login")
|
||||
.queryParam("redirect_uri", config.getRedirectUri())
|
||||
.queryParam("state", getRealState(config.getState()))
|
||||
.queryParam("state", getRealState(state))
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
@@ -3,11 +3,16 @@ package me.zhyd.oauth.request;
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import me.zhyd.oauth.cache.AuthStateCache;
|
||||
import me.zhyd.oauth.config.AuthConfig;
|
||||
import me.zhyd.oauth.config.AuthSource;
|
||||
import me.zhyd.oauth.config.AuthDefaultSource;
|
||||
import me.zhyd.oauth.enums.AuthResponseStatus;
|
||||
import me.zhyd.oauth.enums.AuthUserGender;
|
||||
import me.zhyd.oauth.exception.AuthException;
|
||||
import me.zhyd.oauth.model.*;
|
||||
import me.zhyd.oauth.model.AuthCallback;
|
||||
import me.zhyd.oauth.model.AuthResponse;
|
||||
import me.zhyd.oauth.model.AuthToken;
|
||||
import me.zhyd.oauth.model.AuthUser;
|
||||
import me.zhyd.oauth.utils.UrlBuilder;
|
||||
|
||||
|
||||
@@ -15,13 +20,16 @@ import me.zhyd.oauth.utils.UrlBuilder;
|
||||
* 抖音登录
|
||||
*
|
||||
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
|
||||
* @version 1.0
|
||||
* @since 1.8
|
||||
* @since 1.4.0
|
||||
*/
|
||||
public class AuthDouyinRequest extends AuthDefaultRequest {
|
||||
|
||||
public AuthDouyinRequest(AuthConfig config) {
|
||||
super(config, AuthSource.DOUYIN);
|
||||
super(config, AuthDefaultSource.DOUYIN);
|
||||
}
|
||||
|
||||
public AuthDouyinRequest(AuthConfig config, AuthStateCache authStateCache) {
|
||||
super(config, AuthDefaultSource.DOUYIN, authStateCache);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -42,7 +50,7 @@ public class AuthDouyinRequest extends AuthDefaultRequest {
|
||||
.remark(userInfoObject.getString("description"))
|
||||
.gender(AuthUserGender.UNKNOWN)
|
||||
.token(authToken)
|
||||
.source(source)
|
||||
.source(source.toString())
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -89,18 +97,20 @@ public class AuthDouyinRequest extends AuthDefaultRequest {
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回认证url,可自行跳转页面
|
||||
* 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state}
|
||||
*
|
||||
* @param state state 验证授权流程的参数,可以防止csrf
|
||||
* @return 返回授权地址
|
||||
* @since 1.9.3
|
||||
*/
|
||||
@Override
|
||||
public String authorize() {
|
||||
public String authorize(String state) {
|
||||
return UrlBuilder.fromBaseUrl(source.authorize())
|
||||
.queryParam("response_type", "code")
|
||||
.queryParam("client_key", config.getClientId())
|
||||
.queryParam("redirect_uri", config.getRedirectUri())
|
||||
.queryParam("state", getRealState(config.getState()))
|
||||
.queryParam("scope", "user_info")
|
||||
.queryParam("state", getRealState(state))
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,184 @@
|
||||
package me.zhyd.oauth.request;
|
||||
|
||||
import cn.hutool.core.codec.Base64;
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import me.zhyd.oauth.cache.AuthStateCache;
|
||||
import me.zhyd.oauth.config.AuthConfig;
|
||||
import me.zhyd.oauth.config.AuthDefaultSource;
|
||||
import me.zhyd.oauth.enums.AuthResponseStatus;
|
||||
import me.zhyd.oauth.enums.AuthUserGender;
|
||||
import me.zhyd.oauth.exception.AuthException;
|
||||
import me.zhyd.oauth.model.AuthCallback;
|
||||
import me.zhyd.oauth.model.AuthResponse;
|
||||
import me.zhyd.oauth.model.AuthToken;
|
||||
import me.zhyd.oauth.model.AuthUser;
|
||||
import me.zhyd.oauth.utils.GlobalAuthUtil;
|
||||
import me.zhyd.oauth.utils.UrlBuilder;
|
||||
import me.zhyd.oauth.utils.UuidUtils;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 饿了么
|
||||
* <p>
|
||||
* 注:集成的是正式环境,非沙箱环境
|
||||
*
|
||||
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
|
||||
* @since 1.12.0
|
||||
*/
|
||||
public class AuthElemeRequest extends AuthDefaultRequest {
|
||||
|
||||
public AuthElemeRequest(AuthConfig config) {
|
||||
super(config, AuthDefaultSource.ELEME);
|
||||
}
|
||||
|
||||
public AuthElemeRequest(AuthConfig config, AuthStateCache authStateCache) {
|
||||
super(config, AuthDefaultSource.ELEME, authStateCache);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AuthToken getAccessToken(AuthCallback authCallback) {
|
||||
|
||||
HttpRequest request = HttpRequest.post(source.accessToken())
|
||||
.form("client_id", config.getClientId())
|
||||
.form("redirect_uri", config.getRedirectUri())
|
||||
.form("code", authCallback.getCode())
|
||||
.form("grant_type", "authorization_code");
|
||||
|
||||
// 设置header
|
||||
this.setHeader(request);
|
||||
|
||||
HttpResponse response = request.execute();
|
||||
JSONObject object = JSONObject.parseObject(response.body());
|
||||
|
||||
this.checkResponse(object);
|
||||
|
||||
return AuthToken.builder()
|
||||
.accessToken(object.getString("access_token"))
|
||||
.refreshToken(object.getString("refresh_token"))
|
||||
.tokenType(object.getString("token_type"))
|
||||
.expireIn(object.getIntValue("expires_in"))
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AuthUser getUserInfo(AuthToken authToken) {
|
||||
Map<String, Object> parameters = new HashMap<>();
|
||||
// 获取商户账号信息的API接口名称
|
||||
String action = "eleme.user.getUser";
|
||||
// 时间戳,单位秒。API服务端允许客户端请求最大时间误差为正负5分钟。
|
||||
final long timestamp = System.currentTimeMillis();
|
||||
// 公共参数
|
||||
Map<String, Object> metasHashMap = new HashMap<>();
|
||||
metasHashMap.put("app_key", config.getClientId());
|
||||
metasHashMap.put("timestamp", timestamp);
|
||||
String signature = GlobalAuthUtil.generateElemeSignature(config.getClientId(), config.getClientSecret(), timestamp, action, authToken.getAccessToken(), parameters);
|
||||
|
||||
String requestId = this.getRequestId();
|
||||
|
||||
|
||||
Map<String, Object> paramsMap = new HashMap<>();
|
||||
paramsMap.put("nop", "1.0.0");
|
||||
paramsMap.put("id", requestId);
|
||||
paramsMap.put("action", action);
|
||||
paramsMap.put("token", authToken.getAccessToken());
|
||||
paramsMap.put("metas", metasHashMap);
|
||||
paramsMap.put("params", parameters);
|
||||
paramsMap.put("signature", signature);
|
||||
|
||||
HttpRequest request = HttpRequest.post(source.userInfo())
|
||||
.body(JSONObject.toJSONBytes(paramsMap));
|
||||
|
||||
// 设置header
|
||||
this.setHeader(request, "application/json; charset=utf-8", requestId);
|
||||
|
||||
HttpResponse response = request.execute();
|
||||
|
||||
JSONObject object = JSONObject.parseObject(response.body());
|
||||
|
||||
// 校验请求
|
||||
if (object.containsKey("name")) {
|
||||
throw new AuthException(object.getString("message"));
|
||||
}
|
||||
if (object.containsKey("error") && null != object.get("error")) {
|
||||
throw new AuthException(object.getJSONObject("error").getString("message"));
|
||||
}
|
||||
|
||||
JSONObject result = object.getJSONObject("result");
|
||||
|
||||
return AuthUser.builder()
|
||||
.uuid(result.getString("userId"))
|
||||
.username(result.getString("userName"))
|
||||
.nickname(result.getString("userName"))
|
||||
.gender(AuthUserGender.UNKNOWN)
|
||||
.token(authToken)
|
||||
.source(source.toString())
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthResponse refresh(AuthToken oldToken) {
|
||||
HttpRequest request = HttpRequest.post(source.refresh())
|
||||
.form("refresh_token", oldToken.getRefreshToken())
|
||||
.form("grant_type", "refresh_token");
|
||||
|
||||
// 设置header
|
||||
this.setHeader(request);
|
||||
|
||||
HttpResponse response = request.execute();
|
||||
JSONObject object = JSONObject.parseObject(response.body());
|
||||
|
||||
this.checkResponse(object);
|
||||
|
||||
return AuthResponse.builder()
|
||||
.code(AuthResponseStatus.SUCCESS.getCode())
|
||||
.data(AuthToken.builder()
|
||||
.accessToken(object.getString("access_token"))
|
||||
.refreshToken(object.getString("refresh_token"))
|
||||
.tokenType(object.getString("token_type"))
|
||||
.expireIn(object.getIntValue("expires_in"))
|
||||
.build())
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String authorize(String state) {
|
||||
return UrlBuilder.fromBaseUrl(super.authorize(state))
|
||||
.queryParam("scope", "all")
|
||||
.build();
|
||||
}
|
||||
|
||||
private String getBasic(String appKey, String appSecret) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
String encodeToString = Base64.encode((appKey + ":" + appSecret).getBytes());
|
||||
sb.append("Basic").append(" ").append(encodeToString);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private void setHeader(HttpRequest request) {
|
||||
setHeader(request, "application/x-www-form-urlencoded;charset=UTF-8", getRequestId());
|
||||
request.header("Authorization", this.getBasic(config.getClientId(), config.getClientSecret()));
|
||||
}
|
||||
|
||||
private void setHeader(HttpRequest request, String contentType, String requestId) {
|
||||
request.header("Accept", "text/xml,text/javascript,text/html")
|
||||
.header("Content-Type", contentType)
|
||||
.header("Accept-Encoding", "gzip")
|
||||
.header("User-Agent", "eleme-openapi-java-sdk")
|
||||
.header("x-eleme-requestid", requestId);
|
||||
}
|
||||
|
||||
private String getRequestId() {
|
||||
return (UuidUtils.getUUID() + "|" + System.currentTimeMillis()).toUpperCase();
|
||||
}
|
||||
|
||||
private void checkResponse(JSONObject object) {
|
||||
if (object.containsKey("error")) {
|
||||
throw new AuthException(object.getString("error_description"));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,8 +2,9 @@ package me.zhyd.oauth.request;
|
||||
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import me.zhyd.oauth.cache.AuthStateCache;
|
||||
import me.zhyd.oauth.config.AuthConfig;
|
||||
import me.zhyd.oauth.config.AuthSource;
|
||||
import me.zhyd.oauth.config.AuthDefaultSource;
|
||||
import me.zhyd.oauth.enums.AuthUserGender;
|
||||
import me.zhyd.oauth.exception.AuthException;
|
||||
import me.zhyd.oauth.model.AuthCallback;
|
||||
@@ -15,13 +16,16 @@ import me.zhyd.oauth.utils.UrlBuilder;
|
||||
* Facebook登录
|
||||
*
|
||||
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
|
||||
* @version 1.0
|
||||
* @since 1.8
|
||||
* @since 1.3.0
|
||||
*/
|
||||
public class AuthFacebookRequest extends AuthDefaultRequest {
|
||||
|
||||
public AuthFacebookRequest(AuthConfig config) {
|
||||
super(config, AuthSource.FACEBOOK);
|
||||
super(config, AuthDefaultSource.FACEBOOK);
|
||||
}
|
||||
|
||||
public AuthFacebookRequest(AuthConfig config, AuthStateCache authStateCache) {
|
||||
super(config, AuthDefaultSource.FACEBOOK, authStateCache);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -51,7 +55,7 @@ public class AuthFacebookRequest extends AuthDefaultRequest {
|
||||
.email(object.getString("email"))
|
||||
.gender(AuthUserGender.getRealGender(object.getString("gender")))
|
||||
.token(authToken)
|
||||
.source(source)
|
||||
.source(source.toString())
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
@@ -2,8 +2,9 @@ package me.zhyd.oauth.request;
|
||||
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import me.zhyd.oauth.cache.AuthStateCache;
|
||||
import me.zhyd.oauth.config.AuthConfig;
|
||||
import me.zhyd.oauth.config.AuthSource;
|
||||
import me.zhyd.oauth.config.AuthDefaultSource;
|
||||
import me.zhyd.oauth.enums.AuthUserGender;
|
||||
import me.zhyd.oauth.exception.AuthException;
|
||||
import me.zhyd.oauth.model.AuthCallback;
|
||||
@@ -14,13 +15,16 @@ import me.zhyd.oauth.model.AuthUser;
|
||||
* Gitee登录
|
||||
*
|
||||
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
|
||||
* @version 1.0
|
||||
* @since 1.8
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class AuthGiteeRequest extends AuthDefaultRequest {
|
||||
|
||||
public AuthGiteeRequest(AuthConfig config) {
|
||||
super(config, AuthSource.GITEE);
|
||||
super(config, AuthDefaultSource.GITEE);
|
||||
}
|
||||
|
||||
public AuthGiteeRequest(AuthConfig config, AuthStateCache authStateCache) {
|
||||
super(config, AuthDefaultSource.GITEE, authStateCache);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -55,7 +59,7 @@ public class AuthGiteeRequest extends AuthDefaultRequest {
|
||||
.remark(object.getString("bio"))
|
||||
.gender(AuthUserGender.UNKNOWN)
|
||||
.token(authToken)
|
||||
.source(source)
|
||||
.source(source.toString())
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
@@ -2,8 +2,9 @@ package me.zhyd.oauth.request;
|
||||
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import me.zhyd.oauth.cache.AuthStateCache;
|
||||
import me.zhyd.oauth.config.AuthConfig;
|
||||
import me.zhyd.oauth.config.AuthSource;
|
||||
import me.zhyd.oauth.config.AuthDefaultSource;
|
||||
import me.zhyd.oauth.enums.AuthUserGender;
|
||||
import me.zhyd.oauth.exception.AuthException;
|
||||
import me.zhyd.oauth.model.AuthCallback;
|
||||
@@ -17,22 +18,25 @@ import java.util.Map;
|
||||
* Github登录
|
||||
*
|
||||
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
|
||||
* @version 1.0
|
||||
* @since 1.8
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class AuthGithubRequest extends AuthDefaultRequest {
|
||||
|
||||
public AuthGithubRequest(AuthConfig config) {
|
||||
super(config, AuthSource.GITHUB);
|
||||
super(config, AuthDefaultSource.GITHUB);
|
||||
}
|
||||
|
||||
public AuthGithubRequest(AuthConfig config, AuthStateCache authStateCache) {
|
||||
super(config, AuthDefaultSource.GITHUB, authStateCache);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AuthToken getAccessToken(AuthCallback authCallback) {
|
||||
HttpResponse response = doPostAuthorizationCode(authCallback.getCode());
|
||||
Map<String, String> res = GlobalAuthUtil.parseStringToMap(response.body());
|
||||
if (res.containsKey("error")) {
|
||||
throw new AuthException(res.get("error") + ":" + res.get("error_description"));
|
||||
}
|
||||
|
||||
this.checkResponse(res.containsKey("error"), res.get("error_description"));
|
||||
|
||||
return AuthToken.builder()
|
||||
.accessToken(res.get("access_token"))
|
||||
.scope(res.get("scope"))
|
||||
@@ -44,9 +48,9 @@ public class AuthGithubRequest extends AuthDefaultRequest {
|
||||
protected AuthUser getUserInfo(AuthToken authToken) {
|
||||
HttpResponse response = doGetUserInfo(authToken);
|
||||
JSONObject object = JSONObject.parseObject(response.body());
|
||||
if (object.containsKey("error")) {
|
||||
throw new AuthException(object.getString("error_description"));
|
||||
}
|
||||
|
||||
this.checkResponse(object.containsKey("error"), object.getString("error_description"));
|
||||
|
||||
return AuthUser.builder()
|
||||
.uuid(object.getString("id"))
|
||||
.username(object.getString("login"))
|
||||
@@ -59,16 +63,14 @@ public class AuthGithubRequest extends AuthDefaultRequest {
|
||||
.remark(object.getString("bio"))
|
||||
.gender(AuthUserGender.UNKNOWN)
|
||||
.token(authToken)
|
||||
.source(source)
|
||||
.source(source.toString())
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查响应内容是否正确
|
||||
*
|
||||
* @param object 请求响应内容
|
||||
*/
|
||||
private void checkResponse(JSONObject object) {
|
||||
|
||||
private void checkResponse(boolean error, String error_description) {
|
||||
if (error) {
|
||||
throw new AuthException(error_description);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
package me.zhyd.oauth.request;
|
||||
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import me.zhyd.oauth.cache.AuthStateCache;
|
||||
import me.zhyd.oauth.config.AuthConfig;
|
||||
import me.zhyd.oauth.config.AuthDefaultSource;
|
||||
import me.zhyd.oauth.enums.AuthUserGender;
|
||||
import me.zhyd.oauth.exception.AuthException;
|
||||
import me.zhyd.oauth.model.AuthCallback;
|
||||
import me.zhyd.oauth.model.AuthToken;
|
||||
import me.zhyd.oauth.model.AuthUser;
|
||||
import me.zhyd.oauth.utils.UrlBuilder;
|
||||
|
||||
/**
|
||||
* Gitlab登录
|
||||
*
|
||||
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
|
||||
* @since 1.11.0
|
||||
*/
|
||||
public class AuthGitlabRequest extends AuthDefaultRequest {
|
||||
|
||||
public AuthGitlabRequest(AuthConfig config) {
|
||||
super(config, AuthDefaultSource.GITLAB);
|
||||
}
|
||||
|
||||
public AuthGitlabRequest(AuthConfig config, AuthStateCache authStateCache) {
|
||||
super(config, AuthDefaultSource.GITLAB, authStateCache);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AuthToken getAccessToken(AuthCallback authCallback) {
|
||||
HttpResponse response = doPostAuthorizationCode(authCallback.getCode());
|
||||
JSONObject object = JSONObject.parseObject(response.body());
|
||||
|
||||
this.checkResponse(object);
|
||||
|
||||
return AuthToken.builder()
|
||||
.accessToken(object.getString("access_token"))
|
||||
.refreshToken(object.getString("refresh_token"))
|
||||
.idToken(object.getString("id_token"))
|
||||
.tokenType(object.getString("token_type"))
|
||||
.scope(object.getString("scope"))
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AuthUser getUserInfo(AuthToken authToken) {
|
||||
HttpResponse response = doGetUserInfo(authToken);
|
||||
JSONObject object = JSONObject.parseObject(response.body());
|
||||
|
||||
this.checkResponse(object);
|
||||
|
||||
return AuthUser.builder()
|
||||
.uuid(object.getString("id"))
|
||||
.username(object.getString("username"))
|
||||
.nickname(object.getString("name"))
|
||||
.avatar(object.getString("avatar_url"))
|
||||
.blog(object.getString("web_url"))
|
||||
.company(object.getString("organization"))
|
||||
.location(object.getString("location"))
|
||||
.email(object.getString("email"))
|
||||
.remark(object.getString("bio"))
|
||||
.gender(AuthUserGender.UNKNOWN)
|
||||
.token(authToken)
|
||||
.source(source.toString())
|
||||
.build();
|
||||
}
|
||||
|
||||
private void checkResponse(JSONObject object) {
|
||||
// oauth/token 验证异常
|
||||
if (object.containsKey("error")) {
|
||||
throw new AuthException(object.getString("error_description"));
|
||||
}
|
||||
// user 验证异常
|
||||
if (object.containsKey("message")) {
|
||||
throw new AuthException(object.getString("message"));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state}
|
||||
*
|
||||
* @param state state 验证授权流程的参数,可以防止csrf
|
||||
* @return 返回授权地址
|
||||
* @since 1.11.0
|
||||
*/
|
||||
@Override
|
||||
public String authorize(String state) {
|
||||
return UrlBuilder.fromBaseUrl(super.authorize(state))
|
||||
.queryParam("scope", "read_user+openid+profile+email")
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,8 +3,9 @@ package me.zhyd.oauth.request;
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import me.zhyd.oauth.cache.AuthStateCache;
|
||||
import me.zhyd.oauth.config.AuthConfig;
|
||||
import me.zhyd.oauth.config.AuthSource;
|
||||
import me.zhyd.oauth.config.AuthDefaultSource;
|
||||
import me.zhyd.oauth.enums.AuthUserGender;
|
||||
import me.zhyd.oauth.exception.AuthException;
|
||||
import me.zhyd.oauth.model.AuthCallback;
|
||||
@@ -16,13 +17,16 @@ import me.zhyd.oauth.utils.UrlBuilder;
|
||||
* Google登录
|
||||
*
|
||||
* @author yangkai.shen (https://xkcoding.com)
|
||||
* @version 1.3
|
||||
* @since 1.3
|
||||
* @since 1.3.0
|
||||
*/
|
||||
public class AuthGoogleRequest extends AuthDefaultRequest {
|
||||
|
||||
public AuthGoogleRequest(AuthConfig config) {
|
||||
super(config, AuthSource.GOOGLE);
|
||||
super(config, AuthDefaultSource.GOOGLE);
|
||||
}
|
||||
|
||||
public AuthGoogleRequest(AuthConfig config, AuthStateCache authStateCache) {
|
||||
super(config, AuthDefaultSource.GOOGLE, authStateCache);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -56,24 +60,25 @@ public class AuthGoogleRequest extends AuthDefaultRequest {
|
||||
.email(object.getString("email"))
|
||||
.gender(AuthUserGender.UNKNOWN)
|
||||
.token(authToken)
|
||||
.source(source)
|
||||
.source(source.toString())
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回认证url,可自行跳转页面
|
||||
* https://openidconnect.googleapis.com/v1/userinfo
|
||||
* 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state}
|
||||
*
|
||||
* @param state state 验证授权流程的参数,可以防止csrf
|
||||
* @return 返回授权地址
|
||||
* @since 1.9.3
|
||||
*/
|
||||
@Override
|
||||
public String authorize() {
|
||||
public String authorize(String state) {
|
||||
return UrlBuilder.fromBaseUrl(source.authorize())
|
||||
.queryParam("response_type", "code")
|
||||
.queryParam("client_id", config.getClientId())
|
||||
.queryParam("scope", "openid%20email%20profile")
|
||||
.queryParam("redirect_uri", config.getRedirectUri())
|
||||
.queryParam("state", getRealState(config.getState()))
|
||||
.queryParam("state", getRealState(state))
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,196 @@
|
||||
package me.zhyd.oauth.request;
|
||||
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import me.zhyd.oauth.cache.AuthStateCache;
|
||||
import me.zhyd.oauth.config.AuthConfig;
|
||||
import me.zhyd.oauth.config.AuthDefaultSource;
|
||||
import me.zhyd.oauth.enums.AuthUserGender;
|
||||
import me.zhyd.oauth.exception.AuthException;
|
||||
import me.zhyd.oauth.model.AuthCallback;
|
||||
import me.zhyd.oauth.model.AuthResponse;
|
||||
import me.zhyd.oauth.model.AuthToken;
|
||||
import me.zhyd.oauth.model.AuthUser;
|
||||
import me.zhyd.oauth.utils.UrlBuilder;
|
||||
|
||||
import static me.zhyd.oauth.enums.AuthResponseStatus.SUCCESS;
|
||||
|
||||
/**
|
||||
* 华为授权登录
|
||||
*
|
||||
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
|
||||
* @version 1.0
|
||||
* @since 1.10.0
|
||||
*/
|
||||
public class AuthHuaweiRequest extends AuthDefaultRequest {
|
||||
|
||||
public AuthHuaweiRequest(AuthConfig config) {
|
||||
super(config, AuthDefaultSource.HUAWEI);
|
||||
}
|
||||
|
||||
public AuthHuaweiRequest(AuthConfig config, AuthStateCache authStateCache) {
|
||||
super(config, AuthDefaultSource.HUAWEI, authStateCache);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取access token
|
||||
*
|
||||
* @param authCallback 授权成功后的回调参数
|
||||
* @return token
|
||||
* @see AuthDefaultRequest#authorize()
|
||||
* @see AuthDefaultRequest#authorize(String)
|
||||
*/
|
||||
@Override
|
||||
protected AuthToken getAccessToken(AuthCallback authCallback) {
|
||||
HttpRequest request = HttpRequest.post(source.accessToken())
|
||||
.form("grant_type", "authorization_code")
|
||||
.form("code", authCallback.getAuthorization_code())
|
||||
.form("client_id", config.getClientId())
|
||||
.form("client_secret", config.getClientSecret())
|
||||
.form("redirect_uri", config.getRedirectUri());
|
||||
return getAuthToken(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用token换取用户信息
|
||||
*
|
||||
* @param authToken token信息
|
||||
* @return 用户信息
|
||||
* @see AuthDefaultRequest#getAccessToken(AuthCallback)
|
||||
*/
|
||||
@Override
|
||||
protected AuthUser getUserInfo(AuthToken authToken) {
|
||||
HttpResponse response = HttpRequest.post(source.userInfo())
|
||||
.form("nsp_ts", System.currentTimeMillis())
|
||||
.form("access_token", authToken.getAccessToken())
|
||||
.form("nsp_fmt", "JS")
|
||||
.form("nsp_svc", "OpenUP.User.getInfo")
|
||||
.execute();
|
||||
JSONObject object = JSONObject.parseObject(response.body());
|
||||
|
||||
this.checkResponse(object);
|
||||
|
||||
AuthUserGender gender = getRealGender(object);
|
||||
|
||||
return AuthUser.builder()
|
||||
.uuid(object.getString("userID"))
|
||||
.username(object.getString("userName"))
|
||||
.nickname(object.getString("userName"))
|
||||
.gender(gender)
|
||||
.avatar(object.getString("headPictureURL"))
|
||||
.token(authToken)
|
||||
.source(source.toString())
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新access token (续期)
|
||||
*
|
||||
* @param authToken 登录成功后返回的Token信息
|
||||
* @return AuthResponse
|
||||
*/
|
||||
@Override
|
||||
public AuthResponse refresh(AuthToken authToken) {
|
||||
HttpRequest request = HttpRequest.post(source.refresh())
|
||||
.form("client_id", config.getClientId())
|
||||
.form("client_secret", config.getClientSecret())
|
||||
.form("refresh_token", authToken.getRefreshToken())
|
||||
.form("grant_type", "refresh_token");
|
||||
return AuthResponse.builder()
|
||||
.code(SUCCESS.getCode())
|
||||
.data(getAuthToken(request))
|
||||
.build();
|
||||
}
|
||||
|
||||
private AuthToken getAuthToken(HttpRequest request) {
|
||||
HttpResponse response = request.execute();
|
||||
JSONObject object = JSONObject.parseObject(response.body());
|
||||
|
||||
this.checkResponse(object);
|
||||
|
||||
return AuthToken.builder()
|
||||
.accessToken(object.getString("access_token"))
|
||||
.expireIn(object.getIntValue("expires_in"))
|
||||
.refreshToken(object.getString("refresh_token"))
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state}
|
||||
*
|
||||
* @param state state 验证授权流程的参数,可以防止csrf
|
||||
* @return 返回授权地址
|
||||
* @since 1.9.3
|
||||
*/
|
||||
@Override
|
||||
public String authorize(String state) {
|
||||
return UrlBuilder.fromBaseUrl(source.authorize())
|
||||
.queryParam("response_type", "code")
|
||||
.queryParam("client_id", config.getClientId())
|
||||
.queryParam("redirect_uri", config.getRedirectUri())
|
||||
.queryParam("access_type", "offline")
|
||||
.queryParam("scope", "https%3A%2F%2Fwww.huawei.com%2Fauth%2Faccount%2Fbase.profile")
|
||||
.queryParam("state", getRealState(state))
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回获取accessToken的url
|
||||
*
|
||||
* @param code 授权码
|
||||
* @return 返回获取accessToken的url
|
||||
*/
|
||||
@Override
|
||||
protected String accessTokenUrl(String code) {
|
||||
return UrlBuilder.fromBaseUrl(source.accessToken())
|
||||
.queryParam("grant_type", "authorization_code")
|
||||
.queryParam("code", code)
|
||||
.queryParam("client_id", config.getClientId())
|
||||
.queryParam("client_secret", config.getClientSecret())
|
||||
.queryParam("redirect_uri", config.getRedirectUri())
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回获取userInfo的url
|
||||
*
|
||||
* @param authToken token
|
||||
* @return 返回获取userInfo的url
|
||||
*/
|
||||
@Override
|
||||
protected String userInfoUrl(AuthToken authToken) {
|
||||
return UrlBuilder.fromBaseUrl(source.userInfo())
|
||||
.queryParam("nsp_ts", System.currentTimeMillis())
|
||||
.queryParam("access_token", authToken.getAccessToken())
|
||||
.queryParam("nsp_fmt", "JS")
|
||||
.queryParam("nsp_svc", "OpenUP.User.getInfo")
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户的实际性别。华为系统中,用户的性别:1表示女,0表示男
|
||||
*
|
||||
* @param object obj
|
||||
* @return AuthUserGender
|
||||
*/
|
||||
private AuthUserGender getRealGender(JSONObject object) {
|
||||
int genderCodeInt = object.getIntValue("gender");
|
||||
String genderCode = genderCodeInt == 1 ? "0" : (genderCodeInt == 0) ? "1" : genderCodeInt + "";
|
||||
return AuthUserGender.getRealGender(genderCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验响应结果
|
||||
*
|
||||
* @param object 接口返回的结果
|
||||
*/
|
||||
private void checkResponse(JSONObject object) {
|
||||
if (object.containsKey("NSP_STATUS")) {
|
||||
throw new AuthException(object.getString("error"));
|
||||
}
|
||||
if (object.containsKey("error")) {
|
||||
throw new AuthException(object.getString("sub_error") + ":" + object.getString("error_description"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
package me.zhyd.oauth.request;
|
||||
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import me.zhyd.oauth.cache.AuthStateCache;
|
||||
import me.zhyd.oauth.config.AuthConfig;
|
||||
import me.zhyd.oauth.config.AuthDefaultSource;
|
||||
import me.zhyd.oauth.enums.AuthResponseStatus;
|
||||
import me.zhyd.oauth.exception.AuthException;
|
||||
import me.zhyd.oauth.model.AuthCallback;
|
||||
import me.zhyd.oauth.model.AuthResponse;
|
||||
import me.zhyd.oauth.model.AuthToken;
|
||||
import me.zhyd.oauth.model.AuthUser;
|
||||
import me.zhyd.oauth.utils.StringUtils;
|
||||
import me.zhyd.oauth.utils.UrlBuilder;
|
||||
|
||||
/**
|
||||
* 酷家乐授权登录
|
||||
*
|
||||
* @author shahuang
|
||||
* @since 1.11.0
|
||||
*/
|
||||
public class AuthKujialeRequest extends AuthDefaultRequest {
|
||||
|
||||
public AuthKujialeRequest(AuthConfig config) {
|
||||
super(config, AuthDefaultSource.KUJIALE);
|
||||
}
|
||||
|
||||
public AuthKujialeRequest(AuthConfig config, AuthStateCache authStateCache) {
|
||||
super(config, AuthDefaultSource.KUJIALE, authStateCache);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state}
|
||||
* 默认只向用户请求用户信息授权
|
||||
*
|
||||
* @param state state 验证授权流程的参数,可以防止csrf
|
||||
* @return 返回授权地址
|
||||
* @since 1.11.0
|
||||
*/
|
||||
@Override
|
||||
public String authorize(String state) {
|
||||
return authorize(state, "get_user_info");
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求授权url
|
||||
*
|
||||
* @param state state 验证授权流程的参数,可以防止csrf
|
||||
* @param scopeStr 请求用户授权时向用户显示的可进行授权的列表。如果要填写多个接口名称,请用逗号隔开
|
||||
* 参考https://open.kujiale.com/open/apps/2/docs?doc_id=95#Step1%EF%BC%9A%E8%8E%B7%E5%8F%96Authorization%20Code参数表内的scope字段
|
||||
* @return authorize url
|
||||
*/
|
||||
public String authorize(String state, String scopeStr) {
|
||||
UrlBuilder urlBuilder = UrlBuilder.fromBaseUrl(source.authorize())
|
||||
.queryParam("response_type", "code")
|
||||
.queryParam("client_id", config.getClientId())
|
||||
.queryParam("redirect_uri", config.getRedirectUri())
|
||||
.queryParam("state", getRealState(state));
|
||||
if (StringUtils.isNotEmpty(scopeStr)) {
|
||||
urlBuilder.queryParam("scope", scopeStr);
|
||||
}
|
||||
return urlBuilder.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthToken getAccessToken(AuthCallback authCallback) {
|
||||
HttpResponse response = doPostAuthorizationCode(authCallback.getCode());
|
||||
return getAuthToken(response);
|
||||
}
|
||||
|
||||
private AuthToken getAuthToken(HttpResponse response) {
|
||||
JSONObject accessTokenObject = checkResponse(response);
|
||||
JSONObject resultObject = accessTokenObject.getJSONObject("d");
|
||||
return AuthToken.builder()
|
||||
.accessToken(resultObject.getString("accessToken"))
|
||||
.refreshToken(resultObject.getString("refreshToken"))
|
||||
.expireIn(resultObject.getIntValue("expiresIn"))
|
||||
.build();
|
||||
}
|
||||
|
||||
private JSONObject checkResponse(HttpResponse response) {
|
||||
String accessTokenStr = response.body();
|
||||
JSONObject accessTokenObject = JSONObject.parseObject(accessTokenStr);
|
||||
if (!"0".equals(accessTokenObject.getString("c"))) {
|
||||
throw new AuthException(accessTokenObject.getString("m"));
|
||||
}
|
||||
return accessTokenObject;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthUser getUserInfo(AuthToken authToken) {
|
||||
String openId = this.getOpenId(authToken);
|
||||
HttpResponse response = HttpRequest.get(UrlBuilder.fromBaseUrl(source.userInfo())
|
||||
.queryParam("access_token", authToken.getAccessToken())
|
||||
.queryParam("open_id", openId)
|
||||
.build()).execute();
|
||||
JSONObject object = JSONObject.parseObject(response.body());
|
||||
if (!"0".equals(object.getString("c"))) {
|
||||
throw new AuthException(object.getString("m"));
|
||||
}
|
||||
JSONObject resultObject = object.getJSONObject("d");
|
||||
|
||||
return AuthUser.builder()
|
||||
.username(resultObject.getString("userName"))
|
||||
.nickname(resultObject.getString("userName"))
|
||||
.avatar(resultObject.getString("avatar"))
|
||||
.uuid(resultObject.getString("openId"))
|
||||
.token(authToken)
|
||||
.source(source.toString())
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取酷家乐的openId,此id在当前client范围内可以唯一识别授权用户
|
||||
*
|
||||
* @param authToken 通过{@link AuthKujialeRequest#getAccessToken(AuthCallback)}获取到的{@code authToken}
|
||||
* @return openId
|
||||
*/
|
||||
private String getOpenId(AuthToken authToken) {
|
||||
HttpResponse response = HttpRequest.get(UrlBuilder.fromBaseUrl("https://oauth.kujiale.com/oauth2/auth/user")
|
||||
.queryParam("access_token", authToken.getAccessToken())
|
||||
.build()).execute();
|
||||
JSONObject accessTokenObject = checkResponse(response);
|
||||
return accessTokenObject.getString("d");
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthResponse refresh(AuthToken authToken) {
|
||||
HttpResponse response = HttpRequest.post(refreshTokenUrl(authToken.getRefreshToken())).execute();
|
||||
return AuthResponse.builder().code(AuthResponseStatus.SUCCESS.getCode()).data(getAuthToken(response)).build();
|
||||
}
|
||||
}
|
||||
@@ -5,11 +5,16 @@ import cn.hutool.http.HttpResponse;
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.alibaba.fastjson.JSONPath;
|
||||
import me.zhyd.oauth.cache.AuthStateCache;
|
||||
import me.zhyd.oauth.config.AuthConfig;
|
||||
import me.zhyd.oauth.config.AuthSource;
|
||||
import me.zhyd.oauth.config.AuthDefaultSource;
|
||||
import me.zhyd.oauth.enums.AuthResponseStatus;
|
||||
import me.zhyd.oauth.enums.AuthUserGender;
|
||||
import me.zhyd.oauth.exception.AuthException;
|
||||
import me.zhyd.oauth.model.*;
|
||||
import me.zhyd.oauth.model.AuthCallback;
|
||||
import me.zhyd.oauth.model.AuthResponse;
|
||||
import me.zhyd.oauth.model.AuthToken;
|
||||
import me.zhyd.oauth.model.AuthUser;
|
||||
import me.zhyd.oauth.utils.StringUtils;
|
||||
import me.zhyd.oauth.utils.UrlBuilder;
|
||||
|
||||
@@ -18,13 +23,16 @@ import me.zhyd.oauth.utils.UrlBuilder;
|
||||
* 领英登录
|
||||
*
|
||||
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
|
||||
* @version 1.0
|
||||
* @since 1.8
|
||||
* @since 1.4.0
|
||||
*/
|
||||
public class AuthLinkedinRequest extends AuthDefaultRequest {
|
||||
|
||||
public AuthLinkedinRequest(AuthConfig config) {
|
||||
super(config, AuthSource.LINKEDIN);
|
||||
super(config, AuthDefaultSource.LINKEDIN);
|
||||
}
|
||||
|
||||
public AuthLinkedinRequest(AuthConfig config, AuthStateCache authStateCache) {
|
||||
super(config, AuthDefaultSource.LINKEDIN, authStateCache);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -59,7 +67,7 @@ public class AuthLinkedinRequest extends AuthDefaultRequest {
|
||||
.email(email)
|
||||
.token(authToken)
|
||||
.gender(AuthUserGender.UNKNOWN)
|
||||
.source(AuthSource.LINKEDIN)
|
||||
.source(source.toString())
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -113,7 +121,6 @@ public class AuthLinkedinRequest extends AuthDefaultRequest {
|
||||
* @return 用户的邮箱地址
|
||||
*/
|
||||
private String getUserEmail(String accessToken) {
|
||||
String email = null;
|
||||
HttpResponse emailResponse = HttpRequest.get("https://api.linkedin.com/v2/emailAddress?q=members&projection=(elements*(handle~))")
|
||||
.header("Host", "api.linkedin.com")
|
||||
.header("Connection", "Keep-Alive")
|
||||
@@ -182,18 +189,20 @@ public class AuthLinkedinRequest extends AuthDefaultRequest {
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回认证url,可自行跳转页面
|
||||
* 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state}
|
||||
*
|
||||
* @param state state 验证授权流程的参数,可以防止csrf
|
||||
* @return 返回授权地址
|
||||
* @since 1.9.3
|
||||
*/
|
||||
@Override
|
||||
public String authorize() {
|
||||
public String authorize(String state) {
|
||||
return UrlBuilder.fromBaseUrl(source.authorize())
|
||||
.queryParam("response_type", "code")
|
||||
.queryParam("client_id", config.getClientId())
|
||||
.queryParam("redirect_uri", config.getRedirectUri())
|
||||
.queryParam("state", getRealState(config.getState()))
|
||||
.queryParam("scope", "r_liteprofile%20r_emailaddress%20w_member_social")
|
||||
.queryParam("state", getRealState(state))
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
package me.zhyd.oauth.request;
|
||||
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import me.zhyd.oauth.cache.AuthStateCache;
|
||||
import me.zhyd.oauth.config.AuthConfig;
|
||||
import me.zhyd.oauth.config.AuthDefaultSource;
|
||||
import me.zhyd.oauth.enums.AuthResponseStatus;
|
||||
import me.zhyd.oauth.enums.AuthUserGender;
|
||||
import me.zhyd.oauth.exception.AuthException;
|
||||
import me.zhyd.oauth.model.AuthCallback;
|
||||
import me.zhyd.oauth.model.AuthResponse;
|
||||
import me.zhyd.oauth.model.AuthToken;
|
||||
import me.zhyd.oauth.model.AuthUser;
|
||||
import me.zhyd.oauth.utils.UrlBuilder;
|
||||
|
||||
/**
|
||||
* 美团登录
|
||||
*
|
||||
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
|
||||
* @since 1.12.0
|
||||
*/
|
||||
public class AuthMeituanRequest extends AuthDefaultRequest {
|
||||
|
||||
public AuthMeituanRequest(AuthConfig config) {
|
||||
super(config, AuthDefaultSource.MEITUAN);
|
||||
}
|
||||
|
||||
public AuthMeituanRequest(AuthConfig config, AuthStateCache authStateCache) {
|
||||
super(config, AuthDefaultSource.MEITUAN, authStateCache);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AuthToken getAccessToken(AuthCallback authCallback) {
|
||||
HttpResponse response = HttpRequest.post(source.accessToken())
|
||||
.form("app_id", config.getClientId())
|
||||
.form("secret", config.getClientSecret())
|
||||
.form("code", authCallback.getCode())
|
||||
.form("grant_type", "authorization_code")
|
||||
.execute();
|
||||
JSONObject object = JSONObject.parseObject(response.body());
|
||||
|
||||
this.checkResponse(object);
|
||||
|
||||
return AuthToken.builder()
|
||||
.accessToken(object.getString("access_token"))
|
||||
.refreshToken(object.getString("refresh_token"))
|
||||
.expireIn(object.getIntValue("expires_in"))
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AuthUser getUserInfo(AuthToken authToken) {
|
||||
HttpResponse response = HttpRequest.post(source.userInfo())
|
||||
.form("app_id", config.getClientId())
|
||||
.form("secret", config.getClientSecret())
|
||||
.form("access_token", authToken.getAccessToken())
|
||||
.execute();
|
||||
JSONObject object = JSONObject.parseObject(response.body());
|
||||
|
||||
this.checkResponse(object);
|
||||
|
||||
return AuthUser.builder()
|
||||
.uuid(object.getString("openid"))
|
||||
.username(object.getString("nickname"))
|
||||
.nickname(object.getString("nickname"))
|
||||
.avatar(object.getString("avatar"))
|
||||
.gender(AuthUserGender.UNKNOWN)
|
||||
.token(authToken)
|
||||
.source(source.toString())
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthResponse refresh(AuthToken oldToken) {
|
||||
HttpResponse response = HttpRequest.post(source.refresh())
|
||||
.form("app_id", config.getClientId())
|
||||
.form("secret", config.getClientSecret())
|
||||
.form("refresh_token", oldToken.getRefreshToken())
|
||||
.form("grant_type", "refresh_token")
|
||||
.execute();
|
||||
JSONObject object = JSONObject.parseObject(response.body());
|
||||
|
||||
this.checkResponse(object);
|
||||
|
||||
return AuthResponse.builder()
|
||||
.code(AuthResponseStatus.SUCCESS.getCode())
|
||||
.data(AuthToken.builder()
|
||||
.accessToken(object.getString("access_token"))
|
||||
.refreshToken(object.getString("refresh_token"))
|
||||
.expireIn(object.getIntValue("expires_in"))
|
||||
.build())
|
||||
.build();
|
||||
}
|
||||
|
||||
private void checkResponse(JSONObject object) {
|
||||
if (object.containsKey("error_code")) {
|
||||
throw new AuthException(object.getString("erroe_msg"));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String authorize(String state) {
|
||||
return UrlBuilder.fromBaseUrl(source.authorize())
|
||||
.queryParam("response_type", "code")
|
||||
.queryParam("app_id", config.getClientId())
|
||||
.queryParam("redirect_uri", config.getRedirectUri())
|
||||
.queryParam("state", getRealState(state))
|
||||
.queryParam("scope", "")
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -4,12 +4,17 @@ import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
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.config.AuthDefaultSource;
|
||||
import me.zhyd.oauth.enums.AuthResponseStatus;
|
||||
import me.zhyd.oauth.enums.AuthUserGender;
|
||||
import me.zhyd.oauth.exception.AuthException;
|
||||
import me.zhyd.oauth.model.*;
|
||||
import me.zhyd.oauth.log.Log;
|
||||
import me.zhyd.oauth.model.AuthCallback;
|
||||
import me.zhyd.oauth.model.AuthResponse;
|
||||
import me.zhyd.oauth.model.AuthToken;
|
||||
import me.zhyd.oauth.model.AuthUser;
|
||||
import me.zhyd.oauth.utils.UrlBuilder;
|
||||
|
||||
import java.text.MessageFormat;
|
||||
@@ -18,15 +23,17 @@ import java.text.MessageFormat;
|
||||
* 小米登录
|
||||
*
|
||||
* @author yangkai.shen (https://xkcoding.com)
|
||||
* @version 1.5
|
||||
* @since 1.5
|
||||
* @since 1.5.0
|
||||
*/
|
||||
@Slf4j
|
||||
public class AuthMiRequest extends AuthDefaultRequest {
|
||||
private static final String PREFIX = "&&&START&&&";
|
||||
|
||||
public AuthMiRequest(AuthConfig config) {
|
||||
super(config, AuthSource.MI);
|
||||
super(config, AuthDefaultSource.MI);
|
||||
}
|
||||
|
||||
public AuthMiRequest(AuthConfig config, AuthStateCache authStateCache) {
|
||||
super(config, AuthDefaultSource.MI, authStateCache);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -75,7 +82,7 @@ public class AuthMiRequest extends AuthDefaultRequest {
|
||||
.email(user.getString("mail"))
|
||||
.gender(AuthUserGender.UNKNOWN)
|
||||
.token(authToken)
|
||||
.source(source)
|
||||
.source(source.toString())
|
||||
.build();
|
||||
|
||||
// 获取用户邮箱手机号等信息
|
||||
@@ -88,7 +95,7 @@ public class AuthMiRequest extends AuthDefaultRequest {
|
||||
JSONObject emailPhone = userEmailPhone.getJSONObject("data");
|
||||
authUser.setEmail(emailPhone.getString("email"));
|
||||
} else {
|
||||
log.warn("小米开发平台暂时不对外开放用户手机及邮箱信息的获取");
|
||||
Log.warn("小米开发平台暂时不对外开放用户手机及邮箱信息的获取");
|
||||
}
|
||||
|
||||
return authUser;
|
||||
@@ -109,19 +116,21 @@ public class AuthMiRequest extends AuthDefaultRequest {
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回认证url,可自行跳转页面
|
||||
* 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state}
|
||||
*
|
||||
* @param state state 验证授权流程的参数,可以防止csrf
|
||||
* @return 返回授权地址
|
||||
* @since 1.9.3
|
||||
*/
|
||||
@Override
|
||||
public String authorize() {
|
||||
public String authorize(String state) {
|
||||
return UrlBuilder.fromBaseUrl(source.authorize())
|
||||
.queryParam("response_type", "code")
|
||||
.queryParam("client_id", config.getClientId())
|
||||
.queryParam("redirect_uri", config.getRedirectUri())
|
||||
.queryParam("state", getRealState(config.getState()))
|
||||
.queryParam("scope", "user/profile%20user/openIdV2%20user/phoneAndEmail")
|
||||
.queryParam("skip_confirm", "false")
|
||||
.queryParam("state", getRealState(state))
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
@@ -3,11 +3,16 @@ package me.zhyd.oauth.request;
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import me.zhyd.oauth.cache.AuthStateCache;
|
||||
import me.zhyd.oauth.config.AuthConfig;
|
||||
import me.zhyd.oauth.config.AuthSource;
|
||||
import me.zhyd.oauth.config.AuthDefaultSource;
|
||||
import me.zhyd.oauth.enums.AuthResponseStatus;
|
||||
import me.zhyd.oauth.enums.AuthUserGender;
|
||||
import me.zhyd.oauth.exception.AuthException;
|
||||
import me.zhyd.oauth.model.*;
|
||||
import me.zhyd.oauth.model.AuthCallback;
|
||||
import me.zhyd.oauth.model.AuthResponse;
|
||||
import me.zhyd.oauth.model.AuthToken;
|
||||
import me.zhyd.oauth.model.AuthUser;
|
||||
import me.zhyd.oauth.utils.UrlBuilder;
|
||||
|
||||
import static me.zhyd.oauth.utils.GlobalAuthUtil.parseQueryToMap;
|
||||
@@ -16,12 +21,15 @@ import static me.zhyd.oauth.utils.GlobalAuthUtil.parseQueryToMap;
|
||||
* 微软登录
|
||||
*
|
||||
* @author yangkai.shen (https://xkcoding.com)
|
||||
* @version 1.5
|
||||
* @since 1.5
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public class AuthMicrosoftRequest extends AuthDefaultRequest {
|
||||
public AuthMicrosoftRequest(AuthConfig config) {
|
||||
super(config, AuthSource.MICROSOFT);
|
||||
super(config, AuthDefaultSource.MICROSOFT);
|
||||
}
|
||||
|
||||
public AuthMicrosoftRequest(AuthConfig config, AuthStateCache authStateCache) {
|
||||
super(config, AuthDefaultSource.MICROSOFT, authStateCache);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -83,7 +91,7 @@ public class AuthMicrosoftRequest extends AuthDefaultRequest {
|
||||
.email(object.getString("mail"))
|
||||
.gender(AuthUserGender.UNKNOWN)
|
||||
.token(authToken)
|
||||
.source(source)
|
||||
.source(source.toString())
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -102,26 +110,28 @@ public class AuthMicrosoftRequest extends AuthDefaultRequest {
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回认证url,可自行跳转页面
|
||||
* 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state}
|
||||
*
|
||||
* @param state state 验证授权流程的参数,可以防止csrf
|
||||
* @return 返回授权地址
|
||||
* @since 1.9.3
|
||||
*/
|
||||
@Override
|
||||
public String authorize() {
|
||||
public String authorize(String state) {
|
||||
return UrlBuilder.fromBaseUrl(source.authorize())
|
||||
.queryParam("response_type", "code")
|
||||
.queryParam("client_id", config.getClientId())
|
||||
.queryParam("redirect_uri", config.getRedirectUri())
|
||||
.queryParam("response_mode", "query")
|
||||
.queryParam("scope", "offline_access%20user.read%20mail.read")
|
||||
.queryParam("state", getRealState(config.getState()))
|
||||
.queryParam("state", getRealState(state))
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回获取accessToken的url
|
||||
*
|
||||
* @param code
|
||||
* @param code 授权code
|
||||
* @return 返回获取accessToken的url
|
||||
*/
|
||||
@Override
|
||||
|
||||
@@ -2,8 +2,9 @@ package me.zhyd.oauth.request;
|
||||
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import me.zhyd.oauth.cache.AuthStateCache;
|
||||
import me.zhyd.oauth.config.AuthConfig;
|
||||
import me.zhyd.oauth.config.AuthSource;
|
||||
import me.zhyd.oauth.config.AuthDefaultSource;
|
||||
import me.zhyd.oauth.enums.AuthUserGender;
|
||||
import me.zhyd.oauth.exception.AuthException;
|
||||
import me.zhyd.oauth.model.AuthCallback;
|
||||
@@ -15,13 +16,16 @@ import me.zhyd.oauth.utils.UrlBuilder;
|
||||
* oschina登录
|
||||
*
|
||||
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
|
||||
* @version 1.0
|
||||
* @since 1.8
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class AuthOschinaRequest extends AuthDefaultRequest {
|
||||
|
||||
public AuthOschinaRequest(AuthConfig config) {
|
||||
super(config, AuthSource.OSCHINA);
|
||||
super(config, AuthDefaultSource.OSCHINA);
|
||||
}
|
||||
|
||||
public AuthOschinaRequest(AuthConfig config, AuthStateCache authStateCache) {
|
||||
super(config, AuthDefaultSource.OSCHINA, authStateCache);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -52,14 +56,14 @@ public class AuthOschinaRequest extends AuthDefaultRequest {
|
||||
.gender(AuthUserGender.getRealGender(object.getString("gender")))
|
||||
.email(object.getString("email"))
|
||||
.token(authToken)
|
||||
.source(source)
|
||||
.source(source.toString())
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回获取accessToken的url
|
||||
*
|
||||
* @param code
|
||||
* @param code 授权回调时带回的授权码
|
||||
* @return 返回获取accessToken的url
|
||||
*/
|
||||
@Override
|
||||
|
||||
@@ -3,6 +3,7 @@ package me.zhyd.oauth.request;
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import me.zhyd.oauth.cache.AuthStateCache;
|
||||
import me.zhyd.oauth.config.AuthConfig;
|
||||
import me.zhyd.oauth.enums.AuthUserGender;
|
||||
import me.zhyd.oauth.exception.AuthException;
|
||||
@@ -13,14 +14,13 @@ import me.zhyd.oauth.utils.UrlBuilder;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import static me.zhyd.oauth.config.AuthSource.PINTEREST;
|
||||
import static me.zhyd.oauth.config.AuthDefaultSource.PINTEREST;
|
||||
|
||||
/**
|
||||
* Pinterest登录
|
||||
*
|
||||
* @author hongwei.peng (pengisgood(at)gmail(dot)com)
|
||||
* @version 1.9.0
|
||||
* @since 1.8
|
||||
* @since 1.9.0
|
||||
*/
|
||||
public class AuthPinterestRequest extends AuthDefaultRequest {
|
||||
|
||||
@@ -30,6 +30,10 @@ public class AuthPinterestRequest extends AuthDefaultRequest {
|
||||
super(config, PINTEREST);
|
||||
}
|
||||
|
||||
public AuthPinterestRequest(AuthConfig config, AuthStateCache authStateCache) {
|
||||
super(config, PINTEREST, authStateCache);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AuthToken getAccessToken(AuthCallback authCallback) {
|
||||
HttpResponse response = doPostAuthorizationCode(authCallback.getCode());
|
||||
@@ -56,7 +60,7 @@ public class AuthPinterestRequest extends AuthDefaultRequest {
|
||||
.gender(AuthUserGender.UNKNOWN)
|
||||
.remark(userObj.getString("bio"))
|
||||
.token(authToken)
|
||||
.source(source)
|
||||
.source(source.toString())
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -69,14 +73,21 @@ public class AuthPinterestRequest extends AuthDefaultRequest {
|
||||
return jsonObject.getJSONObject("60x60").getString("url");
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state}
|
||||
*
|
||||
* @param state state 验证授权流程的参数,可以防止csrf
|
||||
* @return 返回授权地址
|
||||
* @since 1.9.3
|
||||
*/
|
||||
@Override
|
||||
public String authorize() {
|
||||
public String authorize(String state) {
|
||||
return UrlBuilder.fromBaseUrl(source.authorize())
|
||||
.queryParam("response_type", "code")
|
||||
.queryParam("client_id", config.getClientId())
|
||||
.queryParam("redirect_uri", config.getRedirectUri())
|
||||
.queryParam("state", getRealState(config.getState()))
|
||||
.queryParam("scope", "read_public")
|
||||
.queryParam("state", getRealState(state))
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -86,6 +97,7 @@ public class AuthPinterestRequest extends AuthDefaultRequest {
|
||||
* @param authToken token
|
||||
* @return 返回获取userInfo的url
|
||||
*/
|
||||
@Override
|
||||
protected String userInfoUrl(AuthToken authToken) {
|
||||
return UrlBuilder.fromBaseUrl(source.userInfo())
|
||||
.queryParam("access_token", authToken.getAccessToken())
|
||||
|
||||
@@ -4,11 +4,16 @@ import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import me.zhyd.oauth.cache.AuthStateCache;
|
||||
import me.zhyd.oauth.config.AuthConfig;
|
||||
import me.zhyd.oauth.config.AuthSource;
|
||||
import me.zhyd.oauth.config.AuthDefaultSource;
|
||||
import me.zhyd.oauth.enums.AuthResponseStatus;
|
||||
import me.zhyd.oauth.enums.AuthUserGender;
|
||||
import me.zhyd.oauth.exception.AuthException;
|
||||
import me.zhyd.oauth.model.*;
|
||||
import me.zhyd.oauth.model.AuthCallback;
|
||||
import me.zhyd.oauth.model.AuthResponse;
|
||||
import me.zhyd.oauth.model.AuthToken;
|
||||
import me.zhyd.oauth.model.AuthUser;
|
||||
import me.zhyd.oauth.utils.GlobalAuthUtil;
|
||||
import me.zhyd.oauth.utils.StringUtils;
|
||||
import me.zhyd.oauth.utils.UrlBuilder;
|
||||
@@ -20,12 +25,15 @@ import java.util.Map;
|
||||
*
|
||||
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
|
||||
* @author yangkai.shen (https://xkcoding.com)
|
||||
* @version 1.0
|
||||
* @since 1.8
|
||||
* @since 1.1.0
|
||||
*/
|
||||
public class AuthQqRequest extends AuthDefaultRequest {
|
||||
public AuthQqRequest(AuthConfig config) {
|
||||
super(config, AuthSource.QQ);
|
||||
super(config, AuthDefaultSource.QQ);
|
||||
}
|
||||
|
||||
public AuthQqRequest(AuthConfig config, AuthStateCache authStateCache) {
|
||||
super(config, AuthDefaultSource.QQ, authStateCache);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -37,10 +45,7 @@ public class AuthQqRequest extends AuthDefaultRequest {
|
||||
@Override
|
||||
public AuthResponse refresh(AuthToken authToken) {
|
||||
HttpResponse response = HttpRequest.get(refreshTokenUrl(authToken.getRefreshToken())).execute();
|
||||
return AuthResponse.builder()
|
||||
.code(AuthResponseStatus.SUCCESS.getCode())
|
||||
.data(getAuthToken(response))
|
||||
.build();
|
||||
return AuthResponse.builder().code(AuthResponseStatus.SUCCESS.getCode()).data(getAuthToken(response)).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -65,10 +70,17 @@ public class AuthQqRequest extends AuthDefaultRequest {
|
||||
.uuid(openId)
|
||||
.gender(AuthUserGender.getRealGender(object.getString("gender")))
|
||||
.token(authToken)
|
||||
.source(source)
|
||||
.source(source.toString())
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取QQ用户的OpenId,支持自定义是否启用查询unionid的功能,如果启用查询unionid的功能,
|
||||
* 那就需要开发者先通过邮件申请unionid功能,参考链接 {@see http://wiki.connect.qq.com/unionid%E4%BB%8B%E7%BB%8D}
|
||||
*
|
||||
* @param authToken 通过{@link AuthQqRequest#getAccessToken(AuthCallback)}获取到的{@code authToken}
|
||||
* @return openId
|
||||
*/
|
||||
private String getOpenId(AuthToken authToken) {
|
||||
HttpResponse response = HttpRequest.get(UrlBuilder.fromBaseUrl("https://graph.qq.com/oauth2.0/me")
|
||||
.queryParam("access_token", authToken.getAccessToken())
|
||||
|
||||
@@ -4,23 +4,26 @@ import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import me.zhyd.oauth.cache.AuthStateCache;
|
||||
import me.zhyd.oauth.config.AuthConfig;
|
||||
import me.zhyd.oauth.enums.AuthUserGender;
|
||||
import me.zhyd.oauth.exception.AuthException;
|
||||
import me.zhyd.oauth.model.*;
|
||||
import me.zhyd.oauth.model.AuthCallback;
|
||||
import me.zhyd.oauth.model.AuthResponse;
|
||||
import me.zhyd.oauth.model.AuthToken;
|
||||
import me.zhyd.oauth.model.AuthUser;
|
||||
import me.zhyd.oauth.utils.UrlBuilder;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import static me.zhyd.oauth.config.AuthSource.RENREN;
|
||||
import static me.zhyd.oauth.model.AuthResponseStatus.SUCCESS;
|
||||
import static me.zhyd.oauth.config.AuthDefaultSource.RENREN;
|
||||
import static me.zhyd.oauth.enums.AuthResponseStatus.SUCCESS;
|
||||
|
||||
/**
|
||||
* 人人登录
|
||||
*
|
||||
* @author hongwei.peng (pengisgood(at)gmail(dot)com)
|
||||
* @version 1.9.0
|
||||
* @since 1.8
|
||||
* @since 1.9.0
|
||||
*/
|
||||
public class AuthRenrenRequest extends AuthDefaultRequest {
|
||||
|
||||
@@ -28,6 +31,10 @@ public class AuthRenrenRequest extends AuthDefaultRequest {
|
||||
super(config, RENREN);
|
||||
}
|
||||
|
||||
public AuthRenrenRequest(AuthConfig config, AuthStateCache authStateCache) {
|
||||
super(config, RENREN, authStateCache);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AuthToken getAccessToken(AuthCallback authCallback) {
|
||||
return this.getToken(accessTokenUrl(authCallback.getCode()));
|
||||
@@ -45,7 +52,7 @@ public class AuthRenrenRequest extends AuthDefaultRequest {
|
||||
.company(getCompany(userObj))
|
||||
.gender(getGender(userObj))
|
||||
.token(authToken)
|
||||
.source(source)
|
||||
.source(source.toString())
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,27 +1,48 @@
|
||||
package me.zhyd.oauth.request;
|
||||
|
||||
import me.zhyd.oauth.enums.AuthResponseStatus;
|
||||
import me.zhyd.oauth.exception.AuthException;
|
||||
import me.zhyd.oauth.model.AuthCallback;
|
||||
import me.zhyd.oauth.model.AuthResponse;
|
||||
import me.zhyd.oauth.model.AuthResponseStatus;
|
||||
import me.zhyd.oauth.model.AuthToken;
|
||||
|
||||
/**
|
||||
* JustAuth {@code Request}公共接口,所有平台的{@code Request}都需要实现该接口
|
||||
* <p>
|
||||
* {@link AuthRequest#authorize()}
|
||||
* {@link AuthRequest#authorize(String)}
|
||||
* {@link AuthRequest#login(AuthCallback)}
|
||||
* {@link AuthRequest#revoke(AuthToken)}
|
||||
* {@link AuthRequest#refresh(AuthToken)}
|
||||
*
|
||||
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
|
||||
* @version 1.0
|
||||
* @since 1.8
|
||||
*/
|
||||
public interface AuthRequest {
|
||||
|
||||
/**
|
||||
* 返回认证url,可自行跳转页面
|
||||
* 返回授权url,可自行跳转页面
|
||||
* <p>
|
||||
* 不建议使用该方式获取授权地址,不带{@code state}的授权地址,容易受到csrf攻击。
|
||||
* 建议使用{@link AuthDefaultRequest#authorize(String)}方法生成授权地址,在回调方法中对{@code state}进行校验
|
||||
*
|
||||
* @return 返回授权地址
|
||||
*/
|
||||
@Deprecated
|
||||
default String authorize() {
|
||||
throw new AuthException(AuthResponseStatus.NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state}
|
||||
*
|
||||
* @param state state 验证授权流程的参数,可以防止csrf
|
||||
* @return 返回授权地址
|
||||
*/
|
||||
default String authorize(String state) {
|
||||
throw new AuthException(AuthResponseStatus.NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
/**
|
||||
* 第三方登录
|
||||
*
|
||||
|
||||
@@ -3,6 +3,7 @@ package me.zhyd.oauth.request;
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import me.zhyd.oauth.cache.AuthStateCache;
|
||||
import me.zhyd.oauth.config.AuthConfig;
|
||||
import me.zhyd.oauth.enums.AuthUserGender;
|
||||
import me.zhyd.oauth.exception.AuthException;
|
||||
@@ -11,15 +12,14 @@ import me.zhyd.oauth.model.AuthToken;
|
||||
import me.zhyd.oauth.model.AuthUser;
|
||||
import me.zhyd.oauth.utils.UrlBuilder;
|
||||
|
||||
import static me.zhyd.oauth.config.AuthSource.STACK_OVERFLOW;
|
||||
import static me.zhyd.oauth.config.AuthDefaultSource.STACK_OVERFLOW;
|
||||
import static me.zhyd.oauth.utils.GlobalAuthUtil.parseQueryToMap;
|
||||
|
||||
/**
|
||||
* Stack Overflow登录
|
||||
*
|
||||
* @author hongwei.peng (pengisgood(at)gmail(dot)com)
|
||||
* @version 1.9.0
|
||||
* @since 1.8
|
||||
* @since 1.9.0
|
||||
*/
|
||||
public class AuthStackOverflowRequest extends AuthDefaultRequest {
|
||||
|
||||
@@ -27,6 +27,10 @@ public class AuthStackOverflowRequest extends AuthDefaultRequest {
|
||||
super(config, STACK_OVERFLOW);
|
||||
}
|
||||
|
||||
public AuthStackOverflowRequest(AuthConfig config, AuthStateCache authStateCache) {
|
||||
super(config, STACK_OVERFLOW, authStateCache);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AuthToken getAccessToken(AuthCallback authCallback) {
|
||||
String accessTokenUrl = accessTokenUrl(authCallback.getCode());
|
||||
@@ -63,18 +67,25 @@ public class AuthStackOverflowRequest extends AuthDefaultRequest {
|
||||
.blog(userObj.getString("website_url"))
|
||||
.gender(AuthUserGender.UNKNOWN)
|
||||
.token(authToken)
|
||||
.source(source)
|
||||
.source(source.toString())
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state}
|
||||
*
|
||||
* @param state state 验证授权流程的参数,可以防止csrf
|
||||
* @return 返回授权地址
|
||||
* @since 1.9.3
|
||||
*/
|
||||
@Override
|
||||
public String authorize() {
|
||||
public String authorize(String state) {
|
||||
return UrlBuilder.fromBaseUrl(source.authorize())
|
||||
.queryParam("response_type", "code")
|
||||
.queryParam("client_id", config.getClientId())
|
||||
.queryParam("redirect_uri", config.getRedirectUri())
|
||||
.queryParam("state", getRealState(config.getState()))
|
||||
.queryParam("scope", "read_inbox")
|
||||
.queryParam("state", getRealState(state))
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
@@ -2,13 +2,14 @@ package me.zhyd.oauth.request;
|
||||
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import me.zhyd.oauth.cache.AuthStateCache;
|
||||
import me.zhyd.oauth.config.AuthConfig;
|
||||
import me.zhyd.oauth.config.AuthSource;
|
||||
import me.zhyd.oauth.config.AuthDefaultSource;
|
||||
import me.zhyd.oauth.enums.AuthUserGender;
|
||||
import me.zhyd.oauth.exception.AuthException;
|
||||
import me.zhyd.oauth.model.AuthCallback;
|
||||
import me.zhyd.oauth.model.AuthToken;
|
||||
import me.zhyd.oauth.model.AuthUser;
|
||||
import me.zhyd.oauth.enums.AuthUserGender;
|
||||
import me.zhyd.oauth.utils.GlobalAuthUtil;
|
||||
import me.zhyd.oauth.utils.UrlBuilder;
|
||||
|
||||
@@ -16,13 +17,16 @@ import me.zhyd.oauth.utils.UrlBuilder;
|
||||
* 淘宝登录
|
||||
*
|
||||
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
|
||||
* @version 1.0
|
||||
* @since 1.8
|
||||
* @since 1.1.0
|
||||
*/
|
||||
public class AuthTaobaoRequest extends AuthDefaultRequest {
|
||||
|
||||
public AuthTaobaoRequest(AuthConfig config) {
|
||||
super(config, AuthSource.TAOBAO);
|
||||
super(config, AuthDefaultSource.TAOBAO);
|
||||
}
|
||||
|
||||
public AuthTaobaoRequest(AuthConfig config, AuthStateCache authStateCache) {
|
||||
super(config, AuthDefaultSource.TAOBAO, authStateCache);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -50,23 +54,25 @@ public class AuthTaobaoRequest extends AuthDefaultRequest {
|
||||
.nickname(nick)
|
||||
.gender(AuthUserGender.UNKNOWN)
|
||||
.token(authToken)
|
||||
.source(source)
|
||||
.source(source.toString())
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回认证url,可自行跳转页面
|
||||
* 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state}
|
||||
*
|
||||
* @param state state 验证授权流程的参数,可以防止csrf
|
||||
* @return 返回授权地址
|
||||
* @since 1.9.3
|
||||
*/
|
||||
@Override
|
||||
public String authorize() {
|
||||
public String authorize(String state) {
|
||||
return UrlBuilder.fromBaseUrl(source.authorize())
|
||||
.queryParam("response_type", "code")
|
||||
.queryParam("client_id", config.getClientId())
|
||||
.queryParam("redirect_uri", config.getRedirectUri())
|
||||
.queryParam("state", getRealState(config.getState()))
|
||||
.queryParam("view", "web")
|
||||
.queryParam("state", getRealState(state))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,23 +3,31 @@ package me.zhyd.oauth.request;
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import me.zhyd.oauth.cache.AuthStateCache;
|
||||
import me.zhyd.oauth.config.AuthConfig;
|
||||
import me.zhyd.oauth.config.AuthSource;
|
||||
import me.zhyd.oauth.config.AuthDefaultSource;
|
||||
import me.zhyd.oauth.enums.AuthResponseStatus;
|
||||
import me.zhyd.oauth.enums.AuthUserGender;
|
||||
import me.zhyd.oauth.exception.AuthException;
|
||||
import me.zhyd.oauth.model.*;
|
||||
import me.zhyd.oauth.model.AuthCallback;
|
||||
import me.zhyd.oauth.model.AuthResponse;
|
||||
import me.zhyd.oauth.model.AuthToken;
|
||||
import me.zhyd.oauth.model.AuthUser;
|
||||
|
||||
/**
|
||||
* Teambition授权登录
|
||||
*
|
||||
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
|
||||
* @version 1.0
|
||||
* @since 1.8
|
||||
* @since 1.9.0
|
||||
*/
|
||||
public class AuthTeambitionRequest extends AuthDefaultRequest {
|
||||
|
||||
public AuthTeambitionRequest(AuthConfig config) {
|
||||
super(config, AuthSource.TEAMBITION);
|
||||
super(config, AuthDefaultSource.TEAMBITION);
|
||||
}
|
||||
|
||||
public AuthTeambitionRequest(AuthConfig config, AuthStateCache authStateCache) {
|
||||
super(config, AuthDefaultSource.TEAMBITION, authStateCache);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -67,7 +75,7 @@ public class AuthTeambitionRequest extends AuthDefaultRequest {
|
||||
.email(object.getString("email"))
|
||||
.gender(AuthUserGender.UNKNOWN)
|
||||
.token(authToken)
|
||||
.source(source)
|
||||
.source(source.toString())
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
@@ -2,8 +2,9 @@ package me.zhyd.oauth.request;
|
||||
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import me.zhyd.oauth.cache.AuthStateCache;
|
||||
import me.zhyd.oauth.config.AuthConfig;
|
||||
import me.zhyd.oauth.config.AuthSource;
|
||||
import me.zhyd.oauth.config.AuthDefaultSource;
|
||||
import me.zhyd.oauth.enums.AuthUserGender;
|
||||
import me.zhyd.oauth.exception.AuthException;
|
||||
import me.zhyd.oauth.model.AuthCallback;
|
||||
@@ -15,13 +16,16 @@ import me.zhyd.oauth.utils.UrlBuilder;
|
||||
* 腾讯云登录
|
||||
*
|
||||
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
|
||||
* @version 1.0
|
||||
* @since 1.8
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class AuthTencentCloudRequest extends AuthDefaultRequest {
|
||||
|
||||
public AuthTencentCloudRequest(AuthConfig config) {
|
||||
super(config, AuthSource.TENCENT_CLOUD);
|
||||
super(config, AuthDefaultSource.TENCENT_CLOUD);
|
||||
}
|
||||
|
||||
public AuthTencentCloudRequest(AuthConfig config, AuthStateCache authStateCache) {
|
||||
super(config, AuthDefaultSource.TENCENT_CLOUD, authStateCache);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -55,7 +59,7 @@ public class AuthTencentCloudRequest extends AuthDefaultRequest {
|
||||
.email(object.getString("email"))
|
||||
.remark(object.getString("slogan"))
|
||||
.token(authToken)
|
||||
.source(source)
|
||||
.source(source.toString())
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -71,18 +75,20 @@ public class AuthTencentCloudRequest extends AuthDefaultRequest {
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回认证url,可自行跳转页面
|
||||
* 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state}
|
||||
*
|
||||
* @param state state 验证授权流程的参数,可以防止csrf
|
||||
* @return 返回授权地址
|
||||
* @since 1.9.3
|
||||
*/
|
||||
@Override
|
||||
public String authorize() {
|
||||
public String authorize(String state) {
|
||||
return UrlBuilder.fromBaseUrl(source.authorize())
|
||||
.queryParam("response_type", "code")
|
||||
.queryParam("client_id", config.getClientId())
|
||||
.queryParam("redirect_uri", config.getRedirectUri())
|
||||
.queryParam("scope", "user")
|
||||
.queryParam("state", getRealState(config.getState()))
|
||||
.queryParam("state", getRealState(state))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,9 @@ package me.zhyd.oauth.request;
|
||||
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import me.zhyd.oauth.cache.AuthStateCache;
|
||||
import me.zhyd.oauth.config.AuthConfig;
|
||||
import me.zhyd.oauth.config.AuthSource;
|
||||
import me.zhyd.oauth.config.AuthDefaultSource;
|
||||
import me.zhyd.oauth.enums.AuthToutiaoErrorCode;
|
||||
import me.zhyd.oauth.enums.AuthUserGender;
|
||||
import me.zhyd.oauth.exception.AuthException;
|
||||
@@ -16,13 +17,16 @@ import me.zhyd.oauth.utils.UrlBuilder;
|
||||
* 今日头条登录
|
||||
*
|
||||
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
|
||||
* @version 1.5
|
||||
* @since 1.5
|
||||
* @since 1.6.0-beta
|
||||
*/
|
||||
public class AuthToutiaoRequest extends AuthDefaultRequest {
|
||||
|
||||
public AuthToutiaoRequest(AuthConfig config) {
|
||||
super(config, AuthSource.TOUTIAO);
|
||||
super(config, AuthDefaultSource.TOUTIAO);
|
||||
}
|
||||
|
||||
public AuthToutiaoRequest(AuthConfig config, AuthStateCache authStateCache) {
|
||||
super(config, AuthDefaultSource.TOUTIAO, authStateCache);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -60,24 +64,26 @@ public class AuthToutiaoRequest extends AuthDefaultRequest {
|
||||
.remark(user.getString("description"))
|
||||
.gender(AuthUserGender.getRealGender(user.getString("gender")))
|
||||
.token(authToken)
|
||||
.source(source)
|
||||
.source(source.toString())
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回认证url,可自行跳转页面
|
||||
* 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state}
|
||||
*
|
||||
* @param state state 验证授权流程的参数,可以防止csrf
|
||||
* @return 返回授权地址
|
||||
* @since 1.9.3
|
||||
*/
|
||||
@Override
|
||||
public String authorize() {
|
||||
public String authorize(String state) {
|
||||
return UrlBuilder.fromBaseUrl(source.authorize())
|
||||
.queryParam("response_type", "code")
|
||||
.queryParam("client_key", config.getClientId())
|
||||
.queryParam("redirect_uri", config.getRedirectUri())
|
||||
.queryParam("state", getRealState(config.getState()))
|
||||
.queryParam("auth_only", 1)
|
||||
.queryParam("display", 0)
|
||||
.queryParam("state", getRealState(state))
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -118,8 +124,7 @@ public class AuthToutiaoRequest extends AuthDefaultRequest {
|
||||
*/
|
||||
private void checkResponse(JSONObject object) {
|
||||
if (object.containsKey("error_code")) {
|
||||
throw new AuthException(AuthToutiaoErrorCode.getErrorCode(object.getIntValue("error_code"))
|
||||
.getDesc());
|
||||
throw new AuthException(AuthToutiaoErrorCode.getErrorCode(object.getIntValue("error_code")).getDesc());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,171 @@
|
||||
package me.zhyd.oauth.request;
|
||||
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import me.zhyd.oauth.cache.AuthStateCache;
|
||||
import me.zhyd.oauth.config.AuthConfig;
|
||||
import me.zhyd.oauth.config.AuthDefaultSource;
|
||||
import me.zhyd.oauth.enums.AuthResponseStatus;
|
||||
import me.zhyd.oauth.enums.AuthUserGender;
|
||||
import me.zhyd.oauth.exception.AuthException;
|
||||
import me.zhyd.oauth.model.AuthCallback;
|
||||
import me.zhyd.oauth.model.AuthToken;
|
||||
import me.zhyd.oauth.model.AuthUser;
|
||||
import me.zhyd.oauth.utils.UrlBuilder;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 企业微信登录
|
||||
* </p>
|
||||
*
|
||||
* @author yangkai.shen (https://xkcoding.com)
|
||||
* @since 1.10.0
|
||||
*/
|
||||
public class AuthWeChatEnterpriseRequest extends AuthDefaultRequest {
|
||||
public AuthWeChatEnterpriseRequest(AuthConfig config) {
|
||||
super(config, AuthDefaultSource.WECHAT_ENTERPRISE);
|
||||
}
|
||||
|
||||
public AuthWeChatEnterpriseRequest(AuthConfig config, AuthStateCache authStateCache) {
|
||||
super(config, AuthDefaultSource.WECHAT_ENTERPRISE, authStateCache);
|
||||
}
|
||||
|
||||
/**
|
||||
* 微信的特殊性,此时返回的信息同时包含 openid 和 access_token
|
||||
*
|
||||
* @param authCallback 回调返回的参数
|
||||
* @return 所有信息
|
||||
*/
|
||||
@Override
|
||||
protected AuthToken getAccessToken(AuthCallback authCallback) {
|
||||
HttpResponse response = doGetAuthorizationCode(accessTokenUrl(authCallback.getCode()));
|
||||
|
||||
JSONObject object = this.checkResponse(response);
|
||||
|
||||
return AuthToken.builder()
|
||||
.accessToken(object.getString("access_token"))
|
||||
.expireIn(object.getIntValue("expires_in"))
|
||||
.code(authCallback.getCode())
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AuthUser getUserInfo(AuthToken authToken) {
|
||||
HttpResponse response = doGetUserInfo(authToken);
|
||||
JSONObject object = this.checkResponse(response);
|
||||
|
||||
// 返回 OpenId 或其他,均代表非当前企业用户,不支持
|
||||
if (!object.containsKey("UserId")) {
|
||||
throw new AuthException(AuthResponseStatus.UNIDENTIFIED_PLATFORM);
|
||||
}
|
||||
String userId = object.getString("UserId");
|
||||
HttpResponse userDetailResponse = getUserDetail(authToken.getAccessToken(), userId);
|
||||
JSONObject userDetail = this.checkResponse(userDetailResponse);
|
||||
|
||||
String gender = getRealGender(userDetail);
|
||||
|
||||
return AuthUser.builder()
|
||||
.username(userDetail.getString("name"))
|
||||
.nickname(userDetail.getString("alias"))
|
||||
.avatar(userDetail.getString("avatar"))
|
||||
.location(userDetail.getString("address"))
|
||||
.email(userDetail.getString("email"))
|
||||
.uuid(userId)
|
||||
.gender(AuthUserGender.getRealGender(gender))
|
||||
.token(authToken)
|
||||
.source(source.toString())
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验请求结果
|
||||
*
|
||||
* @param response 请求结果
|
||||
* @return 如果请求结果正常,则返回JSONObject
|
||||
*/
|
||||
private JSONObject checkResponse(HttpResponse response) {
|
||||
JSONObject object = JSONObject.parseObject(response.body());
|
||||
|
||||
if (object.containsKey("errcode") && object.getIntValue("errcode") != 0) {
|
||||
throw new AuthException(object.getIntValue("errcode"), object.getString("errmsg"));
|
||||
}
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户的实际性别,0表示未定义,1表示男性,2表示女性
|
||||
*
|
||||
* @param userDetail 用户详情
|
||||
* @return 用户性别
|
||||
*/
|
||||
private String getRealGender(JSONObject userDetail) {
|
||||
int gender = userDetail.getIntValue("gender");
|
||||
if (AuthUserGender.MALE.getCode() == gender) {
|
||||
return "1";
|
||||
}
|
||||
return 2 == gender ? "0" : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state}
|
||||
*
|
||||
* @param state state 验证授权流程的参数,可以防止csrf
|
||||
* @return 返回授权地址
|
||||
* @since 1.9.3
|
||||
*/
|
||||
@Override
|
||||
public String authorize(String state) {
|
||||
return UrlBuilder.fromBaseUrl(source.authorize())
|
||||
.queryParam("appid", config.getClientId())
|
||||
.queryParam("agentid", config.getAgentId())
|
||||
.queryParam("redirect_uri", config.getRedirectUri())
|
||||
.queryParam("state", getRealState(state))
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回获取accessToken的url
|
||||
*
|
||||
* @param code 授权码
|
||||
* @return 返回获取accessToken的url
|
||||
*/
|
||||
@Override
|
||||
protected String accessTokenUrl(String code) {
|
||||
return UrlBuilder.fromBaseUrl(source.accessToken())
|
||||
.queryParam("corpid", config.getClientId())
|
||||
.queryParam("corpsecret", config.getClientSecret())
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回获取userInfo的url
|
||||
*
|
||||
* @param authToken 用户授权后的token
|
||||
* @return 返回获取userInfo的url
|
||||
*/
|
||||
@Override
|
||||
protected String userInfoUrl(AuthToken authToken) {
|
||||
return UrlBuilder.fromBaseUrl(source.userInfo())
|
||||
.queryParam("access_token", authToken.getAccessToken())
|
||||
.queryParam("code", authToken.getCode())
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户详情
|
||||
*
|
||||
* @param accessToken accessToken
|
||||
* @param userId 企业内用户id
|
||||
* @return 用户详情
|
||||
*/
|
||||
private HttpResponse getUserDetail(String accessToken, String userId) {
|
||||
String userDetailUrl = UrlBuilder.fromBaseUrl("https://qyapi.weixin.qq.com/cgi-bin/user/get")
|
||||
.queryParam("access_token", accessToken)
|
||||
.queryParam("userid", userId)
|
||||
.build();
|
||||
return HttpRequest.get(userDetailUrl).execute();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,23 +3,31 @@ package me.zhyd.oauth.request;
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import me.zhyd.oauth.cache.AuthStateCache;
|
||||
import me.zhyd.oauth.config.AuthConfig;
|
||||
import me.zhyd.oauth.config.AuthSource;
|
||||
import me.zhyd.oauth.config.AuthDefaultSource;
|
||||
import me.zhyd.oauth.enums.AuthResponseStatus;
|
||||
import me.zhyd.oauth.enums.AuthUserGender;
|
||||
import me.zhyd.oauth.exception.AuthException;
|
||||
import me.zhyd.oauth.model.*;
|
||||
import me.zhyd.oauth.model.AuthCallback;
|
||||
import me.zhyd.oauth.model.AuthResponse;
|
||||
import me.zhyd.oauth.model.AuthToken;
|
||||
import me.zhyd.oauth.model.AuthUser;
|
||||
import me.zhyd.oauth.utils.UrlBuilder;
|
||||
|
||||
/**
|
||||
* 微信登录
|
||||
*
|
||||
* @author yangkai.shen (https://xkcoding.com)
|
||||
* @version 1.0
|
||||
* @since 1.8
|
||||
* @since 1.1.0
|
||||
*/
|
||||
public class AuthWeChatRequest extends AuthDefaultRequest {
|
||||
public AuthWeChatRequest(AuthConfig config) {
|
||||
super(config, AuthSource.WECHAT);
|
||||
super(config, AuthDefaultSource.WECHAT);
|
||||
}
|
||||
|
||||
public AuthWeChatRequest(AuthConfig config, AuthStateCache authStateCache) {
|
||||
super(config, AuthDefaultSource.WECHAT, authStateCache);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -56,7 +64,7 @@ public class AuthWeChatRequest extends AuthDefaultRequest {
|
||||
.uuid(openId)
|
||||
.gender(AuthUserGender.getRealGender(object.getString("sex")))
|
||||
.token(authToken)
|
||||
.source(source)
|
||||
.source(source.toString())
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -100,18 +108,20 @@ public class AuthWeChatRequest extends AuthDefaultRequest {
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回认证url,可自行跳转页面
|
||||
* 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state}
|
||||
*
|
||||
* @param state state 验证授权流程的参数,可以防止csrf
|
||||
* @return 返回授权地址
|
||||
* @since 1.9.3
|
||||
*/
|
||||
@Override
|
||||
public String authorize() {
|
||||
public String authorize(String state) {
|
||||
return UrlBuilder.fromBaseUrl(source.authorize())
|
||||
.queryParam("response_type", "code")
|
||||
.queryParam("appid", config.getClientId())
|
||||
.queryParam("redirect_uri", config.getRedirectUri())
|
||||
.queryParam("scope", "snsapi_login")
|
||||
.queryParam("state", getRealState(config.getState()).concat("#wechat_redirect"))
|
||||
.queryParam("state", getRealState(state))
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
@@ -3,8 +3,9 @@ package me.zhyd.oauth.request;
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import me.zhyd.oauth.cache.AuthStateCache;
|
||||
import me.zhyd.oauth.config.AuthConfig;
|
||||
import me.zhyd.oauth.config.AuthSource;
|
||||
import me.zhyd.oauth.config.AuthDefaultSource;
|
||||
import me.zhyd.oauth.enums.AuthUserGender;
|
||||
import me.zhyd.oauth.exception.AuthException;
|
||||
import me.zhyd.oauth.model.AuthCallback;
|
||||
@@ -19,13 +20,16 @@ import me.zhyd.oauth.utils.UrlBuilder;
|
||||
* 微博登录
|
||||
*
|
||||
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
|
||||
* @version 1.0
|
||||
* @since 1.8
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class AuthWeiboRequest extends AuthDefaultRequest {
|
||||
|
||||
public AuthWeiboRequest(AuthConfig config) {
|
||||
super(config, AuthSource.WEIBO);
|
||||
super(config, AuthDefaultSource.WEIBO);
|
||||
}
|
||||
|
||||
public AuthWeiboRequest(AuthConfig config, AuthStateCache authStateCache) {
|
||||
super(config, AuthDefaultSource.WEIBO, authStateCache);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -51,7 +55,7 @@ public class AuthWeiboRequest extends AuthDefaultRequest {
|
||||
String oauthParam = String.format("uid=%s&access_token=%s", uid, accessToken);
|
||||
HttpResponse response = HttpRequest.get(userInfoUrl(authToken))
|
||||
.header("Authorization", "OAuth2 " + oauthParam)
|
||||
.header("API-RemoteIP", IpUtils.getIp())
|
||||
.header("API-RemoteIP", IpUtils.getLocalIp())
|
||||
.execute();
|
||||
String userInfo = response.body();
|
||||
JSONObject object = JSONObject.parseObject(userInfo);
|
||||
@@ -69,7 +73,7 @@ public class AuthWeiboRequest extends AuthDefaultRequest {
|
||||
.remark(object.getString("description"))
|
||||
.gender(AuthUserGender.getRealGender(object.getString("gender")))
|
||||
.token(authToken)
|
||||
.source(source)
|
||||
.source(source.toString())
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* JustAuth核心组件,所有授权登录都是基于{@code request}实现
|
||||
*/
|
||||
package me.zhyd.oauth.request;
|
||||
@@ -1,16 +1,17 @@
|
||||
package me.zhyd.oauth.utils;
|
||||
|
||||
import me.zhyd.oauth.config.AuthConfig;
|
||||
import me.zhyd.oauth.config.AuthDefaultSource;
|
||||
import me.zhyd.oauth.config.AuthSource;
|
||||
import me.zhyd.oauth.enums.AuthResponseStatus;
|
||||
import me.zhyd.oauth.exception.AuthException;
|
||||
import me.zhyd.oauth.model.AuthResponseStatus;
|
||||
import me.zhyd.oauth.model.AuthCallback;
|
||||
|
||||
/**
|
||||
* 授权配置类的校验器
|
||||
*
|
||||
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
|
||||
* @version 1.0
|
||||
* @since 1.8
|
||||
* @since 1.6.1-beta
|
||||
*/
|
||||
public class AuthChecker {
|
||||
|
||||
@@ -20,15 +21,19 @@ public class AuthChecker {
|
||||
* @param config config
|
||||
* @param source source
|
||||
* @return true or false
|
||||
* @since 1.6.1-beta
|
||||
*/
|
||||
public static boolean isSupportedAuth(AuthConfig config, AuthSource source) {
|
||||
boolean isSupported = StringUtils.isNotEmpty(config.getClientId()) && StringUtils.isNotEmpty(config.getClientSecret()) && StringUtils.isNotEmpty(config.getRedirectUri());
|
||||
if (isSupported && AuthSource.ALIPAY == source) {
|
||||
if (isSupported && AuthDefaultSource.ALIPAY == source) {
|
||||
isSupported = StringUtils.isNotEmpty(config.getAlipayPublicKey());
|
||||
}
|
||||
if (isSupported && AuthSource.STACK_OVERFLOW == source) {
|
||||
if (isSupported && AuthDefaultSource.STACK_OVERFLOW == source) {
|
||||
isSupported = StringUtils.isNotEmpty(config.getStackOverflowKey());
|
||||
}
|
||||
if (isSupported && AuthDefaultSource.WECHAT_ENTERPRISE == source){
|
||||
isSupported = StringUtils.isNotEmpty(config.getAgentId());
|
||||
}
|
||||
return isSupported;
|
||||
}
|
||||
|
||||
@@ -37,6 +42,7 @@ public class AuthChecker {
|
||||
*
|
||||
* @param config config
|
||||
* @param source source
|
||||
* @since 1.6.1-beta
|
||||
*/
|
||||
public static void checkConfig(AuthConfig config, AuthSource source) {
|
||||
String redirectUri = config.getRedirectUri();
|
||||
@@ -44,44 +50,33 @@ public class AuthChecker {
|
||||
throw new AuthException(AuthResponseStatus.ILLEGAL_REDIRECT_URI);
|
||||
}
|
||||
// facebook的回调地址必须为https的链接
|
||||
if (AuthSource.FACEBOOK == source && !GlobalAuthUtil.isHttpsProtocol(redirectUri)) {
|
||||
if (AuthDefaultSource.FACEBOOK == source && !GlobalAuthUtil.isHttpsProtocol(redirectUri)) {
|
||||
throw new AuthException(AuthResponseStatus.ILLEGAL_REDIRECT_URI);
|
||||
}
|
||||
// 支付宝在创建回调地址时,不允许使用localhost或者127.0.0.1
|
||||
if (AuthSource.ALIPAY == source && GlobalAuthUtil.isLocalHost(redirectUri)) {
|
||||
if (AuthDefaultSource.ALIPAY == source && GlobalAuthUtil.isLocalHost(redirectUri)) {
|
||||
throw new AuthException(AuthResponseStatus.ILLEGAL_REDIRECT_URI);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验回调传回的code
|
||||
* <p>
|
||||
* {@code v1.10.0}版本中改为传入{@code source}和{@code callback},对于不同平台使用不同参数接受code的情况统一做处理
|
||||
*
|
||||
* @param code 回调时传回的code
|
||||
* @param source 当前授权平台
|
||||
* @param callback 从第三方授权回调回来时传入的参数集合
|
||||
* @since 1.8.0
|
||||
*/
|
||||
public static void checkCode(String code) {
|
||||
public static void checkCode(AuthSource source, AuthCallback callback) {
|
||||
String code = callback.getCode();
|
||||
if (source == AuthDefaultSource.ALIPAY) {
|
||||
code = callback.getAuth_code();
|
||||
} else if (source == AuthDefaultSource.HUAWEI) {
|
||||
code = callback.getAuthorization_code();
|
||||
}
|
||||
if (StringUtils.isEmpty(code)) {
|
||||
throw new AuthException(AuthResponseStatus.ILLEGAL_CODE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验state的合法性防止被CSRF
|
||||
*
|
||||
* @param newState 新的state,一般为回调时传回的state(可能被篡改)
|
||||
* @param originalState 原始的state,发起授权时向第三方平台传递的state
|
||||
*/
|
||||
public static void checkState(String newState, String originalState) {
|
||||
// 如果原始state为空,表示当前平台未使用state
|
||||
if (StringUtils.isEmpty(originalState)) {
|
||||
return;
|
||||
}
|
||||
// 如果授权之前使用了state,但是回调时未返回state,则表示当前请求为非法的请求,可能正在被CSRF攻击
|
||||
if (StringUtils.isEmpty(newState)) {
|
||||
throw new AuthException(AuthResponseStatus.ILLEGAL_REQUEST);
|
||||
}
|
||||
// 如果授权前后的state不一致,则表示当前请求为非法的请求,新的state可能为伪造
|
||||
if (!newState.equals(originalState)) {
|
||||
throw new AuthException(AuthResponseStatus.ILLEGAL_REQUEST);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,185 +0,0 @@
|
||||
package me.zhyd.oauth.utils;
|
||||
|
||||
import cn.hutool.core.codec.Base64;
|
||||
import cn.hutool.core.util.RandomUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import me.zhyd.oauth.config.AuthSource;
|
||||
import me.zhyd.oauth.exception.AuthException;
|
||||
import me.zhyd.oauth.model.AuthResponseStatus;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* state工具,负责创建、获取和删除state
|
||||
*
|
||||
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
|
||||
* @version 1.0
|
||||
* @since 1.8
|
||||
*/
|
||||
@Slf4j
|
||||
public class AuthState {
|
||||
|
||||
/**
|
||||
* 空字符串
|
||||
*/
|
||||
private static final String EMPTY_STR = "";
|
||||
|
||||
/**
|
||||
* state存储器
|
||||
*/
|
||||
private static ConcurrentHashMap<String, String> stateBucket = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* 生成随机的state
|
||||
*
|
||||
* @param source oauth平台
|
||||
* @return state
|
||||
*/
|
||||
public static String create(AuthSource source) {
|
||||
return create(source.name());
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成随机的state
|
||||
*
|
||||
* @param source oauth平台
|
||||
* @return state
|
||||
*/
|
||||
public static String create(String source) {
|
||||
return create(source, RandomUtil.randomString(4));
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建state
|
||||
*
|
||||
* @param source oauth平台
|
||||
* @param body 希望加密到state的消息体
|
||||
* @return state
|
||||
*/
|
||||
public static String create(String source, Object body) {
|
||||
return create(source, JSON.toJSONString(body));
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建state
|
||||
*
|
||||
* @param source oauth平台
|
||||
* @param body 希望加密到state的消息体
|
||||
* @return state
|
||||
*/
|
||||
public static String create(String source, String body) {
|
||||
String currentIp = getCurrentIp();
|
||||
String simpleKey = ((source + currentIp));
|
||||
String key = Base64.encode(simpleKey.getBytes(Charset.forName("UTF-8")));
|
||||
log.debug("Create the state: ip={}, platform={}, simpleKey={}, key={}, body={}", currentIp, source, simpleKey, key, body);
|
||||
|
||||
if (stateBucket.containsKey(key)) {
|
||||
log.debug("Get from bucket: {}", stateBucket.get(key));
|
||||
return stateBucket.get(key);
|
||||
}
|
||||
|
||||
String simpleState = source + "_" + currentIp + "_" + body;
|
||||
String state = Base64.encode(simpleState.getBytes(Charset.forName("UTF-8")));
|
||||
log.debug("Create a new state: {}", state, simpleState);
|
||||
stateBucket.put(key, state);
|
||||
return state;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取state
|
||||
*
|
||||
* @param source oauth平台
|
||||
* @return state
|
||||
*/
|
||||
public static String get(String source) {
|
||||
String currentIp = getCurrentIp();
|
||||
String simpleKey = ((source + currentIp));
|
||||
String key = Base64.encode(simpleKey.getBytes(Charset.forName("UTF-8")));
|
||||
log.debug("Get state by the key[{}], current ip[{}]", key, currentIp);
|
||||
return stateBucket.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取state中保存的body内容
|
||||
*
|
||||
* @param source oauth平台
|
||||
* @param state 加密后的state
|
||||
* @param clazz body的实际类型
|
||||
* @param <T> 需要转换的具体的class类型
|
||||
* @return state
|
||||
*/
|
||||
public static <T> T getBody(String source, String state, Class<T> clazz) {
|
||||
if (StringUtils.isEmpty(state) || null == clazz) {
|
||||
return null;
|
||||
}
|
||||
log.debug("Get body from the state[{}] of the {} and convert it to {}", state, source, clazz.toString());
|
||||
String currentIp = getCurrentIp();
|
||||
String decodedState = Base64.decodeStr(state);
|
||||
log.debug("The decoded state is [{}]", decodedState);
|
||||
if (!decodedState.startsWith(source)) {
|
||||
return null;
|
||||
}
|
||||
String noneSourceState = decodedState.substring(source.length() + 1);
|
||||
if (!noneSourceState.startsWith(currentIp)) {
|
||||
// ip不相同,可能为非法的请求
|
||||
throw new AuthException(AuthResponseStatus.ILLEGAL_REQUEST);
|
||||
}
|
||||
String body = noneSourceState.substring(currentIp.length() + 1);
|
||||
log.debug("body is [{}]", body);
|
||||
if (clazz == String.class) {
|
||||
return (T) body;
|
||||
}
|
||||
if (clazz == Integer.class) {
|
||||
return (T) Integer.valueOf(Integer.parseInt(body));
|
||||
}
|
||||
if (clazz == Long.class) {
|
||||
return (T) Long.valueOf(Long.parseLong(body));
|
||||
}
|
||||
if (clazz == Short.class) {
|
||||
return (T) Short.valueOf(Short.parseShort(body));
|
||||
}
|
||||
if (clazz == Double.class) {
|
||||
return (T) Double.valueOf(Double.parseDouble(body));
|
||||
}
|
||||
if (clazz == Float.class) {
|
||||
return (T) Float.valueOf(Float.parseFloat(body));
|
||||
}
|
||||
if (clazz == Boolean.class) {
|
||||
return (T) Boolean.valueOf(Boolean.parseBoolean(body));
|
||||
}
|
||||
if (clazz == Byte.class) {
|
||||
return (T) Byte.valueOf(Byte.parseByte(body));
|
||||
}
|
||||
return JSON.parseObject(body, clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录成功后,清除state
|
||||
*
|
||||
* @param source oauth平台
|
||||
*/
|
||||
public static void delete(String source) {
|
||||
String currentIp = getCurrentIp();
|
||||
|
||||
String simpleKey = ((source + currentIp));
|
||||
String key = Base64.encode(simpleKey.getBytes(Charset.forName("UTF-8")));
|
||||
log.debug("Delete used state[{}] by the key[{}], current ip[{}]", stateBucket.get(key), key, currentIp);
|
||||
stateBucket.remove(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录成功后,清除state
|
||||
*
|
||||
* @param source oauth平台
|
||||
*/
|
||||
public static void delete(AuthSource source) {
|
||||
delete(source.name());
|
||||
}
|
||||
|
||||
private static String getCurrentIp() {
|
||||
String currentIp = IpUtils.getIp();
|
||||
return StringUtils.isEmpty(currentIp) ? EMPTY_STR : currentIp;
|
||||
}
|
||||
}
|
||||
@@ -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,采用https://github.com/lets-mica/mica的UUID工具
|
||||
*
|
||||
* @return 随机的state字符串
|
||||
*/
|
||||
public static String createState() {
|
||||
return UuidUtils.getUUID();
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import cn.hutool.core.codec.Base64;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.http.HttpUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import me.zhyd.oauth.exception.AuthException;
|
||||
|
||||
import javax.crypto.Mac;
|
||||
@@ -14,6 +15,7 @@ import java.net.URLEncoder;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.*;
|
||||
|
||||
@@ -21,18 +23,31 @@ import java.util.*;
|
||||
* 全局的工具类
|
||||
*
|
||||
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
|
||||
* @version 1.0
|
||||
* @since 1.8
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class GlobalAuthUtil {
|
||||
private static final Charset DEFAULT_ENCODING = StandardCharsets.UTF_8;
|
||||
private static final String ALGORITHM = "HmacSHA256";
|
||||
|
||||
/**
|
||||
* 生成钉钉请求的Signature
|
||||
*
|
||||
* @param secretKey 平台应用的授权密钥
|
||||
* @param timestamp 时间戳
|
||||
* @return Signature
|
||||
*/
|
||||
public static String generateDingTalkSignature(String secretKey, String timestamp) {
|
||||
byte[] signData = sign(secretKey.getBytes(DEFAULT_ENCODING), timestamp.getBytes(DEFAULT_ENCODING));
|
||||
return urlEncode(new String(Base64.encode(signData, false)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 签名
|
||||
*
|
||||
* @param key key
|
||||
* @param data data
|
||||
* @return byte[]
|
||||
*/
|
||||
private static byte[] sign(byte[] key, byte[] data) {
|
||||
try {
|
||||
Mac mac = Mac.getInstance(ALGORITHM);
|
||||
@@ -45,11 +60,16 @@ public class GlobalAuthUtil {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 编码
|
||||
*
|
||||
* @param value str
|
||||
* @return encode str
|
||||
*/
|
||||
public static String urlEncode(String value) {
|
||||
if (value == null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
try {
|
||||
String encoded = URLEncoder.encode(value, GlobalAuthUtil.DEFAULT_ENCODING.displayName());
|
||||
return encoded.replace("+", "%20").replace("*", "%2A").replace("~", "%7E").replace("/", "%2F");
|
||||
@@ -58,6 +78,13 @@ public class GlobalAuthUtil {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 解码
|
||||
*
|
||||
* @param value str
|
||||
* @return decode str
|
||||
*/
|
||||
public static String urlDecode(String value) {
|
||||
if (value == null) {
|
||||
return "";
|
||||
@@ -69,6 +96,12 @@ public class GlobalAuthUtil {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* string字符串转map,str格式为 {@code xxx=xxx&xxx=xxx}
|
||||
*
|
||||
* @param accessTokenStr 待转换的字符串
|
||||
* @return map
|
||||
*/
|
||||
public static Map<String, String> parseStringToMap(String accessTokenStr) {
|
||||
Map<String, String> res = new HashMap<>();
|
||||
if (accessTokenStr.contains("&")) {
|
||||
@@ -83,7 +116,13 @@ public class GlobalAuthUtil {
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* map转字符串,转换后的字符串格式为 {@code xxx=xxx&xxx=xxx}
|
||||
*
|
||||
* @param params 待转换的map
|
||||
* @param encode 是否转码
|
||||
* @return str
|
||||
*/
|
||||
public static String parseMapToString(Map<String, Object> params, boolean encode) {
|
||||
List<String> paramList = new ArrayList<>();
|
||||
params.forEach((k, v) -> {
|
||||
@@ -96,13 +135,25 @@ public class GlobalAuthUtil {
|
||||
});
|
||||
return CollUtil.join(paramList, "&");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 将url的参数列表转换成map
|
||||
*
|
||||
* @param url 待转换的url
|
||||
* @return map
|
||||
*/
|
||||
public static Map<String, Object> parseQueryToMap(String url) {
|
||||
Map<String, Object> paramMap = new HashMap<>();
|
||||
HttpUtil.decodeParamMap(url, "UTF-8").forEach(paramMap::put);
|
||||
return paramMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为http协议
|
||||
*
|
||||
* @param url 待验证的url
|
||||
* @return true: http协议, false: 非http协议
|
||||
*/
|
||||
public static boolean isHttpProtocol(String url) {
|
||||
if (StringUtils.isEmpty(url)) {
|
||||
return false;
|
||||
@@ -110,6 +161,12 @@ public class GlobalAuthUtil {
|
||||
return url.startsWith("http://");
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为https协议
|
||||
*
|
||||
* @param url 待验证的url
|
||||
* @return true: https协议, false: 非https协议
|
||||
*/
|
||||
public static boolean isHttpsProtocol(String url) {
|
||||
if (StringUtils.isEmpty(url)) {
|
||||
return false;
|
||||
@@ -117,8 +174,68 @@ public class GlobalAuthUtil {
|
||||
return url.startsWith("https://");
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为本地主机(域名)
|
||||
*
|
||||
* @param url 待验证的url
|
||||
* @return true: 本地主机(域名), false: 非本地主机(域名)
|
||||
*/
|
||||
public static boolean isLocalHost(String url) {
|
||||
return StringUtils.isEmpty(url) || url.contains("127.0.0.1") || url.contains("localhost");
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成饿了么请求的Signature
|
||||
* <p>
|
||||
* 代码copy并修改自:https://coding.net/u/napos_openapi/p/eleme-openapi-java-sdk/git/blob/master/src/main/java/eleme/openapi/sdk/utils/SignatureUtil.java
|
||||
*
|
||||
* @param appKey 平台应用的授权key
|
||||
* @param secret 平台应用的授权密钥
|
||||
* @param timestamp 时间戳,单位秒。API服务端允许客户端请求最大时间误差为正负5分钟。
|
||||
* @param action 饿了么请求的api方法
|
||||
* @param token 用户授权的token
|
||||
* @param parameters 加密参数
|
||||
* @return Signature
|
||||
*/
|
||||
public static String generateElemeSignature(String appKey, String secret, long timestamp, String action, String token, Map<String, Object> parameters) {
|
||||
final Map<String, Object> sorted = new TreeMap<>();
|
||||
for (Map.Entry<String, Object> entry : parameters.entrySet()) {
|
||||
sorted.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
sorted.put("app_key", appKey);
|
||||
sorted.put("timestamp", timestamp);
|
||||
StringBuffer string = new StringBuffer();
|
||||
for (Map.Entry<String, Object> entry : sorted.entrySet()) {
|
||||
string.append(entry.getKey()).append("=").append(JSON.toJSONString(entry.getValue()));
|
||||
}
|
||||
String splice = String.format("%s%s%s%s", action, token, string, secret);
|
||||
String calculatedSignature = md5(splice);
|
||||
return calculatedSignature.toUpperCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* MD5加密饿了么请求的Signature
|
||||
* <p>
|
||||
* 代码copy并修改自:https://coding.net/u/napos_openapi/p/eleme-openapi-java-sdk/git/blob/master/src/main/java/eleme/openapi/sdk/utils/SignatureUtil.java
|
||||
*
|
||||
* @param str 饿了么请求的Signature
|
||||
* @return md5 str
|
||||
*/
|
||||
private static String md5(String str) {
|
||||
MessageDigest md = null;
|
||||
StringBuilder buffer = null;
|
||||
try {
|
||||
md = MessageDigest.getInstance("MD5");
|
||||
md.update(str.getBytes(StandardCharsets.UTF_8));
|
||||
byte[] byteData = md.digest();
|
||||
buffer = new StringBuilder();
|
||||
for (byte byteDatum : byteData) {
|
||||
buffer.append(Integer.toString((byteDatum & 0xff) + 0x100, 16).substring(1));
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
return null == buffer ? "" : buffer.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -7,8 +7,7 @@ import java.net.UnknownHostException;
|
||||
* 获取IP的工具类
|
||||
*
|
||||
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
|
||||
* @version 1.0
|
||||
* @since 1.0
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class IpUtils {
|
||||
|
||||
@@ -17,7 +16,7 @@ public class IpUtils {
|
||||
*
|
||||
* @return ip
|
||||
*/
|
||||
public static String getIp() {
|
||||
public static String getLocalIp() {
|
||||
try {
|
||||
return InetAddress.getLocalHost().getHostAddress();
|
||||
} catch (UnknownHostException e) {
|
||||
@@ -25,4 +24,4 @@ public class IpUtils {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -14,8 +14,7 @@ import java.util.Map;
|
||||
* </p>
|
||||
*
|
||||
* @author yangkai.shen (https://xkcoding.com)
|
||||
* @version 1.0
|
||||
* @since 1.8
|
||||
* @since 1.9.0
|
||||
*/
|
||||
@Setter
|
||||
public class UrlBuilder {
|
||||
@@ -72,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;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
package me.zhyd.oauth.utils;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
/**
|
||||
* 高性能的创建UUID的工具类,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
|
||||
* <p>
|
||||
* 关于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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* 提供一些简单的工具和校验等
|
||||
*/
|
||||
package me.zhyd.oauth.utils;
|
||||
@@ -1,281 +0,0 @@
|
||||
package me.zhyd.oauth;
|
||||
|
||||
import me.zhyd.oauth.config.AuthConfig;
|
||||
import me.zhyd.oauth.model.AuthCallback;
|
||||
import me.zhyd.oauth.model.AuthResponse;
|
||||
import me.zhyd.oauth.request.*;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
|
||||
* @version 1.0
|
||||
* @since 1.8
|
||||
*/
|
||||
public class AuthRequestTest {
|
||||
|
||||
@Test
|
||||
public void giteeTest() {
|
||||
AuthRequest authRequest = new AuthGiteeRequest(AuthConfig.builder()
|
||||
.clientId("clientId")
|
||||
.clientSecret("clientSecret")
|
||||
.redirectUri("redirectUri")
|
||||
.state("state")
|
||||
.build());
|
||||
// 返回授权页面,可自行跳转
|
||||
authRequest.authorize();
|
||||
// 授权登录后会返回code(auth_code(仅限支付宝))、state,1.8.0版本后,可以用AuthCallback类作为回调接口的入参
|
||||
authRequest.login(new AuthCallback());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void githubTest() {
|
||||
AuthRequest authRequest = new AuthGithubRequest(AuthConfig.builder()
|
||||
.clientId("clientId")
|
||||
.clientSecret("clientSecret")
|
||||
.redirectUri("redirectUri")
|
||||
.state("state")
|
||||
.build());
|
||||
// 返回授权页面,可自行跳转
|
||||
authRequest.authorize();
|
||||
// 授权登录后会返回code(auth_code(仅限支付宝))、state,1.8.0版本后,可以用AuthCallback类作为回调接口的入参
|
||||
authRequest.login(new AuthCallback());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void weiboTest() {
|
||||
AuthRequest authRequest = new AuthWeiboRequest(AuthConfig.builder()
|
||||
.clientId("clientId")
|
||||
.clientSecret("clientSecret")
|
||||
.redirectUri("redirectUri")
|
||||
.build());
|
||||
// 返回授权页面,可自行跳转
|
||||
authRequest.authorize();
|
||||
// 授权登录后会返回code(auth_code(仅限支付宝))、state,1.8.0版本后,可以用AuthCallback类作为回调接口的入参
|
||||
authRequest.login(new AuthCallback());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dingdingTest() {
|
||||
AuthRequest authRequest = new AuthDingTalkRequest(AuthConfig.builder()
|
||||
.clientId("clientId")
|
||||
.clientSecret("clientSecret")
|
||||
.redirectUri("redirectUri")
|
||||
.state("state")
|
||||
.build());
|
||||
// 返回授权页面,可自行跳转
|
||||
String url = authRequest.authorize();
|
||||
// 授权登录后会返回code(auth_code(仅限支付宝))、state,1.8.0版本后,可以用AuthCallback类作为回调接口的入参
|
||||
authRequest.login(new AuthCallback());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void baiduTest() {
|
||||
AuthRequest authRequest = new AuthBaiduRequest(AuthConfig.builder()
|
||||
.clientId("clientId")
|
||||
.clientSecret("clientSecret")
|
||||
.redirectUri("redirectUri")
|
||||
.state("state")
|
||||
.build());
|
||||
// 返回授权页面,可自行跳转
|
||||
String url = authRequest.authorize();
|
||||
// 授权登录后会返回code(auth_code(仅限支付宝))、state,1.8.0版本后,可以用AuthCallback类作为回调接口的入参
|
||||
authRequest.login(new AuthCallback());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void codingTest() {
|
||||
AuthRequest authRequest = new AuthCodingRequest(AuthConfig.builder()
|
||||
.clientId("clientId")
|
||||
.clientSecret("clientSecret")
|
||||
.redirectUri("redirectUri")
|
||||
.state("state")
|
||||
.build());
|
||||
// 返回授权页面,可自行跳转
|
||||
String url = authRequest.authorize();
|
||||
// 授权登录后会返回code(auth_code(仅限支付宝))、state,1.8.0版本后,可以用AuthCallback类作为回调接口的入参
|
||||
authRequest.login(new AuthCallback());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void tencentCloudTest() {
|
||||
AuthRequest authRequest = new AuthTencentCloudRequest(AuthConfig.builder()
|
||||
.clientId("clientId")
|
||||
.clientSecret("clientSecret")
|
||||
.redirectUri("redirectUri")
|
||||
.state("state")
|
||||
.build());
|
||||
// 返回授权页面,可自行跳转
|
||||
String url = authRequest.authorize();
|
||||
// 授权登录后会返回code(auth_code(仅限支付宝))、state,1.8.0版本后,可以用AuthCallback类作为回调接口的入参
|
||||
authRequest.login(new AuthCallback());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void oschinaTest() {
|
||||
AuthRequest authRequest = new AuthOschinaRequest(AuthConfig.builder()
|
||||
.clientId("clientId")
|
||||
.clientSecret("clientSecret")
|
||||
.redirectUri("redirectUri")
|
||||
.state("state")
|
||||
.build());
|
||||
// 返回授权页面,可自行跳转
|
||||
String url = authRequest.authorize();
|
||||
// 授权登录后会返回code(auth_code(仅限支付宝))、state,1.8.0版本后,可以用AuthCallback类作为回调接口的入参
|
||||
authRequest.login(new AuthCallback());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void alipayTest() {
|
||||
AuthRequest authRequest = new AuthAlipayRequest(AuthConfig.builder()
|
||||
.clientId("clientId")
|
||||
.clientSecret("clientSecret")
|
||||
.redirectUri("redirectUri")
|
||||
.alipayPublicKey("publicKey")
|
||||
.state("state")
|
||||
.build());
|
||||
// 返回授权页面,可自行跳转
|
||||
String url = authRequest.authorize();
|
||||
// 授权登录后会返回code(auth_code(仅限支付宝))、state,1.8.0版本后,可以用AuthCallback类作为回调接口的入参
|
||||
AuthResponse login = authRequest.login(new AuthCallback());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void qqTest() {
|
||||
AuthRequest authRequest = new AuthQqRequest(AuthConfig.builder()
|
||||
.clientId("clientId")
|
||||
.clientSecret("clientSecret")
|
||||
.redirectUri("redirectUri")
|
||||
.state("state")
|
||||
.build());
|
||||
// 返回授权页面,可自行跳转
|
||||
String url = authRequest.authorize();
|
||||
// 授权登录后会返回code(auth_code(仅限支付宝))、state,1.8.0版本后,可以用AuthCallback类作为回调接口的入参
|
||||
AuthResponse login = authRequest.login(new AuthCallback());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void wechatTest() {
|
||||
AuthRequest authRequest = new AuthWeChatRequest(AuthConfig.builder()
|
||||
.clientId("clientId")
|
||||
.clientSecret("clientSecret")
|
||||
.redirectUri("redirectUri")
|
||||
.state("state")
|
||||
.build());
|
||||
// 返回授权页面,可自行跳转
|
||||
String url = authRequest.authorize();
|
||||
// 授权登录后会返回code(auth_code(仅限支付宝))、state,1.8.0版本后,可以用AuthCallback类作为回调接口的入参
|
||||
AuthResponse login = authRequest.login(new AuthCallback());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void taobaoTest() {
|
||||
AuthRequest authRequest = new AuthTaobaoRequest(AuthConfig.builder()
|
||||
.clientId("clientId")
|
||||
.clientSecret("clientSecret")
|
||||
.redirectUri("redirectUri")
|
||||
.state("state")
|
||||
.build());
|
||||
// 返回授权页面,可自行跳转
|
||||
String url = authRequest.authorize();
|
||||
// 授权登录后会返回code(auth_code(仅限支付宝))、state,1.8.0版本后,可以用AuthCallback类作为回调接口的入参
|
||||
AuthResponse login = authRequest.login(new AuthCallback());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void googleTest() {
|
||||
AuthRequest authRequest = new AuthGoogleRequest(AuthConfig.builder()
|
||||
.clientId("clientId")
|
||||
.clientSecret("clientSecret")
|
||||
.redirectUri("redirectUri")
|
||||
.state("state")
|
||||
.build());
|
||||
// 返回授权页面,可自行跳转
|
||||
String url = authRequest.authorize();
|
||||
// 授权登录后会返回code(auth_code(仅限支付宝))、state,1.8.0版本后,可以用AuthCallback类作为回调接口的入参
|
||||
AuthResponse login = authRequest.login(new AuthCallback());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void facebookTest() {
|
||||
AuthRequest authRequest = new AuthFacebookRequest(AuthConfig.builder()
|
||||
.clientId("clientId")
|
||||
.clientSecret("clientSecret")
|
||||
.redirectUri("redirectUri")
|
||||
.state("state")
|
||||
.build());
|
||||
// 返回授权页面,可自行跳转
|
||||
String url = authRequest.authorize();
|
||||
// 授权登录后会返回code(auth_code(仅限支付宝))、state,1.8.0版本后,可以用AuthCallback类作为回调接口的入参
|
||||
AuthResponse login = authRequest.login(new AuthCallback());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void douyinTest() {
|
||||
AuthRequest authRequest = new AuthDouyinRequest(AuthConfig.builder()
|
||||
.clientId("clientId")
|
||||
.clientSecret("clientSecret")
|
||||
.redirectUri("redirectUri")
|
||||
.state("state")
|
||||
.build());
|
||||
// 返回授权页面,可自行跳转
|
||||
String url = authRequest.authorize();
|
||||
// 授权登录后会返回code(auth_code(仅限支付宝))、state,1.8.0版本后,可以用AuthCallback类作为回调接口的入参
|
||||
AuthResponse login = authRequest.login(new AuthCallback());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void linkedinTest() {
|
||||
AuthRequest authRequest = new AuthLinkedinRequest(AuthConfig.builder()
|
||||
.clientId("clientId")
|
||||
.clientSecret("clientSecret")
|
||||
.redirectUri("redirectUri")
|
||||
.state("state")
|
||||
.build());
|
||||
// 返回授权页面,可自行跳转
|
||||
String url = authRequest.authorize();
|
||||
// 授权登录后会返回code(auth_code(仅限支付宝))、state,1.8.0版本后,可以用AuthCallback类作为回调接口的入参
|
||||
AuthResponse login = authRequest.login(new AuthCallback());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void microsoftTest() {
|
||||
AuthRequest authRequest = new AuthMicrosoftRequest(AuthConfig.builder()
|
||||
.clientId("clientId")
|
||||
.clientSecret("clientSecret")
|
||||
.redirectUri("redirectUri")
|
||||
.state("state")
|
||||
.build());
|
||||
// 返回授权页面,可自行跳转
|
||||
String url = authRequest.authorize();
|
||||
// 授权登录后会返回code(auth_code(仅限支付宝))、state,1.8.0版本后,可以用AuthCallback类作为回调接口的入参
|
||||
AuthResponse login = authRequest.login(new AuthCallback());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void miTest() {
|
||||
AuthRequest authRequest = new AuthMiRequest(AuthConfig.builder()
|
||||
.clientId("clientId")
|
||||
.clientSecret("clientSecret")
|
||||
.redirectUri("redirectUri")
|
||||
.state("state")
|
||||
.build());
|
||||
// 返回授权页面,可自行跳转
|
||||
String url = authRequest.authorize();
|
||||
// 授权登录后会返回code(auth_code(仅限支付宝))、state,1.8.0版本后,可以用AuthCallback类作为回调接口的入参
|
||||
AuthResponse login = authRequest.login(new AuthCallback());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toutiaoTest() {
|
||||
AuthRequest authRequest = new AuthToutiaoRequest(AuthConfig.builder()
|
||||
.clientId("clientId")
|
||||
.clientSecret("clientSecret")
|
||||
.redirectUri("redirectUri")
|
||||
.state("state")
|
||||
.build());
|
||||
// 返回授权页面,可自行跳转
|
||||
String url = authRequest.authorize();
|
||||
// 授权登录后会返回code(auth_code(仅限支付宝))、state,1.8.0版本后,可以用AuthCallback类作为回调接口的入参
|
||||
AuthResponse login = authRequest.login(new AuthCallback());
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
AuthDefaultStateCache.INSTANCE.cache("key", "value");
|
||||
Assert.assertEquals(AuthDefaultStateCache.INSTANCE.get("key"), "value");
|
||||
|
||||
TimeUnit.MILLISECONDS.sleep(4);
|
||||
Assert.assertEquals(AuthDefaultStateCache.INSTANCE.get("key"), "value");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cache2() throws InterruptedException {
|
||||
AuthDefaultStateCache.INSTANCE.cache("key", "value", 10);
|
||||
Assert.assertEquals(AuthDefaultStateCache.INSTANCE.get("key"), "value");
|
||||
|
||||
// 没过期
|
||||
TimeUnit.MILLISECONDS.sleep(5);
|
||||
Assert.assertEquals(AuthDefaultStateCache.INSTANCE.get("key"), "value");
|
||||
|
||||
// 过期
|
||||
TimeUnit.MILLISECONDS.sleep(6);
|
||||
Assert.assertNull(AuthDefaultStateCache.INSTANCE.get("key"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package me.zhyd.oauth.config;
|
||||
|
||||
/**
|
||||
* 测试自定义实现{@link AuthSource}接口后的枚举类
|
||||
*
|
||||
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
|
||||
* @version 1.0
|
||||
* @since 1.12.0
|
||||
*/
|
||||
public enum AuthExtendSource implements AuthSource {
|
||||
|
||||
OTHER {
|
||||
/**
|
||||
* 授权的api
|
||||
*
|
||||
* @return url
|
||||
*/
|
||||
@Override
|
||||
public String authorize() {
|
||||
return "http://authorize";
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取accessToken的api
|
||||
*
|
||||
* @return url
|
||||
*/
|
||||
@Override
|
||||
public String accessToken() {
|
||||
return "http://accessToken";
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户信息的api
|
||||
*
|
||||
* @return url
|
||||
*/
|
||||
@Override
|
||||
public String userInfo() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消授权的api
|
||||
*
|
||||
* @return url
|
||||
*/
|
||||
@Override
|
||||
public String revoke() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新授权的api
|
||||
*
|
||||
* @return url
|
||||
*/
|
||||
@Override
|
||||
public String refresh() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
package me.zhyd.oauth.log;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
|
||||
* @version 1.0
|
||||
* @date 2019/8/2 19:36
|
||||
* @since 1.8
|
||||
*/
|
||||
public class LogTest {
|
||||
|
||||
public static void main(String[] args) {
|
||||
// 测试正常打印
|
||||
Log.debug("[1] This is a test.");
|
||||
Log.debug("[1] This is a test.", new NullPointerException("npe"));
|
||||
|
||||
Log.warn("[1] This is a test.");
|
||||
Log.warn("[1] This is a test.", new NullPointerException("npe"));
|
||||
|
||||
Log.error("[1] This is a test.");
|
||||
Log.error("[1] This is a test.", new NullPointerException("npe"));
|
||||
|
||||
// 测试只打印 error级别的日志
|
||||
Log.Config.level = Log.Level.ERROR;
|
||||
|
||||
Log.debug("[2] This is a test.");
|
||||
Log.warn("[2] This is a test.");
|
||||
Log.error("[2] This is a test.");
|
||||
|
||||
// 测试关闭日志
|
||||
Log.Config.enable = false;
|
||||
|
||||
Log.debug("[3] This is a test.");
|
||||
Log.warn("[3] This is a test.");
|
||||
Log.error("[3] This is a test.");
|
||||
}
|
||||
|
||||
/**
|
||||
* 1000000: 23135ms
|
||||
* 100000: 3016ms
|
||||
* 10000: 328ms
|
||||
* 1000: 26ms
|
||||
*/
|
||||
@Test
|
||||
public void testByThread() {
|
||||
long start = System.currentTimeMillis();
|
||||
for (int i = 0; i < 1; i++) {
|
||||
System.out.println(callMethodByThread());
|
||||
}
|
||||
long end = System.currentTimeMillis();
|
||||
System.out.println((end - start) + "ms");
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 1000000: 19058ms
|
||||
* 100000: 2772ms
|
||||
* 10000: 323ms
|
||||
* 1000: 29ms
|
||||
*/
|
||||
@Test
|
||||
public void testByThrowable() {
|
||||
long end = System.currentTimeMillis();
|
||||
for (int i = 0; i < 1; i++) {
|
||||
System.out.println(callMethodByThrowable());
|
||||
}
|
||||
long end2 = System.currentTimeMillis();
|
||||
System.out.println((end2 - end) + "ms");
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBySecurityManager() {
|
||||
long end = System.currentTimeMillis();
|
||||
for (int i = 0; i < 1; i++) {
|
||||
System.out.println(callMethodBySecurityManager());
|
||||
}
|
||||
long end2 = System.currentTimeMillis();
|
||||
System.out.println((end2 - end) + "ms");
|
||||
|
||||
}
|
||||
|
||||
private String callMethodByThread() {
|
||||
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
|
||||
for (StackTraceElement stackTraceElement : stackTrace) {
|
||||
System.out.println(stackTraceElement.getMethodName());
|
||||
}
|
||||
return stackTrace[2].getMethodName();
|
||||
}
|
||||
|
||||
private String callMethodByThrowable() {
|
||||
StackTraceElement[] stackTrace = (new Throwable()).getStackTrace();
|
||||
for (StackTraceElement stackTraceElement : stackTrace) {
|
||||
System.out.println(stackTraceElement.getMethodName());
|
||||
}
|
||||
return stackTrace[2].getMethodName();
|
||||
}
|
||||
|
||||
private String callMethodBySecurityManager() {
|
||||
return new SecurityManager() {
|
||||
String getClassName() {
|
||||
for (Class clazz : getClassContext()) {
|
||||
System.out.println(clazz);
|
||||
}
|
||||
return getClassContext()[0].getName();
|
||||
}
|
||||
}.getClassName();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package me.zhyd.oauth.model;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import me.zhyd.oauth.config.AuthDefaultSource;
|
||||
import me.zhyd.oauth.config.AuthExtendSource;
|
||||
import me.zhyd.oauth.config.AuthSource;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class AuthUserTest {
|
||||
|
||||
@Test
|
||||
public void serialize() {
|
||||
|
||||
AuthUser user = AuthUser.builder()
|
||||
.nickname("test")
|
||||
.build();
|
||||
String json = JSON.toJSONString(user);
|
||||
Assert.assertEquals(json, "{\"nickname\":\"test\"}");
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deserialize() {
|
||||
AuthUser user = AuthUser.builder()
|
||||
.nickname("test")
|
||||
.build();
|
||||
String json = JSON.toJSONString(user);
|
||||
|
||||
AuthUser deserializeUser = JSON.parseObject(json, AuthUser.class);
|
||||
Assert.assertEquals(deserializeUser.getNickname(), "test");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void source() {
|
||||
AuthSource source = AuthDefaultSource.HUAWEI;
|
||||
AuthUser user = AuthUser.builder()
|
||||
.source(source.toString())
|
||||
.build();
|
||||
Assert.assertEquals(user.getSource(), "HUAWEI");
|
||||
|
||||
source = AuthExtendSource.OTHER;
|
||||
user = AuthUser.builder()
|
||||
.source(source.toString())
|
||||
.build();
|
||||
Assert.assertEquals(user.getSource(), "OTHER");
|
||||
|
||||
source = AuthDefaultSource.HUAWEI;
|
||||
Assert.assertEquals(source, AuthDefaultSource.HUAWEI);
|
||||
|
||||
source = AuthExtendSource.OTHER;
|
||||
Assert.assertEquals(source, AuthExtendSource.OTHER);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
package me.zhyd.oauth.request;
|
||||
|
||||
import me.zhyd.oauth.cache.AuthStateCache;
|
||||
import me.zhyd.oauth.config.AuthConfig;
|
||||
import me.zhyd.oauth.config.AuthExtendSource;
|
||||
import me.zhyd.oauth.enums.AuthResponseStatus;
|
||||
import me.zhyd.oauth.enums.AuthUserGender;
|
||||
import me.zhyd.oauth.model.AuthCallback;
|
||||
import me.zhyd.oauth.model.AuthResponse;
|
||||
import me.zhyd.oauth.model.AuthToken;
|
||||
import me.zhyd.oauth.model.AuthUser;
|
||||
|
||||
/**
|
||||
* 测试用自定义扩展的第三方request
|
||||
*
|
||||
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
|
||||
* @version 1.0
|
||||
* @since 1.12.0
|
||||
*/
|
||||
public class AuthExtendRequest extends AuthDefaultRequest {
|
||||
|
||||
public AuthExtendRequest(AuthConfig config) {
|
||||
super(config, AuthExtendSource.OTHER);
|
||||
}
|
||||
|
||||
public AuthExtendRequest(AuthConfig config, AuthStateCache authStateCache) {
|
||||
super(config, AuthExtendSource.OTHER, authStateCache);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取access token
|
||||
*
|
||||
* @param authCallback 授权成功后的回调参数
|
||||
* @return token
|
||||
* @see AuthDefaultRequest#authorize()
|
||||
* @see AuthDefaultRequest#authorize(String)
|
||||
*/
|
||||
@Override
|
||||
protected AuthToken getAccessToken(AuthCallback authCallback) {
|
||||
return AuthToken.builder()
|
||||
.openId("openId")
|
||||
.expireIn(1000)
|
||||
.idToken("idToken")
|
||||
.scope("scope")
|
||||
.refreshToken("refreshToken")
|
||||
.accessToken("accessToken")
|
||||
.code("code")
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用token换取用户信息
|
||||
*
|
||||
* @param authToken token信息
|
||||
* @return 用户信息
|
||||
* @see AuthDefaultRequest#getAccessToken(AuthCallback)
|
||||
*/
|
||||
@Override
|
||||
protected AuthUser getUserInfo(AuthToken authToken) {
|
||||
return AuthUser.builder()
|
||||
.username("test")
|
||||
.nickname("test")
|
||||
.gender(AuthUserGender.MALE)
|
||||
.token(authToken)
|
||||
.source(this.source.toString())
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 撤销授权
|
||||
*
|
||||
* @param authToken 登录成功后返回的Token信息
|
||||
* @return AuthResponse
|
||||
*/
|
||||
@Override
|
||||
public AuthResponse revoke(AuthToken authToken) {
|
||||
return AuthResponse.builder()
|
||||
.code(AuthResponseStatus.SUCCESS.getCode())
|
||||
.msg(AuthResponseStatus.SUCCESS.getMsg())
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新access token (续期)
|
||||
*
|
||||
* @param authToken 登录成功后返回的Token信息
|
||||
* @return AuthResponse
|
||||
*/
|
||||
@Override
|
||||
public AuthResponse refresh(AuthToken authToken) {
|
||||
return AuthResponse.builder()
|
||||
.code(AuthResponseStatus.SUCCESS.getCode())
|
||||
.data(AuthToken.builder()
|
||||
.openId("openId")
|
||||
.expireIn(1000)
|
||||
.idToken("idToken")
|
||||
.scope("scope")
|
||||
.refreshToken("refreshToken")
|
||||
.accessToken("accessToken")
|
||||
.code("code")
|
||||
.build())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user