Compare commits
219 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8b3bc42678 | |||
| 164e12de1f | |||
| 369388c11d | |||
| a1200b2510 | |||
| 6ea1d41211 | |||
| ddde4ef1f5 | |||
| 26884f7218 | |||
| 7f9c12d5bf | |||
| fbeb4c68ab | |||
| 179287f720 | |||
| 8a47735208 | |||
| 0feb859f68 | |||
| 441dda3970 | |||
| b564ce8a89 | |||
| 5e0065508a | |||
| cf6f42b586 | |||
| 89b5fe4543 | |||
| 6f313fa91a | |||
| 36bcebdd46 | |||
| 99b8680fd2 | |||
| 2717d9cf7a | |||
| 166ddffaf4 | |||
| 33385fd30a | |||
| 8e5a679129 | |||
| 5ab2e87157 | |||
| c34e226a54 | |||
| e4985c63f3 | |||
| e93ad07c5f | |||
| 4ca1f46722 | |||
| 571c3438e4 | |||
| 2497519ec1 | |||
| 278a435cdb | |||
| e75df92080 | |||
| 9e6a4c61b0 | |||
| d355699cc3 | |||
| 28466f8ab5 | |||
| c6bd5d9e5d | |||
| d0ae0f2c7a | |||
| 521a423e26 | |||
| 1f1d61fe4e | |||
| ec87ea145c | |||
| b1121ba545 | |||
| 41d0d01329 | |||
| 77603cf7db | |||
| 7be51f1d33 | |||
| e71af6b2e6 | |||
| 54779ab26a | |||
| be1e23e2ab | |||
| 739e7dfd39 | |||
| 58c8aa9010 | |||
| 93aae33f0a | |||
| 268a9b477b | |||
| adaf9d59cd | |||
| 7f0f1e17d2 | |||
| 65b2db32b5 | |||
| 9e79dd7cb3 | |||
| 30f29a49d1 | |||
| 129d1d57ed | |||
| 3da90736c4 | |||
| 05891e48e9 | |||
| 9ef19b1d54 | |||
| 2de19a3824 | |||
| bccf3279af | |||
| 6b4a8b78b1 | |||
| 5a94ff82c1 | |||
| 7b03751a8d | |||
| 823a43b1e2 | |||
| db6e88173a | |||
| c6481a681e | |||
| 1e1038fbd4 | |||
| 7cb0f8dbb6 | |||
| 65c60a61b3 | |||
| d5a8ffcab2 | |||
| c95c118f88 | |||
| fec9a3c214 | |||
| 6439058bda | |||
| a1d31018d3 | |||
| 2a9d8e81a1 | |||
| dda873ca11 | |||
| 8dc4b5d7d6 | |||
| 2038f24b3b | |||
| f8f13fdb10 | |||
| 697c07cccc | |||
| e868d43ca3 | |||
| 0700d7ee25 | |||
| 822b4806a1 | |||
| ab8c24ee5c | |||
| cd24cbdd58 | |||
| 4bc9c09610 | |||
| 4d2518e21a | |||
| f47dc074e7 | |||
| b7f01549c1 | |||
| 264294caee | |||
| 19ec565004 | |||
| 028924ecbf | |||
| d2d3232501 | |||
| 1206847576 | |||
| 2490c0f1ff | |||
| 2fd7255ad4 | |||
| f7873d71b2 | |||
| 602b1eaf58 | |||
| 7c2bc82814 | |||
| 28b764f0d2 | |||
| 2442661291 | |||
| abb121a57c | |||
| 51603e18ad | |||
| 77c9680333 | |||
| 525c406c92 | |||
| e65d87daa3 | |||
| 8639359f53 | |||
| 9eda5aaf2b | |||
| 9d6ceb3898 | |||
| 7bb9864799 | |||
| 95e4db2315 | |||
| fc4606dad3 | |||
| 31330a366d | |||
| 7364c203ab | |||
| c730072f07 | |||
| 3c3e9b63d5 | |||
| 100dacac91 | |||
| 41dee559df | |||
| b0769d09b3 | |||
| c77c74be3c | |||
| 66fdec3e2e | |||
| c674457f2f | |||
| 0a0f6a34e8 | |||
| 366a6ac886 | |||
| 15d03fcd07 | |||
| c0b9010d84 | |||
| 3cb7c0dd82 | |||
| a23610c8b8 | |||
| 4e0fe3ba33 | |||
| 9f341389d2 | |||
| 43459c3192 | |||
| de038b852d | |||
| da8ec82e74 | |||
| 5ed7e1563b | |||
| 3e9fdf7c35 | |||
| 427c5b6d38 | |||
| ea4beb71bc | |||
| 1334cd7378 | |||
| 81eaca75ab | |||
| 9e4815cde7 | |||
| 17630ea1bf | |||
| b8240ac210 | |||
| 3d7d37b651 | |||
| 94550e406f | |||
| fdc78212ff | |||
| 51087658a4 | |||
| 469558bddc | |||
| 6567f17513 | |||
| 6a3e061921 | |||
| 3a5a312b68 | |||
| 3ff0b36b49 | |||
| faae8816be | |||
| 6cf6a7d7dc | |||
| d9952968d1 | |||
| 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 | |||
| 6474c46505 | |||
| 92bc4ab34a | |||
| 4fab7561a4 |
@@ -1,13 +1,12 @@
|
||||
### 哪个平台?
|
||||
为更快的帮您定位问题,推荐您用以下模板反馈问题:
|
||||
|
||||
### 1. 出现问题时,您做了哪些操作?
|
||||
|
||||
### 2. 在哪个步骤出现了问题?
|
||||
|
||||
### 重现步骤
|
||||
|
||||
|
||||
|
||||
### 报错信息
|
||||
|
||||
### 3. 您希望得到什么结果?
|
||||
|
||||
### 4. 您实际得到什么结果?
|
||||
|
||||
### 5. 请附上您出现问题的整屏截图或者整个异常堆栈信息
|
||||
|
||||
|
||||
@@ -1,15 +1,29 @@
|
||||
### 该Pull Request关联的Issue
|
||||
- [ ] 是否为解决Issue?
|
||||
|
||||
|
||||
### 修改描述
|
||||
### 您做了哪些更新?
|
||||
|
||||
- 新增
|
||||
|
||||
- 修改
|
||||
|
||||
- 修复
|
||||
|
||||
- 其他
|
||||
|
||||
|
||||
### 是否做了充分测试?
|
||||
|
||||
- [ ] 是,已经做过测试,并且测试通过
|
||||
- [ ] 否,还没做测试,需要作者自测
|
||||
|
||||
注:测试demo可以使用:
|
||||
- [simple版](https://github.com/justauth/JustAuth-demo)
|
||||
- [jFinal版](https://github.com/xkcoding/jfinal-justauth-demo)
|
||||
- [ActFramework版](https://github.com/xkcoding/act-justauth-demo)
|
||||
- [Nutzboot版](https://github.com/EggsBlue/nutzboot-justauth-demo)
|
||||
- [Blade版](https://github.com/justauth/blade-justauth-demo)
|
||||
|
||||
|
||||
|
||||
### 测试用例
|
||||
|
||||
|
||||
|
||||
### 修复效果的截屏
|
||||
|
||||
|
||||
|
||||
|
||||
+8
-2
@@ -25,6 +25,12 @@ hs_err_pid*
|
||||
# exclude idea files
|
||||
.idea
|
||||
*.iml
|
||||
*.sh
|
||||
bin/codecov.sh
|
||||
bin/deploy.sh
|
||||
bin/docsify-cli.sh
|
||||
bin/push.sh
|
||||
bin/push-dev.sh
|
||||
|
||||
target
|
||||
target
|
||||
/pom.xml.versionsBackup
|
||||
/gpg
|
||||
|
||||
+181
@@ -0,0 +1,181 @@
|
||||
<p align="center">
|
||||
<a href="https://justauth.wiki"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/Justauth.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%20Central--1.15.6-blue" ></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%20Docs--1.15.6-latest-orange" ></img>
|
||||
</a>
|
||||
<a target="_blank" href="https://justauth.wiki" 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" title="微信开放平台"></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>
|
||||
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/twitter.png" width="20"></td>
|
||||
</tr>
|
||||
</table>
|
||||
<center><a href="https://justauth.wiki/#/?id=%E5%B7%B2%E9%9B%86%E6%88%90%E7%9A%84%E5%B9%B3%E5%8F%B0" target="_blank">查看更多</a></center>
|
||||
</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://justauth.wiki)
|
||||
|
||||
## Features
|
||||
|
||||
1. **Multiple platform**: Has integrated more than a dozen third-party platforms.([plan](https://gitee.com/yadong.zhang/JustAuth/issues/IUGRK))
|
||||
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>1.15.6</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);
|
||||
```
|
||||
|
||||
Note, that since [v1.14.0](https://gitee.com/yadong.zhang/JustAuth/releases/v1.14.0) JustAuth has been integrated by default with [simple-http](https://github.com/xkcoding/simple-http) as the HTTP general interface (see the update [JustAuth 1.14.0 release! Perfect decoupling of HTTP tools](https://mp.weixin.qq.com/s?__biz=MzA3NDk3OTIwMg==&mid=2450633197&idx=1&sn=11e625b307db62b2f1c4e82f7744b2a2&chksm=88929300bfe51a16562b45592a264482ae2c74c6dbfa4a3aa9611ad4fea4a9be5b1f0545527d&token=1093833287&lang=zh_CN#rd)). Since most projects already integrate HTTP tools such as OkHttp3, apache HttpClient, and hutool-http), in order to reduce unnecessary dependencies,Starting from [v1.14.0](https://gitee.com/yadong.zhang/JustAuth/releases/v1.14.0), JustAuth will not integrate hutool-http by default. If the developer's project is new or there is no integrated HTTP implementation tool in the project, please add the corresponding HTTP implementation class by yourself. Alternative dependencies are as follows:
|
||||
|
||||
|
||||
- hutool-http
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-http</artifactId>
|
||||
<version>5.2.5</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
- httpclient
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
<version>4.5.12</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
- okhttp
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>com.squareup.okhttp3</groupId>
|
||||
<artifactId>okhttp</artifactId>
|
||||
<version>4.4.1</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
## 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://justauth.wiki/#/contributors)
|
||||
|
||||
## Change Logs
|
||||
|
||||
[CHANGELOGS](https://justauth.wiki/#/update)
|
||||
|
||||
## 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://justauth.wiki"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/Justauth.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.5-blue.svg" ></img>
|
||||
<img src="https://img.shields.io/badge/Maven%20Central--1.15.6-blue" ></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,8 +14,11 @@
|
||||
<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.5-orange.svg" ></img>
|
||||
<a target="_blank" href="https://apidoc.gitee.com/yadong.zhang/JustAuth/" title="API文档">
|
||||
<img src="https://img.shields.io/badge/Api%20Docs-1.15.6-latest-orange" ></img>
|
||||
</a>
|
||||
<a target="_blank" href="https://justauth.wiki" 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" />
|
||||
@@ -31,51 +34,59 @@
|
||||
<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" title="微信开放平台"></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>
|
||||
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/twitter.png" width="20"></td>
|
||||
</tr>
|
||||
</table>
|
||||
<center><a href="https://justauth.wiki/#/?id=%E5%B7%B2%E9%9B%86%E6%88%90%E7%9A%84%E5%B9%B3%E5%8F%B0" target="_blank">查看更多</a></center>
|
||||
</center>
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
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://justauth.wiki)
|
||||
|
||||
## 特点
|
||||
|
||||
废话不多说,就俩字:
|
||||
|
||||
1. **全**:已集成十多家第三方平台(国内外常用的基本都已包含),后续依然还有扩展计划!
|
||||
1. **全**:已集成十多家第三方平台(国内外常用的基本都已包含),仍然还在持续扩展中([开发计划](https://gitee.com/yadong.zhang/JustAuth/issues/IUGRK))!
|
||||
2. **简**:API就是奔着最简单去设计的(见后面`快速开始`),尽量让您用起来没有障碍感!
|
||||
|
||||
## 快速开始
|
||||
@@ -85,7 +96,7 @@ JustAuth,如你所见,它仅仅是一个**第三方授权登录**的**工具
|
||||
<dependency>
|
||||
<groupId>me.zhyd.oauth</groupId>
|
||||
<artifactId>JustAuth</artifactId>
|
||||
<version>1.9.5</version>
|
||||
<version>1.15.6</version>
|
||||
</dependency>
|
||||
```
|
||||
- 调用api
|
||||
@@ -97,60 +108,43 @@ AuthRequest authRequest = new AuthGiteeRequest(AuthConfig.builder()
|
||||
.redirectUri("redirectUri")
|
||||
.build());
|
||||
// 生成授权页面
|
||||
authRequest.authorize();
|
||||
authRequest.authorize("state");
|
||||
// 授权登录后会返回code(auth_code(仅限支付宝))、state,1.8.0版本后,可以用AuthCallback类作为回调接口的参数
|
||||
// 注:JustAuth默认保存state的时效为3分钟,3分钟内未使用则会自动清除过期的state
|
||||
authRequest.login(callback);
|
||||
```
|
||||
|
||||
**配套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)
|
||||
如下**任选一种** HTTP 工具 依赖,_项目内如果已有,请忽略_
|
||||
|
||||
**扩展工具**
|
||||
- hutool-http
|
||||
|
||||
- [justauth-spring-boot-starter](https://github.com/xkcoding/justauth-spring-boot-starter): Spring Boot 集成 JustAuth 的最佳实践
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-http</artifactId>
|
||||
<version>5.2.5</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
**配套SpringBoot starter**:
|
||||
- httpclient
|
||||
|
||||
[justauth-spring-boot-starter](https://github.com/xkcoding/justauth-spring-boot-starter)
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
<version>4.5.12</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
具体的例子可以参考:
|
||||
- okhttp
|
||||
|
||||
- [实现Gitee授权登录](http://t.cn/ExDKxQs)
|
||||
- [实现Github授权登录](http://t.cn/EJ0Fxqo)
|
||||
- [Spring Boot 快速集成第三方登录功能](http://t.cn/AiWWx5kH)
|
||||
|
||||
#### API列表
|
||||
| :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/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登录只能针对少部分用户使用了_
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>com.squareup.okhttp3</groupId>
|
||||
<artifactId>okhttp</artifactId>
|
||||
<version>4.4.1</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
## 后续开发计划
|
||||
|
||||
@@ -167,23 +161,36 @@ _请知悉:经咨询CSDN官方客服得知,CSDN的授权开放平台已经
|
||||
5. 发起PR(pull request) 请求,提交到`dev`分支
|
||||
6. 等待作者合并
|
||||
|
||||
## 贡献者名单
|
||||
|
||||
[contributors](https://justauth.wiki/#/contributors)
|
||||
|
||||
## 更新记录
|
||||
|
||||
[CHANGELOGS](https://justauth.wiki/#/update)
|
||||
|
||||
## 致谢
|
||||
|
||||
在项目立项初期,也对当前开源圈的一些相同类型的项目作过调研,同时本项目也参考过这些项目,再次感谢开源圈内的朋友。
|
||||
|
||||
[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://images.gitee.com/uploads/images/2020/0406/220236_f5275c90_5531506.png" alt="图片引用自lets-mica" style="float:left;">
|
||||
|
||||
[阿里妈妈MUX倾力打造的矢量图标库-iconfont](https://www.iconfont.cn/search/index): 本文档中的图标大部分取自该平台
|
||||
<a href="https://www.producthunt.com/posts/justauth?utm_source=badge-featured&utm_medium=badge&utm_souce=badge-justauth" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=196886&theme=dark" alt="JustAuth - Login, so easy! | Product Hunt Embed" style="width: 250px; height: 54px;" width="250px" height="54px" /></a>
|
||||
|
||||
[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)
|
||||
|
||||
## 关于OAuth
|
||||
|
||||
[The OAuth 2.0 Authorization Framework](https://tools.ietf.org/html/rfc6749)
|
||||
## 开源推荐
|
||||
- `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](https://gitee.com/smallc/SpringBlade)
|
||||
- `MaxKey` 马克思的钥匙,寓意是最大钥匙,是用户单点登录认证系统(Sigle Sign On System),OAuth 2.0/OpenID Connect、SAML 2.0、JWT、CAS等标准化的开放协议,使用JustAuth集成OAuth第三方认证。: [https://shimingxy.github.io/MaxKey/](https://shimingxy.github.io/MaxKey/)
|
||||
|
||||
## 关注&交流
|
||||
|
||||
| 公众号 | 微信(备注:加群) |
|
||||
| 公众号 | 微信(备注:JustAuth) |
|
||||
| :------------: | :------------: |
|
||||
| <img src="https://gitee.com/yadong.zhang/static/raw/master/wx/wechat_account.jpg" width="200" /> | <img src="https://gitee.com/yadong.zhang/static/raw/master/wx/wx.png" width="170"/> |
|
||||
|
||||
@@ -191,11 +198,10 @@ _请知悉:经咨询CSDN官方客服得知,CSDN的授权开放平台已经
|
||||
|
||||
- JustAuth交流群 (230017570):专业交流该项目
|
||||
|
||||
- 开源总群 (190886500):各个开源项目的都有,也有博客建设等方面的朋友。(注意,该群需付费进入,防止发垃圾广告、垃圾推广等人士)
|
||||
|
||||
|
||||
## 请喝咖啡
|
||||
|
||||
| 支付宝 | 微信 |
|
||||
| :------------: | :------------: |
|
||||
| <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" /> |
|
||||
|
||||
通过“[爱发电](https://afdian.net/@zhangyadong)”赞助,感谢您的支持
|
||||
@@ -0,0 +1,35 @@
|
||||
#!/bin/bash
|
||||
|
||||
#-----------------------------------------------------------
|
||||
# 参考自 hutool 工具
|
||||
# 此脚本用于每次升级justauth时替换相应位置的版本号
|
||||
#-----------------------------------------------------------
|
||||
pwd=$(pwd)
|
||||
echo "当前路径:$pwd"
|
||||
|
||||
if [ -n "$1" ];then
|
||||
new_version="$1"
|
||||
old_version=`cat $pwd/bin/version.txt`
|
||||
echo "$old_version 替换为新版本 $new_version"
|
||||
else
|
||||
# 参数错误,退出
|
||||
echo "ERROR: 请指定新版本!"
|
||||
exit
|
||||
fi
|
||||
|
||||
if [ ! -n "$old_version" ]; then
|
||||
echo "ERROR: 旧版本不存在,请确认bin/version.txt中信息正确"
|
||||
exit
|
||||
fi
|
||||
|
||||
# 替换README.md等文件中的版本
|
||||
sed -i "s/${old_version}/${new_version}/g" $pwd/README.md
|
||||
sed -i "s/${old_version}/${new_version}/g" $pwd/README.en-US.md
|
||||
sed -i "s/${old_version}/${new_version}/g" $pwd/docs/README.md
|
||||
sed -i "s/${old_version}/${new_version}/g" $pwd/docs/_coverpage.md
|
||||
|
||||
# 替换pom.xml中的版本
|
||||
sed -i "s/${old_version}/${new_version}/g" $pwd/pom.xml
|
||||
|
||||
# 保留新版本号
|
||||
echo "$new_version" > $pwd/bin/version.txt
|
||||
@@ -0,0 +1,19 @@
|
||||
#!/bin/bash
|
||||
|
||||
#------------------------------------------------
|
||||
# 参考自 hutool 工具
|
||||
# 升级justauth版本,包括:
|
||||
# 1. 升级pom.xml中的版本号
|
||||
# 2. 替换README.md中的版本号
|
||||
#------------------------------------------------
|
||||
|
||||
if [ ! -n "$1" ]; then
|
||||
echo "ERROR: 新版本不存在,请指定参数1"
|
||||
exit
|
||||
fi
|
||||
|
||||
# 替换所有模块pom.xml中的版本
|
||||
mvn versions:set -DnewVersion=$1
|
||||
|
||||
# 替换其它地方的版本
|
||||
source $(pwd)/bin/repVersion.sh "$1"
|
||||
@@ -0,0 +1 @@
|
||||
1.15.6
|
||||
@@ -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>
|
||||
- 千年等一回,我只为等你...
|
||||
@@ -0,0 +1,22 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta name="google-site-verification" content="DJSx-fAGWci-EWueWy2_RfafHJX1iv5XhfftGSg-SR0" />
|
||||
<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!" />
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
window.location.href = "https://justauth.wiki";
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
+1
-1
@@ -98,7 +98,7 @@ _注:非全部平台,部分平台可能不存在图例_
|
||||
|
||||
#### 授权Twitter
|
||||
|
||||
暂无
|
||||

|
||||
|
||||
#### 授权csdn
|
||||
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 参考自 hutool 工具
|
||||
help(){
|
||||
echo "--------------------------------------------------------------------------"
|
||||
echo ""
|
||||
echo "usage: ./ja.sh [updv]"
|
||||
echo ""
|
||||
echo "-updv [version num] Update all justauth related versions."
|
||||
echo ""
|
||||
echo "--------------------------------------------------------------------------"
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
'updv')
|
||||
bin/updVersion.sh $2
|
||||
;;
|
||||
*)
|
||||
help
|
||||
esac
|
||||
@@ -6,12 +6,14 @@
|
||||
|
||||
<groupId>me.zhyd.oauth</groupId>
|
||||
<artifactId>JustAuth</artifactId>
|
||||
<version>1.9.5</version>
|
||||
<version>1.15.6</version>
|
||||
|
||||
<name>JustAuth</name>
|
||||
<url>https://gitee.com/yadong.zhang/JustAuth</url>
|
||||
<description>
|
||||
史上最全的整合第三方登录的工具,目前已支持Github、Gitee、微博、钉钉、百度、Coding、腾讯云开发者平台、OSChina、支付宝、QQ、微信、淘宝、Google、Facebook、抖音、领英、小米、微软和今日头条等第三方平台的授权登录。
|
||||
史上最全的整合第三方登录的开源库。目前已支持Github、Gitee、微博、钉钉、百度、Coding、腾讯云开发者平台、OSChina、支付宝、
|
||||
QQ、微信开放平台、微信公众平台、淘宝、Google、Facebook、抖音、领英、小米、微软、今日头条、Teambition、StackOverflow、Pinterest、人人、华为、
|
||||
企业微信、酷家乐、Gitlab、美团、饿了么和推特等第三方平台的授权登录。
|
||||
Login, so easy!
|
||||
</description>
|
||||
|
||||
@@ -58,12 +60,11 @@
|
||||
<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>
|
||||
<simple-http.version>1.0.2</simple-http.version>
|
||||
<lombok-version>1.18.10</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>
|
||||
<fastjson-version>1.2.71</fastjson-version>
|
||||
<alipay-sdk-version>4.8.10.ALL</alipay-sdk-version>
|
||||
<jacoco-version>0.8.2</jacoco-version>
|
||||
</properties>
|
||||
|
||||
@@ -75,9 +76,9 @@
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-http</artifactId>
|
||||
<version>${hutool-version}</version>
|
||||
<groupId>com.xkcoding.http</groupId>
|
||||
<artifactId>simple-http</artifactId>
|
||||
<version>${simple-http.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
@@ -96,11 +97,6 @@
|
||||
<version>${alipay-sdk-version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-simple</artifactId>
|
||||
<version>${slf4j-version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
@@ -165,65 +161,65 @@
|
||||
<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.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>
|
||||
<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>
|
||||
<distributionManagement>
|
||||
<snapshotRepository>
|
||||
@@ -236,5 +232,36 @@
|
||||
</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,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;
|
||||
}
|
||||
@@ -13,6 +13,9 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
*/
|
||||
public enum AuthCacheScheduler {
|
||||
|
||||
/**
|
||||
* 当前实例
|
||||
*/
|
||||
INSTANCE;
|
||||
|
||||
private AtomicInteger cacheTaskNumber = new AtomicInteger(1);
|
||||
|
||||
+4
-8
@@ -18,12 +18,6 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
*/
|
||||
public class AuthDefaultCache implements AuthCache {
|
||||
|
||||
/**
|
||||
* 默认缓存过期时间:3分钟
|
||||
* 鉴于授权过程中,根据个人的操作习惯,或者授权平台的不同(google等),每个授权流程的耗时也有差异,不过单个授权流程一般不会太长
|
||||
* 本缓存工具默认的过期时间设置为3分钟,即程序默认认为3分钟内的授权有效,超过3分钟则默认失效,失效后删除
|
||||
*/
|
||||
private static final long DEF_TIMEOUT = 3 * 60 * 1000;
|
||||
/**
|
||||
* state cache
|
||||
*/
|
||||
@@ -33,7 +27,9 @@ public class AuthDefaultCache implements AuthCache {
|
||||
private final Lock readLock = cacheLock.readLock();
|
||||
|
||||
public AuthDefaultCache() {
|
||||
this.schedulePrune(DEF_TIMEOUT);
|
||||
if (AuthCacheConfig.schedulePrune) {
|
||||
this.schedulePrune(AuthCacheConfig.timeout);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -44,7 +40,7 @@ public class AuthDefaultCache implements AuthCache {
|
||||
*/
|
||||
@Override
|
||||
public void set(String key, String value) {
|
||||
set(key, value, DEF_TIMEOUT);
|
||||
set(key, value, AuthCacheConfig.timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
+11
-18
@@ -1,22 +1,21 @@
|
||||
package me.zhyd.oauth.cache;
|
||||
|
||||
/**
|
||||
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
|
||||
* @version 1.0
|
||||
* @since 1.8
|
||||
* <p>
|
||||
* State缓存接口,方便用户扩展
|
||||
* </p>
|
||||
*
|
||||
* @author yangkai.shen
|
||||
* @since 1.10.0
|
||||
*/
|
||||
public class AuthStateCache {
|
||||
private static AuthCache authCache = new AuthDefaultCache();
|
||||
|
||||
public interface AuthStateCache {
|
||||
/**
|
||||
* 存入缓存
|
||||
*
|
||||
* @param key 缓存key
|
||||
* @param value 缓存内容
|
||||
*/
|
||||
public static void cache(String key, String value) {
|
||||
authCache.set(key, value);
|
||||
}
|
||||
void cache(String key, String value);
|
||||
|
||||
/**
|
||||
* 存入缓存
|
||||
@@ -25,9 +24,7 @@ public class AuthStateCache {
|
||||
* @param value 缓存内容
|
||||
* @param timeout 指定缓存过期时间(毫秒)
|
||||
*/
|
||||
public static void cache(String key, String value, long timeout) {
|
||||
authCache.set(key, value, timeout);
|
||||
}
|
||||
void cache(String key, String value, long timeout);
|
||||
|
||||
/**
|
||||
* 获取缓存内容
|
||||
@@ -35,9 +32,7 @@ public class AuthStateCache {
|
||||
* @param key 缓存key
|
||||
* @return 缓存内容
|
||||
*/
|
||||
public static String get(String key) {
|
||||
return authCache.get(key);
|
||||
}
|
||||
String get(String key);
|
||||
|
||||
/**
|
||||
* 是否存在key,如果对应key的value值已过期,也返回false
|
||||
@@ -45,7 +40,5 @@ public class AuthStateCache {
|
||||
* @param key 缓存key
|
||||
* @return true:存在key,并且value没过期;false:key不存在或者已过期
|
||||
*/
|
||||
public static boolean containsKey(String key) {
|
||||
return authCache.containsKey(key);
|
||||
}
|
||||
boolean containsKey(String key);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
/**
|
||||
* JustAuth 缓存实现, 提供基础的基于ConcurrentHashMap + ScheduledExecutorService 实现的定时缓存。
|
||||
* 同时对外暴露{@code AuthStateCache}接口,可进行对缓存实现的自定义。
|
||||
*/
|
||||
package me.zhyd.oauth.cache;
|
||||
@@ -1,6 +1,8 @@
|
||||
package me.zhyd.oauth.config;
|
||||
|
||||
import com.xkcoding.http.config.HttpConfig;
|
||||
import lombok.*;
|
||||
import me.zhyd.oauth.model.AuthCallback;
|
||||
|
||||
/**
|
||||
* JustAuth配置类
|
||||
@@ -16,12 +18,12 @@ import lombok.*;
|
||||
public class AuthConfig {
|
||||
|
||||
/**
|
||||
* 客户端id:对应个平台的appKey
|
||||
* 客户端id:对应各平台的appKey
|
||||
*/
|
||||
private String clientId;
|
||||
|
||||
/**
|
||||
* 客户端Secret:对应个平台的appSecret
|
||||
* 客户端Secret:对应各平台的appSecret
|
||||
*/
|
||||
private String clientSecret;
|
||||
|
||||
@@ -32,6 +34,7 @@ public class AuthConfig {
|
||||
|
||||
/**
|
||||
* 支付宝公钥:当选择支付宝登录时,该值可用
|
||||
* 对应“RSA2(SHA256)密钥”中的“支付宝公钥”
|
||||
*/
|
||||
private String alipayPublicKey;
|
||||
|
||||
@@ -47,7 +50,52 @@ public class AuthConfig {
|
||||
/**
|
||||
* Stack Overflow Key
|
||||
* <p>
|
||||
* 1.9.0版本新增参数
|
||||
*
|
||||
* @since 1.9.0
|
||||
*/
|
||||
private String stackOverflowKey;
|
||||
|
||||
/**
|
||||
* 企业微信,授权方的网页应用ID
|
||||
*
|
||||
* @since 1.10.0
|
||||
*/
|
||||
private String agentId;
|
||||
|
||||
/**
|
||||
* 使用 Coding 登录时,需要传该值。
|
||||
*
|
||||
* 团队域名前缀,比如以“ https://justauth.coding.net/ ”为例,{@code codingGroupName} = justauth
|
||||
*
|
||||
* @since 1.15.5
|
||||
*/
|
||||
private String codingGroupName;
|
||||
|
||||
/**
|
||||
* 针对国外服务可以单独设置代理
|
||||
* HttpConfig config = new HttpConfig();
|
||||
* config.setProxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("127.0.0.1", 10080)));
|
||||
* config.setTimeout(15000);
|
||||
*
|
||||
* @since 1.15.5
|
||||
*/
|
||||
private HttpConfig httpConfig;
|
||||
|
||||
/**
|
||||
* 忽略校验 {@code state} 参数,默认不开启。当 {@code ignoreCheckState} 为 {@code true} 时,
|
||||
* {@link me.zhyd.oauth.request.AuthDefaultRequest#login(AuthCallback)} 将不会校验 {@code state} 的合法性。
|
||||
*
|
||||
* 使用场景:当且仅当使用自实现 {@code state} 校验逻辑时开启
|
||||
*
|
||||
* 以下场景使用方案仅作参考:
|
||||
* 1. 授权、登录为同端,并且全部使用 JustAuth 实现时,该值建议设为 {@code false};
|
||||
* 2. 授权和登录为不同端实现时,比如前端页面拼装 {@code authorizeUrl},并且前端自行对{@code state}进行校验,
|
||||
* 后端只负责使用{@code code}获取用户信息时,该值建议设为 {@code true};
|
||||
*
|
||||
* <strong>如非特殊需要,不建议开启这个配置</strong>
|
||||
*
|
||||
* 该方案主要为了解决以下类似场景的问题:
|
||||
* @see <a href="https://github.com/justauth/JustAuth/issues/83">https://github.com/justauth/JustAuth/issues/83</a>
|
||||
*/
|
||||
private boolean ignoreCheckState;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,791 @@
|
||||
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";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String revoke() {
|
||||
return "https://api.weibo.com/oauth2/revokeoauth2";
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 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,
|
||||
*
|
||||
* 参考 https://help.coding.net/docs/project/open/oauth.html#%E7%94%A8%E6%88%B7%E6%8E%88%E6%9D%83 中的说明,
|
||||
* 新版的 coding API 地址需要传入用户团队名,这儿使用动态参数,方便在 request 中使用
|
||||
*/
|
||||
CODING {
|
||||
@Override
|
||||
public String authorize() {
|
||||
return "https://%s.coding.net/oauth_authorize.html";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String accessToken() {
|
||||
return "https://%s.coding.net/api/oauth/access_token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String userInfo() {
|
||||
return "https://%s.coding.net/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_OPEN {
|
||||
@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";
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 微信公众平台
|
||||
*/
|
||||
WECHAT_MP {
|
||||
@Override
|
||||
public String authorize() {
|
||||
return "https://open.weixin.qq.com/connect/oauth2/authorize";
|
||||
}
|
||||
|
||||
@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";
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Twitter
|
||||
*
|
||||
* @since 1.13.0
|
||||
*/
|
||||
TWITTER {
|
||||
@Override
|
||||
public String authorize() {
|
||||
return "https://api.twitter.com/oauth/authenticate";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String accessToken() {
|
||||
return "https://api.twitter.com/oauth/access_token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String userInfo() {
|
||||
return "https://api.twitter.com/1.1/users/show.json";
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 飞书
|
||||
* 注意:该平台暂时存在问题,请不要使用。待修复完成后会重新发版
|
||||
*
|
||||
* @since 1.14.0
|
||||
*/
|
||||
FEISHU {
|
||||
@Override
|
||||
public String authorize() {
|
||||
return "https://open.feishu.cn/connect/qrconnect/page/sso/";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String accessToken() {
|
||||
return "https://open.feishu.cn/connect/qrconnect/oauth2/access_token/";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String userInfo() {
|
||||
return "https://open.feishu.cn/connect/qrconnect/oauth2/user_info/";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String refresh() {
|
||||
return "https://open.feishu.cn/connect/qrconnect/oauth2/access_token/";
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 京东
|
||||
*
|
||||
* @since 1.15.0
|
||||
*/
|
||||
JD {
|
||||
@Override
|
||||
public String authorize() {
|
||||
return "https://open-oauth.jd.com/oauth2/to_login";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String accessToken() {
|
||||
return "https://open-oauth.jd.com/oauth2/access_token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String userInfo() {
|
||||
return "https://api.jd.com/routerjson";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String refresh() {
|
||||
return "https://open-oauth.jd.com/oauth2/refresh_token";
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 阿里云
|
||||
*/
|
||||
ALIYUN {
|
||||
@Override
|
||||
public String authorize() {
|
||||
return "https://signin.aliyun.com/oauth2/v1/auth";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String accessToken() {
|
||||
return "https://oauth.aliyun.com/v1/token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String userInfo() {
|
||||
return "https://oauth.aliyun.com/v1/userinfo";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String refresh() {
|
||||
return "https://oauth.aliyun.com/v1/token";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,552 +1,55 @@
|
||||
package me.zhyd.oauth.config;
|
||||
|
||||
import me.zhyd.oauth.exception.AuthException;
|
||||
import me.zhyd.oauth.enums.AuthResponseStatus;
|
||||
import me.zhyd.oauth.exception.AuthException;
|
||||
import me.zhyd.oauth.model.AuthCallback;
|
||||
|
||||
/**
|
||||
* 各api需要的url, 用枚举类分平台类型管理
|
||||
* OAuth平台的API地址的统一接口,提供以下方法:
|
||||
* 1) {@link AuthSource#authorize()}: 获取授权url. 必须实现
|
||||
* 2) {@link AuthSource#accessToken()}: 获取accessToken的url. 必须实现
|
||||
* 3) {@link AuthSource#userInfo()}: 获取用户信息的url. 必须实现
|
||||
* 4) {@link AuthSource#revoke()}: 获取取消授权的url. 非必须实现接口(部分平台不支持)
|
||||
* 5) {@link AuthSource#refresh()}: 获取刷新授权的url. 非必须实现接口(部分平台不支持)
|
||||
* <p>
|
||||
* 注:
|
||||
* ①、如需通过JustAuth扩展实现第三方授权,请参考{@link AuthDefaultSource}自行创建对应的枚举类并实现{@link AuthSource}接口
|
||||
* ②、如果不是使用的枚举类,那么在授权成功后获取用户信息时,需要单独处理source字段的赋值
|
||||
* ③、如果扩展了对应枚举类时,在{@link me.zhyd.oauth.request.AuthRequest#login(AuthCallback)}中可以通过{@code xx.toString()}获取对应的source
|
||||
*
|
||||
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
|
||||
* @since 1.0
|
||||
* @version 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);
|
||||
}
|
||||
|
||||
@@ -555,8 +58,19 @@ public enum AuthSource {
|
||||
*
|
||||
* @return url
|
||||
*/
|
||||
public String refresh() {
|
||||
default String refresh() {
|
||||
throw new AuthException(AuthResponseStatus.UNSUPPORTED);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Source的字符串名字
|
||||
*
|
||||
* @return name
|
||||
*/
|
||||
default String getName() {
|
||||
if (this instanceof Enum) {
|
||||
return String.valueOf(this);
|
||||
}
|
||||
return this.getClass().getSimpleName();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* JustAuth 核心配置相关,包括{@code AuthConfig}和{@code AuthSource}
|
||||
*/
|
||||
package me.zhyd.oauth.config;
|
||||
@@ -21,11 +21,13 @@ public enum AuthResponseStatus {
|
||||
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"),
|
||||
ILLEGAL_CODE(5008, "Illegal code"),
|
||||
ILLEGAL_STATUS(5009, "Illegal state"),
|
||||
REQUIRED_REFRESH_TOKEN(5010, "The refresh token is required; it must not be null"),
|
||||
;
|
||||
|
||||
private int code;
|
||||
|
||||
@@ -2,6 +2,7 @@ package me.zhyd.oauth.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import me.zhyd.oauth.utils.StringUtils;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
@@ -18,25 +19,41 @@ public enum AuthUserGender {
|
||||
* MALE/FAMALE为正常值,通过{@link AuthUserGender#getRealGender(String)}方法获取真实的性别
|
||||
* UNKNOWN为容错值,部分平台不会返回用户性别,为了方便统一,使用UNKNOWN标记所有未知或不可测的用户性别信息
|
||||
*/
|
||||
MALE(1, "男"),
|
||||
FEMALE(0, "女"),
|
||||
UNKNOWN(-1, "未知");
|
||||
MALE("1", "男"),
|
||||
FEMALE("0", "女"),
|
||||
UNKNOWN("-1", "未知");
|
||||
|
||||
private int code;
|
||||
private String code;
|
||||
private String desc;
|
||||
|
||||
public static AuthUserGender getRealGender(String code) {
|
||||
if (code == null) {
|
||||
/**
|
||||
* 获取用户的实际性别,常规网站
|
||||
*
|
||||
* @param originalGender 用户第三方标注的原始性别
|
||||
* @return 用户性别
|
||||
*/
|
||||
public static AuthUserGender getRealGender(String originalGender) {
|
||||
if (null == originalGender || UNKNOWN.getCode().equals(originalGender)) {
|
||||
return UNKNOWN;
|
||||
}
|
||||
String[] males = {"m", "男", "1", "male"};
|
||||
if (Arrays.asList(males).contains(code.toLowerCase())) {
|
||||
if (Arrays.asList(males).contains(originalGender.toLowerCase())) {
|
||||
return MALE;
|
||||
}
|
||||
String[] females = {"f", "女", "0", "female"};
|
||||
if (Arrays.asList(females).contains(code.toLowerCase())) {
|
||||
return FEMALE;
|
||||
return FEMALE;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取微信平台用户的实际性别,0表示未定义,1表示男性,2表示女性
|
||||
*
|
||||
* @param originalGender 用户第三方标注的原始性别
|
||||
* @return 用户性别
|
||||
* @since 1.13.2
|
||||
*/
|
||||
public static AuthUserGender getWechatRealGender(String originalGender) {
|
||||
if (StringUtils.isEmpty(originalGender) || "0".equals(originalGender)) {
|
||||
return AuthUserGender.UNKNOWN;
|
||||
}
|
||||
return UNKNOWN;
|
||||
return getRealGender(originalGender);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* 提供一些必要的枚举类
|
||||
*/
|
||||
package me.zhyd.oauth.enums;
|
||||
@@ -1,5 +1,6 @@
|
||||
package me.zhyd.oauth.exception;
|
||||
|
||||
import me.zhyd.oauth.config.AuthSource;
|
||||
import me.zhyd.oauth.enums.AuthResponseStatus;
|
||||
|
||||
/**
|
||||
@@ -17,6 +18,10 @@ public class AuthException extends RuntimeException {
|
||||
this(AuthResponseStatus.FAILURE.getCode(), errorMsg);
|
||||
}
|
||||
|
||||
public AuthException(String errorMsg, AuthSource source) {
|
||||
this(AuthResponseStatus.FAILURE.getCode(), errorMsg, source);
|
||||
}
|
||||
|
||||
public AuthException(int errorCode, String errorMsg) {
|
||||
super(errorMsg);
|
||||
this.errorCode = errorCode;
|
||||
@@ -24,7 +29,15 @@ public class AuthException extends RuntimeException {
|
||||
}
|
||||
|
||||
public AuthException(AuthResponseStatus status) {
|
||||
super(status.getMsg());
|
||||
this(status.getCode(), status.getMsg());
|
||||
}
|
||||
|
||||
public AuthException(int errorCode, String errorMsg, AuthSource source) {
|
||||
this(errorCode, String.format("%s [%s]", errorMsg, source.getName()));
|
||||
}
|
||||
|
||||
public AuthException(AuthResponseStatus status, AuthSource source) {
|
||||
this(status.getCode(), status.getMsg(), source);
|
||||
}
|
||||
|
||||
public AuthException(String message, Throwable cause) {
|
||||
|
||||
@@ -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;
|
||||
@@ -1,8 +1,13 @@
|
||||
package me.zhyd.oauth.model;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 授权回调时的参数类
|
||||
*
|
||||
@@ -11,7 +16,10 @@ import lombok.Setter;
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public class AuthCallback {
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class AuthCallback implements Serializable {
|
||||
|
||||
/**
|
||||
* 访问AuthorizeUrl后回调时带的参数code
|
||||
@@ -27,4 +35,26 @@ public class AuthCallback {
|
||||
* 访问AuthorizeUrl后回调时带的参数state,用于和请求AuthorizeUrl前的state比较,防止CSRF攻击
|
||||
*/
|
||||
private String state;
|
||||
|
||||
/**
|
||||
* 华为授权登录接受code的参数名
|
||||
*
|
||||
* @since 1.10.0
|
||||
*/
|
||||
private String authorization_code;
|
||||
|
||||
/**
|
||||
* Twitter回调后返回的oauth_token
|
||||
*
|
||||
* @since 1.13.0
|
||||
*/
|
||||
private String oauth_token;
|
||||
|
||||
/**
|
||||
* Twitter回调后返回的oauth_verifier
|
||||
*
|
||||
* @since 1.13.0
|
||||
*/
|
||||
private String oauth_verifier;
|
||||
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
package me.zhyd.oauth.model;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.*;
|
||||
import me.zhyd.oauth.enums.AuthResponseStatus;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* JustAuth统一授权响应类
|
||||
*
|
||||
@@ -11,8 +12,11 @@ import me.zhyd.oauth.enums.AuthResponseStatus;
|
||||
* @since 1.8
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@Builder
|
||||
public class AuthResponse<T> {
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class AuthResponse<T> implements Serializable {
|
||||
/**
|
||||
* 授权响应状态码
|
||||
*/
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
package me.zhyd.oauth.model;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.*;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 授权所需的token
|
||||
@@ -14,7 +13,9 @@ import lombok.Setter;
|
||||
@Getter
|
||||
@Setter
|
||||
@Builder
|
||||
public class AuthToken {
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class AuthToken implements Serializable {
|
||||
private String accessToken;
|
||||
private int expireIn;
|
||||
private String refreshToken;
|
||||
@@ -36,4 +37,22 @@ public class AuthToken {
|
||||
private String macAlgorithm;
|
||||
private String macKey;
|
||||
|
||||
/**
|
||||
* 企业微信附带属性
|
||||
*
|
||||
* @since 1.10.0
|
||||
*/
|
||||
private String code;
|
||||
|
||||
/**
|
||||
* Twitter附带属性
|
||||
*
|
||||
* @since 1.13.0
|
||||
*/
|
||||
private String oauthToken;
|
||||
private String oauthTokenSecret;
|
||||
private String userId;
|
||||
private String screenName;
|
||||
private Boolean oauthCallbackConfirmed;
|
||||
|
||||
}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
package me.zhyd.oauth.model;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import me.zhyd.oauth.config.AuthSource;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import lombok.*;
|
||||
import me.zhyd.oauth.enums.AuthUserGender;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 授权成功后的用户信息,根据授权平台的不同,获取的数据完整性也不同
|
||||
@@ -15,9 +14,11 @@ import me.zhyd.oauth.enums.AuthUserGender;
|
||||
@Getter
|
||||
@Setter
|
||||
@Builder
|
||||
public class AuthUser {
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class AuthUser implements Serializable {
|
||||
/**
|
||||
* 用户第三方系统的唯一id。在调用方集成改组件时,可以用uuid + source唯一确定一个用户
|
||||
* 用户第三方系统的唯一id。在调用方集成该组件时,可以用uuid + source唯一确定一个用户
|
||||
*
|
||||
* @since 1.3.3
|
||||
*/
|
||||
@@ -61,9 +62,14 @@ public class AuthUser {
|
||||
/**
|
||||
* 用户来源
|
||||
*/
|
||||
private AuthSource source;
|
||||
private String source;
|
||||
/**
|
||||
* 用户授权的token信息
|
||||
*/
|
||||
private AuthToken token;
|
||||
/**
|
||||
* 第三方平台返回的原始用户信息
|
||||
*/
|
||||
private JSONObject rawUserInfo;
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -1,5 +1,6 @@
|
||||
package me.zhyd.oauth.request;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.alipay.api.AlipayApiException;
|
||||
import com.alipay.api.AlipayClient;
|
||||
import com.alipay.api.DefaultAlipayClient;
|
||||
@@ -7,11 +8,14 @@ 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.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.StringUtils;
|
||||
@@ -28,8 +32,14 @@ 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");
|
||||
}
|
||||
|
||||
@@ -55,6 +65,37 @@ public class AuthAlipayRequest extends AuthDefaultRequest {
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新access token (续期)
|
||||
*
|
||||
* @param authToken 登录成功后返回的Token信息
|
||||
* @return AuthResponse
|
||||
*/
|
||||
@Override
|
||||
public AuthResponse refresh(AuthToken authToken) {
|
||||
AlipaySystemOauthTokenRequest request = new AlipaySystemOauthTokenRequest();
|
||||
request.setGrantType("refresh_token");
|
||||
request.setRefreshToken(authToken.getRefreshToken());
|
||||
AlipaySystemOauthTokenResponse response = null;
|
||||
try {
|
||||
response = this.alipayClient.execute(request);
|
||||
} catch (Exception e) {
|
||||
throw new AuthException(e);
|
||||
}
|
||||
if (!response.isSuccess()) {
|
||||
throw new AuthException(response.getSubMsg());
|
||||
}
|
||||
return AuthResponse.builder()
|
||||
.code(AuthResponseStatus.SUCCESS.getCode())
|
||||
.data(AuthToken.builder()
|
||||
.accessToken(response.getAccessToken())
|
||||
.uid(response.getUserId())
|
||||
.expireIn(Integer.parseInt(response.getExpiresIn()))
|
||||
.refreshToken(response.getRefreshToken())
|
||||
.build())
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AuthUser getUserInfo(AuthToken authToken) {
|
||||
String accessToken = authToken.getAccessToken();
|
||||
@@ -73,6 +114,7 @@ public class AuthAlipayRequest extends AuthDefaultRequest {
|
||||
String location = String.format("%s %s", StringUtils.isEmpty(province) ? "" : province, StringUtils.isEmpty(city) ? "" : city);
|
||||
|
||||
return AuthUser.builder()
|
||||
.rawUserInfo(JSONObject.parseObject(JSONObject.toJSONString(response)))
|
||||
.uuid(response.getUserId())
|
||||
.username(StringUtils.isEmpty(response.getUserName()) ? response.getNickName() : response.getUserName())
|
||||
.nickname(response.getNickName())
|
||||
@@ -80,7 +122,7 @@ public class AuthAlipayRequest extends AuthDefaultRequest {
|
||||
.location(location)
|
||||
.gender(AuthUserGender.getRealGender(response.getGender()))
|
||||
.token(authToken)
|
||||
.source(source)
|
||||
.source(source.toString())
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
package me.zhyd.oauth.request;
|
||||
|
||||
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.model.AuthCallback;
|
||||
import me.zhyd.oauth.model.AuthToken;
|
||||
import me.zhyd.oauth.model.AuthUser;
|
||||
import me.zhyd.oauth.utils.UrlBuilder;
|
||||
|
||||
/**
|
||||
* 阿里云登录
|
||||
*
|
||||
* @author snippet0809 (https://github.com/snippet0809)
|
||||
* @since 1.15.5
|
||||
*/
|
||||
public class AuthAliyunRequest extends AuthDefaultRequest {
|
||||
|
||||
public AuthAliyunRequest(AuthConfig config) {
|
||||
super(config, AuthDefaultSource.ALIYUN);
|
||||
}
|
||||
|
||||
public AuthAliyunRequest(AuthConfig config, AuthStateCache authStateCache) {
|
||||
super(config, AuthDefaultSource.ALIYUN, authStateCache);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AuthToken getAccessToken(AuthCallback authCallback) {
|
||||
String response = doPostAuthorizationCode(authCallback.getCode());
|
||||
JSONObject accessTokenObject = JSONObject.parseObject(response);
|
||||
return AuthToken.builder()
|
||||
.accessToken(accessTokenObject.getString("access_token"))
|
||||
.expireIn(accessTokenObject.getIntValue("expires_in"))
|
||||
.tokenType(accessTokenObject.getString("token_type"))
|
||||
.idToken(accessTokenObject.getString("id_token"))
|
||||
.refreshToken(accessTokenObject.getString("refresh_token"))
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AuthUser getUserInfo(AuthToken authToken) {
|
||||
String userInfo = doGetUserInfo(authToken);
|
||||
JSONObject object = JSONObject.parseObject(userInfo);
|
||||
return AuthUser.builder()
|
||||
.rawUserInfo(object)
|
||||
.uuid(object.getString("sub"))
|
||||
.username(object.getString("login_name"))
|
||||
.nickname(object.getString("name"))
|
||||
.gender(AuthUserGender.UNKNOWN)
|
||||
.token(authToken)
|
||||
.source(source.toString())
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,14 +1,17 @@
|
||||
package me.zhyd.oauth.request;
|
||||
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import me.zhyd.oauth.utils.HttpUtils;
|
||||
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;
|
||||
|
||||
@@ -21,30 +24,41 @@ import me.zhyd.oauth.utils.UrlBuilder;
|
||||
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
|
||||
protected AuthToken getAccessToken(AuthCallback authCallback) {
|
||||
HttpResponse response = doPostAuthorizationCode(authCallback.getCode());
|
||||
String response = doPostAuthorizationCode(authCallback.getCode());
|
||||
return getAuthToken(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* https://openapi.baidu.com/rest/2.0/passport/users/getInfo?access_token=121.c86e87cc0828cc1dabb8faee540531d4.YsUIAWvYbgqVni1VhkgKgyLh8nEyELbDOEZs_OA.OgDgmA
|
||||
* https://openapi.baidu.com/rest/2.0/passport/users/getInfo?access_token=121.2907d9facf9fb97adf7287fa75496eda.Y3NSjR3-3HKt1RgT0HEl7GgxRXT5gOOVdngXezY.OcC_7g
|
||||
* 新旧应用返回的用户信息不一致
|
||||
* @param authToken token信息
|
||||
* @return AuthUser
|
||||
*/
|
||||
@Override
|
||||
protected AuthUser getUserInfo(AuthToken authToken) {
|
||||
HttpResponse response = doGetUserInfo(authToken);
|
||||
String userInfo = response.body();
|
||||
String userInfo = doGetUserInfo(authToken);
|
||||
JSONObject object = JSONObject.parseObject(userInfo);
|
||||
this.checkResponse(object);
|
||||
return AuthUser.builder()
|
||||
.uuid(object.getString("userid"))
|
||||
.rawUserInfo(object)
|
||||
.uuid(object.containsKey("userid") ? object.getString("userid") : object.getString("openid"))
|
||||
.username(object.getString("username"))
|
||||
.nickname(object.getString("username"))
|
||||
.avatar(getAvatar(object))
|
||||
.remark(object.getString("userdetail"))
|
||||
.gender(AuthUserGender.getRealGender(object.getString("sex")))
|
||||
.token(authToken)
|
||||
.source(source)
|
||||
.source(source.toString())
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -55,8 +69,8 @@ public class AuthBaiduRequest extends AuthDefaultRequest {
|
||||
|
||||
@Override
|
||||
public AuthResponse revoke(AuthToken authToken) {
|
||||
HttpResponse response = doGetRevoke(authToken);
|
||||
JSONObject object = JSONObject.parseObject(response.body());
|
||||
String response = doGetRevoke(authToken);
|
||||
JSONObject object = JSONObject.parseObject(response);
|
||||
this.checkResponse(object);
|
||||
// 返回1表示取消授权成功,否则失败
|
||||
AuthResponseStatus status = object.getIntValue("result") == 1 ? AuthResponseStatus.SUCCESS : AuthResponseStatus.FAILURE;
|
||||
@@ -71,7 +85,7 @@ public class AuthBaiduRequest extends AuthDefaultRequest {
|
||||
.queryParam("client_id", this.config.getClientId())
|
||||
.queryParam("client_secret", this.config.getClientSecret())
|
||||
.build();
|
||||
HttpResponse response = HttpRequest.get(refreshUrl).execute();
|
||||
String response = new HttpUtils(config.getHttpConfig()).get(refreshUrl);
|
||||
return AuthResponse.builder()
|
||||
.code(AuthResponseStatus.SUCCESS.getCode())
|
||||
.data(this.getAuthToken(response))
|
||||
@@ -108,8 +122,8 @@ public class AuthBaiduRequest extends AuthDefaultRequest {
|
||||
}
|
||||
}
|
||||
|
||||
private AuthToken getAuthToken(HttpResponse response) {
|
||||
JSONObject accessTokenObject = JSONObject.parseObject(response.body());
|
||||
private AuthToken getAuthToken(String response) {
|
||||
JSONObject accessTokenObject = JSONObject.parseObject(response);
|
||||
this.checkResponse(accessTokenObject);
|
||||
return AuthToken.builder()
|
||||
.accessToken(accessTokenObject.getString("access_token"))
|
||||
|
||||
@@ -1,9 +1,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;
|
||||
@@ -20,13 +20,17 @@ import me.zhyd.oauth.utils.UrlBuilder;
|
||||
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
|
||||
protected AuthToken getAccessToken(AuthCallback authCallback) {
|
||||
HttpResponse response = doGetAuthorizationCode(authCallback.getCode());
|
||||
JSONObject accessTokenObject = JSONObject.parseObject(response.body());
|
||||
String response = doGetAuthorizationCode(authCallback.getCode());
|
||||
JSONObject accessTokenObject = JSONObject.parseObject(response);
|
||||
this.checkResponse(accessTokenObject);
|
||||
return AuthToken.builder()
|
||||
.accessToken(accessTokenObject.getString("access_token"))
|
||||
@@ -37,16 +41,17 @@ public class AuthCodingRequest extends AuthDefaultRequest {
|
||||
|
||||
@Override
|
||||
protected AuthUser getUserInfo(AuthToken authToken) {
|
||||
HttpResponse response = doGetUserInfo(authToken);
|
||||
JSONObject object = JSONObject.parseObject(response.body());
|
||||
String response = doGetUserInfo(authToken);
|
||||
JSONObject object = JSONObject.parseObject(response);
|
||||
this.checkResponse(object);
|
||||
|
||||
object = object.getJSONObject("data");
|
||||
return AuthUser.builder()
|
||||
.rawUserInfo(object)
|
||||
.uuid(object.getString("id"))
|
||||
.username(object.getString("name"))
|
||||
.avatar("https://coding.net/" + object.getString("avatar"))
|
||||
.blog("https://coding.net/" + object.getString("path"))
|
||||
.avatar("https://coding.net" + object.getString("avatar"))
|
||||
.blog("https://coding.net" + object.getString("path"))
|
||||
.nickname(object.getString("name"))
|
||||
.company(object.getString("company"))
|
||||
.location(object.getString("location"))
|
||||
@@ -54,7 +59,7 @@ public class AuthCodingRequest extends AuthDefaultRequest {
|
||||
.email(object.getString("email"))
|
||||
.remark(object.getString("slogan"))
|
||||
.token(authToken)
|
||||
.source(source)
|
||||
.source(source.toString())
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -78,7 +83,7 @@ public class AuthCodingRequest extends AuthDefaultRequest {
|
||||
*/
|
||||
@Override
|
||||
public String authorize(String state) {
|
||||
return UrlBuilder.fromBaseUrl(source.authorize())
|
||||
return UrlBuilder.fromBaseUrl(String.format(source.authorize(), config.getCodingGroupName()))
|
||||
.queryParam("response_type", "code")
|
||||
.queryParam("client_id", config.getClientId())
|
||||
.queryParam("redirect_uri", config.getRedirectUri())
|
||||
@@ -86,4 +91,33 @@ public class AuthCodingRequest extends AuthDefaultRequest {
|
||||
.queryParam("state", getRealState(state))
|
||||
.build();
|
||||
}
|
||||
/**
|
||||
* 返回获取accessToken的url
|
||||
*
|
||||
* @param code 授权码
|
||||
* @return 返回获取accessToken的url
|
||||
*/
|
||||
@Override
|
||||
public String accessTokenUrl(String code) {
|
||||
return UrlBuilder.fromBaseUrl(String.format(source.accessToken(), config.getCodingGroupName()))
|
||||
.queryParam("code", code)
|
||||
.queryParam("client_id", config.getClientId())
|
||||
.queryParam("client_secret", config.getClientSecret())
|
||||
.queryParam("grant_type", "authorization_code")
|
||||
.queryParam("redirect_uri", config.getRedirectUri())
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回获取userInfo的url
|
||||
*
|
||||
* @param authToken token
|
||||
* @return 返回获取userInfo的url
|
||||
*/
|
||||
@Override
|
||||
public String userInfoUrl(AuthToken authToken) {
|
||||
return UrlBuilder.fromBaseUrl(String.format(source.userInfo(), config.getCodingGroupName()))
|
||||
.queryParam("access_token", authToken.getAccessToken())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,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;
|
||||
@@ -20,30 +20,35 @@ import me.zhyd.oauth.model.AuthUser;
|
||||
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
|
||||
protected AuthToken getAccessToken(AuthCallback authCallback) {
|
||||
HttpResponse response = doPostAuthorizationCode(authCallback.getCode());
|
||||
JSONObject accessTokenObject = JSONObject.parseObject(response.body());
|
||||
String response = doPostAuthorizationCode(authCallback.getCode());
|
||||
JSONObject accessTokenObject = JSONObject.parseObject(response);
|
||||
this.checkResponse(accessTokenObject);
|
||||
return AuthToken.builder().accessToken(accessTokenObject.getString("access_token")).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AuthUser getUserInfo(AuthToken authToken) {
|
||||
HttpResponse response = doGetUserInfo(authToken);
|
||||
JSONObject object = JSONObject.parseObject(response.body());
|
||||
String response = doGetUserInfo(authToken);
|
||||
JSONObject object = JSONObject.parseObject(response);
|
||||
this.checkResponse(object);
|
||||
return AuthUser.builder()
|
||||
.rawUserInfo(object)
|
||||
.uuid(object.getString("username"))
|
||||
.username(object.getString("username"))
|
||||
.remark(object.getString("description"))
|
||||
.blog(object.getString("website"))
|
||||
.gender(AuthUserGender.UNKNOWN)
|
||||
.token(authToken)
|
||||
.source(source)
|
||||
.source(source.toString())
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
package me.zhyd.oauth.request;
|
||||
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import me.zhyd.oauth.utils.HttpUtils;
|
||||
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.log.Log;
|
||||
import me.zhyd.oauth.model.AuthCallback;
|
||||
import me.zhyd.oauth.model.AuthResponse;
|
||||
import me.zhyd.oauth.model.AuthToken;
|
||||
@@ -24,16 +24,21 @@ import me.zhyd.oauth.utils.UuidUtils;
|
||||
* @author yangkai.shen (https://xkcoding.com)
|
||||
* @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);
|
||||
throw new AuthException(AuthResponseStatus.PARAMETER_INCOMPLETE, source);
|
||||
}
|
||||
// 校验配置合法性
|
||||
AuthChecker.checkConfig(config, source);
|
||||
@@ -68,14 +73,16 @@ public abstract class AuthDefaultRequest implements AuthRequest {
|
||||
@Override
|
||||
public AuthResponse login(AuthCallback authCallback) {
|
||||
try {
|
||||
AuthChecker.checkCode(source == AuthSource.ALIPAY ? authCallback.getAuth_code() : authCallback.getCode());
|
||||
AuthChecker.checkState(authCallback.getState());
|
||||
AuthChecker.checkCode(source, authCallback);
|
||||
if (!config.isIgnoreCheckState()) {
|
||||
AuthChecker.checkState(authCallback.getState(), source, authStateCache);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -88,10 +95,15 @@ public abstract class AuthDefaultRequest implements AuthRequest {
|
||||
*/
|
||||
private AuthResponse responseError(Exception e) {
|
||||
int errorCode = AuthResponseStatus.FAILURE.getCode();
|
||||
String errorMsg = e.getMessage();
|
||||
if (e instanceof AuthException) {
|
||||
errorCode = ((AuthException) e).getErrorCode();
|
||||
AuthException authException = ((AuthException) e);
|
||||
errorCode = authException.getErrorCode();
|
||||
if (StringUtils.isNotEmpty(authException.getErrorMsg())) {
|
||||
errorMsg = authException.getErrorMsg();
|
||||
}
|
||||
}
|
||||
return AuthResponse.builder().code(errorCode).msg(e.getMessage()).build();
|
||||
return AuthResponse.builder().code(errorCode).msg(errorMsg).build();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -189,7 +201,7 @@ public abstract class AuthDefaultRequest implements AuthRequest {
|
||||
state = UuidUtils.getUUID();
|
||||
}
|
||||
// 缓存state
|
||||
AuthStateCache.cache(state, state);
|
||||
authStateCache.cache(state, state);
|
||||
return state;
|
||||
}
|
||||
|
||||
@@ -197,61 +209,62 @@ public abstract class AuthDefaultRequest implements AuthRequest {
|
||||
* 通用的 authorizationCode 协议
|
||||
*
|
||||
* @param code code码
|
||||
* @return HttpResponse
|
||||
* @return Response
|
||||
*/
|
||||
protected HttpResponse doPostAuthorizationCode(String code) {
|
||||
return HttpRequest.post(accessTokenUrl(code)).execute();
|
||||
protected String doPostAuthorizationCode(String code) {
|
||||
return new HttpUtils(config.getHttpConfig()).post(accessTokenUrl(code));
|
||||
}
|
||||
|
||||
/**
|
||||
* 通用的 authorizationCode 协议
|
||||
*
|
||||
* @param code code码
|
||||
* @return HttpResponse
|
||||
* @return Response
|
||||
*/
|
||||
protected HttpResponse doGetAuthorizationCode(String code) {
|
||||
return HttpRequest.get(accessTokenUrl(code)).execute();
|
||||
protected String doGetAuthorizationCode(String code) {
|
||||
return new HttpUtils(config.getHttpConfig()).get(accessTokenUrl(code));
|
||||
}
|
||||
|
||||
/**
|
||||
* 通用的 用户信息
|
||||
*
|
||||
* @param authToken token封装
|
||||
* @return HttpResponse
|
||||
* @return Response
|
||||
*/
|
||||
@Deprecated
|
||||
protected HttpResponse doPostUserInfo(AuthToken authToken) {
|
||||
return HttpRequest.post(userInfoUrl(authToken)).execute();
|
||||
protected String doPostUserInfo(AuthToken authToken) {
|
||||
return new HttpUtils(config.getHttpConfig()).post(userInfoUrl(authToken));
|
||||
}
|
||||
|
||||
/**
|
||||
* 通用的 用户信息
|
||||
*
|
||||
* @param authToken token封装
|
||||
* @return HttpResponse
|
||||
* @return Response
|
||||
*/
|
||||
protected HttpResponse doGetUserInfo(AuthToken authToken) {
|
||||
return HttpRequest.get(userInfoUrl(authToken)).execute();
|
||||
protected String doGetUserInfo(AuthToken authToken) {
|
||||
return new HttpUtils(config.getHttpConfig()).get(userInfoUrl(authToken));
|
||||
}
|
||||
|
||||
/**
|
||||
* 通用的post形式的取消授权方法
|
||||
*
|
||||
* @param authToken token封装
|
||||
* @return HttpResponse
|
||||
* @return Response
|
||||
*/
|
||||
@Deprecated
|
||||
protected HttpResponse doPostRevoke(AuthToken authToken) {
|
||||
return HttpRequest.post(revokeUrl(authToken)).execute();
|
||||
protected String doPostRevoke(AuthToken authToken) {
|
||||
return new HttpUtils(config.getHttpConfig()).post(revokeUrl(authToken));
|
||||
}
|
||||
|
||||
/**
|
||||
* 通用的post形式的取消授权方法
|
||||
*
|
||||
* @param authToken token封装
|
||||
* @return HttpResponse
|
||||
* @return Response
|
||||
*/
|
||||
protected HttpResponse doGetRevoke(AuthToken authToken) {
|
||||
return HttpRequest.get(revokeUrl(authToken)).execute();
|
||||
protected String doGetRevoke(AuthToken authToken) {
|
||||
return new HttpUtils(config.getHttpConfig()).get(revokeUrl(authToken));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
package me.zhyd.oauth.request;
|
||||
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import me.zhyd.oauth.utils.HttpUtils;
|
||||
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.utils.GlobalAuthUtil;
|
||||
import me.zhyd.oauth.utils.GlobalAuthUtils;
|
||||
import me.zhyd.oauth.utils.UrlBuilder;
|
||||
|
||||
/**
|
||||
@@ -23,7 +23,11 @@ import me.zhyd.oauth.utils.UrlBuilder;
|
||||
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
|
||||
@@ -36,8 +40,8 @@ public class AuthDingTalkRequest extends AuthDefaultRequest {
|
||||
String code = authToken.getAccessCode();
|
||||
JSONObject param = new JSONObject();
|
||||
param.put("tmp_auth_code", code);
|
||||
HttpResponse response = HttpRequest.post(userInfoUrl(authToken)).body(param.toJSONString()).execute();
|
||||
JSONObject object = JSON.parseObject(response.body());
|
||||
String response = new HttpUtils(config.getHttpConfig()).post(userInfoUrl(authToken), param.toJSONString());
|
||||
JSONObject object = JSON.parseObject(response);
|
||||
if (object.getIntValue("errcode") != 0) {
|
||||
throw new AuthException(object.getString("errmsg"));
|
||||
}
|
||||
@@ -47,11 +51,12 @@ public class AuthDingTalkRequest extends AuthDefaultRequest {
|
||||
.unionId(object.getString("unionid"))
|
||||
.build();
|
||||
return AuthUser.builder()
|
||||
.rawUserInfo(object)
|
||||
.uuid(object.getString("unionid"))
|
||||
.nickname(object.getString("nick"))
|
||||
.username(object.getString("nick"))
|
||||
.gender(AuthUserGender.UNKNOWN)
|
||||
.source(source)
|
||||
.source(source.toString())
|
||||
.token(token)
|
||||
.build();
|
||||
}
|
||||
@@ -84,7 +89,7 @@ public class AuthDingTalkRequest extends AuthDefaultRequest {
|
||||
protected String userInfoUrl(AuthToken authToken) {
|
||||
// 根据timestamp, appSecret计算签名值
|
||||
String timestamp = System.currentTimeMillis() + "";
|
||||
String urlEncodeSignature = GlobalAuthUtil.generateDingTalkSignature(config.getClientSecret(), timestamp);
|
||||
String urlEncodeSignature = GlobalAuthUtils.generateDingTalkSignature(config.getClientSecret(), timestamp);
|
||||
|
||||
return UrlBuilder.fromBaseUrl(source.userInfo())
|
||||
.queryParam("signature", urlEncodeSignature)
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
package me.zhyd.oauth.request;
|
||||
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import me.zhyd.oauth.utils.HttpUtils;
|
||||
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;
|
||||
|
||||
|
||||
@@ -21,7 +24,11 @@ import me.zhyd.oauth.utils.UrlBuilder;
|
||||
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
|
||||
@@ -31,18 +38,21 @@ public class AuthDouyinRequest extends AuthDefaultRequest {
|
||||
|
||||
@Override
|
||||
protected AuthUser getUserInfo(AuthToken authToken) {
|
||||
HttpResponse response = doGetUserInfo(authToken);
|
||||
JSONObject userInfoObject = JSONObject.parseObject(response.body());
|
||||
String response = doGetUserInfo(authToken);
|
||||
JSONObject userInfoObject = JSONObject.parseObject(response);
|
||||
this.checkResponse(userInfoObject);
|
||||
JSONObject object = userInfoObject.getJSONObject("data");
|
||||
return AuthUser.builder()
|
||||
.uuid(userInfoObject.getString("union_id"))
|
||||
.username(userInfoObject.getString("nickname"))
|
||||
.nickname(userInfoObject.getString("nickname"))
|
||||
.avatar(userInfoObject.getString("avatar"))
|
||||
.remark(userInfoObject.getString("description"))
|
||||
.gender(AuthUserGender.UNKNOWN)
|
||||
.rawUserInfo(object)
|
||||
.uuid(object.getString("union_id"))
|
||||
.username(object.getString("nickname"))
|
||||
.nickname(object.getString("nickname"))
|
||||
.avatar(object.getString("avatar"))
|
||||
.remark(object.getString("description"))
|
||||
.gender(AuthUserGender.getRealGender(object.getString("gender")))
|
||||
.location(String.format("%s %s %s", object.getString("country"), object.getString("province"), object.getString("city")))
|
||||
.token(authToken)
|
||||
.source(source)
|
||||
.source(source.toString())
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -75,16 +85,16 @@ public class AuthDouyinRequest extends AuthDefaultRequest {
|
||||
* @return token对象
|
||||
*/
|
||||
private AuthToken getToken(String accessTokenUrl) {
|
||||
HttpResponse response = HttpRequest.post(accessTokenUrl).execute();
|
||||
String accessTokenStr = response.body();
|
||||
JSONObject object = JSONObject.parseObject(accessTokenStr);
|
||||
String response = new HttpUtils(config.getHttpConfig()).post(accessTokenUrl);
|
||||
JSONObject object = JSONObject.parseObject(response);
|
||||
this.checkResponse(object);
|
||||
JSONObject dataObj = object.getJSONObject("data");
|
||||
return AuthToken.builder()
|
||||
.accessToken(object.getString("access_token"))
|
||||
.openId(object.getString("open_id"))
|
||||
.expireIn(object.getIntValue("expires_in"))
|
||||
.refreshToken(object.getString("refresh_token"))
|
||||
.scope(object.getString("scope"))
|
||||
.accessToken(dataObj.getString("access_token"))
|
||||
.openId(dataObj.getString("open_id"))
|
||||
.expireIn(dataObj.getIntValue("expires_in"))
|
||||
.refreshToken(dataObj.getString("refresh_token"))
|
||||
.scope(dataObj.getString("scope"))
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,178 @@
|
||||
package me.zhyd.oauth.request;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import me.zhyd.oauth.utils.HttpUtils;
|
||||
import com.xkcoding.http.constants.Constants;
|
||||
import com.xkcoding.http.support.HttpHeader;
|
||||
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.Base64Utils;
|
||||
import me.zhyd.oauth.utils.GlobalAuthUtils;
|
||||
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 {
|
||||
|
||||
private static final String CONTENT_TYPE_FORM = "application/x-www-form-urlencoded;charset=UTF-8";
|
||||
private static final String CONTENT_TYPE_JSON = "application/json; charset=utf-8";
|
||||
|
||||
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) {
|
||||
Map<String, String> form = new HashMap<>(4);
|
||||
form.put("client_id", config.getClientId());
|
||||
form.put("redirect_uri", config.getRedirectUri());
|
||||
form.put("code", authCallback.getCode());
|
||||
form.put("grant_type", "authorization_code");
|
||||
|
||||
HttpHeader httpHeader = this.buildHeader(CONTENT_TYPE_FORM, this.getRequestId(), true);
|
||||
String response = new HttpUtils(config.getHttpConfig()).post(source.accessToken(), form, httpHeader, false);
|
||||
JSONObject object = JSONObject.parseObject(response);
|
||||
|
||||
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 = GlobalAuthUtils.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);
|
||||
|
||||
HttpHeader httpHeader = this.buildHeader(CONTENT_TYPE_JSON, requestId, false);
|
||||
String response = new HttpUtils(config.getHttpConfig()).post(source.userInfo(), JSONObject.toJSONString(paramsMap), httpHeader);
|
||||
|
||||
JSONObject object = JSONObject.parseObject(response);
|
||||
|
||||
// 校验请求
|
||||
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()
|
||||
.rawUserInfo(result)
|
||||
.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) {
|
||||
Map<String, String> form = new HashMap<>(2);
|
||||
form.put("refresh_token", oldToken.getRefreshToken());
|
||||
form.put("grant_type", "refresh_token");
|
||||
|
||||
HttpHeader httpHeader = this.buildHeader(CONTENT_TYPE_FORM, this.getRequestId(), true);
|
||||
String response = new HttpUtils(config.getHttpConfig()).post(source.refresh(), form, httpHeader, false);
|
||||
|
||||
JSONObject object = JSONObject.parseObject(response);
|
||||
|
||||
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 = Base64Utils.encode((appKey + ":" + appSecret).getBytes());
|
||||
sb.append("Basic").append(" ").append(encodeToString);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private HttpHeader buildHeader(String contentType, String requestId, boolean auth) {
|
||||
HttpHeader httpHeader = new HttpHeader();
|
||||
httpHeader.add("Accept", "text/xml,text/javascript,text/html");
|
||||
httpHeader.add(Constants.CONTENT_TYPE, contentType);
|
||||
httpHeader.add("Accept-Encoding", "gzip");
|
||||
httpHeader.add("User-Agent", "eleme-openapi-java-sdk");
|
||||
httpHeader.add("x-eleme-requestid", requestId);
|
||||
if (auth) {
|
||||
httpHeader.add("Authorization", this.getBasic(config.getClientId(), config.getClientSecret()));
|
||||
}
|
||||
return httpHeader;
|
||||
}
|
||||
|
||||
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"));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,9 +1,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;
|
||||
@@ -20,13 +20,17 @@ import me.zhyd.oauth.utils.UrlBuilder;
|
||||
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
|
||||
protected AuthToken getAccessToken(AuthCallback authCallback) {
|
||||
HttpResponse response = doPostAuthorizationCode(authCallback.getCode());
|
||||
JSONObject accessTokenObject = JSONObject.parseObject(response.body());
|
||||
String response = doPostAuthorizationCode(authCallback.getCode());
|
||||
JSONObject accessTokenObject = JSONObject.parseObject(response);
|
||||
this.checkResponse(accessTokenObject);
|
||||
return AuthToken.builder()
|
||||
.accessToken(accessTokenObject.getString("access_token"))
|
||||
@@ -37,20 +41,21 @@ public class AuthFacebookRequest extends AuthDefaultRequest {
|
||||
|
||||
@Override
|
||||
protected AuthUser getUserInfo(AuthToken authToken) {
|
||||
HttpResponse response = doGetUserInfo(authToken);
|
||||
String userInfo = response.body();
|
||||
String userInfo = doGetUserInfo(authToken);
|
||||
JSONObject object = JSONObject.parseObject(userInfo);
|
||||
this.checkResponse(object);
|
||||
return AuthUser.builder()
|
||||
.rawUserInfo(object)
|
||||
.uuid(object.getString("id"))
|
||||
.username(object.getString("name"))
|
||||
.nickname(object.getString("name"))
|
||||
.blog(object.getString("link"))
|
||||
.avatar(getUserPicture(object))
|
||||
.location(object.getString("locale"))
|
||||
.email(object.getString("email"))
|
||||
.gender(AuthUserGender.getRealGender(object.getString("gender")))
|
||||
.token(authToken)
|
||||
.source(source)
|
||||
.source(source.toString())
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -76,7 +81,7 @@ public class AuthFacebookRequest extends AuthDefaultRequest {
|
||||
protected String userInfoUrl(AuthToken authToken) {
|
||||
return UrlBuilder.fromBaseUrl(source.userInfo())
|
||||
.queryParam("access_token", authToken.getAccessToken())
|
||||
.queryParam("fields", "id,name,birthday,gender,hometown,email,devices,picture.width(400)")
|
||||
.queryParam("fields", "id,name,birthday,gender,hometown,email,devices,picture.width(400),link")
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
package me.zhyd.oauth.request;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.xkcoding.http.support.HttpHeader;
|
||||
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.GlobalAuthUtils;
|
||||
import me.zhyd.oauth.utils.HttpUtils;
|
||||
import me.zhyd.oauth.utils.UrlBuilder;
|
||||
|
||||
/**
|
||||
* 注意:该平台暂时存在问题,请不要使用。待修复完成后会重新发版by yadong.zhang
|
||||
*
|
||||
* @author beacon
|
||||
* @since 1.14.0
|
||||
*/
|
||||
@Deprecated
|
||||
public class AuthFeishuRequest extends AuthDefaultRequest {
|
||||
|
||||
public AuthFeishuRequest(AuthConfig config) {
|
||||
super(config, AuthDefaultSource.FEISHU);
|
||||
throw new AuthException(AuthResponseStatus.FAILURE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AuthToken getAccessToken(AuthCallback authCallback) {
|
||||
JSONObject requestObject = new JSONObject();
|
||||
requestObject.put("app_id", config.getClientId());
|
||||
requestObject.put("app_secret", config.getClientSecret());
|
||||
requestObject.put("grant_type", "authorization_code");
|
||||
requestObject.put("code", authCallback.getCode());
|
||||
String response = new HttpUtils(config.getHttpConfig()).post(source.accessToken(), requestObject.toJSONString(), new HttpHeader()
|
||||
.add("Content-Type", "application/json"));
|
||||
JSONObject jsonObject = JSON.parseObject(response);
|
||||
this.checkResponse(jsonObject);
|
||||
return AuthToken.builder()
|
||||
.accessToken(jsonObject.getString("access_token"))
|
||||
.refreshToken(jsonObject.getString("refresh_token"))
|
||||
.expireIn(jsonObject.getIntValue("expires_in"))
|
||||
.tokenType(jsonObject.getString("token_type"))
|
||||
.openId(jsonObject.getString("open_id"))
|
||||
.build();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AuthUser getUserInfo(AuthToken authToken) {
|
||||
String accessToken = authToken.getAccessToken();
|
||||
String response = new HttpUtils(config.getHttpConfig()).get(source.userInfo(), null, new HttpHeader()
|
||||
.add("Content-Type", "application/json")
|
||||
.add("Authorization", "Bearer " + accessToken), false);
|
||||
JSONObject object = JSON.parseObject(response);
|
||||
return AuthUser.builder()
|
||||
.rawUserInfo(object)
|
||||
.avatar(object.getString("AvatarUrl"))
|
||||
.username(object.getString("Mobile"))
|
||||
.email(object.getString("Email"))
|
||||
.nickname("Name")
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthResponse refresh(AuthToken authToken) {
|
||||
JSONObject requestObject = new JSONObject();
|
||||
requestObject.put("app_id", config.getClientId());
|
||||
requestObject.put("app_secret", config.getClientSecret());
|
||||
requestObject.put("grant_type", "refresh_token");
|
||||
requestObject.put("refresh_token", authToken.getRefreshToken());
|
||||
String response = new HttpUtils(config.getHttpConfig()).post(source.refresh(), requestObject.toJSONString(), new HttpHeader()
|
||||
.add("Content-Type", "application/json"));
|
||||
JSONObject jsonObject = JSON.parseObject(response);
|
||||
this.checkResponse(jsonObject);
|
||||
return AuthResponse.builder()
|
||||
.code(AuthResponseStatus.SUCCESS.getCode())
|
||||
.data(AuthToken.builder()
|
||||
.accessToken(jsonObject.getString("access_token"))
|
||||
.refreshToken(jsonObject.getString("refresh_token"))
|
||||
.expireIn(jsonObject.getIntValue("expires_in"))
|
||||
.tokenType(jsonObject.getString("token_type"))
|
||||
.openId(jsonObject.getString("open_id"))
|
||||
.build())
|
||||
.build();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String authorize(String state) {
|
||||
return UrlBuilder.fromBaseUrl(source.authorize())
|
||||
.queryParam("app_id", config.getClientId())
|
||||
.queryParam("redirect_uri", GlobalAuthUtils.urlEncode(config.getRedirectUri()))
|
||||
.queryParam("state", getRealState(state))
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 校验响应内容是否正确
|
||||
*
|
||||
* @param jsonObject 响应内容
|
||||
*/
|
||||
private void checkResponse(JSONObject jsonObject) {
|
||||
if (jsonObject.getIntValue("code") != 0) {
|
||||
throw new AuthException(jsonObject.getString("message"));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,9 +1,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;
|
||||
@@ -19,13 +19,17 @@ import me.zhyd.oauth.model.AuthUser;
|
||||
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
|
||||
protected AuthToken getAccessToken(AuthCallback authCallback) {
|
||||
HttpResponse response = doPostAuthorizationCode(authCallback.getCode());
|
||||
JSONObject accessTokenObject = JSONObject.parseObject(response.body());
|
||||
String response = doPostAuthorizationCode(authCallback.getCode());
|
||||
JSONObject accessTokenObject = JSONObject.parseObject(response);
|
||||
this.checkResponse(accessTokenObject);
|
||||
return AuthToken.builder()
|
||||
.accessToken(accessTokenObject.getString("access_token"))
|
||||
@@ -38,11 +42,11 @@ public class AuthGiteeRequest extends AuthDefaultRequest {
|
||||
|
||||
@Override
|
||||
protected AuthUser getUserInfo(AuthToken authToken) {
|
||||
HttpResponse response = doGetUserInfo(authToken);
|
||||
String userInfo = response.body();
|
||||
String userInfo = doGetUserInfo(authToken);
|
||||
JSONObject object = JSONObject.parseObject(userInfo);
|
||||
this.checkResponse(object);
|
||||
return AuthUser.builder()
|
||||
.rawUserInfo(object)
|
||||
.uuid(object.getString("id"))
|
||||
.username(object.getString("login"))
|
||||
.avatar(object.getString("avatar_url"))
|
||||
@@ -54,7 +58,7 @@ public class AuthGiteeRequest extends AuthDefaultRequest {
|
||||
.remark(object.getString("bio"))
|
||||
.gender(AuthUserGender.UNKNOWN)
|
||||
.token(authToken)
|
||||
.source(source)
|
||||
.source(source.toString())
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
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.utils.GlobalAuthUtil;
|
||||
import me.zhyd.oauth.utils.GlobalAuthUtils;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@@ -22,16 +22,20 @@ import java.util.Map;
|
||||
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"));
|
||||
}
|
||||
String response = doPostAuthorizationCode(authCallback.getCode());
|
||||
Map<String, String> res = GlobalAuthUtils.parseStringToMap(response);
|
||||
|
||||
this.checkResponse(res.containsKey("error"), res.get("error_description"));
|
||||
|
||||
return AuthToken.builder()
|
||||
.accessToken(res.get("access_token"))
|
||||
.scope(res.get("scope"))
|
||||
@@ -41,12 +45,13 @@ public class AuthGithubRequest extends AuthDefaultRequest {
|
||||
|
||||
@Override
|
||||
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"));
|
||||
}
|
||||
String response = doGetUserInfo(authToken);
|
||||
JSONObject object = JSONObject.parseObject(response);
|
||||
|
||||
this.checkResponse(object.containsKey("error"), object.getString("error_description"));
|
||||
|
||||
return AuthUser.builder()
|
||||
.rawUserInfo(object)
|
||||
.uuid(object.getString("id"))
|
||||
.username(object.getString("login"))
|
||||
.avatar(object.getString("avatar_url"))
|
||||
@@ -58,8 +63,14 @@ public class AuthGithubRequest extends AuthDefaultRequest {
|
||||
.remark(object.getString("bio"))
|
||||
.gender(AuthUserGender.UNKNOWN)
|
||||
.token(authToken)
|
||||
.source(source)
|
||||
.source(source.toString())
|
||||
.build();
|
||||
}
|
||||
|
||||
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 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) {
|
||||
String response = doPostAuthorizationCode(authCallback.getCode());
|
||||
JSONObject object = JSONObject.parseObject(response);
|
||||
|
||||
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) {
|
||||
String response = doGetUserInfo(authToken);
|
||||
JSONObject object = JSONObject.parseObject(response);
|
||||
|
||||
this.checkResponse(object);
|
||||
|
||||
return AuthUser.builder()
|
||||
.rawUserInfo(object)
|
||||
.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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,10 +1,11 @@
|
||||
package me.zhyd.oauth.request;
|
||||
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import me.zhyd.oauth.utils.HttpUtils;
|
||||
import com.xkcoding.http.support.HttpHeader;
|
||||
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,13 +22,17 @@ import me.zhyd.oauth.utils.UrlBuilder;
|
||||
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
|
||||
protected AuthToken getAccessToken(AuthCallback authCallback) {
|
||||
HttpResponse response = doPostAuthorizationCode(authCallback.getCode());
|
||||
JSONObject accessTokenObject = JSONObject.parseObject(response.body());
|
||||
String response = doPostAuthorizationCode(authCallback.getCode());
|
||||
JSONObject accessTokenObject = JSONObject.parseObject(response);
|
||||
this.checkResponse(accessTokenObject);
|
||||
return AuthToken.builder()
|
||||
.accessToken(accessTokenObject.getString("access_token"))
|
||||
@@ -40,13 +45,13 @@ public class AuthGoogleRequest extends AuthDefaultRequest {
|
||||
|
||||
@Override
|
||||
protected AuthUser getUserInfo(AuthToken authToken) {
|
||||
HttpResponse response = HttpRequest.post(userInfoUrl(authToken))
|
||||
.header("Authorization", "Bearer " + authToken.getAccessToken())
|
||||
.execute();
|
||||
String userInfo = response.body();
|
||||
HttpHeader httpHeader = new HttpHeader();
|
||||
httpHeader.add("Authorization", "Bearer " + authToken.getAccessToken());
|
||||
String userInfo = new HttpUtils(config.getHttpConfig()).post(userInfoUrl(authToken), null, httpHeader);
|
||||
JSONObject object = JSONObject.parseObject(userInfo);
|
||||
this.checkResponse(object);
|
||||
return AuthUser.builder()
|
||||
.rawUserInfo(object)
|
||||
.uuid(object.getString("sub"))
|
||||
.username(object.getString("email"))
|
||||
.avatar(object.getString("picture"))
|
||||
@@ -55,7 +60,7 @@ public class AuthGoogleRequest extends AuthDefaultRequest {
|
||||
.email(object.getString("email"))
|
||||
.gender(AuthUserGender.UNKNOWN)
|
||||
.token(authToken)
|
||||
.source(source)
|
||||
.source(source.toString())
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,200 @@
|
||||
package me.zhyd.oauth.request;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import me.zhyd.oauth.utils.HttpUtils;
|
||||
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 java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
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) {
|
||||
Map<String, String> form = new HashMap<>(5);
|
||||
form.put("grant_type", "authorization_code");
|
||||
form.put("code", authCallback.getAuthorization_code());
|
||||
form.put("client_id", config.getClientId());
|
||||
form.put("client_secret", config.getClientSecret());
|
||||
form.put("redirect_uri", config.getRedirectUri());
|
||||
|
||||
String response = new HttpUtils(config.getHttpConfig()).post(source.accessToken(), form, false);
|
||||
return getAuthToken(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用token换取用户信息
|
||||
*
|
||||
* @param authToken token信息
|
||||
* @return 用户信息
|
||||
* @see AuthDefaultRequest#getAccessToken(AuthCallback)
|
||||
*/
|
||||
@Override
|
||||
protected AuthUser getUserInfo(AuthToken authToken) {
|
||||
Map<String, String> form = new HashMap<>(4);
|
||||
form.put("nsp_ts", System.currentTimeMillis() + "");
|
||||
form.put("access_token", authToken.getAccessToken());
|
||||
form.put("nsp_fmt", "JS");
|
||||
form.put("nsp_svc", "OpenUP.User.getInfo");
|
||||
|
||||
String response = new HttpUtils(config.getHttpConfig()).post(source.userInfo(), form, false);
|
||||
JSONObject object = JSONObject.parseObject(response);
|
||||
|
||||
this.checkResponse(object);
|
||||
|
||||
AuthUserGender gender = getRealGender(object);
|
||||
|
||||
return AuthUser.builder()
|
||||
.rawUserInfo(object)
|
||||
.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) {
|
||||
Map<String, String> form = new HashMap<>(4);
|
||||
form.put("client_id", config.getClientId());
|
||||
form.put("client_secret", config.getClientSecret());
|
||||
form.put("refresh_token", authToken.getRefreshToken());
|
||||
form.put("grant_type", "refresh_token");
|
||||
|
||||
String response = new HttpUtils(config.getHttpConfig()).post(source.refresh(), form, false);
|
||||
return AuthResponse.builder().code(SUCCESS.getCode()).data(getAuthToken(response)).build();
|
||||
}
|
||||
|
||||
private AuthToken getAuthToken(String response) {
|
||||
JSONObject object = JSONObject.parseObject(response);
|
||||
|
||||
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,144 @@
|
||||
package me.zhyd.oauth.request;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import me.zhyd.oauth.utils.HttpUtils;
|
||||
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.GlobalAuthUtils;
|
||||
import me.zhyd.oauth.utils.UrlBuilder;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 京东登录
|
||||
*
|
||||
* @author harry.lee (harryleexyz@qq.com)
|
||||
* @since 1.15.0
|
||||
*/
|
||||
public class AuthJdRequest extends AuthDefaultRequest {
|
||||
|
||||
public AuthJdRequest(AuthConfig config) {
|
||||
super(config, AuthDefaultSource.JD);
|
||||
}
|
||||
|
||||
public AuthJdRequest(AuthConfig config, AuthStateCache authStateCache) {
|
||||
super(config, AuthDefaultSource.JD, authStateCache);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AuthToken getAccessToken(AuthCallback authCallback) {
|
||||
|
||||
Map<String, String> params = new HashMap<>(5);
|
||||
params.put("app_key", config.getClientId());
|
||||
params.put("app_secret", config.getClientSecret());
|
||||
params.put("grant_type", "authorization_code");
|
||||
params.put("code", authCallback.getCode());
|
||||
String response = new HttpUtils(config.getHttpConfig()).post(source.accessToken(), params, false);
|
||||
JSONObject object = JSONObject.parseObject(response);
|
||||
|
||||
this.checkResponse(object);
|
||||
|
||||
return AuthToken.builder()
|
||||
.accessToken(object.getString("access_token"))
|
||||
.expireIn(object.getIntValue("expires_in"))
|
||||
.refreshToken(object.getString("refresh_token"))
|
||||
.scope(object.getString("scope"))
|
||||
.openId(object.getString("open_id"))
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AuthUser getUserInfo(AuthToken authToken) {
|
||||
UrlBuilder urlBuilder = UrlBuilder.fromBaseUrl(source.userInfo())
|
||||
.queryParam("access_token", authToken.getAccessToken())
|
||||
.queryParam("app_key", config.getClientId())
|
||||
.queryParam("method", "jingdong.user.getUserInfoByOpenId")
|
||||
.queryParam("360buy_param_json", "{\"openId\":\"" + authToken.getOpenId() + "\"}")
|
||||
.queryParam("timestamp", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")))
|
||||
.queryParam("v", "2.0");
|
||||
urlBuilder.queryParam("sign", GlobalAuthUtils.generateJdSignature(config.getClientSecret(), urlBuilder.getReadOnlyParams()));
|
||||
String response = new HttpUtils(config.getHttpConfig()).post(urlBuilder.build(true));
|
||||
JSONObject object = JSONObject.parseObject(response);
|
||||
|
||||
this.checkResponse(object);
|
||||
|
||||
JSONObject data = this.getUserDataJsonObject(object);
|
||||
|
||||
return AuthUser.builder()
|
||||
.rawUserInfo(data)
|
||||
.uuid(authToken.getOpenId())
|
||||
.username(data.getString("nickName"))
|
||||
.nickname(data.getString("nickName"))
|
||||
.avatar(data.getString("imageUrl"))
|
||||
.gender(AuthUserGender.getRealGender(data.getString("gendar")))
|
||||
.token(authToken)
|
||||
.source(source.toString())
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 个人用户无法申请应用
|
||||
* 暂时只能参考官网给出的返回结果解析
|
||||
*
|
||||
* @param object 请求返回结果
|
||||
* @return data JSONObject
|
||||
*/
|
||||
private JSONObject getUserDataJsonObject(JSONObject object) {
|
||||
return object.getJSONObject("jingdong_user_getUserInfoByOpenId_response")
|
||||
.getJSONObject("getuserinfobyappidandopenid_result")
|
||||
.getJSONObject("data");
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthResponse refresh(AuthToken oldToken) {
|
||||
Map<String, String> params = new HashMap<>(5);
|
||||
params.put("app_key", config.getClientId());
|
||||
params.put("app_secret", config.getClientSecret());
|
||||
params.put("grant_type", "refresh_token");
|
||||
params.put("refresh_token", oldToken.getRefreshToken());
|
||||
String response = new HttpUtils(config.getHttpConfig()).post(source.refresh(), params, false);
|
||||
JSONObject object = JSONObject.parseObject(response);
|
||||
|
||||
this.checkResponse(object);
|
||||
|
||||
return AuthResponse.builder()
|
||||
.code(AuthResponseStatus.SUCCESS.getCode())
|
||||
.data(AuthToken.builder()
|
||||
.accessToken(object.getString("access_token"))
|
||||
.expireIn(object.getIntValue("expires_in"))
|
||||
.refreshToken(object.getString("refresh_token"))
|
||||
.scope(object.getString("scope"))
|
||||
.openId(object.getString("open_id"))
|
||||
.build())
|
||||
.build();
|
||||
}
|
||||
|
||||
private void checkResponse(JSONObject object) {
|
||||
if (object.containsKey("error_response")) {
|
||||
throw new AuthException(object.getJSONObject("error_response").getString("zh_desc"));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String authorize(String state) {
|
||||
return UrlBuilder.fromBaseUrl(source.authorize())
|
||||
.queryParam("app_key", config.getClientId())
|
||||
.queryParam("response_type", "code")
|
||||
.queryParam("redirect_uri", config.getRedirectUri())
|
||||
.queryParam("scope", "snsapi_base")
|
||||
.queryParam("state", getRealState(state))
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
package me.zhyd.oauth.request;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import me.zhyd.oauth.utils.HttpUtils;
|
||||
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) {
|
||||
String response = doPostAuthorizationCode(authCallback.getCode());
|
||||
return getAuthToken(response);
|
||||
}
|
||||
|
||||
private AuthToken getAuthToken(String 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(String response) {
|
||||
JSONObject accessTokenObject = JSONObject.parseObject(response);
|
||||
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);
|
||||
String response = new HttpUtils(config.getHttpConfig()).get(UrlBuilder.fromBaseUrl(source.userInfo())
|
||||
.queryParam("access_token", authToken.getAccessToken())
|
||||
.queryParam("open_id", openId)
|
||||
.build());
|
||||
JSONObject object = JSONObject.parseObject(response);
|
||||
if (!"0".equals(object.getString("c"))) {
|
||||
throw new AuthException(object.getString("m"));
|
||||
}
|
||||
JSONObject resultObject = object.getJSONObject("d");
|
||||
|
||||
return AuthUser.builder()
|
||||
.rawUserInfo(resultObject)
|
||||
.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) {
|
||||
String response = new HttpUtils(config.getHttpConfig()).get(UrlBuilder.fromBaseUrl("https://oauth.kujiale.com/oauth2/auth/user")
|
||||
.queryParam("access_token", authToken.getAccessToken())
|
||||
.build());
|
||||
JSONObject accessTokenObject = checkResponse(response);
|
||||
return accessTokenObject.getString("d");
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthResponse refresh(AuthToken authToken) {
|
||||
String response = new HttpUtils(config.getHttpConfig()).post(refreshTokenUrl(authToken.getRefreshToken()));
|
||||
return AuthResponse.builder().code(AuthResponseStatus.SUCCESS.getCode()).data(getAuthToken(response)).build();
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,21 @@
|
||||
package me.zhyd.oauth.request;
|
||||
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.alibaba.fastjson.JSONPath;
|
||||
import com.xkcoding.http.constants.Constants;
|
||||
import com.xkcoding.http.support.HttpHeader;
|
||||
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.HttpUtils;
|
||||
import me.zhyd.oauth.utils.StringUtils;
|
||||
import me.zhyd.oauth.utils.UrlBuilder;
|
||||
|
||||
@@ -24,7 +29,11 @@ import me.zhyd.oauth.utils.UrlBuilder;
|
||||
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
|
||||
@@ -35,12 +44,13 @@ public class AuthLinkedinRequest extends AuthDefaultRequest {
|
||||
@Override
|
||||
protected AuthUser getUserInfo(AuthToken authToken) {
|
||||
String accessToken = authToken.getAccessToken();
|
||||
HttpResponse response = HttpRequest.get(userInfoUrl(authToken))
|
||||
.header("Host", "api.linkedin.com")
|
||||
.header("Connection", "Keep-Alive")
|
||||
.header("Authorization", "Bearer " + accessToken)
|
||||
.execute();
|
||||
JSONObject userInfoObject = JSONObject.parseObject(response.body());
|
||||
HttpHeader httpHeader = new HttpHeader();
|
||||
httpHeader.add("Host", "api.linkedin.com");
|
||||
httpHeader.add("Connection", "Keep-Alive");
|
||||
httpHeader.add("Authorization", "Bearer " + accessToken);
|
||||
|
||||
String response = new HttpUtils(config.getHttpConfig()).get(userInfoUrl(authToken), null, httpHeader, false);
|
||||
JSONObject userInfoObject = JSONObject.parseObject(response);
|
||||
|
||||
this.checkResponse(userInfoObject);
|
||||
|
||||
@@ -52,6 +62,7 @@ public class AuthLinkedinRequest extends AuthDefaultRequest {
|
||||
// 获取用户邮箱地址
|
||||
String email = this.getUserEmail(accessToken);
|
||||
return AuthUser.builder()
|
||||
.rawUserInfo(userInfoObject)
|
||||
.uuid(userInfoObject.getString("id"))
|
||||
.username(userName)
|
||||
.nickname(userName)
|
||||
@@ -59,7 +70,7 @@ public class AuthLinkedinRequest extends AuthDefaultRequest {
|
||||
.email(email)
|
||||
.token(authToken)
|
||||
.gender(AuthUserGender.UNKNOWN)
|
||||
.source(AuthSource.LINKEDIN)
|
||||
.source(source.toString())
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -93,17 +104,27 @@ public class AuthLinkedinRequest extends AuthDefaultRequest {
|
||||
* @return 用户的头像地址
|
||||
*/
|
||||
private String getAvatar(JSONObject userInfoObject) {
|
||||
String avatar = null;
|
||||
JSONObject profilePictureObject = userInfoObject.getJSONObject("profilePicture");
|
||||
if (profilePictureObject.containsKey("displayImage~")) {
|
||||
JSONArray displayImageElements = profilePictureObject.getJSONObject("displayImage~")
|
||||
.getJSONArray("elements");
|
||||
if (null != displayImageElements && displayImageElements.size() > 0) {
|
||||
JSONObject largestImageObj = displayImageElements.getJSONObject(displayImageElements.size() - 1);
|
||||
avatar = largestImageObj.getJSONArray("identifiers").getJSONObject(0).getString("identifier");
|
||||
}
|
||||
if (null == profilePictureObject || !profilePictureObject.containsKey("displayImage~")) {
|
||||
return null;
|
||||
}
|
||||
return avatar;
|
||||
JSONObject displayImageObject = profilePictureObject.getJSONObject("displayImage~");
|
||||
if (null == displayImageObject || !displayImageObject.containsKey("elements")) {
|
||||
return null;
|
||||
}
|
||||
JSONArray displayImageElements = displayImageObject.getJSONArray("elements");
|
||||
if (null == displayImageElements || displayImageElements.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
JSONObject largestImageObj = displayImageElements.getJSONObject(displayImageElements.size() - 1);
|
||||
if (null == largestImageObj || !largestImageObj.containsKey("identifiers")) {
|
||||
return null;
|
||||
}
|
||||
JSONArray identifiers = largestImageObj.getJSONArray("identifiers");
|
||||
if (null == identifiers || identifiers.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return identifiers.getJSONObject(0).getString("identifier");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -113,13 +134,16 @@ public class AuthLinkedinRequest extends AuthDefaultRequest {
|
||||
* @return 用户的邮箱地址
|
||||
*/
|
||||
private String getUserEmail(String accessToken) {
|
||||
HttpResponse emailResponse = HttpRequest.get("https://api.linkedin.com/v2/emailAddress?q=members&projection=(elements*(handle~))")
|
||||
.header("Host", "api.linkedin.com")
|
||||
.header("Connection", "Keep-Alive")
|
||||
.header("Authorization", "Bearer " + accessToken)
|
||||
.execute();
|
||||
JSONObject emailObj = JSONObject.parseObject(emailResponse.body());
|
||||
HttpHeader httpHeader = new HttpHeader();
|
||||
httpHeader.add("Host", "api.linkedin.com");
|
||||
httpHeader.add("Connection", "Keep-Alive");
|
||||
httpHeader.add("Authorization", "Bearer " + accessToken);
|
||||
|
||||
String emailResponse = new HttpUtils(config.getHttpConfig()).get("https://api.linkedin.com/v2/emailAddress?q=members&projection=(elements*(handle~))", null, httpHeader, false);
|
||||
JSONObject emailObj = JSONObject.parseObject(emailResponse);
|
||||
|
||||
this.checkResponse(emailObj);
|
||||
|
||||
Object obj = JSONPath.eval(emailObj, "$['elements'][0]['handle~']['emailAddress']");
|
||||
return null == obj ? null : (String) obj;
|
||||
}
|
||||
@@ -133,19 +157,6 @@ public class AuthLinkedinRequest extends AuthDefaultRequest {
|
||||
return firstName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthResponse refresh(AuthToken oldToken) {
|
||||
String refreshToken = oldToken.getRefreshToken();
|
||||
if (StringUtils.isEmpty(refreshToken)) {
|
||||
throw new AuthException(AuthResponseStatus.UNSUPPORTED);
|
||||
}
|
||||
String refreshTokenUrl = refreshTokenUrl(refreshToken);
|
||||
return AuthResponse.builder()
|
||||
.code(AuthResponseStatus.SUCCESS.getCode())
|
||||
.data(this.getToken(refreshTokenUrl))
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查响应内容是否正确
|
||||
*
|
||||
@@ -153,7 +164,7 @@ public class AuthLinkedinRequest extends AuthDefaultRequest {
|
||||
*/
|
||||
private void checkResponse(JSONObject object) {
|
||||
if (object.containsKey("error")) {
|
||||
throw new AuthException(object.getString("error_description"));
|
||||
throw new AuthException(object.getString("error_description"), source);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,12 +175,12 @@ public class AuthLinkedinRequest extends AuthDefaultRequest {
|
||||
* @return token对象
|
||||
*/
|
||||
private AuthToken getToken(String accessTokenUrl) {
|
||||
HttpResponse response = HttpRequest.post(accessTokenUrl)
|
||||
.header("Host", "www.linkedin.com")
|
||||
.contentType("application/x-www-form-urlencoded")
|
||||
.execute();
|
||||
String accessTokenStr = response.body();
|
||||
JSONObject accessTokenObject = JSONObject.parseObject(accessTokenStr);
|
||||
HttpHeader httpHeader = new HttpHeader();
|
||||
httpHeader.add("Host", "www.linkedin.com");
|
||||
httpHeader.add(Constants.CONTENT_TYPE, "application/x-www-form-urlencoded");
|
||||
|
||||
String response = new HttpUtils(config.getHttpConfig()).post(accessTokenUrl, null, httpHeader);
|
||||
JSONObject accessTokenObject = JSONObject.parseObject(response);
|
||||
|
||||
this.checkResponse(accessTokenObject);
|
||||
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
package me.zhyd.oauth.request;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import me.zhyd.oauth.utils.HttpUtils;
|
||||
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;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 美团登录
|
||||
*
|
||||
* @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) {
|
||||
Map<String, String> form = new HashMap<>(4);
|
||||
form.put("app_id", config.getClientId());
|
||||
form.put("secret", config.getClientSecret());
|
||||
form.put("code", authCallback.getCode());
|
||||
form.put("grant_type", "authorization_code");
|
||||
|
||||
String response = new HttpUtils(config.getHttpConfig()).post(source.accessToken(), form, false);
|
||||
JSONObject object = JSONObject.parseObject(response);
|
||||
|
||||
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) {
|
||||
Map<String, String> form = new HashMap<>(3);
|
||||
form.put("app_id", config.getClientId());
|
||||
form.put("secret", config.getClientSecret());
|
||||
form.put("access_token", authToken.getAccessToken());
|
||||
|
||||
String response = new HttpUtils(config.getHttpConfig()).post(source.userInfo(), form, false);
|
||||
JSONObject object = JSONObject.parseObject(response);
|
||||
|
||||
this.checkResponse(object);
|
||||
|
||||
return AuthUser.builder()
|
||||
.rawUserInfo(object)
|
||||
.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) {
|
||||
Map<String, String> form = new HashMap<>(4);
|
||||
form.put("app_id", config.getClientId());
|
||||
form.put("secret", config.getClientSecret());
|
||||
form.put("refresh_token", oldToken.getRefreshToken());
|
||||
form.put("grant_type", "refresh_token");
|
||||
|
||||
String response = new HttpUtils(config.getHttpConfig()).post(source.refresh(), form, false);
|
||||
JSONObject object = JSONObject.parseObject(response);
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,16 +1,19 @@
|
||||
package me.zhyd.oauth.request;
|
||||
|
||||
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.utils.HttpUtils;
|
||||
import com.xkcoding.http.constants.Constants;
|
||||
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;
|
||||
@@ -21,12 +24,15 @@ import java.text.MessageFormat;
|
||||
* @author yangkai.shen (https://xkcoding.com)
|
||||
* @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
|
||||
@@ -35,8 +41,8 @@ public class AuthMiRequest extends AuthDefaultRequest {
|
||||
}
|
||||
|
||||
private AuthToken getToken(String accessTokenUrl) {
|
||||
HttpResponse response = HttpRequest.get(accessTokenUrl).execute();
|
||||
String jsonStr = StrUtil.replace(response.body(), PREFIX, StrUtil.EMPTY);
|
||||
String response = new HttpUtils(config.getHttpConfig()).get(accessTokenUrl);
|
||||
String jsonStr = response.replace(PREFIX, Constants.EMPTY);
|
||||
JSONObject accessTokenObject = JSONObject.parseObject(jsonStr);
|
||||
|
||||
if (accessTokenObject.containsKey("error")) {
|
||||
@@ -58,37 +64,38 @@ public class AuthMiRequest extends AuthDefaultRequest {
|
||||
@Override
|
||||
protected AuthUser getUserInfo(AuthToken authToken) {
|
||||
// 获取用户信息
|
||||
HttpResponse userResponse = doGetUserInfo(authToken);
|
||||
String userResponse = doGetUserInfo(authToken);
|
||||
|
||||
JSONObject userProfile = JSONObject.parseObject(userResponse.body());
|
||||
JSONObject userProfile = JSONObject.parseObject(userResponse);
|
||||
if ("error".equalsIgnoreCase(userProfile.getString("result"))) {
|
||||
throw new AuthException(userProfile.getString("description"));
|
||||
}
|
||||
|
||||
JSONObject user = userProfile.getJSONObject("data");
|
||||
JSONObject object = userProfile.getJSONObject("data");
|
||||
|
||||
AuthUser authUser = AuthUser.builder()
|
||||
.rawUserInfo(object)
|
||||
.uuid(authToken.getOpenId())
|
||||
.username(user.getString("miliaoNick"))
|
||||
.nickname(user.getString("miliaoNick"))
|
||||
.avatar(user.getString("miliaoIcon"))
|
||||
.email(user.getString("mail"))
|
||||
.username(object.getString("miliaoNick"))
|
||||
.nickname(object.getString("miliaoNick"))
|
||||
.avatar(object.getString("miliaoIcon"))
|
||||
.email(object.getString("mail"))
|
||||
.gender(AuthUserGender.UNKNOWN)
|
||||
.token(authToken)
|
||||
.source(source)
|
||||
.source(source.toString())
|
||||
.build();
|
||||
|
||||
// 获取用户邮箱手机号等信息
|
||||
String emailPhoneUrl = MessageFormat.format("{0}?clientId={1}&token={2}", "https://open.account.xiaomi.com/user/phoneAndEmail", config
|
||||
.getClientId(), authToken.getAccessToken());
|
||||
|
||||
HttpResponse emailResponse = HttpRequest.get(emailPhoneUrl).execute();
|
||||
JSONObject userEmailPhone = JSONObject.parseObject(emailResponse.body());
|
||||
String emailResponse = new HttpUtils(config.getHttpConfig()).get(emailPhoneUrl);
|
||||
JSONObject userEmailPhone = JSONObject.parseObject(emailResponse);
|
||||
if (!"error".equalsIgnoreCase(userEmailPhone.getString("result"))) {
|
||||
JSONObject emailPhone = userEmailPhone.getJSONObject("data");
|
||||
authUser.setEmail(emailPhone.getString("email"));
|
||||
} else {
|
||||
log.warn("小米开发平台暂时不对外开放用户手机及邮箱信息的获取");
|
||||
Log.warn("小米开发平台暂时不对外开放用户手机及邮箱信息的获取");
|
||||
}
|
||||
|
||||
return authUser;
|
||||
|
||||
@@ -1,17 +1,23 @@
|
||||
package me.zhyd.oauth.request;
|
||||
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import me.zhyd.oauth.utils.HttpUtils;
|
||||
import com.xkcoding.http.constants.Constants;
|
||||
import com.xkcoding.http.support.HttpHeader;
|
||||
import com.xkcoding.http.util.MapUtil;
|
||||
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;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 微软登录
|
||||
@@ -21,7 +27,11 @@ import static me.zhyd.oauth.utils.GlobalAuthUtil.parseQueryToMap;
|
||||
*/
|
||||
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
|
||||
@@ -36,13 +46,12 @@ public class AuthMicrosoftRequest extends AuthDefaultRequest {
|
||||
* @return token对象
|
||||
*/
|
||||
private AuthToken getToken(String accessTokenUrl) {
|
||||
HttpResponse response = HttpRequest.post(accessTokenUrl)
|
||||
.header("Host", "https://login.microsoftonline.com")
|
||||
.contentType("application/x-www-form-urlencoded")
|
||||
.form(parseQueryToMap(accessTokenUrl))
|
||||
.execute();
|
||||
String accessTokenStr = response.body();
|
||||
JSONObject accessTokenObject = JSONObject.parseObject(accessTokenStr);
|
||||
HttpHeader httpHeader = new HttpHeader();
|
||||
|
||||
Map<String, String> form = MapUtil.parseStringToMap(accessTokenUrl, false);
|
||||
|
||||
String response = new HttpUtils(config.getHttpConfig()).post(accessTokenUrl, form, httpHeader, false);
|
||||
JSONObject accessTokenObject = JSONObject.parseObject(response);
|
||||
|
||||
this.checkResponse(accessTokenObject);
|
||||
|
||||
@@ -71,11 +80,15 @@ public class AuthMicrosoftRequest extends AuthDefaultRequest {
|
||||
String token = authToken.getAccessToken();
|
||||
String tokenType = authToken.getTokenType();
|
||||
String jwt = tokenType + " " + token;
|
||||
HttpResponse response = HttpRequest.get(userInfoUrl(authToken)).header("Authorization", jwt).execute();
|
||||
String userInfo = response.body();
|
||||
|
||||
HttpHeader httpHeader = new HttpHeader();
|
||||
httpHeader.add("Authorization", jwt);
|
||||
|
||||
String userInfo = new HttpUtils(config.getHttpConfig()).get(userInfoUrl(authToken), null, httpHeader, false);
|
||||
JSONObject object = JSONObject.parseObject(userInfo);
|
||||
this.checkResponse(object);
|
||||
return AuthUser.builder()
|
||||
.rawUserInfo(object)
|
||||
.uuid(object.getString("id"))
|
||||
.username(object.getString("userPrincipalName"))
|
||||
.nickname(object.getString("displayName"))
|
||||
@@ -83,7 +96,7 @@ public class AuthMicrosoftRequest extends AuthDefaultRequest {
|
||||
.email(object.getString("mail"))
|
||||
.gender(AuthUserGender.UNKNOWN)
|
||||
.token(authToken)
|
||||
.source(source)
|
||||
.source(source.toString())
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -115,7 +128,7 @@ public class AuthMicrosoftRequest extends AuthDefaultRequest {
|
||||
.queryParam("client_id", config.getClientId())
|
||||
.queryParam("redirect_uri", config.getRedirectUri())
|
||||
.queryParam("response_mode", "query")
|
||||
.queryParam("scope", "offline_access%20user.read%20mail.read")
|
||||
.queryParam("scope", "offline_access user.read mail.read")
|
||||
.queryParam("state", getRealState(state))
|
||||
.build();
|
||||
}
|
||||
@@ -133,7 +146,7 @@ public class AuthMicrosoftRequest extends AuthDefaultRequest {
|
||||
.queryParam("client_id", config.getClientId())
|
||||
.queryParam("client_secret", config.getClientSecret())
|
||||
.queryParam("grant_type", "authorization_code")
|
||||
.queryParam("scope", "user.read%20mail.read")
|
||||
.queryParam("scope", "offline_access user.read mail.read")
|
||||
.queryParam("redirect_uri", config.getRedirectUri())
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -1,9 +1,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;
|
||||
@@ -20,13 +20,17 @@ import me.zhyd.oauth.utils.UrlBuilder;
|
||||
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
|
||||
protected AuthToken getAccessToken(AuthCallback authCallback) {
|
||||
HttpResponse response = doPostAuthorizationCode(authCallback.getCode());
|
||||
JSONObject accessTokenObject = JSONObject.parseObject(response.body());
|
||||
String response = doPostAuthorizationCode(authCallback.getCode());
|
||||
JSONObject accessTokenObject = JSONObject.parseObject(response);
|
||||
this.checkResponse(accessTokenObject);
|
||||
return AuthToken.builder()
|
||||
.accessToken(accessTokenObject.getString("access_token"))
|
||||
@@ -38,10 +42,11 @@ public class AuthOschinaRequest extends AuthDefaultRequest {
|
||||
|
||||
@Override
|
||||
protected AuthUser getUserInfo(AuthToken authToken) {
|
||||
HttpResponse response = doGetUserInfo(authToken);
|
||||
JSONObject object = JSONObject.parseObject(response.body());
|
||||
String response = doGetUserInfo(authToken);
|
||||
JSONObject object = JSONObject.parseObject(response);
|
||||
this.checkResponse(object);
|
||||
return AuthUser.builder()
|
||||
.rawUserInfo(object)
|
||||
.uuid(object.getString("id"))
|
||||
.username(object.getString("name"))
|
||||
.nickname(object.getString("name"))
|
||||
@@ -51,7 +56,7 @@ public class AuthOschinaRequest extends AuthDefaultRequest {
|
||||
.gender(AuthUserGender.getRealGender(object.getString("gender")))
|
||||
.email(object.getString("email"))
|
||||
.token(authToken)
|
||||
.source(source)
|
||||
.source(source.toString())
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package me.zhyd.oauth.request;
|
||||
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import me.zhyd.oauth.utils.HttpUtils;
|
||||
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,7 +13,7 @@ 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登录
|
||||
@@ -29,10 +29,14 @@ 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());
|
||||
JSONObject accessTokenObject = JSONObject.parseObject(response.body());
|
||||
String response = doPostAuthorizationCode(authCallback.getCode());
|
||||
JSONObject accessTokenObject = JSONObject.parseObject(response);
|
||||
this.checkResponse(accessTokenObject);
|
||||
return AuthToken.builder()
|
||||
.accessToken(accessTokenObject.getString("access_token"))
|
||||
@@ -43,11 +47,13 @@ public class AuthPinterestRequest extends AuthDefaultRequest {
|
||||
@Override
|
||||
protected AuthUser getUserInfo(AuthToken authToken) {
|
||||
String userinfoUrl = userInfoUrl(authToken);
|
||||
HttpResponse response = HttpRequest.get(userinfoUrl).setFollowRedirects(true).execute();
|
||||
JSONObject object = JSONObject.parseObject(response.body());
|
||||
// TODO: 是否需要 .setFollowRedirects(true)
|
||||
String response = new HttpUtils(config.getHttpConfig()).get(userinfoUrl);
|
||||
JSONObject object = JSONObject.parseObject(response);
|
||||
this.checkResponse(object);
|
||||
JSONObject userObj = object.getJSONObject("data");
|
||||
return AuthUser.builder()
|
||||
.rawUserInfo(userObj)
|
||||
.uuid(userObj.getString("id"))
|
||||
.avatar(getAvatarUrl(userObj))
|
||||
.username(userObj.getString("username"))
|
||||
@@ -55,7 +61,7 @@ public class AuthPinterestRequest extends AuthDefaultRequest {
|
||||
.gender(AuthUserGender.UNKNOWN)
|
||||
.remark(userObj.getString("bio"))
|
||||
.token(authToken)
|
||||
.source(source)
|
||||
.source(source.toString())
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
package me.zhyd.oauth.request;
|
||||
|
||||
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.utils.HttpUtils;
|
||||
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.utils.GlobalAuthUtil;
|
||||
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.GlobalAuthUtils;
|
||||
import me.zhyd.oauth.utils.StringUtils;
|
||||
import me.zhyd.oauth.utils.UrlBuilder;
|
||||
|
||||
@@ -25,29 +27,30 @@ import java.util.Map;
|
||||
*/
|
||||
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
|
||||
protected AuthToken getAccessToken(AuthCallback authCallback) {
|
||||
HttpResponse response = doGetAuthorizationCode(authCallback.getCode());
|
||||
String response = doGetAuthorizationCode(authCallback.getCode());
|
||||
return getAuthToken(response);
|
||||
}
|
||||
|
||||
@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();
|
||||
String response = new HttpUtils(config.getHttpConfig()).get(refreshTokenUrl(authToken.getRefreshToken()));
|
||||
return AuthResponse.builder().code(AuthResponseStatus.SUCCESS.getCode()).data(getAuthToken(response)).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AuthUser getUserInfo(AuthToken authToken) {
|
||||
String openId = this.getOpenId(authToken);
|
||||
HttpResponse response = doGetUserInfo(authToken);
|
||||
JSONObject object = JSONObject.parseObject(response.body());
|
||||
String response = doGetUserInfo(authToken);
|
||||
JSONObject object = JSONObject.parseObject(response);
|
||||
if (object.getIntValue("ret") != 0) {
|
||||
throw new AuthException(object.getString("msg"));
|
||||
}
|
||||
@@ -58,6 +61,7 @@ public class AuthQqRequest extends AuthDefaultRequest {
|
||||
|
||||
String location = String.format("%s-%s", object.getString("province"), object.getString("city"));
|
||||
return AuthUser.builder()
|
||||
.rawUserInfo(object)
|
||||
.username(object.getString("nickname"))
|
||||
.nickname(object.getString("nickname"))
|
||||
.avatar(avatar)
|
||||
@@ -65,39 +69,34 @@ 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}
|
||||
* 那就需要开发者先通过邮件申请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")
|
||||
String response = new HttpUtils(config.getHttpConfig()).get(UrlBuilder.fromBaseUrl("https://graph.qq.com/oauth2.0/me")
|
||||
.queryParam("access_token", authToken.getAccessToken())
|
||||
.queryParam("unionid", config.isUnionId() ? 1 : 0)
|
||||
.build()).execute();
|
||||
if (response.isOk()) {
|
||||
String body = response.body();
|
||||
String removePrefix = StrUtil.replace(body, "callback(", "");
|
||||
String removeSuffix = StrUtil.replace(removePrefix, ");", "");
|
||||
String openId = StrUtil.trim(removeSuffix);
|
||||
JSONObject object = JSONObject.parseObject(openId);
|
||||
if (object.containsKey("error")) {
|
||||
throw new AuthException(object.get("error") + ":" + object.get("error_description"));
|
||||
}
|
||||
authToken.setOpenId(object.getString("openid"));
|
||||
if (object.containsKey("unionid")) {
|
||||
authToken.setUnionId(object.getString("unionid"));
|
||||
}
|
||||
return StringUtils.isEmpty(authToken.getUnionId()) ? authToken.getOpenId() : authToken.getUnionId();
|
||||
.build());
|
||||
String removePrefix = response.replace("callback(", "");
|
||||
String removeSuffix = removePrefix.replace(");", "");
|
||||
String openId = removeSuffix.trim();
|
||||
JSONObject object = JSONObject.parseObject(openId);
|
||||
if (object.containsKey("error")) {
|
||||
throw new AuthException(object.get("error") + ":" + object.get("error_description"));
|
||||
}
|
||||
|
||||
throw new AuthException("request error");
|
||||
authToken.setOpenId(object.getString("openid"));
|
||||
if (object.containsKey("unionid")) {
|
||||
authToken.setUnionId(object.getString("unionid"));
|
||||
}
|
||||
return StringUtils.isEmpty(authToken.getUnionId()) ? authToken.getOpenId() : authToken.getUnionId();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -115,8 +114,8 @@ public class AuthQqRequest extends AuthDefaultRequest {
|
||||
.build();
|
||||
}
|
||||
|
||||
private AuthToken getAuthToken(HttpResponse response) {
|
||||
Map<String, String> accessTokenObject = GlobalAuthUtil.parseStringToMap(response.body());
|
||||
private AuthToken getAuthToken(String response) {
|
||||
Map<String, String> accessTokenObject = GlobalAuthUtils.parseStringToMap(response);
|
||||
if (!accessTokenObject.containsKey("access_token") || accessTokenObject.containsKey("code")) {
|
||||
throw new AuthException(accessTokenObject.get("msg"));
|
||||
}
|
||||
|
||||
@@ -1,18 +1,22 @@
|
||||
package me.zhyd.oauth.request;
|
||||
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.xkcoding.http.util.UrlUtil;
|
||||
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.HttpUtils;
|
||||
import me.zhyd.oauth.utils.UrlBuilder;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import static me.zhyd.oauth.config.AuthSource.RENREN;
|
||||
import static me.zhyd.oauth.config.AuthDefaultSource.RENREN;
|
||||
import static me.zhyd.oauth.enums.AuthResponseStatus.SUCCESS;
|
||||
|
||||
/**
|
||||
@@ -27,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()));
|
||||
@@ -34,17 +42,18 @@ public class AuthRenrenRequest extends AuthDefaultRequest {
|
||||
|
||||
@Override
|
||||
protected AuthUser getUserInfo(AuthToken authToken) {
|
||||
HttpResponse response = doGetUserInfo(authToken);
|
||||
JSONObject userObj = JSONObject.parseObject(response.body()).getJSONObject("response");
|
||||
String response = doGetUserInfo(authToken);
|
||||
JSONObject userObj = JSONObject.parseObject(response).getJSONObject("response");
|
||||
|
||||
return AuthUser.builder()
|
||||
.rawUserInfo(userObj)
|
||||
.uuid(userObj.getString("id"))
|
||||
.avatar(getAvatarUrl(userObj))
|
||||
.nickname(userObj.getString("name"))
|
||||
.company(getCompany(userObj))
|
||||
.gender(getGender(userObj))
|
||||
.token(authToken)
|
||||
.source(source)
|
||||
.source(source.toString())
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -57,8 +66,8 @@ public class AuthRenrenRequest extends AuthDefaultRequest {
|
||||
}
|
||||
|
||||
private AuthToken getToken(String url) {
|
||||
HttpResponse response = HttpRequest.post(url).execute();
|
||||
JSONObject jsonObject = JSONObject.parseObject(response.body());
|
||||
String response = new HttpUtils(config.getHttpConfig()).post(url);
|
||||
JSONObject jsonObject = JSONObject.parseObject(response);
|
||||
if (jsonObject.containsKey("error")) {
|
||||
throw new AuthException("Failed to get token from Renren: " + jsonObject);
|
||||
}
|
||||
@@ -66,8 +75,8 @@ public class AuthRenrenRequest extends AuthDefaultRequest {
|
||||
return AuthToken.builder()
|
||||
.tokenType(jsonObject.getString("token_type"))
|
||||
.expireIn(jsonObject.getIntValue("expires_in"))
|
||||
.accessToken(jsonObject.getString("access_token"))
|
||||
.refreshToken(jsonObject.getString("refresh_token"))
|
||||
.accessToken(UrlUtil.urlEncode(jsonObject.getString("access_token")))
|
||||
.refreshToken(UrlUtil.urlEncode(jsonObject.getString("refresh_token")))
|
||||
.openId(jsonObject.getJSONObject("user").getString("id"))
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -1,12 +1,20 @@
|
||||
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.enums.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)
|
||||
* @since 1.8
|
||||
*/
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
package me.zhyd.oauth.request;
|
||||
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import me.zhyd.oauth.utils.HttpUtils;
|
||||
import com.xkcoding.http.constants.Constants;
|
||||
import com.xkcoding.http.support.HttpHeader;
|
||||
import com.xkcoding.http.util.MapUtil;
|
||||
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,8 +14,9 @@ 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.utils.GlobalAuthUtil.parseQueryToMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static me.zhyd.oauth.config.AuthDefaultSource.STACK_OVERFLOW;
|
||||
|
||||
/**
|
||||
* Stack Overflow登录
|
||||
@@ -26,14 +30,19 @@ 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());
|
||||
HttpResponse response = HttpRequest.post(accessTokenUrl)
|
||||
.contentType("application/x-www-form-urlencoded")
|
||||
.form(parseQueryToMap(accessTokenUrl))
|
||||
.execute();
|
||||
JSONObject accessTokenObject = JSONObject.parseObject(response.body());
|
||||
Map<String, String> form = MapUtil.parseStringToMap(accessTokenUrl, false);
|
||||
HttpHeader httpHeader = new HttpHeader();
|
||||
httpHeader.add(Constants.CONTENT_TYPE, "application/x-www-form-urlencoded");
|
||||
String response = new HttpUtils(config.getHttpConfig()).post(accessTokenUrl, form, httpHeader, false);
|
||||
|
||||
JSONObject accessTokenObject = JSONObject.parseObject(response);
|
||||
this.checkResponse(accessTokenObject);
|
||||
|
||||
return AuthToken.builder()
|
||||
@@ -49,12 +58,13 @@ public class AuthStackOverflowRequest extends AuthDefaultRequest {
|
||||
.queryParam("site", "stackoverflow")
|
||||
.queryParam("key", this.config.getStackOverflowKey())
|
||||
.build();
|
||||
HttpResponse response = HttpRequest.get(userInfoUrl).execute();
|
||||
JSONObject object = JSONObject.parseObject(response.body());
|
||||
String response = new HttpUtils(config.getHttpConfig()).get(userInfoUrl);
|
||||
JSONObject object = JSONObject.parseObject(response);
|
||||
this.checkResponse(object);
|
||||
JSONObject userObj = object.getJSONArray("items").getJSONObject(0);
|
||||
|
||||
return AuthUser.builder()
|
||||
.rawUserInfo(userObj)
|
||||
.uuid(userObj.getString("user_id"))
|
||||
.avatar(userObj.getString("profile_image"))
|
||||
.location(userObj.getString("location"))
|
||||
@@ -62,7 +72,7 @@ public class AuthStackOverflowRequest extends AuthDefaultRequest {
|
||||
.blog(userObj.getString("website_url"))
|
||||
.gender(AuthUserGender.UNKNOWN)
|
||||
.token(authToken)
|
||||
.source(source)
|
||||
.source(source.toString())
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
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.utils.GlobalAuthUtil;
|
||||
import me.zhyd.oauth.utils.GlobalAuthUtils;
|
||||
import me.zhyd.oauth.utils.UrlBuilder;
|
||||
|
||||
/**
|
||||
@@ -21,7 +21,11 @@ import me.zhyd.oauth.utils.UrlBuilder;
|
||||
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
|
||||
@@ -31,8 +35,8 @@ public class AuthTaobaoRequest extends AuthDefaultRequest {
|
||||
|
||||
@Override
|
||||
protected AuthUser getUserInfo(AuthToken authToken) {
|
||||
HttpResponse response = doPostAuthorizationCode(authToken.getAccessCode());
|
||||
JSONObject accessTokenObject = JSONObject.parseObject(response.body());
|
||||
String response = doPostAuthorizationCode(authToken.getAccessCode());
|
||||
JSONObject accessTokenObject = JSONObject.parseObject(response);
|
||||
if (accessTokenObject.containsKey("error")) {
|
||||
throw new AuthException(accessTokenObject.getString("error_description"));
|
||||
}
|
||||
@@ -42,14 +46,15 @@ public class AuthTaobaoRequest extends AuthDefaultRequest {
|
||||
authToken.setUid(accessTokenObject.getString("taobao_user_id"));
|
||||
authToken.setOpenId(accessTokenObject.getString("taobao_open_uid"));
|
||||
|
||||
String nick = GlobalAuthUtil.urlDecode(accessTokenObject.getString("taobao_user_nick"));
|
||||
String nick = GlobalAuthUtils.urlDecode(accessTokenObject.getString("taobao_user_nick"));
|
||||
return AuthUser.builder()
|
||||
.rawUserInfo(new JSONObject())
|
||||
.uuid(accessTokenObject.getString("taobao_user_id"))
|
||||
.username(nick)
|
||||
.nickname(nick)
|
||||
.gender(AuthUserGender.UNKNOWN)
|
||||
.token(authToken)
|
||||
.source(source)
|
||||
.source(source.toString())
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +1,21 @@
|
||||
package me.zhyd.oauth.request;
|
||||
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import me.zhyd.oauth.utils.HttpUtils;
|
||||
import com.xkcoding.http.support.HttpHeader;
|
||||
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 java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Teambition授权登录
|
||||
@@ -19,7 +26,11 @@ import me.zhyd.oauth.model.*;
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -28,13 +39,14 @@ public class AuthTeambitionRequest extends AuthDefaultRequest {
|
||||
*/
|
||||
@Override
|
||||
protected AuthToken getAccessToken(AuthCallback authCallback) {
|
||||
HttpResponse response = HttpRequest.post(source.accessToken())
|
||||
.form("client_id", config.getClientId())
|
||||
.form("client_secret", config.getClientSecret())
|
||||
.form("code", authCallback.getCode())
|
||||
.form("grant_type", "code")
|
||||
.execute();
|
||||
JSONObject accessTokenObject = JSONObject.parseObject(response.body());
|
||||
Map<String, String> form = new HashMap<>(4);
|
||||
form.put("client_id", config.getClientId());
|
||||
form.put("client_secret", config.getClientSecret());
|
||||
form.put("code", authCallback.getCode());
|
||||
form.put("grant_type", "code");
|
||||
|
||||
String response = new HttpUtils(config.getHttpConfig()).post(source.accessToken(), form, false);
|
||||
JSONObject accessTokenObject = JSONObject.parseObject(response);
|
||||
|
||||
this.checkResponse(accessTokenObject);
|
||||
|
||||
@@ -48,16 +60,18 @@ public class AuthTeambitionRequest extends AuthDefaultRequest {
|
||||
protected AuthUser getUserInfo(AuthToken authToken) {
|
||||
String accessToken = authToken.getAccessToken();
|
||||
|
||||
HttpResponse response = HttpRequest.get(source.userInfo())
|
||||
.header("Authorization", "OAuth2 " + accessToken)
|
||||
.execute();
|
||||
JSONObject object = JSONObject.parseObject(response.body());
|
||||
HttpHeader httpHeader = new HttpHeader();
|
||||
httpHeader.add("Authorization", "OAuth2 " + accessToken);
|
||||
|
||||
String response = new HttpUtils(config.getHttpConfig()).get(source.userInfo(), null, httpHeader, false);
|
||||
JSONObject object = JSONObject.parseObject(response);
|
||||
|
||||
this.checkResponse(object);
|
||||
|
||||
authToken.setUid(object.getString("_id"));
|
||||
|
||||
return AuthUser.builder()
|
||||
.rawUserInfo(object)
|
||||
.uuid(object.getString("_id"))
|
||||
.username(object.getString("name"))
|
||||
.nickname(object.getString("name"))
|
||||
@@ -67,7 +81,7 @@ public class AuthTeambitionRequest extends AuthDefaultRequest {
|
||||
.email(object.getString("email"))
|
||||
.gender(AuthUserGender.UNKNOWN)
|
||||
.token(authToken)
|
||||
.source(source)
|
||||
.source(source.toString())
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -75,11 +89,12 @@ public class AuthTeambitionRequest extends AuthDefaultRequest {
|
||||
public AuthResponse refresh(AuthToken oldToken) {
|
||||
String uid = oldToken.getUid();
|
||||
String refreshToken = oldToken.getRefreshToken();
|
||||
HttpResponse response = HttpRequest.post(source.refresh())
|
||||
.form("_userId", uid)
|
||||
.form("refresh_token", refreshToken)
|
||||
.execute();
|
||||
JSONObject refreshTokenObject = JSONObject.parseObject(response.body());
|
||||
|
||||
Map<String, String> form = new HashMap<>(2);
|
||||
form.put("_userId", uid);
|
||||
form.put("refresh_token", refreshToken);
|
||||
String response = new HttpUtils(config.getHttpConfig()).post(source.refresh(), form, false);
|
||||
JSONObject refreshTokenObject = JSONObject.parseObject(response);
|
||||
|
||||
this.checkResponse(refreshTokenObject);
|
||||
|
||||
|
||||
@@ -1,89 +0,0 @@
|
||||
package me.zhyd.oauth.request;
|
||||
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import me.zhyd.oauth.config.AuthConfig;
|
||||
import me.zhyd.oauth.config.AuthSource;
|
||||
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;
|
||||
|
||||
/**
|
||||
* 腾讯云登录
|
||||
*
|
||||
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class AuthTencentCloudRequest extends AuthDefaultRequest {
|
||||
|
||||
public AuthTencentCloudRequest(AuthConfig config) {
|
||||
super(config, AuthSource.TENCENT_CLOUD);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AuthToken getAccessToken(AuthCallback authCallback) {
|
||||
HttpResponse response = doGetAuthorizationCode(authCallback.getCode());
|
||||
JSONObject accessTokenObject = JSONObject.parseObject(response.body());
|
||||
this.checkResponse(accessTokenObject);
|
||||
return AuthToken.builder()
|
||||
.accessToken(accessTokenObject.getString("access_token"))
|
||||
.expireIn(accessTokenObject.getIntValue("expires_in"))
|
||||
.refreshToken(accessTokenObject.getString("refresh_token"))
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AuthUser getUserInfo(AuthToken authToken) {
|
||||
HttpResponse response = doGetUserInfo(authToken);
|
||||
JSONObject object = JSONObject.parseObject(response.body());
|
||||
this.checkResponse(object);
|
||||
|
||||
object = object.getJSONObject("data");
|
||||
return AuthUser.builder()
|
||||
.uuid(object.getString("id"))
|
||||
.username(object.getString("name"))
|
||||
.avatar("https://dev.tencent.com/" + object.getString("avatar"))
|
||||
.blog("https://dev.tencent.com/" + object.getString("path"))
|
||||
.nickname(object.getString("name"))
|
||||
.company(object.getString("company"))
|
||||
.location(object.getString("location"))
|
||||
.gender(AuthUserGender.getRealGender(object.getString("sex")))
|
||||
.email(object.getString("email"))
|
||||
.remark(object.getString("slogan"))
|
||||
.token(authToken)
|
||||
.source(source)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查响应内容是否正确
|
||||
*
|
||||
* @param object 请求响应内容
|
||||
*/
|
||||
private void checkResponse(JSONObject object) {
|
||||
if (object.getIntValue("code") != 0) {
|
||||
throw new AuthException(object.getString("msg"));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回带{@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("scope", "user")
|
||||
.queryParam("state", getRealState(state))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,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;
|
||||
@@ -21,13 +21,17 @@ import me.zhyd.oauth.utils.UrlBuilder;
|
||||
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
|
||||
protected AuthToken getAccessToken(AuthCallback authCallback) {
|
||||
HttpResponse response = doGetAuthorizationCode(authCallback.getCode());
|
||||
JSONObject accessTokenObject = JSONObject.parseObject(response.body());
|
||||
String response = doGetAuthorizationCode(authCallback.getCode());
|
||||
JSONObject accessTokenObject = JSONObject.parseObject(response);
|
||||
|
||||
this.checkResponse(accessTokenObject);
|
||||
|
||||
@@ -40,9 +44,9 @@ public class AuthToutiaoRequest extends AuthDefaultRequest {
|
||||
|
||||
@Override
|
||||
protected AuthUser getUserInfo(AuthToken authToken) {
|
||||
HttpResponse userResponse = doGetUserInfo(authToken);
|
||||
String userResponse = doGetUserInfo(authToken);
|
||||
|
||||
JSONObject userProfile = JSONObject.parseObject(userResponse.body());
|
||||
JSONObject userProfile = JSONObject.parseObject(userResponse);
|
||||
|
||||
this.checkResponse(userProfile);
|
||||
|
||||
@@ -52,6 +56,7 @@ public class AuthToutiaoRequest extends AuthDefaultRequest {
|
||||
String anonymousUserName = "匿名用户";
|
||||
|
||||
return AuthUser.builder()
|
||||
.rawUserInfo(user)
|
||||
.uuid(user.getString("uid"))
|
||||
.username(isAnonymousUser ? anonymousUserName : user.getString("screen_name"))
|
||||
.nickname(isAnonymousUser ? anonymousUserName : user.getString("screen_name"))
|
||||
@@ -59,7 +64,7 @@ public class AuthToutiaoRequest extends AuthDefaultRequest {
|
||||
.remark(user.getString("description"))
|
||||
.gender(AuthUserGender.getRealGender(user.getString("gender")))
|
||||
.token(authToken)
|
||||
.source(source)
|
||||
.source(source.toString())
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -119,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,183 @@
|
||||
package me.zhyd.oauth.request;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import me.zhyd.oauth.utils.HttpUtils;
|
||||
import com.xkcoding.http.constants.Constants;
|
||||
import com.xkcoding.http.support.HttpHeader;
|
||||
import com.xkcoding.http.util.MapUtil;
|
||||
import me.zhyd.oauth.cache.AuthStateCache;
|
||||
import me.zhyd.oauth.config.AuthConfig;
|
||||
import me.zhyd.oauth.model.AuthCallback;
|
||||
import me.zhyd.oauth.model.AuthToken;
|
||||
import me.zhyd.oauth.model.AuthUser;
|
||||
import me.zhyd.oauth.utils.GlobalAuthUtils;
|
||||
import me.zhyd.oauth.utils.UrlBuilder;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static me.zhyd.oauth.config.AuthDefaultSource.TWITTER;
|
||||
import static me.zhyd.oauth.utils.GlobalAuthUtils.generateTwitterSignature;
|
||||
import static me.zhyd.oauth.utils.GlobalAuthUtils.urlEncode;
|
||||
|
||||
/**
|
||||
* Twitter登录
|
||||
*
|
||||
* @author hongwei.peng (pengisgood(at)gmail(dot)com)
|
||||
* @since 1.13.0
|
||||
*/
|
||||
public class AuthTwitterRequest extends AuthDefaultRequest {
|
||||
|
||||
private static final String PREAMBLE = "OAuth";
|
||||
|
||||
public AuthTwitterRequest(AuthConfig config) {
|
||||
super(config, TWITTER);
|
||||
}
|
||||
|
||||
public AuthTwitterRequest(AuthConfig config, AuthStateCache authStateCache) {
|
||||
super(config, TWITTER, authStateCache);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state}
|
||||
*
|
||||
* @param state state 验证授权流程的参数,可以防止csrf
|
||||
* @return 返回授权地址
|
||||
* @since 1.9.3
|
||||
*/
|
||||
@Override
|
||||
public String authorize(String state) {
|
||||
AuthToken token = this.getRequestToken();
|
||||
return UrlBuilder.fromBaseUrl(source.authorize())
|
||||
.queryParam("oauth_token", token.getOauthToken())
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtaining a request token
|
||||
* https://developer.twitter.com/en/docs/twitter-for-websites/log-in-with-twitter/guides/implementing-sign-in-with-twitter
|
||||
*
|
||||
* @return request token
|
||||
*/
|
||||
public AuthToken getRequestToken() {
|
||||
String baseUrl = "https://api.twitter.com/oauth/request_token";
|
||||
|
||||
Map<String, String> oauthParams = buildOauthParams();
|
||||
oauthParams.put("oauth_callback", config.getRedirectUri());
|
||||
oauthParams.put("oauth_signature", generateTwitterSignature(oauthParams, "POST", baseUrl, config.getClientSecret(), null));
|
||||
String header = buildHeader(oauthParams);
|
||||
|
||||
HttpHeader httpHeader = new HttpHeader();
|
||||
httpHeader.add("Authorization", header);
|
||||
httpHeader.add("User-Agent", "themattharris' HTTP Client");
|
||||
httpHeader.add("Host", "api.twitter.com");
|
||||
httpHeader.add("Accept", "*/*");
|
||||
String requestToken = new HttpUtils(config.getHttpConfig()).post(baseUrl, null, httpHeader);
|
||||
|
||||
Map<String, String> res = MapUtil.parseStringToMap(requestToken, false);
|
||||
|
||||
return AuthToken.builder()
|
||||
.oauthToken(res.get("oauth_token"))
|
||||
.oauthTokenSecret(res.get("oauth_token_secret"))
|
||||
.oauthCallbackConfirmed(Boolean.valueOf(res.get("oauth_callback_confirmed")))
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert request token to access token
|
||||
* https://developer.twitter.com/en/docs/twitter-for-websites/log-in-with-twitter/guides/implementing-sign-in-with-twitter
|
||||
*
|
||||
* @return access token
|
||||
*/
|
||||
@Override
|
||||
protected AuthToken getAccessToken(AuthCallback authCallback) {
|
||||
Map<String, String> oauthParams = buildOauthParams();
|
||||
oauthParams.put("oauth_token", authCallback.getOauth_token());
|
||||
oauthParams.put("oauth_verifier", authCallback.getOauth_verifier());
|
||||
oauthParams.put("oauth_signature", generateTwitterSignature(oauthParams, "POST", source.accessToken(), config.getClientSecret(), authCallback
|
||||
.getOauth_token()));
|
||||
String header = buildHeader(oauthParams);
|
||||
|
||||
HttpHeader httpHeader = new HttpHeader();
|
||||
httpHeader.add("Authorization", header);
|
||||
httpHeader.add(Constants.CONTENT_TYPE, "application/x-www-form-urlencoded");
|
||||
|
||||
Map<String, String> form = new HashMap<>(1);
|
||||
form.put("oauth_verifier", authCallback.getOauth_verifier());
|
||||
String response = new HttpUtils(config.getHttpConfig()).post(source.accessToken(), form, httpHeader, false);
|
||||
|
||||
Map<String, String> requestToken = MapUtil.parseStringToMap(response, false);
|
||||
|
||||
return AuthToken.builder()
|
||||
.oauthToken(requestToken.get("oauth_token"))
|
||||
.oauthTokenSecret(requestToken.get("oauth_token_secret"))
|
||||
.userId(requestToken.get("user_id"))
|
||||
.screenName(requestToken.get("screen_name"))
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AuthUser getUserInfo(AuthToken authToken) {
|
||||
Map<String, String> queryParams = new HashMap<>();
|
||||
queryParams.put("user_id", authToken.getUserId());
|
||||
queryParams.put("screen_name", authToken.getScreenName());
|
||||
queryParams.put("include_entities", Boolean.toString(true));
|
||||
|
||||
Map<String, String> oauthParams = buildOauthParams();
|
||||
oauthParams.put("oauth_token", authToken.getOauthToken());
|
||||
|
||||
Map<String, String> params = new HashMap<>(oauthParams);
|
||||
params.putAll(queryParams);
|
||||
oauthParams.put("oauth_signature", generateTwitterSignature(params, "GET", source.userInfo(), config.getClientSecret(), authToken
|
||||
.getOauthTokenSecret()));
|
||||
String header = buildHeader(oauthParams);
|
||||
|
||||
HttpHeader httpHeader = new HttpHeader();
|
||||
httpHeader.add("Authorization", header);
|
||||
String response = new HttpUtils(config.getHttpConfig()).get(userInfoUrl(authToken), null, httpHeader, false);
|
||||
JSONObject userInfo = JSONObject.parseObject(response);
|
||||
|
||||
return AuthUser.builder()
|
||||
.rawUserInfo(userInfo)
|
||||
.uuid(userInfo.getString("id_str"))
|
||||
.username(userInfo.getString("screen_name"))
|
||||
.nickname(userInfo.getString("name"))
|
||||
.remark(userInfo.getString("description"))
|
||||
.avatar(userInfo.getString("profile_image_url_https"))
|
||||
.blog(userInfo.getString("url"))
|
||||
.location(userInfo.getString("location"))
|
||||
.avatar(userInfo.getString("profile_image_url"))
|
||||
.source(source.toString())
|
||||
.token(authToken)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String userInfoUrl(AuthToken authToken) {
|
||||
return UrlBuilder.fromBaseUrl(source.userInfo())
|
||||
.queryParam("user_id", authToken.getUserId())
|
||||
.queryParam("screen_name", authToken.getScreenName())
|
||||
.queryParam("include_entities", true)
|
||||
.build();
|
||||
}
|
||||
|
||||
private Map<String, String> buildOauthParams() {
|
||||
Map<String, String> params = new HashMap<>(5);
|
||||
params.put("oauth_consumer_key", config.getClientId());
|
||||
params.put("oauth_nonce", GlobalAuthUtils.generateNonce(32));
|
||||
params.put("oauth_signature_method", "HMAC-SHA1");
|
||||
params.put("oauth_timestamp", GlobalAuthUtils.getTimestamp());
|
||||
params.put("oauth_version", "1.0");
|
||||
return params;
|
||||
}
|
||||
|
||||
private String buildHeader(Map<String, String> oauthParams) {
|
||||
final StringBuilder sb = new StringBuilder(PREAMBLE + " ");
|
||||
|
||||
for (Map.Entry<String, String> param : oauthParams.entrySet()) {
|
||||
sb.append(param.getKey()).append("=\"").append(urlEncode(param.getValue())).append('"').append(", ");
|
||||
}
|
||||
|
||||
return sb.deleteCharAt(sb.length() - 2).toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
package me.zhyd.oauth.request;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import me.zhyd.oauth.utils.HttpUtils;
|
||||
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) {
|
||||
String 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) {
|
||||
String response = doGetUserInfo(authToken);
|
||||
JSONObject object = this.checkResponse(response);
|
||||
|
||||
// 返回 OpenId 或其他,均代表非当前企业用户,不支持
|
||||
if (!object.containsKey("UserId")) {
|
||||
throw new AuthException(AuthResponseStatus.UNIDENTIFIED_PLATFORM, source);
|
||||
}
|
||||
String userId = object.getString("UserId");
|
||||
String userDetailResponse = getUserDetail(authToken.getAccessToken(), userId);
|
||||
JSONObject userDetail = this.checkResponse(userDetailResponse);
|
||||
|
||||
return AuthUser.builder()
|
||||
.rawUserInfo(userDetail)
|
||||
.username(userDetail.getString("name"))
|
||||
.nickname(userDetail.getString("alias"))
|
||||
.avatar(userDetail.getString("avatar"))
|
||||
.location(userDetail.getString("address"))
|
||||
.email(userDetail.getString("email"))
|
||||
.uuid(userId)
|
||||
.gender(AuthUserGender.getWechatRealGender(userDetail.getString("gender")))
|
||||
.token(authToken)
|
||||
.source(source.toString())
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验请求结果
|
||||
*
|
||||
* @param response 请求结果
|
||||
* @return 如果请求结果正常,则返回JSONObject
|
||||
*/
|
||||
private JSONObject checkResponse(String response) {
|
||||
JSONObject object = JSONObject.parseObject(response);
|
||||
|
||||
if (object.containsKey("errcode") && object.getIntValue("errcode") != 0) {
|
||||
throw new AuthException(object.getString("errmsg"), source);
|
||||
}
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回带{@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 String 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 new HttpUtils(config.getHttpConfig()).get(userDetailUrl);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
package me.zhyd.oauth.request;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import me.zhyd.oauth.utils.HttpUtils;
|
||||
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.GlobalAuthUtils;
|
||||
import me.zhyd.oauth.utils.UrlBuilder;
|
||||
|
||||
/**
|
||||
* 微信公众平台登录
|
||||
*
|
||||
* @author yangkai.shen (https://xkcoding.com)
|
||||
* @since 1.1.0
|
||||
*/
|
||||
public class AuthWeChatMpRequest extends AuthDefaultRequest {
|
||||
public AuthWeChatMpRequest(AuthConfig config) {
|
||||
super(config, AuthDefaultSource.WECHAT_MP);
|
||||
}
|
||||
|
||||
public AuthWeChatMpRequest(AuthConfig config, AuthStateCache authStateCache) {
|
||||
super(config, AuthDefaultSource.WECHAT_MP, authStateCache);
|
||||
}
|
||||
|
||||
/**
|
||||
* 微信的特殊性,此时返回的信息同时包含 openid 和 access_token
|
||||
*
|
||||
* @param authCallback 回调返回的参数
|
||||
* @return 所有信息
|
||||
*/
|
||||
@Override
|
||||
protected AuthToken getAccessToken(AuthCallback authCallback) {
|
||||
return this.getToken(accessTokenUrl(authCallback.getCode()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AuthUser getUserInfo(AuthToken authToken) {
|
||||
String openId = authToken.getOpenId();
|
||||
|
||||
String response = doGetUserInfo(authToken);
|
||||
JSONObject object = JSONObject.parseObject(response);
|
||||
|
||||
this.checkResponse(object);
|
||||
|
||||
String location = String.format("%s-%s-%s", object.getString("country"), object.getString("province"), object.getString("city"));
|
||||
|
||||
if (object.containsKey("unionid")) {
|
||||
authToken.setUnionId(object.getString("unionid"));
|
||||
}
|
||||
|
||||
return AuthUser.builder()
|
||||
.rawUserInfo(object)
|
||||
.username(object.getString("nickname"))
|
||||
.nickname(object.getString("nickname"))
|
||||
.avatar(object.getString("headimgurl"))
|
||||
.location(location)
|
||||
.uuid(openId)
|
||||
.gender(AuthUserGender.getWechatRealGender(object.getString("sex")))
|
||||
.token(authToken)
|
||||
.source(source.toString())
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthResponse refresh(AuthToken oldToken) {
|
||||
return AuthResponse.builder()
|
||||
.code(AuthResponseStatus.SUCCESS.getCode())
|
||||
.data(this.getToken(refreshTokenUrl(oldToken.getRefreshToken())))
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查响应内容是否正确
|
||||
*
|
||||
* @param object 请求响应内容
|
||||
*/
|
||||
private void checkResponse(JSONObject object) {
|
||||
if (object.containsKey("errcode")) {
|
||||
throw new AuthException(object.getIntValue("errcode"), object.getString("errmsg"));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取token,适用于获取access_token和刷新token
|
||||
*
|
||||
* @param accessTokenUrl 实际请求token的地址
|
||||
* @return token对象
|
||||
*/
|
||||
private AuthToken getToken(String accessTokenUrl) {
|
||||
String response = new HttpUtils(config.getHttpConfig()).get(accessTokenUrl);
|
||||
JSONObject accessTokenObject = JSONObject.parseObject(response);
|
||||
|
||||
this.checkResponse(accessTokenObject);
|
||||
|
||||
return AuthToken.builder()
|
||||
.accessToken(accessTokenObject.getString("access_token"))
|
||||
.refreshToken(accessTokenObject.getString("refresh_token"))
|
||||
.expireIn(accessTokenObject.getIntValue("expires_in"))
|
||||
.openId(accessTokenObject.getString("openid"))
|
||||
.scope(accessTokenObject.getString("scope"))
|
||||
.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("appid", config.getClientId())
|
||||
.queryParam("redirect_uri", GlobalAuthUtils.urlEncode(config.getRedirectUri()))
|
||||
.queryParam("response_type", "code")
|
||||
.queryParam("scope", "snsapi_userinfo")
|
||||
.queryParam("state", getRealState(state).concat("#wechat_redirect"))
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回获取accessToken的url
|
||||
*
|
||||
* @param code 授权码
|
||||
* @return 返回获取accessToken的url
|
||||
*/
|
||||
@Override
|
||||
protected String accessTokenUrl(String code) {
|
||||
return UrlBuilder.fromBaseUrl(source.accessToken())
|
||||
.queryParam("appid", config.getClientId())
|
||||
.queryParam("secret", config.getClientSecret())
|
||||
.queryParam("code", code)
|
||||
.queryParam("grant_type", "authorization_code")
|
||||
.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("openid", authToken.getOpenId())
|
||||
.queryParam("lang", "zh_CN")
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回获取userInfo的url
|
||||
*
|
||||
* @param refreshToken getAccessToken方法返回的refreshToken
|
||||
* @return 返回获取userInfo的url
|
||||
*/
|
||||
@Override
|
||||
protected String refreshTokenUrl(String refreshToken) {
|
||||
return UrlBuilder.fromBaseUrl(source.refresh())
|
||||
.queryParam("appid", config.getClientId())
|
||||
.queryParam("grant_type", "refresh_token")
|
||||
.queryParam("refresh_token", refreshToken)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
+22
-14
@@ -1,25 +1,32 @@
|
||||
package me.zhyd.oauth.request;
|
||||
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import me.zhyd.oauth.utils.HttpUtils;
|
||||
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)
|
||||
* @since 1.1.0
|
||||
*/
|
||||
public class AuthWeChatRequest extends AuthDefaultRequest {
|
||||
public AuthWeChatRequest(AuthConfig config) {
|
||||
super(config, AuthSource.WECHAT);
|
||||
public class AuthWeChatOpenRequest extends AuthDefaultRequest {
|
||||
public AuthWeChatOpenRequest(AuthConfig config) {
|
||||
super(config, AuthDefaultSource.WECHAT_OPEN);
|
||||
}
|
||||
|
||||
public AuthWeChatOpenRequest(AuthConfig config, AuthStateCache authStateCache) {
|
||||
super(config, AuthDefaultSource.WECHAT_OPEN, authStateCache);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -37,8 +44,8 @@ public class AuthWeChatRequest extends AuthDefaultRequest {
|
||||
protected AuthUser getUserInfo(AuthToken authToken) {
|
||||
String openId = authToken.getOpenId();
|
||||
|
||||
HttpResponse response = doGetUserInfo(authToken);
|
||||
JSONObject object = JSONObject.parseObject(response.body());
|
||||
String response = doGetUserInfo(authToken);
|
||||
JSONObject object = JSONObject.parseObject(response);
|
||||
|
||||
this.checkResponse(object);
|
||||
|
||||
@@ -49,14 +56,15 @@ public class AuthWeChatRequest extends AuthDefaultRequest {
|
||||
}
|
||||
|
||||
return AuthUser.builder()
|
||||
.rawUserInfo(object)
|
||||
.username(object.getString("nickname"))
|
||||
.nickname(object.getString("nickname"))
|
||||
.avatar(object.getString("headimgurl"))
|
||||
.location(location)
|
||||
.uuid(openId)
|
||||
.gender(AuthUserGender.getRealGender(object.getString("sex")))
|
||||
.gender(AuthUserGender.getWechatRealGender(object.getString("sex")))
|
||||
.token(authToken)
|
||||
.source(source)
|
||||
.source(source.toString())
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -86,8 +94,8 @@ public class AuthWeChatRequest extends AuthDefaultRequest {
|
||||
* @return token对象
|
||||
*/
|
||||
private AuthToken getToken(String accessTokenUrl) {
|
||||
HttpResponse response = HttpRequest.get(accessTokenUrl).execute();
|
||||
JSONObject accessTokenObject = JSONObject.parseObject(response.body());
|
||||
String response = new HttpUtils(config.getHttpConfig()).get(accessTokenUrl);
|
||||
JSONObject accessTokenObject = JSONObject.parseObject(response);
|
||||
|
||||
this.checkResponse(accessTokenObject);
|
||||
|
||||
@@ -1,13 +1,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.utils.HttpUtils;
|
||||
import com.xkcoding.http.support.HttpHeader;
|
||||
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.AuthCallback;
|
||||
import me.zhyd.oauth.model.AuthResponse;
|
||||
import me.zhyd.oauth.model.AuthToken;
|
||||
import me.zhyd.oauth.model.AuthUser;
|
||||
import me.zhyd.oauth.utils.IpUtils;
|
||||
@@ -24,14 +27,17 @@ import me.zhyd.oauth.utils.UrlBuilder;
|
||||
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
|
||||
protected AuthToken getAccessToken(AuthCallback authCallback) {
|
||||
HttpResponse response = doPostAuthorizationCode(authCallback.getCode());
|
||||
String accessTokenStr = response.body();
|
||||
JSONObject accessTokenObject = JSONObject.parseObject(accessTokenStr);
|
||||
String response = doPostAuthorizationCode(authCallback.getCode());
|
||||
JSONObject accessTokenObject = JSONObject.parseObject(response);
|
||||
if (accessTokenObject.containsKey("error")) {
|
||||
throw new AuthException(accessTokenObject.getString("error_description"));
|
||||
}
|
||||
@@ -48,16 +54,17 @@ public class AuthWeiboRequest extends AuthDefaultRequest {
|
||||
String accessToken = authToken.getAccessToken();
|
||||
String uid = authToken.getUid();
|
||||
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.getLocalIp())
|
||||
.execute();
|
||||
String userInfo = response.body();
|
||||
|
||||
HttpHeader httpHeader = new HttpHeader();
|
||||
httpHeader.add("Authorization", "OAuth2 " + oauthParam);
|
||||
httpHeader.add("API-RemoteIP", IpUtils.getLocalIp());
|
||||
String userInfo = new HttpUtils(config.getHttpConfig()).get(userInfoUrl(authToken), null, httpHeader, false);
|
||||
JSONObject object = JSONObject.parseObject(userInfo);
|
||||
if (object.containsKey("error")) {
|
||||
throw new AuthException(object.getString("error"));
|
||||
}
|
||||
return AuthUser.builder()
|
||||
.rawUserInfo(object)
|
||||
.uuid(object.getString("id"))
|
||||
.username(object.getString("name"))
|
||||
.avatar(object.getString("profile_image_url"))
|
||||
@@ -68,7 +75,7 @@ public class AuthWeiboRequest extends AuthDefaultRequest {
|
||||
.remark(object.getString("description"))
|
||||
.gender(AuthUserGender.getRealGender(object.getString("gender")))
|
||||
.token(authToken)
|
||||
.source(source)
|
||||
.source(source.toString())
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -85,4 +92,19 @@ public class AuthWeiboRequest extends AuthDefaultRequest {
|
||||
.queryParam("uid", authToken.getUid())
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthResponse revoke(AuthToken authToken) {
|
||||
String response = doGetRevoke(authToken);
|
||||
JSONObject object = JSONObject.parseObject(response);
|
||||
if (object.containsKey("error")) {
|
||||
return AuthResponse.builder()
|
||||
.code(AuthResponseStatus.FAILURE.getCode())
|
||||
.msg(object.getString("error"))
|
||||
.build();
|
||||
}
|
||||
// 返回 result = true 表示取消授权成功,否则失败
|
||||
AuthResponseStatus status = object.getBooleanValue("result") ? AuthResponseStatus.SUCCESS : AuthResponseStatus.FAILURE;
|
||||
return AuthResponse.builder().code(status.getCode()).msg(status.getMsg()).build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* JustAuth核心组件,所有授权登录都是基于{@code request}实现
|
||||
*/
|
||||
package me.zhyd.oauth.request;
|
||||
@@ -2,9 +2,11 @@ package me.zhyd.oauth.utils;
|
||||
|
||||
import me.zhyd.oauth.cache.AuthStateCache;
|
||||
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.AuthCallback;
|
||||
|
||||
/**
|
||||
* 授权配置类的校验器
|
||||
@@ -24,12 +26,18 @@ public class AuthChecker {
|
||||
*/
|
||||
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());
|
||||
}
|
||||
if (isSupported && AuthDefaultSource.CODING == source) {
|
||||
isSupported = StringUtils.isNotEmpty(config.getCodingGroupName());
|
||||
}
|
||||
return isSupported;
|
||||
}
|
||||
|
||||
@@ -42,39 +50,64 @@ public class AuthChecker {
|
||||
*/
|
||||
public static void checkConfig(AuthConfig config, AuthSource source) {
|
||||
String redirectUri = config.getRedirectUri();
|
||||
if (!GlobalAuthUtil.isHttpProtocol(redirectUri) && !GlobalAuthUtil.isHttpsProtocol(redirectUri)) {
|
||||
throw new AuthException(AuthResponseStatus.ILLEGAL_REDIRECT_URI);
|
||||
if (!GlobalAuthUtils.isHttpProtocol(redirectUri) && !GlobalAuthUtils.isHttpsProtocol(redirectUri)) {
|
||||
throw new AuthException(AuthResponseStatus.ILLEGAL_REDIRECT_URI, source);
|
||||
}
|
||||
// facebook的回调地址必须为https的链接
|
||||
if (AuthSource.FACEBOOK == source && !GlobalAuthUtil.isHttpsProtocol(redirectUri)) {
|
||||
throw new AuthException(AuthResponseStatus.ILLEGAL_REDIRECT_URI);
|
||||
if (AuthDefaultSource.FACEBOOK == source && !GlobalAuthUtils.isHttpsProtocol(redirectUri)) {
|
||||
// Facebook's redirect uri must use the HTTPS protocol
|
||||
throw new AuthException(AuthResponseStatus.ILLEGAL_REDIRECT_URI, source);
|
||||
}
|
||||
// 支付宝在创建回调地址时,不允许使用localhost或者127.0.0.1
|
||||
if (AuthSource.ALIPAY == source && GlobalAuthUtil.isLocalHost(redirectUri)) {
|
||||
throw new AuthException(AuthResponseStatus.ILLEGAL_REDIRECT_URI);
|
||||
if (AuthDefaultSource.ALIPAY == source && GlobalAuthUtils.isLocalHost(redirectUri)) {
|
||||
// The redirect uri of alipay is forbidden to use localhost or 127.0.0.1
|
||||
throw new AuthException(AuthResponseStatus.ILLEGAL_REDIRECT_URI, source);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验回调传回的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) {
|
||||
// 推特平台不支持回调 code 和 state
|
||||
if (source == AuthDefaultSource.TWITTER) {
|
||||
return;
|
||||
}
|
||||
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);
|
||||
throw new AuthException(AuthResponseStatus.ILLEGAL_CODE, source);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验回调传回的state
|
||||
* 校验回调传回的{@code state},为空或者不存在
|
||||
* <p>
|
||||
* {@code state}不存在的情况只有两种:
|
||||
* 1. {@code state}已使用,被正常清除
|
||||
* 2. {@code state}为前端伪造,本身就不存在
|
||||
*
|
||||
* @param state {@code state}一定不为空
|
||||
* @param state {@code state}一定不为空
|
||||
* @param source {@code source}当前授权平台
|
||||
* @param authStateCache {@code authStateCache} state缓存实现
|
||||
*/
|
||||
public static void checkState(String state) {
|
||||
if (StringUtils.isEmpty(state) || !AuthStateCache.containsKey(state)) {
|
||||
throw new AuthException(AuthResponseStatus.ILLEGAL_REQUEST);
|
||||
public static void checkState(String state, AuthSource source, AuthStateCache authStateCache) {
|
||||
// 推特平台不支持回调 code 和 state
|
||||
if (source == AuthDefaultSource.TWITTER) {
|
||||
return;
|
||||
}
|
||||
if (StringUtils.isEmpty(state) || !authStateCache.containsKey(state)) {
|
||||
throw new AuthException(AuthResponseStatus.ILLEGAL_STATUS, source);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,197 @@
|
||||
package me.zhyd.oauth.utils;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* Base64编码
|
||||
*
|
||||
* @author looly
|
||||
* @since 3.2.0
|
||||
*/
|
||||
public class Base64Utils {
|
||||
|
||||
private static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;
|
||||
/**
|
||||
* 标准编码表
|
||||
*/
|
||||
private static final byte[] STANDARD_ENCODE_TABLE = { //
|
||||
'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', '0', '1', '2', '3', //
|
||||
'4', '5', '6', '7', '8', '9', '+', '/' //
|
||||
};
|
||||
/**
|
||||
* URL安全的编码表,将 + 和 / 替换为 - 和 _
|
||||
*/
|
||||
private static final byte[] URL_SAFE_ENCODE_TABLE = { //
|
||||
'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', '0', '1', '2', '3', //
|
||||
'4', '5', '6', '7', '8', '9', '-', '_' //
|
||||
};
|
||||
|
||||
// -------------------------------------------------------------------- encode
|
||||
|
||||
/**
|
||||
* 编码为Base64,非URL安全的
|
||||
*
|
||||
* @param arr 被编码的数组
|
||||
* @param lineSep 在76个char之后是CRLF还是EOF
|
||||
* @return 编码后的bytes
|
||||
*/
|
||||
public static byte[] encode(byte[] arr, boolean lineSep) {
|
||||
return encode(arr, lineSep, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 编码为Base64,URL安全的
|
||||
*
|
||||
* @param arr 被编码的数组
|
||||
* @param lineSep 在76个char之后是CRLF还是EOF
|
||||
* @return 编码后的bytes
|
||||
* @since 3.0.6
|
||||
*/
|
||||
public static byte[] encodeUrlSafe(byte[] arr, boolean lineSep) {
|
||||
return encode(arr, lineSep, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* base64编码
|
||||
*
|
||||
* @param source 被编码的base64字符串
|
||||
* @return 被加密后的字符串
|
||||
*/
|
||||
public static String encode(CharSequence source) {
|
||||
return encode(source, DEFAULT_CHARSET);
|
||||
}
|
||||
|
||||
/**
|
||||
* base64编码,URL安全
|
||||
*
|
||||
* @param source 被编码的base64字符串
|
||||
* @return 被加密后的字符串
|
||||
* @since 3.0.6
|
||||
*/
|
||||
public static String encodeUrlSafe(CharSequence source) {
|
||||
return encodeUrlSafe(source, DEFAULT_CHARSET);
|
||||
}
|
||||
|
||||
/**
|
||||
* base64编码
|
||||
*
|
||||
* @param source 被编码的base64字符串
|
||||
* @param charset 字符集
|
||||
* @return 被加密后的字符串
|
||||
*/
|
||||
public static String encode(CharSequence source, Charset charset) {
|
||||
return encode(StringUtils.bytes(source, charset));
|
||||
}
|
||||
|
||||
/**
|
||||
* base64编码,URL安全的
|
||||
*
|
||||
* @param source 被编码的base64字符串
|
||||
* @param charset 字符集
|
||||
* @return 被加密后的字符串
|
||||
* @since 3.0.6
|
||||
*/
|
||||
public static String encodeUrlSafe(CharSequence source, Charset charset) {
|
||||
return encodeUrlSafe(StringUtils.bytes(source, charset));
|
||||
}
|
||||
|
||||
/**
|
||||
* base64编码
|
||||
*
|
||||
* @param source 被编码的base64字符串
|
||||
* @return 被加密后的字符串
|
||||
*/
|
||||
public static String encode(byte[] source) {
|
||||
return StringUtils.str(encode(source, false), DEFAULT_CHARSET);
|
||||
}
|
||||
|
||||
/**
|
||||
* base64编码,URL安全的
|
||||
*
|
||||
* @param source 被编码的base64字符串
|
||||
* @return 被加密后的字符串
|
||||
* @since 3.0.6
|
||||
*/
|
||||
public static String encodeUrlSafe(byte[] source) {
|
||||
return StringUtils.str(encodeUrlSafe(source, false), DEFAULT_CHARSET);
|
||||
}
|
||||
|
||||
/**
|
||||
* 编码为Base64<br>
|
||||
* 如果isMultiLine为<code>true</code>,则每76个字符一个换行符,否则在一行显示
|
||||
*
|
||||
* @param arr 被编码的数组
|
||||
* @param isMultiLine 在76个char之后是CRLF还是EOF
|
||||
* @param isUrlSafe 是否使用URL安全字符,一般为<code>false</code>
|
||||
* @return 编码后的bytes
|
||||
*/
|
||||
public static byte[] encode(byte[] arr, boolean isMultiLine, boolean isUrlSafe) {
|
||||
if (null == arr) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int len = arr.length;
|
||||
if (len == 0) {
|
||||
return new byte[0];
|
||||
}
|
||||
|
||||
int evenlen = (len / 3) * 3;
|
||||
int cnt = ((len - 1) / 3 + 1) << 2;
|
||||
int destlen = cnt + (isMultiLine ? (cnt - 1) / 76 << 1 : 0);
|
||||
byte[] dest = new byte[destlen];
|
||||
|
||||
byte[] encodeTable = isUrlSafe ? URL_SAFE_ENCODE_TABLE : STANDARD_ENCODE_TABLE;
|
||||
|
||||
for (int s = 0, d = 0, cc = 0; s < evenlen; ) {
|
||||
int i = (arr[s++] & 0xff) << 16 | (arr[s++] & 0xff) << 8 | (arr[s++] & 0xff);
|
||||
|
||||
dest[d++] = encodeTable[(i >>> 18) & 0x3f];
|
||||
dest[d++] = encodeTable[(i >>> 12) & 0x3f];
|
||||
dest[d++] = encodeTable[(i >>> 6) & 0x3f];
|
||||
dest[d++] = encodeTable[i & 0x3f];
|
||||
|
||||
if (isMultiLine && ++cc == 19 && d < destlen - 2) {
|
||||
dest[d++] = '\r';
|
||||
dest[d++] = '\n';
|
||||
cc = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int left = len - evenlen;// 剩余位数
|
||||
if (left > 0) {
|
||||
int i = ((arr[evenlen] & 0xff) << 10) | (left == 2 ? ((arr[len - 1] & 0xff) << 2) : 0);
|
||||
|
||||
dest[destlen - 4] = encodeTable[i >> 12];
|
||||
dest[destlen - 3] = encodeTable[(i >>> 6) & 0x3f];
|
||||
|
||||
if (isUrlSafe) {
|
||||
// 在URL Safe模式下,=为URL中的关键字符,不需要补充。空余的byte位要去掉。
|
||||
int urlSafeLen = destlen - 2;
|
||||
if (2 == left) {
|
||||
dest[destlen - 2] = encodeTable[i & 0x3f];
|
||||
urlSafeLen += 1;
|
||||
}
|
||||
byte[] urlSafeDest = new byte[urlSafeLen];
|
||||
System.arraycopy(dest, 0, urlSafeDest, 0, urlSafeLen);
|
||||
return urlSafeDest;
|
||||
} else {
|
||||
dest[destlen - 2] = (left == 2) ? encodeTable[i & 0x3f] : (byte) '=';
|
||||
dest[destlen - 1] = '=';
|
||||
}
|
||||
}
|
||||
return dest;
|
||||
}
|
||||
}
|
||||
@@ -1,123 +0,0 @@
|
||||
package me.zhyd.oauth.utils;
|
||||
|
||||
import cn.hutool.core.codec.Base64;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.http.HttpUtil;
|
||||
import me.zhyd.oauth.exception.AuthException;
|
||||
|
||||
import javax.crypto.Mac;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLDecoder;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 全局的工具类
|
||||
*
|
||||
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class GlobalAuthUtil {
|
||||
private static final Charset DEFAULT_ENCODING = StandardCharsets.UTF_8;
|
||||
private static final String ALGORITHM = "HmacSHA256";
|
||||
|
||||
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)));
|
||||
}
|
||||
|
||||
private static byte[] sign(byte[] key, byte[] data) {
|
||||
try {
|
||||
Mac mac = Mac.getInstance(ALGORITHM);
|
||||
mac.init(new SecretKeySpec(key, ALGORITHM));
|
||||
return mac.doFinal(data);
|
||||
} catch (NoSuchAlgorithmException ex) {
|
||||
throw new AuthException("Unsupported algorithm: " + ALGORITHM, ex);
|
||||
} catch (InvalidKeyException ex) {
|
||||
throw new AuthException("Invalid key: " + Arrays.toString(key), ex);
|
||||
}
|
||||
}
|
||||
|
||||
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");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new AuthException("Failed To Encode Uri", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static String urlDecode(String value) {
|
||||
if (value == null) {
|
||||
return "";
|
||||
}
|
||||
try {
|
||||
return URLDecoder.decode(value, GlobalAuthUtil.DEFAULT_ENCODING.displayName());
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new AuthException("Failed To Decode Uri", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static Map<String, String> parseStringToMap(String accessTokenStr) {
|
||||
Map<String, String> res = new HashMap<>();
|
||||
if (accessTokenStr.contains("&")) {
|
||||
String[] fields = accessTokenStr.split("&");
|
||||
for (String field : fields) {
|
||||
if (field.contains("=")) {
|
||||
String[] keyValue = field.split("=");
|
||||
res.put(GlobalAuthUtil.urlDecode(keyValue[0]), keyValue.length == 2 ? GlobalAuthUtil.urlDecode(keyValue[1]) : null);
|
||||
}
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
public static String parseMapToString(Map<String, Object> params, boolean encode) {
|
||||
List<String> paramList = new ArrayList<>();
|
||||
params.forEach((k, v) -> {
|
||||
if (ObjectUtil.isNull(v)) {
|
||||
paramList.add(k + "=");
|
||||
} else {
|
||||
String valueString = v.toString();
|
||||
paramList.add(k + "=" + (encode ? urlEncode(valueString) : valueString));
|
||||
}
|
||||
});
|
||||
return CollUtil.join(paramList, "&");
|
||||
}
|
||||
|
||||
public static Map<String, Object> parseQueryToMap(String url) {
|
||||
Map<String, Object> paramMap = new HashMap<>();
|
||||
HttpUtil.decodeParamMap(url, "UTF-8").forEach(paramMap::put);
|
||||
return paramMap;
|
||||
}
|
||||
|
||||
public static boolean isHttpProtocol(String url) {
|
||||
if (StringUtils.isEmpty(url)) {
|
||||
return false;
|
||||
}
|
||||
return url.startsWith("http://");
|
||||
}
|
||||
|
||||
public static boolean isHttpsProtocol(String url) {
|
||||
if (StringUtils.isEmpty(url)) {
|
||||
return false;
|
||||
}
|
||||
return url.startsWith("https://");
|
||||
}
|
||||
|
||||
public static boolean isLocalHost(String url) {
|
||||
return StringUtils.isEmpty(url) || url.contains("127.0.0.1") || url.contains("localhost");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,302 @@
|
||||
package me.zhyd.oauth.utils;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import me.zhyd.oauth.exception.AuthException;
|
||||
|
||||
import javax.crypto.Mac;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLDecoder;
|
||||
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.*;
|
||||
|
||||
/**
|
||||
* 全局的工具类
|
||||
*
|
||||
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class GlobalAuthUtils {
|
||||
private static final Charset DEFAULT_ENCODING = StandardCharsets.UTF_8;
|
||||
private static final String HMAC_SHA1 = "HmacSHA1";
|
||||
private static final String HMAC_SHA_256 = "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), HMAC_SHA_256);
|
||||
return urlEncode(new String(Base64Utils.encode(signData, false)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 签名
|
||||
*
|
||||
* @param key key
|
||||
* @param data data
|
||||
* @param algorithm algorithm
|
||||
* @return byte[]
|
||||
*/
|
||||
private static byte[] sign(byte[] key, byte[] data, String algorithm) {
|
||||
try {
|
||||
Mac mac = Mac.getInstance(algorithm);
|
||||
mac.init(new SecretKeySpec(key, algorithm));
|
||||
return mac.doFinal(data);
|
||||
} catch (NoSuchAlgorithmException ex) {
|
||||
throw new AuthException("Unsupported algorithm: " + algorithm, ex);
|
||||
} catch (InvalidKeyException ex) {
|
||||
throw new AuthException("Invalid key: " + Arrays.toString(key), ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 编码
|
||||
*
|
||||
* @param value str
|
||||
* @return encode str
|
||||
*/
|
||||
public static String urlEncode(String value) {
|
||||
if (value == null) {
|
||||
return "";
|
||||
}
|
||||
try {
|
||||
String encoded = URLEncoder.encode(value, GlobalAuthUtils.DEFAULT_ENCODING.displayName());
|
||||
return encoded.replace("+", "%20").replace("*", "%2A").replace("~", "%7E").replace("/", "%2F");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new AuthException("Failed To Encode Uri", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 解码
|
||||
*
|
||||
* @param value str
|
||||
* @return decode str
|
||||
*/
|
||||
public static String urlDecode(String value) {
|
||||
if (value == null) {
|
||||
return "";
|
||||
}
|
||||
try {
|
||||
return URLDecoder.decode(value, GlobalAuthUtils.DEFAULT_ENCODING.displayName());
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new AuthException("Failed To Decode Uri", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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<>(6);
|
||||
if (accessTokenStr.contains("&")) {
|
||||
String[] fields = accessTokenStr.split("&");
|
||||
for (String field : fields) {
|
||||
if (field.contains("=")) {
|
||||
String[] keyValue = field.split("=");
|
||||
res.put(GlobalAuthUtils.urlDecode(keyValue[0]), keyValue.length == 2 ? GlobalAuthUtils.urlDecode(keyValue[1]) : null);
|
||||
}
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* map转字符串,转换后的字符串格式为 {@code xxx=xxx&xxx=xxx}
|
||||
*
|
||||
* @param params 待转换的map
|
||||
* @param encode 是否转码
|
||||
* @return str
|
||||
*/
|
||||
public static String parseMapToString(Map<String, String> params, boolean encode) {
|
||||
if (null == params || params.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
List<String> paramList = new ArrayList<>();
|
||||
params.forEach((k, v) -> {
|
||||
if (null == v) {
|
||||
paramList.add(k + "=");
|
||||
} else {
|
||||
paramList.add(k + "=" + (encode ? urlEncode(v) : v));
|
||||
}
|
||||
});
|
||||
return String.join("&", paramList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为http协议
|
||||
*
|
||||
* @param url 待验证的url
|
||||
* @return true: http协议, false: 非http协议
|
||||
*/
|
||||
public static boolean isHttpProtocol(String url) {
|
||||
if (StringUtils.isEmpty(url)) {
|
||||
return false;
|
||||
}
|
||||
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;
|
||||
}
|
||||
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");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generate nonce with given length
|
||||
*
|
||||
* @param len length
|
||||
* @return nonce string
|
||||
*/
|
||||
public static String generateNonce(int len) {
|
||||
String s = "0123456789QWERTYUIOPLKJHGFDSAZXCVBNMqwertyuioplkjhgfdsazxcvbnm";
|
||||
Random rng = new Random();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < len; i++) {
|
||||
int index = rng.nextInt(62);
|
||||
sb.append(s, index, index + 1);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current timestamp
|
||||
*
|
||||
* @return timestamp string
|
||||
*/
|
||||
public static String getTimestamp() {
|
||||
return String.valueOf(System.currentTimeMillis() / 1000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate Twitter signature
|
||||
* https://developer.twitter.com/en/docs/basics/authentication/guides/creating-a-signature
|
||||
*
|
||||
* @param params parameters including: oauth headers, query params, body params
|
||||
* @param method HTTP method
|
||||
* @param baseUrl base url
|
||||
* @param apiSecret api key secret can be found in the developer portal by viewing the app details page
|
||||
* @param tokenSecret oauth token secret
|
||||
* @return BASE64 encoded signature string
|
||||
*/
|
||||
public static String generateTwitterSignature(Map<String, String> params, String method, String baseUrl, String apiSecret, String tokenSecret) {
|
||||
TreeMap<String, String> map = new TreeMap<>(params);
|
||||
String str = parseMapToString(map, true);
|
||||
String baseStr = method.toUpperCase() + "&" + urlEncode(baseUrl) + "&" + urlEncode(str);
|
||||
String signKey = apiSecret + "&" + (StringUtils.isEmpty(tokenSecret) ? "" : tokenSecret);
|
||||
byte[] signature = sign(signKey.getBytes(DEFAULT_ENCODING), baseStr.getBytes(DEFAULT_ENCODING), HMAC_SHA1);
|
||||
|
||||
return new String(Base64Utils.encode(signature, false));
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成饿了么请求的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<>(parameters);
|
||||
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加密
|
||||
* <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 待加密的字符串
|
||||
* @return md5 str
|
||||
*/
|
||||
public 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();
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成京东宙斯平台的签名字符串
|
||||
* 宙斯签名规则过程如下:
|
||||
* 将所有请求参数按照字母先后顺序排列,例如将access_token,app_key,method,timestamp,v 排序为access_token,app_key,method,timestamp,v
|
||||
* 1.把所有参数名和参数值进行拼接,例如:access_tokenxxxapp_keyxxxmethodxxxxxxtimestampxxxxxxvx
|
||||
* 2.把appSecret夹在字符串的两端,例如:appSecret+XXXX+appSecret
|
||||
* 3.使用MD5进行加密,再转化成大写
|
||||
* link: http://open.jd.com/home/home#/doc/common?listId=890
|
||||
* link: https://github.com/pingjiang/jd-open-api-sdk-src/blob/master/src/main/java/com/jd/open/api/sdk/DefaultJdClient.java
|
||||
*
|
||||
* @param appSecret 京东应用密钥
|
||||
* @param params 签名参数
|
||||
* @return 签名后的字符串
|
||||
* @since 1.15.0
|
||||
*/
|
||||
public static String generateJdSignature(String appSecret, Map<String, Object> params) {
|
||||
Map<String, Object> treeMap = new TreeMap<>(params);
|
||||
StringBuilder signBuilder = new StringBuilder(appSecret);
|
||||
for (Map.Entry<String, Object> entry : treeMap.entrySet()) {
|
||||
String name = entry.getKey();
|
||||
String value = String.valueOf(entry.getValue());
|
||||
if (StringUtils.isNotEmpty(name) && StringUtils.isNotEmpty(value)) {
|
||||
signBuilder.append(name).append(value);
|
||||
}
|
||||
}
|
||||
signBuilder.append(appSecret);
|
||||
return md5(signBuilder.toString()).toUpperCase();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
package me.zhyd.oauth.utils;
|
||||
|
||||
import com.xkcoding.http.HttpUtil;
|
||||
import com.xkcoding.http.config.HttpConfig;
|
||||
import com.xkcoding.http.support.HttpHeader;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* HttpUtil 工具,统一处理 http 请求,方便对 simple-http 做定制
|
||||
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
|
||||
* @version 1.0.0
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class HttpUtils {
|
||||
|
||||
public HttpUtils(HttpConfig config) {
|
||||
HttpUtil.setConfig(config);
|
||||
}
|
||||
|
||||
public HttpUtils() {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* GET 请求
|
||||
*
|
||||
* @param url URL
|
||||
* @return 结果
|
||||
*/
|
||||
public String get(String url) {
|
||||
return HttpUtil.get(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* GET 请求
|
||||
*
|
||||
* @param url URL
|
||||
* @param params 参数
|
||||
* @param header 请求头
|
||||
* @param encode 是否需要 url encode
|
||||
* @return 结果
|
||||
*/
|
||||
public String get(String url, Map<String, String> params, HttpHeader header, boolean encode) {
|
||||
return HttpUtil.get(url, params, header, encode);
|
||||
}
|
||||
|
||||
/**
|
||||
* POST 请求
|
||||
*
|
||||
* @param url URL
|
||||
* @return 结果
|
||||
*/
|
||||
public String post(String url) {
|
||||
return HttpUtil.post(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* POST 请求
|
||||
*
|
||||
* @param url URL
|
||||
* @param data JSON 参数
|
||||
* @return 结果
|
||||
*/
|
||||
public String post(String url, String data) {
|
||||
return HttpUtil.post(url, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* POST 请求
|
||||
*
|
||||
* @param url URL
|
||||
* @param data JSON 参数
|
||||
* @param header 请求头
|
||||
* @return 结果
|
||||
*/
|
||||
public String post(String url, String data, HttpHeader header) {
|
||||
return HttpUtil.post(url, data, header);
|
||||
}
|
||||
|
||||
/**
|
||||
* POST 请求
|
||||
*
|
||||
* @param url URL
|
||||
* @param params form 参数
|
||||
* @param encode 是否需要 url encode
|
||||
* @return 结果
|
||||
*/
|
||||
public String post(String url, Map<String, String> params, boolean encode) {
|
||||
return HttpUtil.post(url, params, encode);
|
||||
}
|
||||
|
||||
/**
|
||||
* POST 请求
|
||||
*
|
||||
* @param url URL
|
||||
* @param params form 参数
|
||||
* @param header 请求头
|
||||
* @param encode 是否需要 url encode
|
||||
* @return 结果
|
||||
*/
|
||||
public String post(String url, Map<String, String> params, HttpHeader header, boolean encode) {
|
||||
return HttpUtil.post(url, params, header, encode);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
package me.zhyd.oauth.utils;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
/**
|
||||
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
|
||||
@@ -36,4 +35,40 @@ public class StringUtils {
|
||||
return str.concat(appendStr);
|
||||
}
|
||||
|
||||
/**
|
||||
* 编码字符串
|
||||
*
|
||||
* @param str 字符串
|
||||
* @param charset 字符集,如果此字段为空,则解码的结果取决于平台
|
||||
* @return 编码后的字节码
|
||||
*/
|
||||
public static byte[] bytes(CharSequence str, Charset charset) {
|
||||
if (str == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (null == charset) {
|
||||
return str.toString().getBytes();
|
||||
}
|
||||
return str.toString().getBytes(charset);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解码字节码
|
||||
*
|
||||
* @param data 字符串
|
||||
* @param charset 字符集,如果此字段为空,则解码的结果取决于平台
|
||||
* @return 解码后的字符串
|
||||
*/
|
||||
public static String str(byte[] data, Charset charset) {
|
||||
if (data == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (null == charset) {
|
||||
return new String(data);
|
||||
}
|
||||
return new String(data, charset);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package me.zhyd.oauth.utils;
|
||||
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.xkcoding.http.util.MapUtil;
|
||||
import com.xkcoding.http.util.StringUtil;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -19,7 +19,7 @@ import java.util.Map;
|
||||
@Setter
|
||||
public class UrlBuilder {
|
||||
|
||||
private final Map<String, Object> params = new LinkedHashMap<>(7);
|
||||
private final Map<String, String> params = new LinkedHashMap<>(7);
|
||||
private String baseUrl;
|
||||
|
||||
private UrlBuilder() {
|
||||
@@ -36,6 +36,16 @@ public class UrlBuilder {
|
||||
return builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* 只读的参数Map
|
||||
*
|
||||
* @return unmodifiable Map
|
||||
* @since 1.15.0
|
||||
*/
|
||||
public Map<String, Object> getReadOnlyParams() {
|
||||
return Collections.unmodifiableMap(params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加参数
|
||||
*
|
||||
@@ -44,8 +54,9 @@ public class UrlBuilder {
|
||||
* @return this UrlBuilder
|
||||
*/
|
||||
public UrlBuilder queryParam(String key, Object value) {
|
||||
Assert.notBlank(key, "参数名不能为空");
|
||||
|
||||
if (StringUtil.isEmpty(key)) {
|
||||
throw new RuntimeException("参数名不能为空");
|
||||
}
|
||||
String valueAsString = (value != null ? value.toString() : null);
|
||||
this.params.put(key, valueAsString);
|
||||
|
||||
@@ -72,7 +83,7 @@ public class UrlBuilder {
|
||||
return this.baseUrl;
|
||||
}
|
||||
String baseUrl = StringUtils.appendIfNotContain(this.baseUrl, "?", "&");
|
||||
String paramString = GlobalAuthUtil.parseMapToString(this.params, encode);
|
||||
String paramString = MapUtil.parseMapToString(this.params, encode);
|
||||
return baseUrl + paramString;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* 提供一些简单的工具和校验等
|
||||
*/
|
||||
package me.zhyd.oauth.utils;
|
||||
@@ -1,280 +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)
|
||||
*/
|
||||
public class AuthRequestTest {
|
||||
|
||||
@Test
|
||||
public void giteeTest() {
|
||||
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(new AuthCallback());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void githubTest() {
|
||||
AuthRequest authRequest = new AuthGithubRequest(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(new AuthCallback());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void weiboTest() {
|
||||
AuthRequest authRequest = new AuthWeiboRequest(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(new AuthCallback());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dingdingTest() {
|
||||
AuthRequest authRequest = new AuthDingTalkRequest(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(new AuthCallback());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void baiduTest() {
|
||||
AuthRequest authRequest = new AuthBaiduRequest(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(new AuthCallback());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void codingTest() {
|
||||
AuthRequest authRequest = new AuthCodingRequest(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(new AuthCallback());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void tencentCloudTest() {
|
||||
AuthRequest authRequest = new AuthTencentCloudRequest(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(new AuthCallback());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void oschinaTest() {
|
||||
AuthRequest authRequest = new AuthOschinaRequest(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(new AuthCallback());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void alipayTest() {
|
||||
AuthRequest authRequest = new AuthAlipayRequest(AuthConfig.builder()
|
||||
.clientId("clientId")
|
||||
.clientSecret("clientSecret")
|
||||
.redirectUri("redirectUri")
|
||||
.alipayPublicKey("publicKey")
|
||||
.build());
|
||||
// 返回授权页面,可自行跳转
|
||||
authRequest.authorize("state");
|
||||
// 授权登录后会返回code(auth_code(仅限支付宝))、state,1.8.0版本后,可以用AuthCallback类作为回调接口的入参
|
||||
// 注:JustAuth默认保存state的时效为3分钟,3分钟内未使用则会自动清除过期的state
|
||||
AuthResponse login = authRequest.login(new AuthCallback());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void qqTest() {
|
||||
AuthRequest authRequest = new AuthQqRequest(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
|
||||
AuthResponse login = authRequest.login(new AuthCallback());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void wechatTest() {
|
||||
AuthRequest authRequest = new AuthWeChatRequest(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
|
||||
AuthResponse login = authRequest.login(new AuthCallback());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void taobaoTest() {
|
||||
AuthRequest authRequest = new AuthTaobaoRequest(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
|
||||
AuthResponse login = authRequest.login(new AuthCallback());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void googleTest() {
|
||||
AuthRequest authRequest = new AuthGoogleRequest(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
|
||||
AuthResponse login = authRequest.login(new AuthCallback());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void facebookTest() {
|
||||
AuthRequest authRequest = new AuthFacebookRequest(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
|
||||
AuthResponse login = authRequest.login(new AuthCallback());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void douyinTest() {
|
||||
AuthRequest authRequest = new AuthDouyinRequest(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
|
||||
AuthResponse login = authRequest.login(new AuthCallback());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void linkedinTest() {
|
||||
AuthRequest authRequest = new AuthLinkedinRequest(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
|
||||
AuthResponse login = authRequest.login(new AuthCallback());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void microsoftTest() {
|
||||
AuthRequest authRequest = new AuthMicrosoftRequest(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
|
||||
AuthResponse login = authRequest.login(new AuthCallback());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void miTest() {
|
||||
AuthRequest authRequest = new AuthMiRequest(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
|
||||
AuthResponse login = authRequest.login(new AuthCallback());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toutiaoTest() {
|
||||
AuthRequest authRequest = new AuthToutiaoRequest(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
|
||||
AuthResponse login = authRequest.login(new AuthCallback());
|
||||
}
|
||||
}
|
||||
@@ -9,24 +9,24 @@ public class AuthStateCacheTest {
|
||||
|
||||
@Test
|
||||
public void cache1() throws InterruptedException {
|
||||
AuthStateCache.cache("key", "value");
|
||||
Assert.assertEquals(AuthStateCache.get("key"), "value");
|
||||
AuthDefaultStateCache.INSTANCE.cache("key", "value");
|
||||
Assert.assertEquals(AuthDefaultStateCache.INSTANCE.get("key"), "value");
|
||||
|
||||
TimeUnit.MILLISECONDS.sleep(4);
|
||||
Assert.assertEquals(AuthStateCache.get("key"), "value");
|
||||
Assert.assertEquals(AuthDefaultStateCache.INSTANCE.get("key"), "value");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cache2() throws InterruptedException {
|
||||
AuthStateCache.cache("key", "value", 10);
|
||||
Assert.assertEquals(AuthStateCache.get("key"), "value");
|
||||
AuthDefaultStateCache.INSTANCE.cache("key", "value", 10);
|
||||
Assert.assertEquals(AuthDefaultStateCache.INSTANCE.get("key"), "value");
|
||||
|
||||
// 没过期
|
||||
TimeUnit.MILLISECONDS.sleep(5);
|
||||
Assert.assertEquals(AuthStateCache.get("key"), "value");
|
||||
Assert.assertEquals(AuthDefaultStateCache.INSTANCE.get("key"), "value");
|
||||
|
||||
// 过期
|
||||
TimeUnit.MILLISECONDS.sleep(6);
|
||||
Assert.assertNull(AuthStateCache.get("key"));
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package me.zhyd.oauth.request;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import me.zhyd.oauth.config.AuthConfig;
|
||||
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.AuthStateUtils;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* 自定义扩展的第三方request的测试类,用于演示具体的用法
|
||||
*
|
||||
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
|
||||
* @version 1.0
|
||||
* @since 1.12.0
|
||||
*/
|
||||
public class AuthExtendRequestTest {
|
||||
|
||||
@Test
|
||||
public void authorize() {
|
||||
AuthRequest request = new AuthExtendRequest(AuthConfig.builder()
|
||||
.clientId("clientId")
|
||||
.clientSecret("clientSecret")
|
||||
.redirectUri("http://redirectUri")
|
||||
.build());
|
||||
String authorize = request.authorize(AuthStateUtils.createState());
|
||||
System.out.println(authorize);
|
||||
Assert.assertNotNull(authorize);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void login() {
|
||||
AuthRequest request = new AuthExtendRequest(AuthConfig.builder()
|
||||
.clientId("clientId")
|
||||
.clientSecret("clientSecret")
|
||||
.redirectUri("http://redirectUri")
|
||||
.build());
|
||||
|
||||
String state = AuthStateUtils.createState();
|
||||
request.authorize(state);
|
||||
AuthCallback callback = AuthCallback.builder()
|
||||
.code("code")
|
||||
.state(state)
|
||||
.build();
|
||||
AuthResponse response = request.login(callback);
|
||||
Assert.assertNotNull(response);
|
||||
|
||||
AuthUser user = (AuthUser) response.getData();
|
||||
Assert.assertNotNull(user);
|
||||
System.out.println(JSON.toJSONString(user));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void revoke() {
|
||||
AuthRequest request = new AuthExtendRequest(AuthConfig.builder()
|
||||
.clientId("clientId")
|
||||
.clientSecret("clientSecret")
|
||||
.redirectUri("http://redirectUri")
|
||||
.build());
|
||||
|
||||
AuthResponse response = request.revoke(AuthToken.builder().build());
|
||||
Assert.assertNotNull(response);
|
||||
System.out.println(JSON.toJSONString(response));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void refresh() {
|
||||
AuthRequest request = new AuthExtendRequest(AuthConfig.builder()
|
||||
.clientId("clientId")
|
||||
.clientSecret("clientSecret")
|
||||
.redirectUri("http://redirectUri")
|
||||
.build());
|
||||
|
||||
AuthResponse response = request.refresh(AuthToken.builder().build());
|
||||
Assert.assertNotNull(response);
|
||||
System.out.println(JSON.toJSONString(response.getData()));
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
package me.zhyd.oauth.utils;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.alibaba.fastjson.JSONPath;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 其他测试方法
|
||||
*
|
||||
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
|
||||
*/
|
||||
public class CustomTest {
|
||||
|
||||
/**
|
||||
* 1000000: 23135ms
|
||||
* 100000: 3016ms
|
||||
* 10000: 328ms
|
||||
* 1000: 26ms
|
||||
*/
|
||||
@Test
|
||||
public void test() {
|
||||
long start = System.currentTimeMillis();
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
callMethod();
|
||||
}
|
||||
long end = System.currentTimeMillis();
|
||||
System.out.println((end - start) + "ms");
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 1000000: 19058ms
|
||||
* 100000: 2772ms
|
||||
* 10000: 323ms
|
||||
* 1000: 29ms
|
||||
*/
|
||||
@Test
|
||||
public void test2() {
|
||||
long end = System.currentTimeMillis();
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
callMethod2();
|
||||
}
|
||||
long end2 = System.currentTimeMillis();
|
||||
System.out.println((end2 - end) + "ms");
|
||||
|
||||
}
|
||||
|
||||
public String callMethod() {
|
||||
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
|
||||
// for (StackTraceElement stackTraceElement : stackTrace) {
|
||||
// System.out.println(stackTraceElement.getMethodName());
|
||||
// }
|
||||
return stackTrace[2].getMethodName();
|
||||
}
|
||||
|
||||
public String callMethod2() {
|
||||
StackTraceElement[] stackTrace = (new Throwable()).getStackTrace();
|
||||
// for (StackTraceElement stackTraceElement : stackTrace) {
|
||||
// System.out.println(stackTraceElement.getMethodName());
|
||||
// }
|
||||
return stackTrace[2].getMethodName();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void jsonpath() {
|
||||
List<Map<String, Map<String, Object>>> list = new ArrayList<>();
|
||||
|
||||
Map<String, Map<String, Object>> map = new HashMap<>();
|
||||
Map<String, Object> node = new HashMap<>();
|
||||
node.put("emailAddress", "xxxx");
|
||||
map.put("handle~", node);
|
||||
list.add(map);
|
||||
|
||||
|
||||
Map<String, Object> master = new HashMap<>();
|
||||
// master.put("elements", list);
|
||||
JSONObject emailObj = JSONObject.parseObject(JSON.toJSONString(master));
|
||||
Object object = JSONPath.eval(emailObj, "$['elements'][0]['handle~']['emailAddress']");
|
||||
System.out.println(object);
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
package me.zhyd.oauth.utils;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class GlobalAuthUtilTest {
|
||||
|
||||
@Test
|
||||
public void testGenerateDingTalkSignature() {
|
||||
Assert.assertEquals("mLTZEMqIlpAA3xtJ43KcRT0EDLwgSamFe%2FNis5lq9ik%3D",
|
||||
GlobalAuthUtil.generateDingTalkSignature(
|
||||
"SHA-256", "1562325753000 "));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUrlDecode() {
|
||||
Assert.assertEquals("", GlobalAuthUtil.urlDecode(null));
|
||||
Assert.assertEquals("https://www.foo.bar",
|
||||
GlobalAuthUtil.urlDecode("https://www.foo.bar"));
|
||||
Assert.assertEquals("mLTZEMqIlpAA3xtJ43KcRT0EDLwgSamFe/Nis5lq9ik=",
|
||||
GlobalAuthUtil.urlDecode(
|
||||
"mLTZEMqIlpAA3xtJ43KcRT0EDLwgSamFe%2FNis5lq9ik%3D"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseStringToMap() {
|
||||
Map expected = new HashMap();
|
||||
expected.put("bar", "baz");
|
||||
Assert.assertEquals(expected,
|
||||
GlobalAuthUtil.parseStringToMap("foo&bar=baz"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsHttpProtocol() {
|
||||
Assert.assertFalse(GlobalAuthUtil.isHttpProtocol(""));
|
||||
Assert.assertFalse(GlobalAuthUtil.isHttpProtocol("foo"));
|
||||
|
||||
Assert.assertTrue(GlobalAuthUtil.isHttpProtocol("http://www.foo.bar"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsHttpsProtocol() {
|
||||
Assert.assertFalse(GlobalAuthUtil.isHttpsProtocol(""));
|
||||
Assert.assertFalse(GlobalAuthUtil.isHttpsProtocol("foo"));
|
||||
|
||||
Assert.assertTrue(
|
||||
GlobalAuthUtil.isHttpsProtocol("https://www.foo.bar"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsLocalHost() {
|
||||
Assert.assertFalse(GlobalAuthUtil.isLocalHost("foo"));
|
||||
|
||||
Assert.assertTrue(GlobalAuthUtil.isLocalHost(""));
|
||||
Assert.assertTrue(GlobalAuthUtil.isLocalHost("127.0.0.1"));
|
||||
Assert.assertTrue(GlobalAuthUtil.isLocalHost("localhost"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,231 @@
|
||||
package me.zhyd.oauth.utils;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import me.zhyd.oauth.config.AuthConfig;
|
||||
import me.zhyd.oauth.model.AuthCallback;
|
||||
import me.zhyd.oauth.model.AuthToken;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import static me.zhyd.oauth.config.AuthDefaultSource.TWITTER;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
public class GlobalAuthUtilsTest {
|
||||
|
||||
@Test
|
||||
public void generateDingTalkSignature() {
|
||||
assertEquals("mLTZEMqIlpAA3xtJ43KcRT0EDLwgSamFe%2FNis5lq9ik%3D", GlobalAuthUtils.generateDingTalkSignature("SHA-256", "1562325753000 "));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void urlDecode() {
|
||||
assertEquals("", GlobalAuthUtils.urlDecode(null));
|
||||
assertEquals("https://www.foo.bar", GlobalAuthUtils.urlDecode("https://www.foo.bar"));
|
||||
assertEquals("mLTZEMqIlpAA3xtJ43KcRT0EDLwgSamFe/Nis5lq9ik=", GlobalAuthUtils.urlDecode("mLTZEMqIlpAA3xtJ43KcRT0EDLwgSamFe%2FNis5lq9ik%3D"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseStringToMap() {
|
||||
Map expected = new HashMap();
|
||||
expected.put("bar", "baz");
|
||||
assertEquals(expected, GlobalAuthUtils.parseStringToMap("foo&bar=baz"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isHttpProtocol() {
|
||||
Assert.assertFalse(GlobalAuthUtils.isHttpProtocol(""));
|
||||
Assert.assertFalse(GlobalAuthUtils.isHttpProtocol("foo"));
|
||||
|
||||
Assert.assertTrue(GlobalAuthUtils.isHttpProtocol("http://www.foo.bar"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isHttpsProtocol() {
|
||||
Assert.assertFalse(GlobalAuthUtils.isHttpsProtocol(""));
|
||||
Assert.assertFalse(GlobalAuthUtils.isHttpsProtocol("foo"));
|
||||
|
||||
Assert.assertTrue(GlobalAuthUtils.isHttpsProtocol("https://www.foo.bar"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isLocalHost() {
|
||||
Assert.assertFalse(GlobalAuthUtils.isLocalHost("foo"));
|
||||
|
||||
Assert.assertTrue(GlobalAuthUtils.isLocalHost(""));
|
||||
Assert.assertTrue(GlobalAuthUtils.isLocalHost("127.0.0.1"));
|
||||
Assert.assertTrue(GlobalAuthUtils.isLocalHost("localhost"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenerateTwitterSignatureForRequestToken() {
|
||||
AuthConfig config = AuthConfig.builder()
|
||||
.clientId("HD0XLqzi5Wz0G08rh45Cg8mgh")
|
||||
.clientSecret("0YX3RH2DnPiT77pgzLzFdfpMKX8ENLIWQKYQ7lG5TERuZNgXN5")
|
||||
.redirectUri("https://codinglife.tech")
|
||||
.build();
|
||||
Map<String, String> params = new HashMap<>();
|
||||
params.put("oauth_consumer_key", config.getClientId());
|
||||
params.put("oauth_nonce", "sTj7Ivg73u052eXstpoS1AWQCynuDEPN");
|
||||
params.put("oauth_signature_method", "HMAC-SHA1");
|
||||
params.put("oauth_timestamp", "1569750981");
|
||||
params.put("oauth_callback", config.getRedirectUri());
|
||||
params.put("oauth_version", "1.0");
|
||||
|
||||
String baseUrl = "https://api.twitter.com/oauth/request_token";
|
||||
params.put("oauth_signature", GlobalAuthUtils.generateTwitterSignature(params, "POST", baseUrl, config.getClientSecret(), null));
|
||||
|
||||
params.forEach((k, v) -> params.put(k, "\"" + GlobalAuthUtils.urlEncode(v) + "\""));
|
||||
String actual = "OAuth " + GlobalAuthUtils.parseMapToString(params, false).replaceAll("&", ", ");
|
||||
|
||||
assertEquals("OAuth oauth_nonce=\"sTj7Ivg73u052eXstpoS1AWQCynuDEPN\", oauth_signature=\"%2BL5Jq%2FTaKubge04cWw%2B4yfjFlaU%3D\", oauth_callback=\"https%3A%2F%2Fcodinglife.tech\", oauth_consumer_key=\"HD0XLqzi5Wz0G08rh45Cg8mgh\", oauth_signature_method=\"HMAC-SHA1\", oauth_timestamp=\"1569750981\", oauth_version=\"1.0\"", actual);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenerateTwitterSignatureForAccessToken() {
|
||||
AuthConfig config = AuthConfig.builder()
|
||||
.clientId("HD0XLqzi5Wz0G08rh45Cg8mgh")
|
||||
.clientSecret("0YX3RH2DnPiT77pgzLzFdfpMKX8ENLIWQKYQ7lG5TERuZNgXN5")
|
||||
.build();
|
||||
AuthCallback authCallback = AuthCallback.builder()
|
||||
.oauth_token("W_KLmAAAAAAAxq5LAAABbXxJeD0")
|
||||
.oauth_verifier("lYou4gxfA6S5KioUa8VF8HCShzA2nSxp")
|
||||
.build();
|
||||
Map<String, String> params = new HashMap<>();
|
||||
params.put("oauth_consumer_key", config.getClientId());
|
||||
params.put("oauth_nonce", "sTj7Ivg73u052eXstpoS1AWQCynuDEPN");
|
||||
params.put("oauth_signature_method", "HMAC-SHA1");
|
||||
params.put("oauth_timestamp", "1569751082");
|
||||
params.put("oauth_token", authCallback.getOauth_token());
|
||||
params.put("oauth_verifier", authCallback.getOauth_verifier());
|
||||
params.put("oauth_version", "1.0");
|
||||
|
||||
params.put("oauth_signature", GlobalAuthUtils.generateTwitterSignature(params, "POST", TWITTER.accessToken(), config.getClientSecret(), authCallback
|
||||
.getOauth_token()));
|
||||
|
||||
params.forEach((k, v) -> params.put(k, "\"" + GlobalAuthUtils.urlEncode(v) + "\""));
|
||||
String actual = "OAuth " + GlobalAuthUtils.parseMapToString(params, false).replaceAll("&", ", ");
|
||||
|
||||
assertEquals("OAuth oauth_verifier=\"lYou4gxfA6S5KioUa8VF8HCShzA2nSxp\", oauth_nonce=\"sTj7Ivg73u052eXstpoS1AWQCynuDEPN\", oauth_signature=\"9i0lmWgvphtkl2KcCO9VyZ3K2%2F0%3D\", oauth_token=\"W_KLmAAAAAAAxq5LAAABbXxJeD0\", oauth_consumer_key=\"HD0XLqzi5Wz0G08rh45Cg8mgh\", oauth_signature_method=\"HMAC-SHA1\", oauth_timestamp=\"1569751082\", oauth_version=\"1.0\"", actual);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenerateTwitterSignatureForUserInfo() {
|
||||
AuthConfig config = AuthConfig.builder()
|
||||
.clientId("HD0XLqzi5Wz0G08rh45Cg8mgh")
|
||||
.clientSecret("0YX3RH2DnPiT77pgzLzFdfpMKX8ENLIWQKYQ7lG5TERuZNgXN5")
|
||||
.build();
|
||||
AuthToken authToken = AuthToken.builder()
|
||||
.oauthToken("1961977975-PcFQaCnpN9h9xqtqHwHlpGBXFrHJ9bOLy7OtGAL")
|
||||
.oauthTokenSecret("ffyKe39GYYf8tAyhliSe3QmazpO65kZp5b49xOFX6wHho")
|
||||
.userId("1961977975")
|
||||
.screenName("pengisgood")
|
||||
.build();
|
||||
|
||||
Map<String, String> oauthParams = new HashMap<>();
|
||||
oauthParams.put("oauth_consumer_key", config.getClientId());
|
||||
oauthParams.put("oauth_nonce", "sTj7Ivg73u052eXstpoS1AWQCynuDEPN");
|
||||
oauthParams.put("oauth_signature_method", "HMAC-SHA1");
|
||||
oauthParams.put("oauth_timestamp", "1569751082");
|
||||
oauthParams.put("oauth_token", authToken.getOauthToken());
|
||||
oauthParams.put("oauth_version", "1.0");
|
||||
|
||||
Map<String, String> queryParams = new HashMap<>();
|
||||
queryParams.put("user_id", authToken.getUserId());
|
||||
queryParams.put("screen_name", authToken.getScreenName());
|
||||
queryParams.put("include_entities", Boolean.toString(true));
|
||||
|
||||
Map<String, String> params = new HashMap<>(queryParams);
|
||||
oauthParams.put("oauth_signature", GlobalAuthUtils.generateTwitterSignature(params, "GET", TWITTER.userInfo(), config.getClientSecret(), authToken
|
||||
.getOauthTokenSecret()));
|
||||
oauthParams.forEach((k, v) -> oauthParams.put(k, "\"" + GlobalAuthUtils.urlEncode(v) + "\""));
|
||||
|
||||
String actual = "OAuth " + GlobalAuthUtils.parseMapToString(oauthParams, false).replaceAll("&", ", ");
|
||||
assertEquals("OAuth oauth_nonce=\"sTj7Ivg73u052eXstpoS1AWQCynuDEPN\", oauth_signature=\"yHHq2J1W5QLAO8gGipnY1V%2Bzxqk%3D\", oauth_token=\"1961977975-PcFQaCnpN9h9xqtqHwHlpGBXFrHJ9bOLy7OtGAL\", oauth_consumer_key=\"HD0XLqzi5Wz0G08rh45Cg8mgh\", oauth_signature_method=\"HMAC-SHA1\", oauth_timestamp=\"1569751082\", oauth_version=\"1.0\"", actual);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void md5() {
|
||||
String str = "helloworld,iamjustauth";
|
||||
String md5Str = GlobalAuthUtils.md5(str);
|
||||
assertEquals("b0d923de4289b69976448cac718528b8", md5Str);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void treemap() {
|
||||
Map<String, Object> parameters = new HashMap<>();
|
||||
parameters.put("user_id", "1");
|
||||
parameters.put("screen_name", "222");
|
||||
parameters.put("a", "222");
|
||||
parameters.put("include_entities", Boolean.toString(true));
|
||||
final Map<String, Object> sorted = new TreeMap<>(parameters);
|
||||
assertEquals("{\"a\":\"222\",\"include_entities\":\"true\",\"screen_name\":\"222\",\"user_id\":\"1\"}", JSON.toJSONString(sorted));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void urlEncode() {
|
||||
assertEquals("", GlobalAuthUtils.urlEncode(null));
|
||||
assertEquals("https%3A%2F%2Fwww.foo.bar", GlobalAuthUtils.urlEncode("https://www.foo.bar"));
|
||||
assertEquals("mLTZEMqIlpAA3xtJ43KcRT0EDLwgSamFe%252FNis5lq9ik%253D", GlobalAuthUtils.urlEncode("mLTZEMqIlpAA3xtJ43KcRT0EDLwgSamFe%2FNis5lq9ik%3D"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseMapToString() {
|
||||
Map<String, String> parameters = new HashMap<>();
|
||||
parameters.put("user_id", "1");
|
||||
parameters.put("screen_name", "史上最全的第三方授权登录库");
|
||||
parameters.put("include_entities", Boolean.toString(true));
|
||||
assertEquals("user_id=1&screen_name=史上最全的第三方授权登录库&include_entities=true", GlobalAuthUtils.parseMapToString(parameters, false));
|
||||
assertEquals("user_id=1&screen_name=%E5%8F%B2%E4%B8%8A%E6%9C%80%E5%85%A8%E7%9A%84%E7%AC%AC%E4%B8%89%E6%96%B9%E6%8E%88%E6%9D%83%E7%99%BB%E5%BD%95%E5%BA%93&include_entities=true", GlobalAuthUtils.parseMapToString(parameters, true));
|
||||
assertEquals("", GlobalAuthUtils.parseMapToString(null, true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void generateNonce() {
|
||||
assertEquals(10, GlobalAuthUtils.generateNonce(10).length());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getTimestamp() {
|
||||
assertNotNull(GlobalAuthUtils.getTimestamp());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void generateTwitterSignature() {
|
||||
Map<String, String> queryParams = new HashMap<>();
|
||||
queryParams.put("name", "你好");
|
||||
queryParams.put("gender", "male");
|
||||
|
||||
assertEquals("J6MAQH1kcgUdj2jmygN3rdfI4lo=", GlobalAuthUtils.generateTwitterSignature(queryParams, "GET", TWITTER.userInfo(), "xxxxx", "xxxxx"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void generateElemeSignature() {
|
||||
Map<String, Object> parameters = new HashMap<>();
|
||||
parameters.put("name", "你好");
|
||||
parameters.put("gender", "male");
|
||||
|
||||
String appKey = "appKey";
|
||||
String secret = "appKey";
|
||||
long timestamp = 1233456789;
|
||||
String action = "appKey";
|
||||
String token = "appKey";
|
||||
|
||||
assertEquals("26FEB8BF7E84FED2619D9C5D97F421BD", GlobalAuthUtils.generateElemeSignature(appKey, secret, timestamp, action, token, parameters));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void generateJdSignature() {
|
||||
Map<String, Object> parameters = new HashMap<>();
|
||||
parameters.put("name", "你好");
|
||||
parameters.put("gender", "male");
|
||||
|
||||
String appSecret = "appKey";
|
||||
|
||||
assertEquals("FE04EC03BA8A619802CF309959C2B43F", GlobalAuthUtils.generateJdSignature(appSecret, parameters));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package me.zhyd.oauth.utils;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.alibaba.fastjson.JSONPath;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* JsonPath用法测试
|
||||
*
|
||||
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
|
||||
*/
|
||||
public class JsonPathTest {
|
||||
|
||||
@Test
|
||||
public void jsonPath() {
|
||||
List<Map<String, Map<String, Object>>> list = new ArrayList<>();
|
||||
|
||||
Map<String, Map<String, Object>> map = new HashMap<>();
|
||||
Map<String, Object> node = new HashMap<>();
|
||||
node.put("emailAddress", "xxxx");
|
||||
map.put("handle~", node);
|
||||
list.add(map);
|
||||
|
||||
|
||||
Map<String, Object> master = new HashMap<>();
|
||||
master.put("elements", list);
|
||||
JSONObject emailObj = JSONObject.parseObject(JSON.toJSONString(master));
|
||||
Object object = JSONPath.eval(emailObj, "$['elements'][0]['handle~']['emailAddress']");
|
||||
Assert.assertEquals("xxxx", object);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
package me.zhyd.oauth.utils;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
public class StringUtilsTest {
|
||||
@Rule
|
||||
public final ExpectedException thrown =
|
||||
ExpectedException.none();
|
||||
|
||||
@Test
|
||||
public void isEmptyNonEmptyInput() {
|
||||
Assert.assertFalse(StringUtils.isEmpty("non-empty string"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isEmptyEmptyInput() {
|
||||
Assert.assertTrue(StringUtils.isEmpty(""));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isEmptyInputNull() {
|
||||
Assert.assertTrue(StringUtils.isEmpty(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isNotEmptyNonEmptyInput() {
|
||||
Assert.assertTrue(StringUtils.isNotEmpty("non-empty string"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isNotEmptyEmptyInput() {
|
||||
Assert.assertFalse(StringUtils.isNotEmpty(""));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isNotEmptyInputNull() {
|
||||
Assert.assertFalse(StringUtils.isNotEmpty(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void appendIfNotContainAppendedStringNotPresent() {
|
||||
// (Check the case where appendStr doesn't occur in str)
|
||||
final String str = "Prefix ";
|
||||
final String appendStr = "suffix";
|
||||
final String otherwise = "should be discarded";
|
||||
|
||||
final String result =
|
||||
StringUtils.appendIfNotContain(str, appendStr, otherwise);
|
||||
|
||||
Assert.assertEquals("Prefix suffix", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void appendIfNotContainAppendedStringPresent() {
|
||||
// (Check the case where appendStr occurs in str)
|
||||
final String str = "Prefix ";
|
||||
final String appendStr = "Prefix";
|
||||
final String otherwise = "should be appended";
|
||||
|
||||
final String result =
|
||||
StringUtils.appendIfNotContain(str, appendStr, otherwise);
|
||||
|
||||
Assert.assertEquals("Prefix should be appended", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void appendIfNotContainEmptyString() {
|
||||
// (Check the special-case for str being empty)
|
||||
final String str = "";
|
||||
final String appendStr = "should not be appended";
|
||||
final String otherwise = "should also not be appended";
|
||||
|
||||
final String result =
|
||||
StringUtils.appendIfNotContain(str, appendStr, otherwise);
|
||||
|
||||
Assert.assertEquals("", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void appendIfNotContainAppendingEmptyString() {
|
||||
// (Check the special-case for appendStr being empty)
|
||||
final String str = "should be kept";
|
||||
final String appendStr = "";
|
||||
final String otherwise = "should also not be appended";
|
||||
|
||||
final String result =
|
||||
StringUtils.appendIfNotContain(str, appendStr, otherwise);
|
||||
|
||||
Assert.assertEquals("should be kept", result);
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
package me.zhyd.oauth.utils;
|
||||
|
||||
import me.zhyd.oauth.config.AuthConfig;
|
||||
import me.zhyd.oauth.config.AuthSource;
|
||||
import me.zhyd.oauth.request.AuthWeChatRequest;
|
||||
import me.zhyd.oauth.config.AuthDefaultSource;
|
||||
import me.zhyd.oauth.request.AuthWeChatOpenRequest;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
@@ -22,7 +22,7 @@ public class UrlBuilderTest {
|
||||
.clientSecret("secret-110110110")
|
||||
.redirectUri("https://xkcoding.com")
|
||||
.build();
|
||||
String build = UrlBuilder.fromBaseUrl(AuthSource.WECHAT.authorize())
|
||||
String build = UrlBuilder.fromBaseUrl(AuthDefaultSource.WECHAT_OPEN.authorize())
|
||||
.queryParam("appid", config.getClientId())
|
||||
.queryParam("redirect_uri", config.getRedirectUri())
|
||||
.queryParam("response_type", "code")
|
||||
@@ -30,7 +30,7 @@ public class UrlBuilderTest {
|
||||
.queryParam("state", "")
|
||||
.build(false);
|
||||
System.out.println(build);
|
||||
AuthWeChatRequest request = new AuthWeChatRequest(config);
|
||||
AuthWeChatOpenRequest request = new AuthWeChatOpenRequest(config);
|
||||
String authorize = request.authorize("state");
|
||||
System.out.println(authorize);
|
||||
}
|
||||
|
||||
@@ -1,152 +0,0 @@
|
||||
### 2019/07/31 ([v1.9.5](https://gitee.com/yadong.zhang/JustAuth/releases/v1.9.5))
|
||||
|
||||
`v1.9.4`版本发布失败,请升级到`1.9.5`版本!
|
||||
|
||||
由此给您带来的不便,敬请谅解!
|
||||
|
||||
|
||||
### 2019/07/30 ([v1.9.4](https://gitee.com/yadong.zhang/JustAuth/releases/v1.9.4))
|
||||
|
||||
1. 升级`hutool-http`版本到`v4.6.1`
|
||||
2. 去除`AuthCallback`中增加的默认的校验state的方法,挪到`AuthDefaultRequest`中做统一处理
|
||||
3. `alipay-sdk-java`依赖改为`provided`,如果需要使用支付宝登录,需要使用方手动引入相关依赖,具体操作方式,见项目WIKI;
|
||||
4. 规范注释
|
||||
|
||||
### 2019/07/30 ([v1.9.3](https://gitee.com/yadong.zhang/JustAuth/releases/v1.9.3))
|
||||
|
||||
1. 规范注释
|
||||
2. 增加State缓存,`AuthCallback`中增加默认的校验state的方法
|
||||
3. 增加默认的state生成方法,参考`AuthStateUtils.java`和`UuidUtils.java`
|
||||
4. 升级`hutool-http`版本到`v4.6.0`
|
||||
5. 修复其他一些问题
|
||||
|
||||
### 2019/07/27
|
||||
|
||||
1. `IpUtils.getIp`改名为`IpUtils.getLocalIp`
|
||||
2. 规范注释
|
||||
|
||||
### 2019/07/25
|
||||
|
||||
1. `AuthConfig`类中去掉state参数
|
||||
2. 删除`AuthState`类
|
||||
3. 增加`authorize(String)`方法,并且使用`@Deprecated`标记`authorize()`方法
|
||||
|
||||
### 2019/07/22 ([v1.9.2](https://gitee.com/yadong.zhang/JustAuth/releases/v1.9.2))
|
||||
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) 直接使用
|
||||
|
||||
### 2019/07/22 ([v1.9.1](https://gitee.com/yadong.zhang/JustAuth/releases/v1.9.1))
|
||||
1. 增加`stackoverflow`参数校验
|
||||
2. 解决`Pinterest`获取用户失败的问题
|
||||
3. 添加注释
|
||||
|
||||
### 2019/07/19 ([v1.9.0](https://gitee.com/yadong.zhang/JustAuth/releases/v1.9.0))
|
||||
|
||||
1. 合并github上[@dyc12ii](https://github.com/dyc12ii) 的[pr#25](https://github.com/zhangyd-c/JustAuth/pull/25),升级fastjson版本至1.2.58,避免安全漏洞
|
||||
2. `AuthUserGender`枚举类挪到`enums`包下
|
||||
3. 删除`AuthBaiduErrorCode`和`AuthDingTalkErrorCode`枚举类
|
||||
4. 优化百度授权流程,增加refresh token的方法
|
||||
5. 优化`AuthConfig`、`AuthResponse`类,去掉不必要的lombonk注解,减少编译后的代码量
|
||||
6. 使用lombok注解优化枚举类
|
||||
7. `AuthQqRequest`增加refresh方法
|
||||
8. 修复google登录无法获取用户信息的问题
|
||||
9. 优化代码
|
||||
|
||||
### 2019/07/18
|
||||
|
||||
1. 合并github上[@pengisgood](https://github.com/pengisgood) 的[pr#19](https://github.com/zhangyd-c/JustAuth/pull/19),集成人人
|
||||
2. 合并github上[@pengisgood](https://github.com/pengisgood) 的[pr#20](https://github.com/zhangyd-c/JustAuth/pull/20),集成Pinterest
|
||||
3. 合并github上[@pengisgood](https://github.com/pengisgood) 的[pr#21](https://github.com/zhangyd-c/JustAuth/pull/21),集成StackOverflow
|
||||
4. 合并github上[@xkcoding](https://github.com/xkcoding) 的[pr#23](https://github.com/zhangyd-c/JustAuth/pull/23),重构代码、新增编辑器规范,规范PR代码风格
|
||||
|
||||
### 2019/07/17
|
||||
1. 优化代码
|
||||
2. 集成Teambition登录
|
||||
|
||||
### 2019/07/16
|
||||
1. 重构UrlBuilder类
|
||||
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),修复小米回调错误问题 同时 支持微信获取unionId
|
||||
|
||||
### 2019/07/15 ([v1.8.1](https://gitee.com/yadong.zhang/JustAuth/releases/v1.8.1))
|
||||
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](https://gitee.com/yadong.zhang/JustAuth/releases/v1.8.0))
|
||||
1. 修复百度登录获取不到token失效时间的问题
|
||||
2. 增加state参数校验,预防CSRF。**强烈建议启用state**!
|
||||
|
||||
### 2019/06/27
|
||||
1. 修复百度登录获取不到token失效时间的问题
|
||||
2. 增加state参数校验,预防CSRF。**强烈建议启用state**!
|
||||
3. 修改login方法的参数为AuthCallback,封装回调返回的参数
|
||||
4. 支持state参数
|
||||
5. 增加code和state参数校验
|
||||
|
||||
由于state安全问题,1.8.0以前的版本都有隐藏的CSRF漏洞问题,所以强烈建议正在使用JustAuth的朋友升级到1.8.0版本!
|
||||
|
||||
### 2019/06/25 ([v1.7.1](https://gitee.com/yadong.zhang/JustAuth/releases/v1.7.1))
|
||||
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](https://gitee.com/yadong.zhang/JustAuth/releases/v1.7.0))
|
||||
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](https://gitee.com/yadong.zhang/JustAuth/releases/v1.6.1-beta))
|
||||
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)
|
||||
4. 修复部分注释、拼写错误
|
||||
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](https://gitee.com/yadong.zhang/JustAuth/releases/v1.6.0-beta))
|
||||
1. 增加今日头条的授权登陆
|
||||
2. 发布1.6.0-beta版本,今日头条开发者暂时不能认证, 所以无法做测试,等测试通过后,正式发布release版本
|
||||
|
||||
### 2019/05/28 ([v1.5.0](https://gitee.com/yadong.zhang/JustAuth/releases/v1.5.0))
|
||||
1. 增加小米账号和微软的授权登陆
|
||||
2. 发布1.5.0版本
|
||||
|
||||
### 2019/05/26 ([v1.4.0](https://gitee.com/yadong.zhang/JustAuth/releases/v1.4.0))
|
||||
1. 增加抖音和Linkedin的授权登陆
|
||||
2. 修改部分图片命名
|
||||
3. 优化部分代码
|
||||
4. 修复`AuthSource`中腾讯云开发平台的拼写错误:`TENCEN_CLOUD`->`TENCENT_CLOUD`
|
||||
5. 修复支付宝登陆时用户名为空的问题
|
||||
|
||||
|
||||
### 2019/05/24 ([v1.3.3](https://gitee.com/yadong.zhang/JustAuth/releases/v1.3.3))
|
||||
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](https://gitee.com/yadong.zhang/JustAuth/releases/v1.3.1))
|
||||
1. 修复QQ登录的问题
|
||||
2. 发布1.3.1版本的jar包到公开仓库
|
||||
|
||||
### 2019/05/21 ([v1.3.0](https://gitee.com/yadong.zhang/JustAuth/releases/v1.3.0))
|
||||
1. 新增google授权登录
|
||||
2. 新增facebook授权登录
|
||||
3. 发布1.3.0版本的jar包到公开仓库
|
||||
|
||||
### 2019/05/18 ([v1.1.0](https://gitee.com/yadong.zhang/JustAuth/releases/v1.1.0) | [v1.2.0](https://gitee.com/yadong.zhang/JustAuth/releases/v1.2.0))
|
||||
1. 发布1.1.0版本的jar包到公开仓库(支持qq和微信登录)
|
||||
2. 支持淘宝登录
|
||||
3. 修改`AuthUser.java`类中的`accessToken`属性,由原本的~~accessToken (String)~~改为`token (AuthToken)`
|
||||
4. 修复一些bug
|
||||
5. 发布1.2.0版本的jar包到公开仓库(支持淘宝登录)
|
||||
|
||||
### 2019/05/17
|
||||
1. 增加qq和微信的授权登录
|
||||
2. 修改getAccessToken方法的返回值
|
||||
|
||||
### 2019/03/27 ([v1.0.1](https://gitee.com/yadong.zhang/JustAuth/releases/v1.0.1))
|
||||
集成 支付宝授权登录
|
||||
|
||||
### 2019/03/25 ([v1.0.0](https://gitee.com/yadong.zhang/JustAuth/releases/v1.0.0))
|
||||
史上最全的整合第三方登录的工具,目前已支持Github、Gitee、微博、钉钉和百度、Coding、腾讯云开发者平台和OSChina登录。 Login, so easy!
|
||||
Reference in New Issue
Block a user