1
0
mirror of synced 2026-05-22 21:53:18 +00:00

Compare commits

..

99 Commits

Author SHA1 Message Date
yadong.zhang 798e7556ba 🔖 增加stackoverflow参数校验、解决Pinterest获取用户失败的问题,发布v1.9.1 2019-07-20 17:21:21 +08:00
yadong.zhang 1244524da0 Merge branch 'master' of https://github.com/zhangyd-c/JustAuth 2019-07-19 22:28:35 +08:00
yadong.zhang ce1f71c707 📌 version 2019-07-19 22:27:49 +08:00
yadong.zhang 93c301e05b 优化代码 2019-07-19 22:25:44 +08:00
yadong.zhang 53df992474 优化代码、修复google获取用户信息失败的问题 2019-07-19 22:00:07 +08:00
yadong.zhang e29df531fa Merge branch 'dev'
# Conflicts:
#	example.md
#	src/main/java/me/zhyd/oauth/request/AuthMiRequest.java
#	src/main/java/me/zhyd/oauth/request/AuthWeChatRequest.java
#	src/main/java/me/zhyd/oauth/utils/UrlBuilder.java
2019-07-19 19:03:41 +08:00
yadong.zhang 3a04098e4c 优化代码 2019-07-19 18:05:24 +08:00
yadong.zhang 81691a3462 🍻 优化代码 2019-07-19 14:26:42 +08:00
yadong.zhang 6708525d99 Merge pull request #25 from dyc12ii/master
升级fastjson版本至1.2.58,避免安全漏洞
2019-07-19 11:19:44 +08:00
dingyanchao a599ced6b0 fastjson version 2019-07-19 11:15:08 +08:00
yadong.zhang f815f12acc 🍻 调整部分内容 2019-07-18 23:41:34 +08:00
yadong.zhang 78cb32a9f2 Merge branch 'dev' of https://gitee.com/yadong.zhang/JustAuth into dev 2019-07-18 22:48:56 +08:00
yadong.zhang 6505df15a7 🍻 调整部分内容 2019-07-18 22:48:23 +08:00
yadong.zhang d7d87c7f04 更新 example.md 2019-07-18 22:42:14 +08:00
yadong.zhang 188e98c0f2 🍻 醉酒写代码 2019-07-18 21:59:05 +08:00
yadong.zhang f2fb942808 🍻 醉酒写代码 2019-07-18 21:57:29 +08:00
yadong.zhang d1d7587f4e 🔀 merge 2019-07-18 21:55:23 +08:00
yadong.zhang d5e60165c0 Merge pull request #23 from xkcoding/remove-urlbuilder
♻️ 重构部分代码
2019-07-18 08:22:04 -05:00
yadong.zhang d5189aa30c Merge branch 'dev' into remove-urlbuilder 2019-07-18 08:21:13 -05:00
yadong.zhang 32c540cf1e Merge pull request #22 from pengisgood/dev
add refresh token method
2019-07-18 08:13:10 -05:00
Hongwei Peng 9aa693d2da add refresh token method 2019-07-18 21:06:23 +08:00
Yangkai.Shen 0d4e52d615 ♻️ 移除 UrlBuilder 2019-07-18 20:37:41 +08:00
Yangkai.Shen 72792c3c83 🚨 新增编辑器规范,规范代码风格 2019-07-18 20:37:12 +08:00
yadong.zhang a6f25ec312 更新 example.md 2019-07-18 19:27:08 +08:00
yadong.zhang 6dc42ac8b3 📝 编写文档 2019-07-18 19:25:23 +08:00
yadong.zhang b67483c263 Merge pull request #21 from pengisgood/dev
Integrate Stack Overflow
2019-07-18 06:03:07 -05:00
Hongwei Peng 66ee684e1f refactor: extract common method 2019-07-18 18:23:45 +08:00
Hongwei Peng d0375ce31a update document 2019-07-18 18:13:34 +08:00
Hongwei Peng 698c922b6b integrate stack overflow 2019-07-18 18:08:05 +08:00
Yangkai.Shen 5694d48288 🎨 增加部分工具类及方法、重载AuthState部分方法 2019-07-18 16:53:54 +08:00
yadong.zhang 44d4323fb2 更新 example.md 2019-07-18 15:51:42 +08:00
yadong.zhang f7561e97fc 更新 example.md 2019-07-18 15:50:54 +08:00
yadong.zhang e8cadef4ce 📝 编写文档 2019-07-18 15:29:16 +08:00
yadong.zhang 49f169abb9 📝 编写文档 2019-07-18 15:26:07 +08:00
yadong.zhang 917239a89a 📝 编写文档 2019-07-18 15:22:52 +08:00
yadong.zhang 5a5827a705 Merge branch 'dev' of https://github.com/zhangyd-c/JustAuth into dev 2019-07-18 15:14:38 +08:00
yadong.zhang 27583b15cc Merge pull request #20 from pengisgood/dev
Integrate Pinterest
2019-07-18 15:12:50 +08:00
Hongwei Peng 090d7d3662 integrate Pinterest 2019-07-18 14:58:27 +08:00
Hongwei Peng b5d8dbaede update contributor 2019-07-18 12:50:57 +08:00
yadong.zhang a5da3b9765 🔀 合并github pr#19 2019-07-18 09:27:59 +08:00
yadong.zhang ae1b5815d8 Merge branch 'dev' of https://github.com/zhangyd-c/JustAuth into dev 2019-07-18 09:20:26 +08:00
yadong.zhang 350f9d70ae Merge pull request #19 from pengisgood/dev
Integrate Renren
2019-07-18 09:19:59 +08:00
Hongwei Peng 0ed3092d53 fix spelling 2019-07-17 21:37:36 +08:00
Hongwei Peng 8d1f700e7f integrate Renren 2019-07-17 21:36:16 +08:00
Hongwei Peng d977e05a3d fix bug and enhance 2019-07-17 21:36:16 +08:00
yadong.zhang abcdc105e0 📝 编写文档 2019-07-17 16:51:30 +08:00
yadong.zhang 267200c434 集成teambition 2019-07-17 15:29:18 +08:00
yadong.zhang e0242f9929 集成teambition 2019-07-17 15:25:48 +08:00
yadong.zhang 59b5ba5a1b 🍻 醉酒写代码 2019-07-17 06:45:21 +08:00
yadong.zhang 12c6930182 🎨 调整throw信息的格式 2019-07-16 22:12:04 +08:00
yadong.zhang a518e8c0cb Merge pull request #18 from xkcoding/patch-5
♻️ 修复小米回调错误问题 同时 支持微信获取 unionId
2019-07-16 19:51:26 +08:00
yadong.zhang 4c1fdfc62d 🔀 手动合并PR #18 到dev分支 2019-07-16 19:49:18 +08:00
yadong.zhang dc74501b1b 🔖 升级到1.9.0版本 2019-07-16 19:40:20 +08:00
yadong.zhang 8a4861075e ♻️ rename 2019-07-16 17:59:36 +08:00
yadong.zhang 65334d0f3f ♻️ 优化代码 2019-07-16 17:42:52 +08:00
yadong.zhang 67c668b740 ♻️ authorize方法提到父类中 2019-07-16 16:46:31 +08:00
yadong.zhang 7fe8a4d4bb ♻️ 从UrlBuilder中拆分alipay、baidu、coding、csdn、dingtalk、douyin、facebook和gitee相关的URL 2019-07-16 16:42:37 +08:00
yadong.zhang 4f272348da ♻️ 从UrlBuilder中拆分github、google、linkedin和microsoft相关的URL 2019-07-16 16:12:56 +08:00
yadong.zhang ec31b7cbf7 ♻️ 从UrlBuilder中拆分小米和OSChina相关的URL 2019-07-16 15:50:44 +08:00
yadong.zhang 6b196456a1 ♻️ 从UrlBuilder中拆分qq、淘宝、腾讯云和头条相关的URL 2019-07-16 15:37:44 +08:00
yadong.zhang af723a8b08 ♻️ 从UrlBuilder中拆分微信相关的URL 2019-07-16 15:11:18 +08:00
yadong.zhang 1360db60ae 💡 添加源码注释 2019-07-16 14:59:51 +08:00
Yangkai.Shen 8d1b329c63 微信登录获取unionId 2019-07-16 10:30:29 +08:00
yadong.zhang 47971438c7 拆分UrlBuilder工具类,已拆分WeiboRequest 2019-07-16 10:04:00 +08:00
Yangkai.Shen e2e1a082d1 🐛 修复小米回调错误的问题 2019-07-16 10:01:17 +08:00
yadong.zhang 3b80e11824 💡 添加源码注释 2019-07-15 19:17:22 +08:00
yadong.zhang 44bb03d63f 新增AuthState类,内置默认的state生成规则和校验规则 2019-07-15 17:46:37 +08:00
yadong.zhang bfe2122962 🎨 将枚举类移到enums包下 2019-07-15 10:40:42 +08:00
yadong.zhang fa2b9114d1 📝 编写文档 2019-07-15 10:25:41 +08:00
yadong.zhang 93bee4f5d9 🔀 合并Braavos96提交的github PR #16 2019-07-12 09:47:03 +08:00
yadong.zhang 5c0b8eb94a Merge pull request #16 from Diffblue-benchmarks/add-diffblue-tests
Add unit tests for me.zhyd.oauth.utils.GlobalAuthUtil
2019-07-12 09:29:25 +08:00
Eric Hettiaratchi e03088a9b4 Add unit tests for me.zhyd.oauth.utils.GlobalAuthUtil
These tests were written using Diffblue Cover.
2019-07-10 14:29:21 +01:00
yadong.zhang 41a52767b3 📝 编写文档 2019-06-29 15:52:50 +08:00
yadong.zhang 88048b7637 📝 编写文档 2019-06-29 15:12:49 +08:00
yadong.zhang 186ee58b72 修改测试代码 2019-06-29 07:36:08 +08:00
yadong.zhang fa51358072 📌 升级hutool版本 2019-06-28 23:08:09 +08:00
yadong.zhang 80329c2496 全面开启state校验 2019-06-28 22:58:34 +08:00
yadong.zhang 78988555b0 🍻 完善百度登录,增加gitee登录的state校验 2019-06-28 21:33:49 +08:00
yadong.zhang ac4ede74bf 👽 修改login方法的参数为AuthCallback,封装回调返回的参数、支持state参数、增加code和state参数校验 2019-06-27 19:39:21 +08:00
yadong.zhang 9941ce7e2a Merge branch 'master' of https://gitee.com/yadong.zhang/JustAuth 2019-06-26 21:53:28 +08:00
yadong.zhang 4f594a4178 📝 更新githubapi文档 2019-06-26 21:52:16 +08:00
yadong.zhang b9268f296b Merge remote-tracking branch 'origin/master' 2019-06-25 19:33:27 +08:00
yadong.zhang 1c30f6ab2f 🎨 适配qq授权登录时开发者账号没有申请unionId权限而导致报错的问题 2019-06-25 19:32:18 +08:00
yadong.zhang af7baa924c 📝 Writing docs. 2019-06-22 08:10:09 +08:00
yadong.zhang 9902e7eb0d !4 优化微博登录
Merge pull request !4 from skqing/master
2019-06-22 07:43:45 +08:00
yadong.zhang 739fa786ce Merge pull request #15 from xkcoding/patch-4
调整部分代码
2019-06-22 07:34:23 +08:00
Yangkai.Shen 6f1cead802 ♻️ 枚举类使用==替换equals,提高性能
参考:https://github.com/giantray/stackoverflow-java-top-qa/blob/master/contents/comparing-java-enum-members-or-equals.md
2019-06-21 15:37:42 +08:00
Yangkai.Shen 374b71e5fe ♻️ 去除工具类方法,使用原生方法 2019-06-21 15:36:41 +08:00
Yangkai.Shen 9d1ab36e21 ♻️ 使用 StandardCharsets.UTF_8 替换 字符串UTF-8
1. 字符串形式的 utf-8 会抛异常
2. Charset 的性能要好
2019-06-21 15:34:38 +08:00
Yangkai.Shen b8d9f2ebc9 🐛 修复小米 scope 错误 2019-06-21 15:32:30 +08:00
skqing c201a9ac90 优化微博登录:
1.remind_in:该参数即将废弃,开发者请使用expires_in
2.uid作为openid,否则openid为空,理论上uid也是可以作为openid的
2019-06-21 11:29:20 +08:00
yadong.zhang 12ec6226bc 📝 编写文档 2019-06-20 10:28:29 +08:00
yadong.zhang 9a62332f30 📝 编写文档 2019-06-20 10:25:22 +08:00
yadong.zhang a962c96a2c Merge pull request #14 from xkcoding/refactor-v1.6.0-beta
🏗️ 架构调整,简化代码
2019-06-20 10:24:07 +08:00
yadong.zhang d442018c02 Merge branch 'master' into refactor-v1.6.0-beta 2019-06-20 10:23:41 +08:00
yadong.zhang 8d6d4533e4 🔀 合并Gitee上的pr 2019-06-20 10:19:20 +08:00
yadong.zhang 2f88c23405 🔀 合并github中的pr 2019-06-20 10:10:18 +08:00
Yangkai.Shen bb613010dc 🏗️ 架构调整,简化代码 2019-06-19 18:16:13 +08:00
skqing 7ace410351 解决抖音登录问题:
1.抖音的client_id换成client_key了
2.抖音的地址后面必须加上/否则报错
2019-06-19 17:14:14 +08:00
79 changed files with 3192 additions and 2839 deletions
+19
View File
@@ -0,0 +1,19 @@
# JustAuth 开发组IDE 编辑器标准
root = true
# 空格替代Tab缩进在各种编辑工具下效果一致
[*]
indent_style = space
indent_size = 2
charset = utf-8
end_of_line = lf
trim_trailing_whitespace = true
insert_final_newline = true
[*.java]
indent_size = 4
[*.md]
insert_final_newline = false
trim_trailing_whitespace = false
+39 -13
View File
@@ -6,7 +6,7 @@
</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.6.1_beta-blue.svg" ></img>
<img src="https://img.shields.io/badge/Maven Central-1.9.1-blue.svg" ></img>
</a>
<a target="_blank" href="https://gitee.com/yadong.zhang/JustAuth/blob/master/LICENSE">
<img src="https://img.shields.io/apm/l/vim-mode.svg?color=yellow" ></img>
@@ -14,6 +14,9 @@
<a target="_blank" href="https://www.oracle.com/technetwork/java/javase/downloads/index.html">
<img src="https://img.shields.io/badge/JDK-1.8+-green.svg" ></img>
</a>
<a target="_blank" href="https://apidoc.gitee.com/yadong.zhang/JustAuth/">
<img src="https://img.shields.io/badge/Docs-1.9.1-orange.svg" ></img>
</a>
</p>
<center>
@@ -34,14 +37,23 @@
<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>
</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>
</tr>
</table>
</center>
-------------------------------------------------------------------------------
@@ -64,7 +76,7 @@ JustAuth,如你所见,它仅仅是一个**第三方授权登录**的**工具
<dependency>
<groupId>me.zhyd.oauth</groupId>
<artifactId>JustAuth</artifactId>
<version>1.6.1-beta</version>
<version>1.9.1</version>
</dependency>
```
- 调用api
@@ -74,42 +86,52 @@ AuthRequest authRequest = new AuthGiteeRequest(AuthConfig.builder()
.clientId("clientId")
.clientSecret("clientSecret")
.redirectUri("redirectUri")
.state("state")
.build());
// 生成授权页面
authRequest.authorize();
// 授权登录后会返回一个code,用这个code进行登录
authRequest.login("code");
// 授权登录后会返回code(auth_code(仅限支付宝))、state1.8.0版本后,可以用AuthCallback类作为回调接口的参数
authRequest.login(callback);
```
**配套Demo**[JustAuth-demo](https://gitee.com/yadong.zhang/JustAuth-demo)
注:`1.8.0`版本后,增加了`state`参数校验,用于防止[CSRF](https://zh.wikipedia.org/wiki/%E8%B7%A8%E7%AB%99%E8%AF%B7%E6%B1%82%E4%BC%AA%E9%80%A0)。强烈建议,保证单次流程内`state`的唯一性,且每个`state`只可用一次。
**配套Demo**
- [Springboot版](https://gitee.com/yadong.zhang/JustAuth-demo)
- [jFinal版](https://github.com/zhangyd-c/jfinal-justauth-demo)
具体的例子可以参考:
- [实现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://github.com/settings/developers" 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/%E5%BE%AE%E5%8D%9AAPI" 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="https://developer.baidu.com/" 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/openapi_user" 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="http://wiki.connect.qq.com/" 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" 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登录只能针对少部分用户使用了_
@@ -126,7 +148,7 @@ _请知悉:经咨询CSDN官方客服得知,CSDN的授权开放平台已经
2. 把fork过去的项目也就是你仓库中的项目clone到你的本地
3. 修改代码
4. commit后push到自己的库
5. 发起PRpull request 请求
5. 发起PRpull request 请求,提交到`dev`分支
6. 等待作者合并
## 致谢
@@ -137,6 +159,10 @@ _请知悉:经咨询CSDN官方客服得知,CSDN的授权开放平台已经
[阿里妈妈MUX倾力打造的矢量图标库-iconfont](https://www.iconfont.cn/search/index): 本文档中的图标大部分取自该平台
## 关于OAuth
[The OAuth 2.0 Authorization Framework](https://tools.ietf.org/html/rfc6749)
## 关注&交流
| 公众号 | 微信(备注:加群) |
@@ -154,4 +180,4 @@ _请知悉:经咨询CSDN官方客服得知,CSDN的授权开放平台已经
| 支付宝 | 微信 |
| :------------: | :------------: |
| <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" /> |
| <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" /> |
+2
View File
@@ -2,4 +2,6 @@
- <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>
- 千年等一回,我只为等你...
+31 -2
View File
@@ -40,7 +40,7 @@ _注:非全部平台,部分平台可能不存在图例_
#### 授权qq
待续
暂无
#### 授权微信
@@ -61,6 +61,7 @@ _注:非全部平台,部分平台可能不存在图例_
#### 授权抖音
暂无
#### 授权领英
@@ -69,10 +70,38 @@ _注:非全部平台,部分平台可能不存在图例_
#### 授权微软
![授权微软](https://images.gitee.com/uploads/images/2019/0718/224146_681aa535_784199.png "授权微软")
#### 授权小米
暂无
#### 授权今日头条
暂无
#### 授权Teambition
![授权Teambition](https://images.gitee.com/uploads/images/2019/0718/224119_3da514ab_784199.png "授权Teambition")
#### 授权Pinterest
![授权Pinterest](https://images.gitee.com/uploads/images/2019/0718/155012_6290f500_784199.jpeg "授权Pinterest")
#### 授权Renren
![授权Renre](https://images.gitee.com/uploads/images/2019/0718/155035_8e26c10a_784199.jpeg "授权Renren")
#### 授权Stack Overflow
![授权Stack Overflow](https://images.gitee.com/uploads/images/2019/0718/192639_cc301ba7_784199.png "授权Stack Overflow")
#### 授权Twitter
暂无
#### 授权csdn
_请知悉:经咨询CSDN官方客服得知,CSDN的授权开放平台已经下线。如果以前申请过的应用,可以继续使用,但是不再支持申请新的应用。so, 本项目中的CSDN登录只能针对少部分用户使用了_
暂无
_请知悉:经咨询CSDN官方客服得知,CSDN的授权开放平台已经下线。如果以前申请过的应用,可以继续使用,但是不再支持申请新的应用。so, 本项目中的CSDN登录只能针对少部分用户使用了_
+16 -7
View File
@@ -6,7 +6,7 @@
<groupId>me.zhyd.oauth</groupId>
<artifactId>JustAuth</artifactId>
<version>1.6.1-beta</version>
<version>1.9.1</version>
<name>JustAuth</name>
<url>https://gitee.com/yadong.zhang/JustAuth</url>
@@ -30,7 +30,7 @@
<developers>
<developer>
<name>yadong.zhang</name>
<name>Yadong.Zhang</name>
<email>yadong.zhang0415@gmail.com</email>
<url>https://www.zhyd.me</url>
</developer>
@@ -39,6 +39,11 @@
<email>shenyangkai1994@gmail.com</email>
<url>https://xkcoding.com</url>
</developer>
<developer>
<name>Hongwei.Peng</name>
<email>pengisgood@gmail.com</email>
<url>https://github.com/pengisgood</url>
</developer>
</developers>
<properties>
@@ -49,13 +54,12 @@
<maven-source.version>2.2.1</maven-source.version>
<maven-compiler.version>3.7.0</maven-compiler.version>
<maven.test.skip>true</maven.test.skip>
<hutool-version>4.1.21</hutool-version>
<hutool-version>4.5.15</hutool-version>
<lombok-version>1.18.4</lombok-version>
<junit-version>4.11</junit-version>
<fastjson-version>1.2.44</fastjson-version>
<google-api-version>1.28.0</google-api-version>
<guava-version>27.1-jre</guava-version>
<fastjson-version>1.2.58</fastjson-version>
<alipay-sdk-version>3.7.4.ALL</alipay-sdk-version>
<slf4j-version>1.7.25</slf4j-version>
</properties>
<dependencies>
@@ -86,6 +90,11 @@
<version>${alipay-sdk-version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>${slf4j-version}</version>
</dependency>
</dependencies>
<build>
@@ -181,4 +190,4 @@
</distributionManagement>
</profile>
</profiles>
</project>
</project>
@@ -1,19 +0,0 @@
package me.zhyd.oauth.authorization;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.utils.UrlBuilder;
/**
* 支付宝授权
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0
* @since 1.8
*/
public class AlipayAuthorization implements Authorization {
@Override
public String getAuthorizeUrl(AuthConfig config) {
return UrlBuilder.getAlipayAuthorizeUrl(config.getClientId(), config.getRedirectUri());
}
}
@@ -1,21 +0,0 @@
package me.zhyd.oauth.authorization;
import me.zhyd.oauth.config.AuthConfig;
/**
* 授权接口,用来获取具体第三方平台的授权地址
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0
* @since 1.8
*/
public interface Authorization {
/**
* 获取授权页面地址
*
* @param config 授权基础配置
* @return 授权页面地址
*/
String getAuthorizeUrl(AuthConfig config);
}
@@ -1,81 +0,0 @@
package me.zhyd.oauth.authorization;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.AuthSource;
import me.zhyd.oauth.request.ResponseStatus;
import java.util.HashMap;
import java.util.Map;
/**
* 授权工厂类,负责创建指定平台的授权类获取授权地址
* <p>
* 使用策略模式 + 工厂模式 避免大量的if elseswitch)操作
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0
* @since 1.8
*/
public class AuthorizationFactory {
private static Map<String, Authorization> authorizationMap = new HashMap<>();
private static boolean loader = false;
private AuthorizationFactory() {
}
/**
* 根据第三方平台,获取具体的授权工具
*
* @param source 平台
* @return 具体的Authorization
*/
public static Authorization getAuthorize(AuthSource source) {
if (null == source) {
throw new AuthException(ResponseStatus.NO_AUTH_SOURCE);
}
registerAllAuthorize();
Authorization authorization = authorizationMap.get(source.toString());
if (null == authorization) {
throw new AuthException(ResponseStatus.UNIDENTIFIED_PLATFORM);
}
return authorization;
}
/**
* 将所有Authorize的实现类注册到authorizeMap中,
* 每次增加新的平台都需要在这儿添加注册代码
*/
private static void registerAllAuthorize() {
if (loader) {
return;
}
AuthorizationFactory.register(AuthSource.ALIPAY, new AlipayAuthorization());
AuthorizationFactory.register(AuthSource.BAIDU, new BaiduAuthorization());
AuthorizationFactory.register(AuthSource.CODING, new CodingAuthorization());
AuthorizationFactory.register(AuthSource.CSDN, new CsdnAuthorization());
AuthorizationFactory.register(AuthSource.DINGTALK, new DingTalkAuthorization());
AuthorizationFactory.register(AuthSource.GITEE, new GiteeAuthorization());
AuthorizationFactory.register(AuthSource.GITHUB, new GithubAuthorization());
AuthorizationFactory.register(AuthSource.GOOGLE, new GoogleAuthorization());
AuthorizationFactory.register(AuthSource.OSCHINA, new OschinaAuthorization());
AuthorizationFactory.register(AuthSource.QQ, new QqAuthorization());
AuthorizationFactory.register(AuthSource.TAOBAO, new TaobaoAuthorization());
AuthorizationFactory.register(AuthSource.TENCENT_CLOUD, new TencentCloudAuthorization());
AuthorizationFactory.register(AuthSource.WECHAT, new WeChatAuthorization());
AuthorizationFactory.register(AuthSource.WEIBO, new WeiboAuthorization());
AuthorizationFactory.register(AuthSource.FACEBOOK, new FacebookAuthorization());
AuthorizationFactory.register(AuthSource.DOUYIN, new DouyinAuthorization());
AuthorizationFactory.register(AuthSource.LINKEDIN, new LinkedinAuthorization());
AuthorizationFactory.register(AuthSource.MICROSOFT, new MicrosoftAuthorization());
AuthorizationFactory.register(AuthSource.MI, new MiAuthorization());
AuthorizationFactory.register(AuthSource.TOUTIAO, new ToutiaoAuthorization());
loader = true;
}
private static void register(AuthSource authSource, Authorization authorization) {
authorizationMap.put(authSource.toString(), authorization);
}
}
@@ -1,19 +0,0 @@
package me.zhyd.oauth.authorization;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.utils.UrlBuilder;
/**
* 百度授权
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0
* @since 1.8
*/
public class BaiduAuthorization implements Authorization {
@Override
public String getAuthorizeUrl(AuthConfig config) {
return UrlBuilder.getBaiduAuthorizeUrl(config.getClientId(), config.getRedirectUri());
}
}
@@ -1,19 +0,0 @@
package me.zhyd.oauth.authorization;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.utils.UrlBuilder;
/**
* Coding授权
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0
* @since 1.8
*/
public class CodingAuthorization implements Authorization {
@Override
public String getAuthorizeUrl(AuthConfig config) {
return UrlBuilder.getCodingAuthorizeUrl(config.getClientId(), config.getRedirectUri());
}
}
@@ -1,19 +0,0 @@
package me.zhyd.oauth.authorization;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.utils.UrlBuilder;
/**
* CSDN授权
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0
* @since 1.8
*/
public class CsdnAuthorization implements Authorization {
@Override
public String getAuthorizeUrl(AuthConfig config) {
return UrlBuilder.getCsdnAuthorizeUrl(config.getClientId(), config.getRedirectUri());
}
}
@@ -1,19 +0,0 @@
package me.zhyd.oauth.authorization;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.utils.UrlBuilder;
/**
* 钉钉授权
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0
* @since 1.8
*/
public class DingTalkAuthorization implements Authorization {
@Override
public String getAuthorizeUrl(AuthConfig config) {
return UrlBuilder.getDingTalkQrConnectUrl(config.getClientId(), config.getRedirectUri());
}
}
@@ -1,19 +0,0 @@
package me.zhyd.oauth.authorization;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.utils.UrlBuilder;
/**
* 抖音授权
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0
* @since 1.8
*/
public class DouyinAuthorization implements Authorization {
@Override
public String getAuthorizeUrl(AuthConfig config) {
return UrlBuilder.getDouyinAuthorizeUrl(config.getClientId(), config.getRedirectUri());
}
}
@@ -1,19 +0,0 @@
package me.zhyd.oauth.authorization;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.utils.UrlBuilder;
/**
* Facebook授权
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.3
* @since 1.3
*/
public class FacebookAuthorization implements Authorization {
@Override
public String getAuthorizeUrl(AuthConfig config) {
return UrlBuilder.getFacebookAuthorizeUrl(config.getClientId(), config.getRedirectUri());
}
}
@@ -1,19 +0,0 @@
package me.zhyd.oauth.authorization;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.utils.UrlBuilder;
/**
* 码云授权
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0
* @since 1.8
*/
public class GiteeAuthorization implements Authorization {
@Override
public String getAuthorizeUrl(AuthConfig config) {
return UrlBuilder.getGiteeAuthorizeUrl(config.getClientId(), config.getRedirectUri());
}
}
@@ -1,19 +0,0 @@
package me.zhyd.oauth.authorization;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.utils.UrlBuilder;
/**
* Github授权
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0
* @since 1.8
*/
public class GithubAuthorization implements Authorization {
@Override
public String getAuthorizeUrl(AuthConfig config) {
return UrlBuilder.getGithubAuthorizeUrl(config.getClientId(), config.getRedirectUri());
}
}
@@ -1,19 +0,0 @@
package me.zhyd.oauth.authorization;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.utils.UrlBuilder;
/**
* Google授权
*
* @author yangkai.shen (https://xkcoding.com)
* @version 1.3
* @since 1.3
*/
public class GoogleAuthorization implements Authorization {
@Override
public String getAuthorizeUrl(AuthConfig config) {
return UrlBuilder.getGoogleAuthorizeUrl(config.getClientId(), config.getRedirectUri());
}
}
@@ -1,19 +0,0 @@
package me.zhyd.oauth.authorization;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.utils.UrlBuilder;
/**
* 领英授权
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0
* @since 1.8
*/
public class LinkedinAuthorization implements Authorization {
@Override
public String getAuthorizeUrl(AuthConfig config) {
return UrlBuilder.getLinkedinAuthorizeUrl(config.getClientId(), config.getRedirectUri());
}
}
@@ -1,19 +0,0 @@
package me.zhyd.oauth.authorization;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.utils.UrlBuilder;
/**
* 小米授权
*
* @author yangkai.shen (https://xkcoding.com)
* @version 1.5
* @since 1.5
*/
public class MiAuthorization implements Authorization {
@Override
public String getAuthorizeUrl(AuthConfig config) {
return UrlBuilder.getMiAuthorizeUrl(config.getClientId(), config.getRedirectUri());
}
}
@@ -1,19 +0,0 @@
package me.zhyd.oauth.authorization;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.utils.UrlBuilder;
/**
* 微软授权
*
* @author yangkai.shen (https://xkcoding.com)
* @version 1.5
* @since 1.5
*/
public class MicrosoftAuthorization implements Authorization {
@Override
public String getAuthorizeUrl(AuthConfig config) {
return UrlBuilder.getMicrosoftAuthorizeUrl(config.getClientId(), config.getRedirectUri());
}
}
@@ -1,19 +0,0 @@
package me.zhyd.oauth.authorization;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.utils.UrlBuilder;
/**
* 开源中国授权
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0
* @since 1.8
*/
public class OschinaAuthorization implements Authorization {
@Override
public String getAuthorizeUrl(AuthConfig config) {
return UrlBuilder.getOschinaAuthorizeUrl(config.getClientId(), config.getRedirectUri());
}
}
@@ -1,19 +0,0 @@
package me.zhyd.oauth.authorization;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.utils.UrlBuilder;
/**
* QQ授权
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0
* @since 1.8
*/
public class QqAuthorization implements Authorization {
@Override
public String getAuthorizeUrl(AuthConfig config) {
return UrlBuilder.getQqAuthorizeUrl(config.getClientId(), config.getRedirectUri());
}
}
@@ -1,19 +0,0 @@
package me.zhyd.oauth.authorization;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.utils.UrlBuilder;
/**
* 淘宝授权
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0
* @since 1.8
*/
public class TaobaoAuthorization implements Authorization {
@Override
public String getAuthorizeUrl(AuthConfig config) {
return UrlBuilder.getTaobaoAuthorizeUrl(config.getClientId(), config.getRedirectUri());
}
}
@@ -1,19 +0,0 @@
package me.zhyd.oauth.authorization;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.utils.UrlBuilder;
/**
* 腾讯云授权
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0
* @since 1.8
*/
public class TencentCloudAuthorization implements Authorization {
@Override
public String getAuthorizeUrl(AuthConfig config) {
return UrlBuilder.getTencentCloudAuthorizeUrl(config.getClientId(), config.getRedirectUri());
}
}
@@ -1,19 +0,0 @@
package me.zhyd.oauth.authorization;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.utils.UrlBuilder;
/**
* 今日头条授权
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0
* @since 1.8
*/
public class ToutiaoAuthorization implements Authorization {
@Override
public String getAuthorizeUrl(AuthConfig config) {
return UrlBuilder.getToutiaoAuthorizeUrl(config.getClientId(), config.getRedirectUri());
}
}
@@ -1,19 +0,0 @@
package me.zhyd.oauth.authorization;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.utils.UrlBuilder;
/**
* 微信授权
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0
* @since 1.8
*/
public class WeChatAuthorization implements Authorization {
@Override
public String getAuthorizeUrl(AuthConfig config) {
return UrlBuilder.getWeChatAuthorizeUrl(config.getClientId(), config.getRedirectUri());
}
}
@@ -1,19 +0,0 @@
package me.zhyd.oauth.authorization;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.utils.UrlBuilder;
/**
* 微博授权
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0
* @since 1.8
*/
public class WeiboAuthorization implements Authorization {
@Override
public String getAuthorizeUrl(AuthConfig config) {
return UrlBuilder.getWeiboAuthorizeUrl(config.getClientId(), config.getRedirectUri());
}
}
@@ -1,6 +1,7 @@
package me.zhyd.oauth.config;
import lombok.*;
import lombok.Builder;
import lombok.Getter;
/**
* JustAuth配置类
@@ -9,11 +10,8 @@ import lombok.*;
* @version 1.0
* @since 1.8
*/
@Setter
@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class AuthConfig {
/**
@@ -35,4 +33,27 @@ public class AuthConfig {
* 支付宝公钥:当选择支付宝登录时,该值可用
*/
private String alipayPublicKey;
/**
* 是否需要申请unionid,目前只针对qq登录
* 注:qq授权登录时,获取unionid需要单独发送邮件申请权限。如果个人开发者账号中申请了该权限,可以将该值置为true,在获取openId时就会同步获取unionId
* 参考链接:http://wiki.connect.qq.com/unionid%E4%BB%8B%E7%BB%8D
* <p>
* 1.7.1版本新增参数
*/
private boolean unionId;
/**
* 一个神奇的参数,最好使用随机的不可测的内容,可以用来防止CSRF攻击
* <p>
* 1.8.0版本新增参数
*/
private String state;
/**
* Stack Overflow Key
* <p>
* 1.9.0版本新增参数
*/
private String stackOverflowKey;
}
@@ -1,7 +1,7 @@
package me.zhyd.oauth.consts;
package me.zhyd.oauth.config;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.request.ResponseStatus;
import me.zhyd.oauth.model.AuthResponseStatus;
/**
* 各api需要的url 用枚举类分平台类型管理
@@ -10,7 +10,7 @@ import me.zhyd.oauth.request.ResponseStatus;
* @version 1.0
* @since 1.0
*/
public enum ApiUrl {
public enum AuthSource {
/**
* Github
*/
@@ -29,16 +29,6 @@ public enum ApiUrl {
public String userInfo() {
return "https://api.github.com/user";
}
@Override
public String revoke() {
throw new AuthException(ResponseStatus.UNSUPPORTED);
}
@Override
public String refresh() {
throw new AuthException(ResponseStatus.UNSUPPORTED);
}
},
/**
* 新浪微博
@@ -58,16 +48,6 @@ public enum ApiUrl {
public String userInfo() {
return "https://api.weibo.com/2/users/show.json";
}
@Override
public String revoke() {
throw new AuthException(ResponseStatus.UNSUPPORTED);
}
@Override
public String refresh() {
throw new AuthException(ResponseStatus.UNSUPPORTED);
}
},
/**
* gitee
@@ -87,16 +67,6 @@ public enum ApiUrl {
public String userInfo() {
return "https://gitee.com/api/v5/user";
}
@Override
public String revoke() {
throw new AuthException(ResponseStatus.UNSUPPORTED);
}
@Override
public String refresh() {
throw new AuthException(ResponseStatus.UNSUPPORTED);
}
},
/**
* 钉钉
@@ -109,23 +79,13 @@ public enum ApiUrl {
@Override
public String accessToken() {
throw new AuthException(ResponseStatus.UNSUPPORTED);
throw new AuthException(AuthResponseStatus.UNSUPPORTED);
}
@Override
public String userInfo() {
return "https://oapi.dingtalk.com/sns/getuserinfo_bycode";
}
@Override
public String revoke() {
throw new AuthException(ResponseStatus.UNSUPPORTED);
}
@Override
public String refresh() {
throw new AuthException(ResponseStatus.UNSUPPORTED);
}
},
/**
* 百度
@@ -153,7 +113,7 @@ public enum ApiUrl {
@Override
public String refresh() {
throw new AuthException(ResponseStatus.UNSUPPORTED);
return "https://openapi.baidu.com/oauth/2.0/token";
}
},
/**
@@ -174,16 +134,6 @@ public enum ApiUrl {
public String userInfo() {
return "https://api.csdn.net/user/getinfo";
}
@Override
public String revoke() {
throw new AuthException(ResponseStatus.UNSUPPORTED);
}
@Override
public String refresh() {
throw new AuthException(ResponseStatus.UNSUPPORTED);
}
},
/**
* Coding
@@ -203,21 +153,11 @@ public enum ApiUrl {
public String userInfo() {
return "https://coding.net/api/account/current_user";
}
@Override
public String revoke() {
throw new AuthException(ResponseStatus.UNSUPPORTED);
}
@Override
public String refresh() {
throw new AuthException(ResponseStatus.UNSUPPORTED);
}
},
/**
* 腾讯云开发者平台coding升级后就变成腾讯云开发者平台了
*/
TENCENTCLOUD {
TENCENT_CLOUD {
@Override
public String authorize() {
return "https://dev.tencent.com/oauth_authorize.html";
@@ -232,16 +172,6 @@ public enum ApiUrl {
public String userInfo() {
return "https://dev.tencent.com/api/account/current_user";
}
@Override
public String revoke() {
throw new AuthException(ResponseStatus.UNSUPPORTED);
}
@Override
public String refresh() {
throw new AuthException(ResponseStatus.UNSUPPORTED);
}
},
/**
* oschina 开源中国
@@ -261,16 +191,6 @@ public enum ApiUrl {
public String userInfo() {
return "https://www.oschina.net/action/openapi/user";
}
@Override
public String revoke() {
throw new AuthException(ResponseStatus.UNSUPPORTED);
}
@Override
public String refresh() {
throw new AuthException(ResponseStatus.UNSUPPORTED);
}
},
/**
* 支付宝
@@ -290,16 +210,6 @@ public enum ApiUrl {
public String userInfo() {
return "https://openapi.alipay.com/gateway.do";
}
@Override
public String revoke() {
throw new AuthException(ResponseStatus.UNSUPPORTED);
}
@Override
public String refresh() {
throw new AuthException(ResponseStatus.UNSUPPORTED);
}
},
/**
* QQ
@@ -320,14 +230,9 @@ public enum ApiUrl {
return "https://graph.qq.com/user/get_user_info";
}
@Override
public String revoke() {
throw new AuthException(ResponseStatus.UNSUPPORTED);
}
@Override
public String refresh() {
throw new AuthException(ResponseStatus.UNSUPPORTED);
return "https://graph.qq.com/oauth2.0/token";
}
},
/**
@@ -349,11 +254,6 @@ public enum ApiUrl {
return "https://api.weixin.qq.com/sns/userinfo";
}
@Override
public String revoke() {
throw new AuthException(ResponseStatus.UNSUPPORTED);
}
@Override
public String refresh() {
return "https://api.weixin.qq.com/sns/oauth2/refresh_token";
@@ -375,17 +275,7 @@ public enum ApiUrl {
@Override
public String userInfo() {
throw new AuthException(ResponseStatus.UNSUPPORTED);
}
@Override
public String revoke() {
throw new AuthException(ResponseStatus.UNSUPPORTED);
}
@Override
public String refresh() {
throw new AuthException(ResponseStatus.UNSUPPORTED);
throw new AuthException(AuthResponseStatus.UNSUPPORTED);
}
},
/**
@@ -404,17 +294,7 @@ public enum ApiUrl {
@Override
public String userInfo() {
return "https://oauth2.googleapis.com/tokeninfo";
}
@Override
public String revoke() {
throw new AuthException(ResponseStatus.UNSUPPORTED);
}
@Override
public String refresh() {
throw new AuthException(ResponseStatus.UNSUPPORTED);
return "https://www.googleapis.com/oauth2/v3/userinfo";
}
},
/**
@@ -435,16 +315,6 @@ public enum ApiUrl {
public String userInfo() {
return "https://graph.facebook.com/v3.3/me";
}
@Override
public String revoke() {
throw new AuthException(ResponseStatus.UNSUPPORTED);
}
@Override
public String refresh() {
throw new AuthException(ResponseStatus.UNSUPPORTED);
}
},
/**
* 抖音
@@ -457,22 +327,17 @@ public enum ApiUrl {
@Override
public String accessToken() {
return "https://open.douyin.com/oauth/access_token";
return "https://open.douyin.com/oauth/access_token/";
}
@Override
public String userInfo() {
return "https://open.douyin.com/oauth/userinfo";
}
@Override
public String revoke() {
throw new AuthException(ResponseStatus.UNSUPPORTED);
return "https://open.douyin.com/oauth/userinfo/";
}
@Override
public String refresh() {
return "https://open.douyin.com/oauth/refresh_token";
return "https://open.douyin.com/oauth/refresh_token/";
}
},
/**
@@ -494,11 +359,6 @@ public enum ApiUrl {
return "https://api.linkedin.com/v2/me";
}
@Override
public String revoke() {
throw new AuthException(ResponseStatus.UNSUPPORTED);
}
@Override
public String refresh() {
return "https://www.linkedin.com/oauth/v2/accessToken";
@@ -523,11 +383,6 @@ public enum ApiUrl {
return "https://graph.microsoft.com/v1.0/me";
}
@Override
public String revoke() {
throw new AuthException(ResponseStatus.UNSUPPORTED);
}
@Override
public String refresh() {
return "https://login.microsoftonline.com/common/oauth2/v2.0/token";
@@ -552,11 +407,6 @@ public enum ApiUrl {
return "https://open.account.xiaomi.com/user/profile";
}
@Override
public String revoke() {
throw new AuthException(ResponseStatus.UNSUPPORTED);
}
@Override
public String refresh() {
return "https://account.xiaomi.com/oauth2/token";
@@ -580,15 +430,94 @@ public enum ApiUrl {
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 revoke() {
throw new AuthException(ResponseStatus.UNSUPPORTED);
public String accessToken() {
return "https://account.teambition.com/oauth2/access_token";
}
@Override
public String refresh() {
throw new AuthException(ResponseStatus.UNSUPPORTED);
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";
}
};
@@ -618,13 +547,17 @@ public enum ApiUrl {
*
* @return url
*/
public abstract String revoke();
public String revoke() {
throw new AuthException(AuthResponseStatus.UNSUPPORTED);
}
/**
* 刷新授权的api
*
* @return url
*/
public abstract String refresh();
public String refresh() {
throw new AuthException(AuthResponseStatus.UNSUPPORTED);
}
}
@@ -1,4 +1,7 @@
package me.zhyd.oauth.model;
package me.zhyd.oauth.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 今日头条授权登录时的异常状态码
@@ -7,6 +10,8 @@ package me.zhyd.oauth.model;
* @version 1.0
* @since 1.8
*/
@Getter
@AllArgsConstructor
public enum AuthToutiaoErrorCode {
EC0(0, "接口调用成功"),
EC1(1, "API配置错误,未传入Client Key"),
@@ -29,11 +34,6 @@ public enum AuthToutiaoErrorCode {
private int code;
private String desc;
AuthToutiaoErrorCode(int code, String desc) {
this.code = code;
this.desc = desc;
}
public static AuthToutiaoErrorCode getErrorCode(int errorCode) {
AuthToutiaoErrorCode[] errorCodes = AuthToutiaoErrorCode.values();
for (AuthToutiaoErrorCode code : errorCodes) {
@@ -43,12 +43,4 @@ public enum AuthToutiaoErrorCode {
}
return EC999;
}
public int getCode() {
return code;
}
public String getDesc() {
return desc;
}
}
@@ -0,0 +1,36 @@
package me.zhyd.oauth.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
/**
* 用户性别
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0
* @since 1.8
*/
@Getter
@AllArgsConstructor
public enum AuthUserGender {
MALE(1, ""), FEMALE(0, ""), UNKNOWN(-1, "未知");
private int code;
private String desc;
public static AuthUserGender getRealGender(String code) {
if (code == null) {
return UNKNOWN;
}
String[] males = {"m", "", "1", "male"};
if (Arrays.asList(males).contains(code.toLowerCase())) {
return MALE;
}
String[] females = {"f", "", "0", "female"};
if (Arrays.asList(females).contains(code.toLowerCase())) {
return FEMALE;
}
return UNKNOWN;
}
}
@@ -1,6 +1,6 @@
package me.zhyd.oauth.exception;
import me.zhyd.oauth.request.ResponseStatus;
import me.zhyd.oauth.model.AuthResponseStatus;
/**
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
@@ -13,16 +13,16 @@ public class AuthException extends RuntimeException {
private String errorMsg;
public AuthException(String errorMsg) {
this(ResponseStatus.FAILURE.getCode(), errorMsg);
this(AuthResponseStatus.FAILURE.getCode(), errorMsg);
}
public AuthException(int errorCode, String errorMsg) {
super(errorCode + ":" + errorMsg);
super(errorMsg);
this.errorCode = errorCode;
this.errorMsg = errorMsg;
}
public AuthException(ResponseStatus status) {
public AuthException(AuthResponseStatus status) {
super(status.getMsg());
}
@@ -30,6 +30,10 @@ public class AuthException extends RuntimeException {
super(message, cause);
}
public AuthException(Throwable cause) {
super(cause);
}
public int getErrorCode() {
return errorCode;
}
@@ -1,62 +0,0 @@
package me.zhyd.oauth.model;
import me.zhyd.oauth.utils.StringUtils;
/**
* 百度授权登录时的异常状态码
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0
* @since 1.8
*/
public enum AuthBaiduErrorCode {
OK("ok", "ok", "ok"),
INVALID_REQUEST("invalid_request", "invalid refresh token", "请求缺少某个必需参数,包含一个不支持的参数或参数值,或者格式不正确。"),
INVALID_CLIENT("invalid_client", "unknown client id", "client_id”、“client_secret”参数无效。"),
INVALID_GRANT("invalid_grant", "The provided authorization grant is revoked", "提供的Access Grant是无效的、过期的或已撤销的,例如,Authorization Code无效(一个授权码只能使用一次)、Refresh Token无效、redirect_uri与获取Authorization Code时提供的不一致、Devie Code无效(一个设备授权码只能使用一次)等。"),
UNAUTHORIZED_CLIENT("unauthorized_client", "The client is not authorized to use this authorization grant type", "应用没有被授权,无法使用所指定的grant_type。"),
UNSUPPORTED_GRANT_TYPE("unsupported_grant_type", "The authorization grant type is not supported", "“grant_type”百度OAuth2.0服务不支持该参数。"),
INVALID_SCOPE("invalid_scope", "The requested scope is exceeds the scope granted by the resource owner", "请求的“scope”参数是无效的、未知的、格式不正确的、或所请求的权限范围超过了数据拥有者所授予的权限范围。"),
EXPIRED_TOKEN("expired_token", "refresh token has been used", "提供的Refresh Token已过期"),
REDIRECT_URI_MISMATCH("redirect_uri_mismatch", "Invalid redirect uri", "“redirect_uri”所在的根域与开发者注册应用时所填写的根域名不匹配。"),
UNSUPPORTED_RESPONSE_TYPE("unsupported_response_type", "The response type is not supported", "“response_type”参数值不为百度OAuth2.0服务所支持,或者应用已经主动禁用了对应的授权模式"),
SLOW_DOWN("slow_down", "The device is polling too frequently", "Device Flow中,设备通过Device Code换取Access Token的接口过于频繁,两次尝试的间隔应大于5秒。"),
AUTHORIZATION_PENDING("authorization_pending", "User has not yet completed the authorization", "Device Flow中,用户还没有对Device Code完成授权操作。"),
AUTHORIZATION_DECLINED("authorization_declined", "User has declined the authorization", "Device Flow中,用户拒绝了对Device Code的授权操作。"),
INVALID_REFERER("invalid_referer", "Invalid Referer", "Implicit Grant模式中,浏览器请求的Referer与根域名绑定不匹配");
private String code;
private String msg;
private String desc;
AuthBaiduErrorCode(String code, String msg, String desc) {
this.code = code;
this.msg = msg;
this.desc = desc;
}
public static AuthBaiduErrorCode getErrorCode(String code) {
if (StringUtils.isEmpty(code)) {
return OK;
}
AuthBaiduErrorCode[] errorCodes = AuthBaiduErrorCode.values();
for (AuthBaiduErrorCode errorCode : errorCodes) {
if (code.equalsIgnoreCase(errorCode.getCode())) {
return errorCode;
}
}
return OK;
}
public String getCode() {
return code;
}
public String getMsg() {
return msg;
}
public String getDesc() {
return desc;
}
}
@@ -0,0 +1,31 @@
package me.zhyd.oauth.model;
import lombok.Getter;
import lombok.Setter;
/**
* 授权回调时的参数类
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0
* @since 1.8
*/
@Getter
@Setter
public class AuthCallback {
/**
* 访问AuthorizeUrl后回调时带的参数code
*/
private String code;
/**
* 访问AuthorizeUrl后回调时带的参数auth_code,该参数目前只使用于支付宝登录
*/
private String auth_code;
/**
* 访问AuthorizeUrl后回调时带的参数state,用于和请求AuthorizeUrl前的state比较,防止CSRF攻击
*/
private String state;
}
@@ -1,404 +0,0 @@
package me.zhyd.oauth.model;
/**
* 钉钉授权登录时的异常状态码
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0
* @since 1.8
*/
public enum AuthDingTalkErrorCode {
EC1_MINUS(-1, "系统繁忙", "服务器暂不可用,建议稍候再重试1次,最多重试3次"),
EC0(0, "请求成功", "接口调用成功"),
EC404(404, "请求的URI地址不存在", "地址不存在,检查下url是否和文档里写的一致"),
EC33001(33001, "无效的企业ID", "请确认下access_token是否正确"),
EC33002(33002, "无效的微应用的名称", "校验下微应用的名称字段,不能为空且长度不能超过10个字符"),
EC33003(33003, "无效的微应用的描述", "校验下微应用的描述字段,不能为空且长度不能超过20个字符"),
EC33004(33004, "无效的微应用的ICON", "校验下微应用的icon字段,不能为空且确保图标存在"),
EC33005(33005, "无效的微应用的移动端主页", "校验下微应用的移动端主页,不能为空且必须以http开头或https开头"),
EC33006(33006, "无效的微应用的PC端主页", "校验下微应用的PC端主页,必须以http开头或https开头"),
EC33007(33007, "微应用的移动端的主页与PC端主页不同", "校验下微应用的PC端主页,确保它和移动端主页的域名保持一致"),
EC33008(33008, "无效的微应用OA后台的主页", "校验下微应用的后台管理的主页失败,必须以http开头或https开头"),
EC34001(34001, "无效的会话id", "检查下所传的chatId字段是否为空"),
EC34002(34002, "无效的会话消息的发送者", "检查下sender字段是否为空"),
EC34003(34003, "无效的会话消息的发送者的企业Id", "检查下发送者的企业Id"),
EC34004(34004, "无效的会话消息的类型", "检查下msgtype字段,是否为空,是否是定义的那几种类型"),
EC34005(34005, "无效的会话音频消息的播放时间", "该错误码已废弃"),
EC34006(34006, "发送者不在企业中", "检查下发送者是否在企业中"),
EC34007(34007, "发送者不在会话中", "检查下发送者是否在会话id中"),
EC34008(34008, "图片不能为空", "如果发的是图片消息,检查下图片是否为空"),
EC34009(34009, "链接内容不能为空", "检查下messageUrl字段是否为空"),
EC34010(34010, "文件不能为空", "检查下media_id字段是否为空"),
EC34011(34011, "音频文件不能为空", "检查下media_id字段是否为空"),
EC34012(34012, "找不到发送者的企业", "检查下发送者是否是真实的"),
EC34013(34013, "找不到群会话对象", "检查下chatid是否真实存在"),
EC34014(34014, "会话消息的json结构无效或不完整", "检查下消息的json格式是否正确,json的key对应msgtype的value值"),
EC34015(34015, "发送群会话消息失败", "消息发送失败,建议稍后再重试下"),
EC34016(34016, "消息内容长度超过限制", "检查下消息的content字段长度是否超过5000title字段长度是否超过64markdown字段长度是否超过5000single_title字段长度是否超过20single_url字段长度是否超过500btn_json_list字段长度是否超过1000"),
EC40001(40001, "获取access_token时Secret错误,或者access_token无效", "检查下access_token是否正确"),
EC40002(40002, "不合法的凭证类型", ""),
EC40003(40003, "不合法的UserID", "确保该id在通讯录中存在,且是在你所传access_token对应的企业里"),
EC40004(40004, "不合法的媒体文件类型", "检查下type字段,只支持imagevoicefile"),
EC40005(40005, "不合法的文件类型", "如果是文件类型,检查下是否是支持的那几种,目前只支持doc,docxxlsxlsxpptpptxzippdfrar"),
EC40006(40006, "不合法的文件大小", "检查下文件大小,image类型最大1MBfile类型最大10MBvoice类型最大2MB"),
EC40007(40007, "不合法的媒体文件id", "检查下mediaId是否为空,是否真实存在"),
EC40008(40008, "不合法的消息类型", "检查下msgtype是否为空,确保它在开放平台定义的几种类型里,具体见消息类型及格式"),
EC40009(40009, "不合法的部门id", "检查下部门id是否为空,是否为数字且大于0"),
EC40010(40010, "不合法的父部门id", "检查下父部门id是否为一个数字"),
EC40011(40011, "不合法的排序order", "检查下order字段是否为空,是否为数字且大于0"),
EC40012(40012, "不合法的发送者", "检查下sender字段是否为空,是否真实存在"),
EC40013(40013, "不合法的corpid", "检查下corpid是否有效"),
EC40014(40014, "不合法的access_token", "检查下access_token是否正确,注意access_token这个参数应该是带在url后面的"),
EC40015(40015, "发送者不在会话中", "检查下sender字段和cid字段是否能对应上"),
EC40016(40016, "不合法的会话ID", "检查下cid字段是否为空,是否有效"),
EC40017(40017, "在会话中没有找到与发送者在同一企业的人", "cid对应的消息接收者为空,检查下cid字段"),
EC40018(40018, "不允许以递归方式查询部门用户列表", "检查下fetchChild字段,目前不支持递归查询"),
EC40019(40019, "该手机号码对应的用户最多可以加入5个非认证企业", ""),
EC40020(40020, "当前团队人数已经达到上限,用电脑登录钉钉企业管理后台,升级成为认证企业", ""),
EC40021(40021, "更换的号码已注册过钉钉,无法使用该号码", ""),
EC40022(40022, "企业中的手机号码和登录钉钉的手机号码不一致,暂时不支持修改用户信息,可以删除后重新添加", ""),
EC40023(40023, "部门人数达到上限", "部门人数不能超过1000"),
EC40024(40024, "(安全校验不通过)保存失败,团队人数超限。请在手机钉钉绑定支付宝完成实名认证,或者申请企业认证,人数上限自动扩充", ""),
EC40025(40025, "无效的部门JSONArray对象,合法格式需要用中括号括起来,且如果属于多部门,部门id需要用逗号分隔", ""),
EC40029(40029, "不合法的oauth_code", ""),
EC40031(40031, "不合法的UserID列表", "指定的UserID列表,至少存在一个UserID不在通讯录中"),
EC40032(40032, "不合法的UserID列表长度", "检查下列表是否为空,且长度合适。创建部门接口的userPerimits最多接收10000个"),
EC40033(40033, "不合法的请求字符,不能包含\\uxxxx格式的字符", ""),
EC40035(40035, "不合法的参数", "检查下有没有传请求参数,一般发生在http post形式的接口里,没有传参数"),
EC40038(40038, "不合法的请求格式", "检查下参数中是不是少了某个字段,具体参考各个文档的参数介绍"),
EC40039(40039, "不合法的URL长度", ""),
EC40048(40048, "url中包含不合法domain", "发消息接口中消息url链接不安全"),
EC40055(40055, "不合法的agent结构", "已废弃"),
EC40056(40056, "不合法的agentid", "检查下agentid字段是否为空,是否真实存在"),
EC40057(40057, "不合法的callbackurl", ""),
EC40061(40061, "设置应用头像失败", ""),
EC40062(40062, "不合法的应用模式", ""),
EC40063(40063, "不合法的分机号", "tel字段长度超长,长度不能超过50"),
EC40064(40064, "不合法的工作地址", "workPlace长度超长,长度不能超过50个字符"),
EC40065(40065, "不合法的备注", "remark长度超长,长度不能超过1024个字符"),
EC40066(40066, "不合法的部门列表", "部门id列表长度太长,不能超过10000,并且每个id必须是数字"),
EC40067(40067, "标题长度不合法", "检查下标题长度"),
EC40068(40068, "不合法的偏移量", "偏移量必须大于0"),
EC40069(40069, "不合法的分页大小", "分页大小不合法,具体参考每个接口的参数定义"),
EC40070(40070, "不合法的排序参数", "具体参考获取部门成员接口里面对order字段的定义"),
EC40073(40073, "不存在的openid", "openid不能为空"),
EC40077(40077, "不存在的预授权码", ""),
EC40078(40078, "不存在的临时授权码", "临时授权码不能为空,且只能被使用一次"),
EC40079(40079, "不存在的授权信息", "检查下企业是否授权"),
EC40080(40080, "不合法的suitesecret", ""),
EC40082(40082, "不合法的suitetoken", "检查下token"),
EC40083(40083, "不合法的suiteid", "suiteKey字段不合法"),
EC40084(40084, "不合法的永久授权码", "检查下永久授权码是否正确"),
EC40085(40085, "不存在的suiteticket", "检查下suiteticket是否正确,确保是由回调接口正确来接收suiteticket"),
EC40086(40086, "不合法的第三方应用appid", "appid字段不能为空"),
EC40087(40087, "创建永久授权码失败", "稍后再重试下,确保参数都传对"),
EC40088(40088, "不合法的套件key或secret", "稍后再重试下,确保suiteKey和suiteSecret都传对且一一对应"),
EC40089(40089, "不合法的corpid或corpsecret", "稍后再重试下,确保corpid和corpsecret字段传对且一一对应"),
EC40090(40090, "套件已经不存在", "检查下suiteKey字段是否正确"),
EC40091(40091, "用户授权码创建失败,需要用户重新授权", "创建永久授权码失败,需要用户重新授权产生临时授权码"),
EC40103(40103, "用户开启了账号保护,无法被加入到您的团队", "用户在钉钉“我的-设置-隐私”出开启了账号保护"),
EC40104(40104, "无效手机号", "检查手机号格式是否正确"),
EC41001(41001, "缺少access_token参数", "检查下access_token是否传了,注意该参数必须跟在请求url中"),
EC41002(41002, "缺少corpid参数", "检查下corpid是否为空"),
EC41003(41003, "缺少refresh_token参数", "检查下refresh_token是否为空"),
EC41004(41004, "缺少secret参数", "检查下secret参数是否为空"),
EC41005(41005, "缺少多媒体文件数据", ""),
EC41006(41006, "缺少media_id参数", "检查下media_id参数是否为空"),
EC41007(41007, "无效的ssocode", "sso的永久授权code无效,检查下是否为空"),
EC41008(41008, "缺少oauth", ""),
EC41009(41009, "缺少UserID", "检查下UserID是否为空"),
EC41010(41010, "缺少url", "检查下url是否为空"),
EC41011(41011, "缺少agentid", "检查下agentid是否为空"),
EC41012(41012, "缺少应用头像mediaid", "检查下mediaid是否为空"),
EC41013(41013, "缺少应用名字", "检查应用名字是否为空"),
EC41014(41014, "缺少应用描述", "检查应用描述是否为空"),
EC41015(41015, "缺少JSON参数", "检查JSON参数是否为空"),
EC41021(41021, "缺少suitekey", "检查suitekey参数是否为空"),
EC41022(41022, "缺少suitetoken", "检查suitetoken参数是否为空"),
EC41023(41023, "缺少suiteticket", "检查suiteticket参数是否为空"),
EC41024(41024, "缺少suitesecret", "检查suitesecret参数是否为空"),
EC41025(41025, "缺少permanent_code", "检查permanent_code永久授权码参数是否为空"),
EC41026(41026, "缺少tmp_auth_code", "检查tmp_auth_code临时授权码参数是否为空"),
EC41027(41027, "需要授权企业的corpid参数", "检查corpid参数是否为空"),
EC41028(41028, "禁止给全员发送消息", "检查是否有全员发送消息的权限,ISV没有该权限"),
EC41029(41029, "超过消息接收者人数上限", "发送OA消息人数超上限(企业消息人数上限:5000,ISV消息人数上限:1000"),
EC41030(41030, "企业未对该套件授权", "检查该企业是否已经对该套件进行授权"),
EC41031(41031, "auth_corpid和permanent_code不匹配", "激活套件时使用的auth_corpid和permanent_code不匹配"),
EC41041(41041, "查询间隔时间太长", "考勤打卡数据查询间隔时间超过7天"),
EC41044(41044, "禁止发送消息", "检查是否有权限发送消息"),
EC41045(41045, "单应用全员消息/每天总量超限", ""),
EC41046(41046, "超过发送全员消息的每分钟次数上限", "企业OA消息全员发送每天不能超过3次,ISV不能发送全员消息"),
EC41047(41047, "超过给该企业发消息的每分钟次数上限", "企业OA消息每分钟不能超过1500次,ISV OA消息每分钟不能超过200次"),
EC41048(41048, "超过给企业发消息的每分钟次数总上限", ""),
EC41049(41049, "包含违禁内容", "检查消息文本中是否有黄色、反动等词语"),
EC41050(41050, "无效的活动编码", ""),
EC41051(41051, "活动权益的校验失败", ""),
EC41100(41100, "时间参数不合法", "时间参数不能为空,且为“yyyy-MM-dd hh:mm:ss”格式"),
EC41101(41101, "数据内容过长", "请求体字符长度不能大于4096"),
EC41102(41102, "参数值过大", "上传文件或者idlist等参数过大"),
EC42001(42001, "access_token超时", "请检查网络状态"),
EC42002(42002, "refresh_token超时", "请检查网络状态"),
EC42003(42003, "oauth_code超时", "请检查网络状态"),
EC42007(42007, "预授权码失效", "请检查该预授权码是否已经使用过"),
EC42008(42008, "临时授权码失效", "请检查该临时授权码是否已经使用过或者是否不正确"),
EC42009(42009, "suitetoken失效", "请检查该suitetoken是否已经过期"),
EC43001(43001, "需要GET请求", "请检查http请求方式是否正确"),
EC43002(43002, "需要POST请求", "请检查http请求方式是否正确"),
EC43003(43003, "需要HTTPS", "请检查调用接口协议是否是https"),
EC43004(43004, "无效的HTTP HEADER Content-Type", "请检查请求头中的content-type是否正确"),
EC43005(43005, "需要Content-Type为application/json;charset=UTF-8", "请检查请求头中的content-type是否是“application/json;charset=UTF-8”"),
EC43007(43007, "需要授权", "该接口需要access_token才能调用"),
EC43008(43008, "参数需要multipart类型", "检查提交参数中的ENCTYPE是否是multipart类型"),
EC43009(43009, "post参数需要json类型", "请检查post参数数据是否是json类型"),
EC44001(44001, "多媒体文件为空", "请检查多媒体文件数据是否为空"),
EC44002(44002, "POST的数据包为空", "请检查POST的数据包是否为空"),
EC44003(44003, "图文消息内容为空", "请检查图文消息参数是否为空"),
EC44004(44004, "文本消息内容为空", "请检查文本消息参数是否为空"),
EC45001(45001, "多媒体文件大小超过限制", ""),
EC45002(45002, "消息内容超过限制", ""),
EC45003(45003, "标题字段超过限制", ""),
EC45004(45004, "描述字段超过限制", ""),
EC45005(45005, "链接字段超过限制", ""),
EC45006(45006, "图片链接字段超过限制", ""),
EC45007(45007, "语音播放时间超过限制", ""),
EC45008(45008, "图文消息超过限制", ""),
EC45009(45009, "接口调用超过限制", ""),
EC45016(45016, "系统分组,不允许修改", ""),
EC45017(45017, "分组名字过长", ""),
EC45018(45018, "分组数量超过上限", ""),
EC45024(45024, "账号数量超过上限", ""),
EC46001(46001, "不存在媒体数据", ""),
EC46004(46004, "不存在的员工", ""),
EC47001(47001, "解析JSON/XML内容错误", ""),
EC48002(48002, "Api禁用", ""),
EC48003(48003, "suitetoken无效", ""),
EC48004(48004, "授权关系无效", ""),
EC49000(49000, "缺少chatid", "请检查参数中是否有chatid"),
EC49001(49001, "绑定的微应用超过个数限制", "绑定群会话和微应用超过5个"),
EC49002(49002, "一个群只能被一个ISV套件绑定一次", ""),
EC49003(49003, "操作者必须为群主", ""),
EC49004(49004, "添加成员列表和删除成员列表不能有交集", ""),
EC49005(49005, "群人数超过人数限制", ""),
EC49006(49006, "群成员列表必须包含群主", ""),
EC49007(49007, "超过创建群的个数上限", ""),
EC49008(49008, "不合法的群类型,只能传入0或2", ""),
EC49009(49009, "企业群不能添加外部联系人,群主只能为企业员工", ""),
EC49010(49010, "群成员不能为空", ""),
EC49011(49011, "群员工列表超长", ""),
EC49012(49012, "群外部联系人列表超长", ""),
EC49013(49013, "群主不能为空", ""),
EC49014(49014, "非法的群主类型,只能为emp或者ext", ""),
EC49015(49015, "不合法的群名称", ""),
EC49016(49016, "查询企业员工不存在", ""),
EC49017(49017, "查询企业外部联系人不存在", ""),
EC49018(49018, "群主非企业员工", ""),
EC49019(49019, "群主非企业外部通讯录人员", ""),
EC49020(49020, "某人处于勿扰模式,拒绝加入群聊;请先与TA建立好友关系", ""),
EC49021(49021, "非好友建立群聊,认证用户一天只能拉50个人,非认证用户一天只能拉10个人", ""),
EC49022(49022, "某人拒绝加入群聊", ""),
EC49023(49023, "某人处于勿扰模式,拒绝加入群聊;请先与TA建立好友关系", ""),
EC50001(50001, "redirect_uri未授权", ""),
EC50002(50002, "员工不在权限范围", ""),
EC50003(50003, "应用已停用", ""),
EC50004(50004, "企业部门不在授权范围", "检查企业部门是否设置可见范围,具体排查方法请参考通讯录FAQ"),
EC50005(50005, "企业已禁用", ""),
EC52010(52010, "无效的corpid", "请检查corpid参数是否正确"),
EC52011(52011, "jsapi ticket 读取失败", "请检查ticket参数是否正确"),
EC52012(52012, "jsapi 签名生成失败", "请检查“url, nonceStr, timestamp, ticket”等参数是否正确"),
EC52013(52013, "签名校验失败", "请检查“url, nonceStr, timestamp, ticket”等参数是否正确"),
EC52014(52014, "无效的url参数", "请检查url参数是否正确"),
EC52015(52015, "无效的随机字符串参数", "请检查nonceStr参数是否正确"),
EC52016(52016, "无效的签名参数", "请检查“url, nonceStr, timestamp, ticket”等参数是否正确"),
EC52017(52017, "无效的jsapi列表参数", "请检查dd.config中的jsApiList参数是否正确"),
EC52018(52018, "无效的时间戳", "请检查timestamp参数是否正确"),
EC52019(52019, "无效的agentid", "请检查agentid参数是否正确"),
EC60001(60001, "不合法的部门名称", "请检查部门名称是否正确,长度不能超过64个字符"),
EC60002(60002, "部门层级深度超过限制", ""),
EC60003(60003, "部门不存在", ""),
EC60004(60004, "父亲部门不存在", ""),
EC60005(60005, "不允许删除有成员的部门", ""),
EC60006(60006, "不允许删除有子部门的部门", ""),
EC60007(60007, "不允许删除根部门", ""),
EC60008(60008, "父部门下该部门名称已存在", ""),
EC60009(60009, "部门名称含有非法字符", ""),
EC60010(60010, "部门存在循环关系", ""),
EC60011(60011, "没有调用该接口的权限", "请检查当前请求使用的access_token是否有对该部门/人的操作权限,查看获取appSecret授权范围"),
EC60012(60012, "不允许删除默认应用", ""),
EC60013(60013, "不允许关闭应用", ""),
EC60014(60014, "不允许开启应用", ""),
EC60015(60015, "不允许修改默认应用可见范围", ""),
EC60016(60016, "部门id已经存在", ""),
EC60017(60017, "不允许设置企业", ""),
EC60018(60018, "不允许更新根部门", ""),
EC60019(60019, "从部门查询人员失败", "请检查该成员是否在该部门中"),
EC60020(60020, "访问ip不在白名单之中", "如果是企业应用,检查配置的服务器出口ip地址是否和请求ip地址一致。如果是isv应用,请检查套件ip白名单和请求ip是否一致"),
EC60067(60067, "部门的企业群群主不存在", ""),
EC60068(60068, "部门的管理员不存在", ""),
EC60102(60102, "UserID在公司中已存在", ""),
EC60103(60103, "手机号码不合法", ""),
EC60104(60104, "手机号码在公司中已存在", ""),
EC60105(60105, "邮箱不合法", ""),
EC60106(60106, "邮箱已存在", ""),
EC60107(60107, "使用该手机登录钉钉的用户已经在企业中", ""),
EC60110(60110, "部门个数超出限制", ""),
EC60111(60111, "UserID不存在", ""),
EC60112(60112, "用户name不合法", ""),
EC60113(60113, "身份认证信息(手机/邮箱)不能同时为空", ""),
EC60114(60114, "性别不合法", ""),
EC60118(60118, "用户无有效邀请字段(邮箱,手机号)", ""),
EC60119(60119, "不合法的position", ""),
EC60120(60120, "用户已禁用", ""),
EC60121(60121, "找不到该用户", "检查该企业下该员工是否存在"),
EC60122(60122, "不合法的extattr", ""),
EC60123(60123, "不合法的jobnumber", ""),
EC60124(60124, "用户不在此群中", ""),
EC60125(60125, "CRM配置信息创建失败", ""),
EC60126(60126, "CRM配置信息更新失败", ""),
EC60127(60127, "CRM人员配置信息删除失败", ""),
EC70001(70001, "企业不存在或者已经被解散", ""),
EC70002(70002, "获取套件下的微应用失败", ""),
EC70003(70003, "agentid对应微应用不存在", ""),
EC70004(70004, "企业下没有对应该agentid的微应用", "注意:代表应用和企业映射关系的ID (appId的实例化ID),同一个ISV应用在不同企业的agentId不一致"),
EC70005(70005, "ISV激活套件失败", "请检查激活套件使用的参数是否正确"),
EC71006(71006, "回调地址已经存在", ""),
EC71007(71007, "回调地址已不存在", ""),
EC71008(71008, "回调call_back_tag必须在指定的call_back_tag列表中", ""),
EC71009(71009, "返回文本非success", "回调地址返回的内容必须是“success”文本经过加密后的结果"),
EC71010(71010, "POST的JSON数据不包含所需要的参数字段或包含的参数格式非法", ""),
EC71011(71011, "传入的url参数不是合法的url格式", "合法的URL地址是协议+域名+端口+路径path+参数组成"),
EC71012(71012, "url地址访问异常", ""),
EC71013(71013, "此域名或IP不能注册或者接收回调事件", "注意回调地址的域名或者IP必须在套件的ip白名单中,并且该ip必须为外网ip"),
EC72001(72001, "获取钉盘空间失败", "检查domain、agent_id、access_token参数是否正确有效"),
EC72002(72002, "授权钉盘空间访问权限失败", ""),
EC80001(80001, "可信域名没有IPC备案,后续将不能在该域名下正常使用jssdk", ""),
EC81001(81001, "两个用户没有任何关系,请先相互成为好友", ""),
EC81002(81002, "用户拒收消息", ""),
EC88005(88005, "管理日历个人日历操作失败", ""),
EC89001(89001, "管理日历启动导出任务失败", ""),
EC89011(89011, "管理日历写入数据失败", ""),
EC89012(89012, "管理日历更新数据失败", ""),
EC90001(90001, "您的服务器调用钉钉开放平台所有接口的请求都被暂时禁用了", ""),
EC90002(90002, "您的服务器调用钉钉开放平台当前接口的所有请求都被暂时禁用了", ""),
EC90003(90003, "您的企业调用钉钉开放平台所有接口的请求都被暂时禁用了,仅对企业自己的Accesstoken有效", ""),
EC90004(90004, "您当前使用的CorpId及CorpSecret被暂时禁用了,仅对企业自己的Accesstoken有效", ""),
EC90005(90005, "您的企业调用当前接口次数过多,请求被暂时禁用了,仅对企业自己的Accesstoken有效", ""),
EC90006(90006, "您当前使用的CorpId及CorpSecret调用当前接口次数过多,请求被暂时禁用了,仅对企业自己的Accesstoken有效", ""),
EC90007(90007, "您当前要调用的企业的接口次数过多,对该企业的所有请求都被暂时禁用了,仅对企业授权给ISV的Accesstoken有效", ""),
EC90008(90008, "您当前要调用的企业的当前接口次数过多,对此企业下该接口的所有请求都被暂时禁用了,仅对企业授权给ISV的Accesstoken有效", ""),
EC90009(90009, "您调用企业接口超过了限制,对所有企业的所有接口的请求都被暂时禁用了,仅对企业授权给ISV的Accesstoken有效", ""),
EC90010(90010, "您调用企业当前接口超过了限制,对所有企业的该接口的请求都被暂时禁用了,仅对企业授权给ISV的Accesstoken有效", ""),
EC90011(90011, "您的套件调用企业接口超过了限制,该套件的所有请求都被暂时禁用了,仅对企业授权给ISV的Accesstoken有效", ""),
EC90012(90012, "您的套件调用企业当前接口超过了限制,该套件对此接口的所有请求都被暂时禁用了,仅对企业授权给ISV的Accesstoken有效", ""),
EC90013(90013, "您的套件调用当前企业的接口超过了限制,该套件对此企业的所有请求都被暂时禁用了,仅对企业授权给ISV的Accesstoken有效", ""),
EC90014(90014, "您的套件调用企业当前接口超过了限制,该套件对此企业该接口的所有请求都被暂时禁用了,仅对企业授权给ISV的Accesstoken有效", ""),
EC900001(900001, "加密明文文本非法", "加密明文不能为空"),
EC900002(900002, "加密时间戳参数非法", "加密时间戳不能为空"),
EC900003(900003, "加密随机字符串参数非法", "加密随机字符串不能为空"),
EC900004(900004, "不合法的aeskey", "检查aeskey是否符合规格,长度固定为43个字符,从a-z, A-Z, 0-9共62个字符中选取,是AESKey的Base64编码。解码后即为32字节长的AESKey"),
EC900005(900005, "签名不匹配", "检查签名计算的参数是否正确。请参考文档获取签名参数"),
EC900006(900006, "计算签名错误", "检查签名计算的参数是否正确。请参考文档获取签名参数"),
EC900007(900007, "计算加密文字错误", "检查是否安装JRE补丁或者对应的JRE版本是否正常。 请参考文档ISV应用开发准备工作"),
EC900008(900008, "计算解密文字错误", "检查是否安装JRE补丁或者对应的JRE版本是否正常。 请参考文档ISV应用开发准备工作"),
EC900009(900009, "计算解密文字长度不匹配", "检查aeskey是否符合规格。长度固定为43个字符,从a-z, A-Z, 0-9共62个字符中选取,是AESKey的Base64编码"),
EC900010(900010, "计算解密文字corpid不匹配", "检查corpid是否正确或者是否为当前企业的corpid"),
EC400010(400010, "激活的设备不存在(未绑定)", ""),
EC400011(400011, "设备已经激活", ""),
EC400020(400020, "无访问权限", ""),
EC400021(400021, "密钥错误", ""),
EC400022(400022, "设备不存在", ""),
EC400023(400023, "用户不存在", ""),
EC400040(400040, "回调不存在", "检查是否注册回调事件"),
EC400041(400041, "回调已经存在", "检查该回调事件是否已注册过"),
EC400042(400042, "企业不存在", ""),
EC400043(400043, "企业不合法", ""),
EC400050(400050, "回调地址无效", "检查回调地址是否正确或者符合地址格式"),
EC400051(400051, "回调地址访问异常", "注意回调地址必须部署到外网以便开发平台通过回调地址推送回调信息"),
EC400052(400052, "回调地址访返回数据错误", ""),
EC400053(400053, "回调地址在黑名单中无法注册", "回调地址已添加黑名单,无法注册"),
EC400054(400054, "回调URL访问超时", ""),
EC400055(400055, "回调设备不在线", ""),
EC400056(400056, "回调访问设备失败", ""),
EC400057(400057, "回调访问设备不存在", ""),
EC420001(420001, "客户不存在", ""),
EC420002(420002, "客户查询失败", ""),
EC420003(420003, "联系人不存在", ""),
EC420004(420004, "联系人查询失败", ""),
EC420005(420005, "客户删除失败", ""),
EC420006(420006, "联系人删除失败", ""),
EC420007(420007, "跟进人绑定失败", ""),
EC420008(420008, "客户id非法", ""),
EC420009(420009, "跟进人id非法", ""),
EC4200010(4200010, "客户联系人id非法", ""),
EC4200011(4200011, "客户描述表单不存在", ""),
EC4200012(4200012, "客户描述表单查询失败", ""),
EC4200013(4200013, "联系人描述表单不存在", ""),
EC4200014(4200014, "联系人描述表单查询失败", ""),
EC4200015(4200015, "客户描述表单格式校验错误", ""),
EC4200016(4200016, "客户描述表单格缺少固定字段", ""),
EC4200017(4200017, "客户联系人描述表单格式校验错误", ""),
EC4200018(4200018, "客户联系人描述表单格缺少固定字段", ""),
EC4200019(4200019, "客户描述表单数据格式校验错误", ""),
EC4200020(4200020, "客户描述表单数据缺少固定字段", ""),
EC4200021(4200021, "客户联系人描述表单数据格式校验错误", ""),
EC4200022(4200022, "客户联系人描述表单数据缺少固定字段", ""),
EC800001(800001, "仅限ISV调用", "只有ISV微应用才能调用"),
EC41042(41042, "加密失败", ""),
EC41043(41043, "解密失败", ""),
EC40100(40100, "分机号已经存在", ""),
EC40101(40101, "邮箱已经存在", ""),
EC33013(33013, "企业自建微应用的个数过多,通过接口创建微应用受限", "此限制只针对企业自建微应用,对ISV应用没有限制"),
EC90017(90017, "此IP使用CorpId及CorpSecret调用接口的CorpId个数超过限制", "从该ip发起超过XX个corpid的请求被限制"),
EC40102(40102, "过期的临时授权码", "注意临时授权只能使用一次后就不能在使用。 需要重新执行授权操作有开放平台推送新的临时授权码"),
EC52020(52020, "未找到服务窗授权", ""),
EC52021(52021, "未找到微应用授权", ""),
EC52022(52022, "无效的jsapi类型", ""),
EC52023(52023, "无效的服务窗agentid", "检查服务窗微应用是否停用或者删除"),
EC52024(52024, "无效的jsapi tag", ""),
EC52025(52025, "无效的安全微应用", ""),
EC52026(52026, "无效的安全微应用URL", ""),
EC71014(71014, "获取套件下的服务窗应用失败", ""),
EC72003(72003, "钉盘空间添加文件失败", ""),
EC60128(60128, "无效的主管id", ""),
EC200001(200001, "表单不能为空", ""),
EC200004(200004, "APP_ID 不允许为空", "app_id为创建套件成功后,创建的ISV微应用的微应用ID。 可以登录开发者后台查看"),
EC200005(200005, "表单名称不允许为空", ""),
EC200006(200006, "表单内容不允许为空", ""),
EC200007(200007, "表单值不允许为空", ""),
EC200008(200008, "表单uuid不存在", ""),
EC400001(400001, "系统错误", ""),
EC400002(400002, "参数错误", "检查参数是否符合规格。具体请参考当前接口的文档的参数说明和参数示例"),
EC400003(400003, "时间戳无效", "检查随机时间戳是否符合规格。具体请参考当前接口的文档的参数说明和参数示例"),
EC400004(400004, "随机数无效", "检查随机随机数是否符合规格。具体请参考当前接口的文档的参数说明和参数示例");
private int code;
private String desc;
private String solution;
AuthDingTalkErrorCode(int code, String desc, String solution) {
this.code = code;
this.desc = desc;
this.solution = solution;
}
public static AuthDingTalkErrorCode getErrorCode(int errorCode) {
AuthDingTalkErrorCode[] errorCodes = AuthDingTalkErrorCode.values();
for (AuthDingTalkErrorCode code : errorCodes) {
if (code.getCode() == errorCode) {
return code;
}
}
return EC1_MINUS;
}
public int getCode() {
return code;
}
public String getDesc() {
return desc;
}
public String getSolution() {
return solution;
}
}
@@ -1,8 +1,8 @@
package me.zhyd.oauth.model;
import lombok.Builder;
import lombok.Data;
import me.zhyd.oauth.request.ResponseStatus;
import lombok.Getter;
import lombok.Setter;
/**
* JustAuth统一授权响应类
@@ -11,8 +11,8 @@ import me.zhyd.oauth.request.ResponseStatus;
* @version 1.0
* @since 1.8
*/
@Getter
@Builder
@Data
public class AuthResponse<T> {
/**
* 授权响应状态码
@@ -35,6 +35,6 @@ public class AuthResponse<T> {
* @return true or false
*/
public boolean ok() {
return this.code == ResponseStatus.SUCCESS.getCode();
return this.code == AuthResponseStatus.SUCCESS.getCode();
}
}
@@ -1,11 +1,16 @@
package me.zhyd.oauth.request;
package me.zhyd.oauth.model;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0
* @since 1.8
*/
public enum ResponseStatus {
@Getter
@AllArgsConstructor
public enum AuthResponseStatus {
SUCCESS(2000, "Success"),
FAILURE(5000, "Failure"),
NOT_IMPLEMENTED(5001, "Not Implemented"),
@@ -14,22 +19,11 @@ public enum ResponseStatus {
NO_AUTH_SOURCE(5004, "AuthSource 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"),
;
private int code;
private String msg;
ResponseStatus(int code, String msg) {
this.code = code;
this.msg = msg;
}
public int getCode() {
return code;
}
public String getMsg() {
return msg;
}
}
@@ -1,31 +0,0 @@
package me.zhyd.oauth.model;
/**
* 授权来源(平台)
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0
* @since 1.8
*/
public enum AuthSource {
GITHUB,
GITEE,
WEIBO,
DINGTALK,
BAIDU,
CSDN,
CODING,
OSCHINA,
TENCENT_CLOUD,
ALIPAY,
TAOBAO,
QQ,
WECHAT,
GOOGLE,
FACEBOOK,
DOUYIN,
LINKEDIN,
MICROSOFT,
MI,
TOUTIAO
}
@@ -2,6 +2,8 @@ package me.zhyd.oauth.model;
import lombok.Builder;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
/**
* 授权所需的token
@@ -10,7 +12,8 @@ import lombok.Data;
* @version 1.0
* @since 1.8
*/
@Data
@Getter
@Setter
@Builder
public class AuthToken {
private String accessToken;
@@ -1,7 +1,10 @@
package me.zhyd.oauth.model;
import lombok.Builder;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import me.zhyd.oauth.config.AuthSource;
import me.zhyd.oauth.enums.AuthUserGender;
/**
* 授权成功后的用户信息,根据授权平台的不同,获取的数据完整性也不同
@@ -10,9 +13,14 @@ import lombok.Data;
* @version 1.0
* @since 1.8
*/
@Getter
@Setter
@Builder
@Data
public class AuthUser {
/**
* 用户第三方系统的唯一id。在调用方集成改组件时,可以用uuid + source唯一确定一个用户
*/
private String uuid;
/**
* 用户名
*/
@@ -57,8 +65,4 @@ public class AuthUser {
* 用户授权的token信息
*/
private AuthToken token;
/**
* 用户第三方系统的唯一id。在调用方集成改组件时,可以用uuid + source唯一确定一个用户
*/
private String uuid;
}
@@ -1,44 +0,0 @@
package me.zhyd.oauth.model;
import java.util.Arrays;
/**
* 用户性别
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0
* @since 1.8
*/
public enum AuthUserGender {
MALE(1, ""), FEMALE(0, ""), UNKNOW(-1, "未知");
private int code;
private String desc;
AuthUserGender(int code, String desc) {
this.code = code;
this.desc = desc;
}
public static AuthUserGender getRealGender(String code) {
if (code == null) {
return UNKNOW;
}
String[] males = {"m", "", "1", "male", "F"};
if (Arrays.asList(males).contains(code)) {
return MALE;
}
String[] females = {"f", "", "0", "female"};
if (Arrays.asList(females).contains(code)) {
return FEMALE;
}
return UNKNOW;
}
public int getCode() {
return code;
}
public String getDesc() {
return desc;
}
}
@@ -8,13 +8,14 @@ import com.alipay.api.request.AlipayUserInfoShareRequest;
import com.alipay.api.response.AlipaySystemOauthTokenResponse;
import com.alipay.api.response.AlipayUserInfoShareResponse;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.consts.ApiUrl;
import me.zhyd.oauth.config.AuthSource;
import me.zhyd.oauth.enums.AuthUserGender;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.AuthSource;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.model.AuthUserGender;
import me.zhyd.oauth.utils.StringUtils;
import me.zhyd.oauth.utils.UrlBuilder;
/**
* 支付宝登录
@@ -23,35 +24,36 @@ import me.zhyd.oauth.utils.StringUtils;
* @version 1.0
* @since 1.8
*/
public class AuthAlipayRequest extends BaseAuthRequest {
public class AuthAlipayRequest extends AuthDefaultRequest {
private AlipayClient alipayClient;
public AuthAlipayRequest(AuthConfig config) {
super(config, AuthSource.ALIPAY);
this.alipayClient = new DefaultAlipayClient(ApiUrl.ALIPAY.accessToken(), config.getClientId(), config.getClientSecret(), "json", "UTF-8", config.getAlipayPublicKey(), "RSA2");
this.alipayClient = new DefaultAlipayClient(AuthSource.ALIPAY.accessToken(), config.getClientId(), config.getClientSecret(), "json", "UTF-8", config
.getAlipayPublicKey(), "RSA2");
}
@Override
protected AuthToken getAccessToken(String code) {
protected AuthToken getAccessToken(AuthCallback authCallback) {
AlipaySystemOauthTokenRequest request = new AlipaySystemOauthTokenRequest();
request.setGrantType("authorization_code");
request.setCode(code);
request.setCode(authCallback.getAuth_code());
AlipaySystemOauthTokenResponse response = null;
try {
response = this.alipayClient.execute(request);
} catch (Exception e) {
throw new AuthException("Unable to get token from alipay using code [" + code + "]", e);
throw new AuthException(e);
}
if (!response.isSuccess()) {
throw new AuthException(response.getSubMsg());
}
return AuthToken.builder()
.accessToken(response.getAccessToken())
.uid(response.getUserId())
.expireIn(Integer.parseInt(response.getExpiresIn()))
.refreshToken(response.getRefreshToken())
.build();
.accessToken(response.getAccessToken())
.uid(response.getUserId())
.expireIn(Integer.parseInt(response.getExpiresIn()))
.refreshToken(response.getRefreshToken())
.build();
}
@Override
@@ -68,19 +70,33 @@ public class AuthAlipayRequest extends BaseAuthRequest {
throw new AuthException(response.getSubMsg());
}
String province = response.getProvince(),
city = response.getCity();
String province = response.getProvince(), city = response.getCity();
String location = String.format("%s %s", StringUtils.isEmpty(province) ? "" : province, StringUtils.isEmpty(city) ? "" : city);
return AuthUser.builder()
.uuid(response.getUserId())
.username(StringUtils.isEmpty(response.getUserName()) ? response.getNickName() : response.getUserName())
.nickname(response.getNickName())
.avatar(response.getAvatar())
.location(location)
.gender(AuthUserGender.getRealGender(response.getGender()))
.token(authToken)
.source(AuthSource.ALIPAY)
.build();
.uuid(response.getUserId())
.username(StringUtils.isEmpty(response.getUserName()) ? response.getNickName() : response.getUserName())
.nickname(response.getNickName())
.avatar(response.getAvatar())
.location(location)
.gender(AuthUserGender.getRealGender(response.getGender()))
.token(authToken)
.source(source)
.build();
}
/**
* 返回认证url,可自行跳转页面
*
* @return 返回授权地址
*/
@Override
public String authorize() {
return UrlBuilder.fromBaseUrl(source.authorize())
.queryParam("app_id", config.getClientId())
.queryParam("scope", "auth_user")
.queryParam("redirect_uri", config.getRedirectUri())
.queryParam("state", getRealState(config.getState()))
.build();
}
}
@@ -3,10 +3,12 @@ package me.zhyd.oauth.request;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
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.*;
import me.zhyd.oauth.utils.StringUtils;
import me.zhyd.oauth.utils.UrlBuilder;
/**
@@ -16,60 +18,102 @@ import me.zhyd.oauth.utils.UrlBuilder;
* @version 1.0
* @since 1.8
*/
public class AuthBaiduRequest extends BaseAuthRequest {
public class AuthBaiduRequest extends AuthDefaultRequest {
public AuthBaiduRequest(AuthConfig config) {
super(config, AuthSource.BAIDU);
}
@Override
protected AuthToken getAccessToken(String code) {
String accessTokenUrl = UrlBuilder.getBaiduAccessTokenUrl(config.getClientId(), config.getClientSecret(), code, config.getRedirectUri());
HttpResponse response = HttpRequest.post(accessTokenUrl).execute();
JSONObject accessTokenObject = JSONObject.parseObject(response.body());
AuthBaiduErrorCode errorCode = AuthBaiduErrorCode.getErrorCode(accessTokenObject.getString("error"));
if (!AuthBaiduErrorCode.OK.equals(errorCode)) {
throw new AuthException(errorCode.getDesc());
}
return AuthToken.builder()
.accessToken(accessTokenObject.getString("access_token"))
.build();
protected AuthToken getAccessToken(AuthCallback authCallback) {
HttpResponse response = doPostAuthorizationCode(authCallback.getCode());
return getAuthToken(response);
}
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
String accessToken = authToken.getAccessToken();
HttpResponse response = HttpRequest.get(UrlBuilder.getBaiduUserInfoUrl(accessToken)).execute();
HttpResponse response = doGetUserInfo(authToken);
String userInfo = response.body();
JSONObject object = JSONObject.parseObject(userInfo);
AuthBaiduErrorCode errorCode = AuthBaiduErrorCode.getErrorCode(object.getString("error"));
if (!AuthBaiduErrorCode.OK.equals(errorCode)) {
throw new AuthException(errorCode.getDesc());
}
this.checkResponse(object);
return AuthUser.builder()
.uuid(object.getString("userid"))
.username(object.getString("username"))
.nickname(object.getString("username"))
.gender(AuthUserGender.getRealGender(object.getString("sex")))
.token(authToken)
.source(AuthSource.BAIDU)
.build();
.uuid(object.getString("userid"))
.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)
.build();
}
private String getAvatar(JSONObject object) {
String protrait = object.getString("portrait");
return StringUtils.isEmpty(protrait) ? null : String.format("http://himg.bdimg.com/sys/portrait/item/%s.jpg", protrait);
}
@Override
public AuthResponse revoke(AuthToken authToken) {
String accessToken = authToken.getAccessToken();
HttpResponse response = HttpRequest.get(UrlBuilder.getBaiduRevokeUrl(accessToken)).execute();
String userInfo = response.body();
JSONObject object = JSONObject.parseObject(userInfo);
if(object.containsKey("error_code")) {
return AuthResponse.builder()
.code(ResponseStatus.FAILURE.getCode())
.msg(object.getString("error_msg"))
.build();
}
ResponseStatus status = object.getIntValue("result") == 1 ? ResponseStatus.SUCCESS : ResponseStatus.FAILURE;
HttpResponse response = doGetRevoke(authToken);
JSONObject object = JSONObject.parseObject(response.body());
this.checkResponse(object);
// 返回1表示取消授权成功,否则失败
AuthResponseStatus status = object.getIntValue("result") == 1 ? AuthResponseStatus.SUCCESS : AuthResponseStatus.FAILURE;
return AuthResponse.builder().code(status.getCode()).msg(status.getMsg()).build();
}
@Override
public AuthResponse refresh(AuthToken authToken) {
String refreshUrl = UrlBuilder.fromBaseUrl(this.source.refresh())
.queryParam("grant_type", "refresh_token")
.queryParam("refresh_token", authToken.getRefreshToken())
.queryParam("client_id", this.config.getClientId())
.queryParam("client_secret", this.config.getClientSecret())
.build();
HttpResponse response = HttpRequest.get(refreshUrl).execute();
return AuthResponse.builder()
.code(AuthResponseStatus.SUCCESS.getCode())
.data(this.getAuthToken(response))
.build();
}
/**
* 返回认证url,可自行跳转页面
*
* @return 返回授权地址
*/
@Override
public String authorize() {
return UrlBuilder.fromBaseUrl(source.authorize())
.queryParam("response_type", "code")
.queryParam("client_id", config.getClientId())
.queryParam("redirect_uri", config.getRedirectUri())
.queryParam("display", "popup")
.queryParam("state", getRealState(config.getState()))
.build();
}
/**
* 检查响应内容是否正确
*
* @param object 请求响应内容
*/
private void checkResponse(JSONObject object) {
if (object.containsKey("error") || object.containsKey("error_code")) {
String msg = object.containsKey("error_description") ? object.getString("error_description") : object.getString("error_msg");
throw new AuthException(msg);
}
}
private AuthToken getAuthToken(HttpResponse response) {
JSONObject accessTokenObject = JSONObject.parseObject(response.body());
this.checkResponse(accessTokenObject);
return AuthToken.builder()
.accessToken(accessTokenObject.getString("access_token"))
.refreshToken(accessTokenObject.getString("refresh_token"))
.scope(accessTokenObject.getString("scope"))
.expireIn(accessTokenObject.getIntValue("expires_in"))
.build();
}
}
@@ -1,14 +1,14 @@
package me.zhyd.oauth.request;
import cn.hutool.http.HttpRequest;
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.exception.AuthException;
import me.zhyd.oauth.model.AuthSource;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.model.AuthUserGender;
import me.zhyd.oauth.enums.AuthUserGender;
import me.zhyd.oauth.utils.UrlBuilder;
/**
@@ -18,48 +18,71 @@ import me.zhyd.oauth.utils.UrlBuilder;
* @version 1.0
* @since 1.8
*/
public class AuthCodingRequest extends BaseAuthRequest {
public class AuthCodingRequest extends AuthDefaultRequest {
public AuthCodingRequest(AuthConfig config) {
super(config, AuthSource.CODING);
}
@Override
protected AuthToken getAccessToken(String code) {
String accessTokenUrl = UrlBuilder.getCodingAccessTokenUrl(config.getClientId(), config.getClientSecret(), code);
HttpResponse response = HttpRequest.get(accessTokenUrl).execute();
protected AuthToken getAccessToken(AuthCallback authCallback) {
HttpResponse response = doGetAuthorizationCode(authCallback.getCode());
JSONObject accessTokenObject = JSONObject.parseObject(response.body());
if (accessTokenObject.getIntValue("code") != 0) {
throw new AuthException("Unable to get token from coding using code [" + code + "]");
}
this.checkResponse(accessTokenObject);
return AuthToken.builder()
.accessToken(accessTokenObject.getString("access_token"))
.build();
.accessToken(accessTokenObject.getString("access_token"))
.expireIn(accessTokenObject.getIntValue("expires_in"))
.refreshToken(accessTokenObject.getString("refresh_token"))
.build();
}
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
String accessToken = authToken.getAccessToken();
HttpResponse response = HttpRequest.get(UrlBuilder.getCodingUserInfoUrl(accessToken)).execute();
HttpResponse response = doGetUserInfo(authToken);
JSONObject object = JSONObject.parseObject(response.body());
if (object.getIntValue("code") != 0) {
throw new AuthException(object.getString("msg"));
}
this.checkResponse(object);
object = object.getJSONObject("data");
return AuthUser.builder()
.uuid(object.getString("id"))
.username(object.getString("name"))
.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"))
.gender(AuthUserGender.getRealGender(object.getString("sex")))
.email(object.getString("email"))
.remark(object.getString("slogan"))
.token(authToken)
.source(AuthSource.CODING)
.build();
.uuid(object.getString("id"))
.username(object.getString("name"))
.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"))
.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"));
}
}
/**
* 返回认证url,可自行跳转页面
*
* @return 返回授权地址
*/
@Override
public String authorize() {
return UrlBuilder.fromBaseUrl(source.authorize())
.queryParam("response_type", "code")
.queryParam("client_id", config.getClientId())
.queryParam("redirect_uri", config.getRedirectUri())
.queryParam("scope", "user")
.queryParam("state", getRealState(config.getState()))
.build();
}
}
@@ -1,15 +1,14 @@
package me.zhyd.oauth.request;
import cn.hutool.http.HttpRequest;
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.AuthSource;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.model.AuthUserGender;
import me.zhyd.oauth.utils.UrlBuilder;
/**
* CSDN登录
@@ -18,41 +17,45 @@ import me.zhyd.oauth.utils.UrlBuilder;
* @version 1.0
* @since 1.8
*/
public class AuthCsdnRequest extends BaseAuthRequest {
@Deprecated
public class AuthCsdnRequest extends AuthDefaultRequest {
public AuthCsdnRequest(AuthConfig config) {
super(config, AuthSource.CSDN);
}
@Override
protected AuthToken getAccessToken(String code) {
String accessTokenUrl = UrlBuilder.getCsdnAccessTokenUrl(config.getClientId(), config.getClientSecret(), code, config.getRedirectUri());
HttpResponse response = HttpRequest.post(accessTokenUrl).execute();
protected AuthToken getAccessToken(AuthCallback authCallback) {
HttpResponse response = doPostAuthorizationCode(authCallback.getCode());
JSONObject accessTokenObject = JSONObject.parseObject(response.body());
if (accessTokenObject.containsKey("error_code")) {
throw new AuthException("Unable to get token from csdn using code [" + code + "]");
}
return AuthToken.builder()
.accessToken(accessTokenObject.getString("access_token"))
.build();
this.checkResponse(accessTokenObject);
return AuthToken.builder().accessToken(accessTokenObject.getString("access_token")).build();
}
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
String accessToken = authToken.getAccessToken();
HttpResponse response = HttpRequest.get(UrlBuilder.getCsdnUserInfoUrl(accessToken)).execute();
HttpResponse response = doGetUserInfo(authToken);
JSONObject object = JSONObject.parseObject(response.body());
this.checkResponse(object);
return AuthUser.builder()
.uuid(object.getString("username"))
.username(object.getString("username"))
.remark(object.getString("description"))
.blog(object.getString("website"))
.gender(AuthUserGender.UNKNOWN)
.token(authToken)
.source(source)
.build();
}
/**
* 检查响应内容是否正确
*
* @param object 请求响应内容
*/
private void checkResponse(JSONObject object) {
if (object.containsKey("error_code")) {
throw new AuthException(object.getString("error"));
}
return AuthUser.builder()
.uuid(object.getString("username"))
.username(object.getString("username"))
.remark(object.getString("description"))
.blog(object.getString("website"))
.gender(AuthUserGender.UNKNOW)
.token(authToken)
.source(AuthSource.CSDN)
.build();
}
}
@@ -0,0 +1,201 @@
package me.zhyd.oauth.request;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.config.AuthSource;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.*;
import me.zhyd.oauth.utils.AuthChecker;
import me.zhyd.oauth.utils.StringUtils;
import me.zhyd.oauth.utils.UrlBuilder;
/**
* 默认的request处理类
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @author yangkai.shen (https://xkcoding.com)
* @version 1.0
* @since 1.8
*/
@Slf4j
public abstract class AuthDefaultRequest implements AuthRequest {
protected AuthConfig config;
protected AuthSource source;
public AuthDefaultRequest(AuthConfig config, AuthSource source) {
this.config = config;
this.source = source;
if (!AuthChecker.isSupportedAuth(config, source)) {
throw new AuthException(AuthResponseStatus.PARAMETER_INCOMPLETE);
}
// 校验配置合法性
AuthChecker.checkConfig(config, source);
}
protected abstract AuthToken getAccessToken(AuthCallback authCallback);
protected abstract AuthUser getUserInfo(AuthToken authToken);
@Override
public AuthResponse login(AuthCallback authCallback) {
try {
AuthChecker.checkCode(source == AuthSource.ALIPAY ? authCallback.getAuth_code() : authCallback.getCode());
AuthChecker.checkState(authCallback.getState(), config.getState());
AuthToken authToken = this.getAccessToken(authCallback);
AuthUser user = this.getUserInfo(authToken);
return AuthResponse.builder().code(AuthResponseStatus.SUCCESS.getCode()).data(user).build();
} catch (Exception e) {
log.error("Failed to login with oauth authorization.", e);
return this.responseError(e);
}
}
private AuthResponse responseError(Exception e) {
int errorCode = AuthResponseStatus.FAILURE.getCode();
if (e instanceof AuthException) {
errorCode = ((AuthException) e).getErrorCode();
}
return AuthResponse.builder().code(errorCode).msg(e.getMessage()).build();
}
/**
* 返回认证url,可自行跳转页面
*
* @return 返回授权地址
*/
@Override
public String authorize() {
return UrlBuilder.fromBaseUrl(source.authorize())
.queryParam("response_type", "code")
.queryParam("client_id", config.getClientId())
.queryParam("redirect_uri", config.getRedirectUri())
.queryParam("state", getRealState(config.getState()))
.build();
}
/**
* 返回获取accessToken的url
*
* @param code 授权码
* @return 返回获取accessToken的url
*/
protected String accessTokenUrl(String code) {
return UrlBuilder.fromBaseUrl(source.accessToken())
.queryParam("code", code)
.queryParam("client_id", config.getClientId())
.queryParam("client_secret", config.getClientSecret())
.queryParam("grant_type", "authorization_code")
.queryParam("redirect_uri", config.getRedirectUri())
.build();
}
/**
* 返回获取accessToken的url
*
* @param refreshToken refreshToken
* @return 返回获取accessToken的url
*/
protected String refreshTokenUrl(String refreshToken) {
return UrlBuilder.fromBaseUrl(source.refresh())
.queryParam("client_id", config.getClientId())
.queryParam("client_secret", config.getClientSecret())
.queryParam("refresh_token", refreshToken)
.queryParam("grant_type", "refresh_token")
.queryParam("redirect_uri", config.getRedirectUri())
.build();
}
/**
* 返回获取userInfo的url
*
* @param authToken token
* @return 返回获取userInfo的url
*/
protected String userInfoUrl(AuthToken authToken) {
return UrlBuilder.fromBaseUrl(source.userInfo()).queryParam("access_token", authToken.getAccessToken()).build();
}
/**
* 返回获取revoke authorization的url
*
* @param authToken token
* @return 返回获取revoke authorization的url
*/
protected String revokeUrl(AuthToken authToken) {
return UrlBuilder.fromBaseUrl(source.revoke()).queryParam("access_token", authToken.getAccessToken()).build();
}
/**
* 获取state,如果为空, 则默认去当前日期的时间戳
*
* @param state 原始的state
* @return 返回不为null的state
*/
protected String getRealState(String state) {
return StringUtils.isEmpty(state) ? String.valueOf(System.currentTimeMillis()) : state;
}
/**
* 通用的 authorizationCode 协议
*
* @param code code码
* @return HttpResponse
*/
protected HttpResponse doPostAuthorizationCode(String code) {
return HttpRequest.post(accessTokenUrl(code)).execute();
}
/**
* 通用的 authorizationCode 协议
*
* @param code code码
* @return HttpResponse
*/
protected HttpResponse doGetAuthorizationCode(String code) {
return HttpRequest.get(accessTokenUrl(code)).execute();
}
/**
* 通用的 用户信息
*
* @param authToken token封装
* @return HttpResponse
*/
protected HttpResponse doPostUserInfo(AuthToken authToken) {
return HttpRequest.post(userInfoUrl(authToken)).execute();
}
/**
* 通用的 用户信息
*
* @param authToken token封装
* @return HttpResponse
*/
protected HttpResponse doGetUserInfo(AuthToken authToken) {
return HttpRequest.get(userInfoUrl(authToken)).execute();
}
/**
* 通用的post形式的取消授权方法
*
* @param authToken token封装
* @return HttpResponse
*/
protected HttpResponse doPostRevoke(AuthToken authToken) {
return HttpRequest.post(revokeUrl(authToken)).execute();
}
/**
* 通用的post形式的取消授权方法
*
* @param authToken token封装
* @return HttpResponse
*/
protected HttpResponse doGetRevoke(AuthToken authToken) {
return HttpRequest.get(revokeUrl(authToken)).execute();
}
}
@@ -5,8 +5,12 @@ import cn.hutool.http.HttpResponse;
import com.alibaba.fastjson.JSON;
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.*;
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.UrlBuilder;
@@ -17,48 +21,74 @@ import me.zhyd.oauth.utils.UrlBuilder;
* @version 1.0
* @since 1.8
*/
public class AuthDingTalkRequest extends BaseAuthRequest {
public class AuthDingTalkRequest extends AuthDefaultRequest {
public AuthDingTalkRequest(AuthConfig config) {
super(config, AuthSource.DINGTALK);
}
@Override
protected AuthToken getAccessToken(String code) {
return AuthToken.builder()
.accessCode(code)
.build();
protected AuthToken getAccessToken(AuthCallback authCallback) {
return AuthToken.builder().accessCode(authCallback.getCode()).build();
}
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
String code = authToken.getAccessCode();
// 根据timestamp, appSecret计算签名值
String timestamp = System.currentTimeMillis() + "";
String urlEncodeSignature = GlobalAuthUtil.generateDingTalkSignature(config.getClientSecret(), timestamp);
JSONObject param = new JSONObject();
param.put("tmp_auth_code", code);
HttpResponse response = HttpRequest.post(UrlBuilder.getDingTalkUserInfoUrl(urlEncodeSignature, timestamp, config.getClientId()))
.body(param.toJSONString())
.execute();
String userInfo = response.body();
JSONObject object = JSON.parseObject(userInfo);
AuthDingTalkErrorCode errorCode = AuthDingTalkErrorCode.getErrorCode(object.getIntValue("errcode"));
if (!AuthDingTalkErrorCode.EC0.equals(errorCode)) {
throw new AuthException(errorCode.getDesc());
HttpResponse response = HttpRequest.post(userInfoUrl(authToken)).body(param.toJSONString()).execute();
JSONObject object = JSON.parseObject(response.body());
if (object.getIntValue("errcode") != 0) {
throw new AuthException(object.getString("errmsg"));
}
object = object.getJSONObject("user_info");
AuthToken token = AuthToken.builder()
.openId(object.getString("openid"))
.unionId(object.getString("unionid"))
.build();
.openId(object.getString("openid"))
.unionId(object.getString("unionid"))
.build();
return AuthUser.builder()
.uuid(object.getString("unionid"))
.nickname(object.getString("nick"))
.username(object.getString("nick"))
.gender(AuthUserGender.UNKNOW)
.source(AuthSource.DINGTALK)
.token(token)
.build();
.uuid(object.getString("unionid"))
.nickname(object.getString("nick"))
.username(object.getString("nick"))
.gender(AuthUserGender.UNKNOWN)
.source(source)
.token(token)
.build();
}
/**
* 返回认证url,可自行跳转页面
*
* @return 返回授权地址
*/
@Override
public String authorize() {
return UrlBuilder.fromBaseUrl(source.authorize())
.queryParam("response_type", "code")
.queryParam("appid", config.getClientId())
.queryParam("scope", "snsapi_login")
.queryParam("redirect_uri", config.getRedirectUri())
.queryParam("state", getRealState(config.getState()))
.build();
}
/**
* 返回获取userInfo的url
*
* @param authToken 用户授权后的token
* @return 返回获取userInfo的url
*/
@Override
protected String userInfoUrl(AuthToken authToken) {
// 根据timestamp, appSecret计算签名值
String timestamp = System.currentTimeMillis() + "";
String urlEncodeSignature = GlobalAuthUtil.generateDingTalkSignature(config.getClientSecret(), timestamp);
return UrlBuilder.fromBaseUrl(source.userInfo())
.queryParam("signature", urlEncodeSignature)
.queryParam("timestamp", timestamp)
.queryParam("accessKey", config.getClientId())
.build();
}
}
@@ -4,6 +4,8 @@ import cn.hutool.http.HttpRequest;
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.*;
import me.zhyd.oauth.utils.UrlBuilder;
@@ -16,61 +18,54 @@ import me.zhyd.oauth.utils.UrlBuilder;
* @version 1.0
* @since 1.8
*/
public class AuthDouyinRequest extends BaseAuthRequest {
public class AuthDouyinRequest extends AuthDefaultRequest {
public AuthDouyinRequest(AuthConfig config) {
super(config, AuthSource.DOUYIN);
}
@Override
protected AuthToken getAccessToken(String code) {
String accessTokenUrl = UrlBuilder.getDouyinAccessTokenUrl(config.getClientId(), config.getClientSecret(), code);
return this.getToken(accessTokenUrl);
protected AuthToken getAccessToken(AuthCallback authCallback) {
return this.getToken(accessTokenUrl(authCallback.getCode()));
}
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
String accessToken = authToken.getAccessToken();
String openId = authToken.getOpenId();
HttpResponse response = HttpRequest.get(UrlBuilder.getDouyinUserInfoUrl(accessToken, openId)).execute();
JSONObject object = JSONObject.parseObject(response.body());
JSONObject userInfoObject = this.checkResponse(object);
HttpResponse response = doGetUserInfo(authToken);
JSONObject userInfoObject = JSONObject.parseObject(response.body());
this.checkResponse(userInfoObject);
return AuthUser.builder()
.uuid(userInfoObject.getString("open_id"))
.username(userInfoObject.getString("nickname"))
.nickname(userInfoObject.getString("nickname"))
.avatar(userInfoObject.getString("avatar"))
.gender(AuthUserGender.UNKNOW)
.token(authToken)
.source(AuthSource.DOUYIN)
.build();
.uuid(userInfoObject.getString("union_id"))
.username(userInfoObject.getString("nickname"))
.nickname(userInfoObject.getString("nickname"))
.avatar(userInfoObject.getString("avatar"))
.remark(userInfoObject.getString("description"))
.gender(AuthUserGender.UNKNOWN)
.token(authToken)
.source(source)
.build();
}
@Override
public AuthResponse refresh(AuthToken oldToken) {
String refreshTokenUrl = UrlBuilder.getDouyinRefreshUrl(config.getClientId(), oldToken.getRefreshToken());
return AuthResponse.builder()
.code(ResponseStatus.SUCCESS.getCode())
.data(this.getToken(refreshTokenUrl))
.build();
.code(AuthResponseStatus.SUCCESS.getCode())
.data(getToken(refreshTokenUrl(oldToken.getRefreshToken())))
.build();
}
/**
* 检查响应内容是否正确
*
* @param object 请求响应内容
* @return 实际请求数据的json对象
*/
private JSONObject checkResponse(JSONObject object) {
private void checkResponse(JSONObject object) {
String message = object.getString("message");
JSONObject data = object.getJSONObject("data");
int errorCode = data.getIntValue("error_code");
if ("error".equals(message) || errorCode != 0) {
throw new AuthException(errorCode, data.getString("description"));
}
return data;
}
/**
@@ -83,14 +78,74 @@ public class AuthDouyinRequest extends BaseAuthRequest {
HttpResponse response = HttpRequest.post(accessTokenUrl).execute();
String accessTokenStr = response.body();
JSONObject object = JSONObject.parseObject(accessTokenStr);
JSONObject accessTokenObject = this.checkResponse(object);
this.checkResponse(object);
return AuthToken.builder()
.accessToken(accessTokenObject.getString("access_token"))
.openId(accessTokenObject.getString("open_id"))
.expireIn(accessTokenObject.getIntValue("expires_in"))
.refreshToken(accessTokenObject.getString("refresh_token"))
.scope(accessTokenObject.getString("scope"))
.build();
.accessToken(object.getString("access_token"))
.openId(object.getString("open_id"))
.expireIn(object.getIntValue("expires_in"))
.refreshToken(object.getString("refresh_token"))
.scope(object.getString("scope"))
.build();
}
/**
* 返回认证url,可自行跳转页面
*
* @return 返回授权地址
*/
@Override
public String authorize() {
return UrlBuilder.fromBaseUrl(source.authorize())
.queryParam("response_type", "code")
.queryParam("client_key", config.getClientId())
.queryParam("redirect_uri", config.getRedirectUri())
.queryParam("state", getRealState(config.getState()))
.queryParam("scope", "user_info")
.build();
}
/**
* 返回获取accessToken的url
*
* @param code oauth的授权码
* @return 返回获取accessToken的url
*/
@Override
protected String accessTokenUrl(String code) {
return UrlBuilder.fromBaseUrl(source.accessToken())
.queryParam("code", code)
.queryParam("client_key", config.getClientId())
.queryParam("client_secret", config.getClientSecret())
.queryParam("grant_type", "authorization_code")
.build();
}
/**
* 返回获取userInfo的url
*
* @param authToken oauth返回的token
* @return 返回获取userInfo的url
*/
@Override
protected String userInfoUrl(AuthToken authToken) {
return UrlBuilder.fromBaseUrl(source.userInfo())
.queryParam("access_token", authToken.getAccessToken())
.queryParam("open_id", authToken.getOpenId())
.build();
}
/**
* 返回获取accessToken的url
*
* @param refreshToken oauth返回的refreshtoken
* @return 返回获取accessToken的url
*/
@Override
protected String refreshTokenUrl(String refreshToken) {
return UrlBuilder.fromBaseUrl(source.refresh())
.queryParam("client_key", config.getClientId())
.queryParam("refresh_token", refreshToken)
.queryParam("grant_type", "refresh_token")
.build();
}
}
@@ -1,14 +1,14 @@
package me.zhyd.oauth.request;
import cn.hutool.http.HttpRequest;
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.AuthSource;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.model.AuthUserGender;
import me.zhyd.oauth.utils.UrlBuilder;
/**
@@ -18,38 +18,44 @@ import me.zhyd.oauth.utils.UrlBuilder;
* @version 1.0
* @since 1.8
*/
public class AuthFacebookRequest extends BaseAuthRequest {
public class AuthFacebookRequest extends AuthDefaultRequest {
public AuthFacebookRequest(AuthConfig config) {
super(config, AuthSource.FACEBOOK);
}
@Override
protected AuthToken getAccessToken(String code) {
String accessTokenUrl = UrlBuilder.getFacebookAccessTokenUrl(config.getClientId(), config.getClientSecret(), code, config.getRedirectUri());
HttpResponse response = HttpRequest.post(accessTokenUrl).execute();
JSONObject object = JSONObject.parseObject(response.body());
if (object.containsKey("error")) {
throw new AuthException(object.getJSONObject("error").getString("message"));
}
protected AuthToken getAccessToken(AuthCallback authCallback) {
HttpResponse response = doPostAuthorizationCode(authCallback.getCode());
JSONObject accessTokenObject = JSONObject.parseObject(response.body());
this.checkResponse(accessTokenObject);
return AuthToken.builder()
.accessToken(object.getString("access_token"))
.expireIn(object.getIntValue("expires_in"))
.tokenType(object.getString("token_type"))
.build();
.accessToken(accessTokenObject.getString("access_token"))
.expireIn(accessTokenObject.getIntValue("expires_in"))
.tokenType(accessTokenObject.getString("token_type"))
.build();
}
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
String accessToken = authToken.getAccessToken();
HttpResponse response = HttpRequest.get(UrlBuilder.getFacebookUserInfoUrl(accessToken)).execute();
HttpResponse response = doGetUserInfo(authToken);
String userInfo = response.body();
JSONObject object = JSONObject.parseObject(userInfo);
if (object.containsKey("error")) {
throw new AuthException(object.getJSONObject("error").getString("message"));
}
this.checkResponse(object);
return AuthUser.builder()
.uuid(object.getString("id"))
.username(object.getString("name"))
.nickname(object.getString("name"))
.avatar(getUserPicture(object))
.location(object.getString("locale"))
.email(object.getString("email"))
.gender(AuthUserGender.getRealGender(object.getString("gender")))
.token(authToken)
.source(source)
.build();
}
private String getUserPicture(JSONObject object) {
String picture = null;
if (object.containsKey("picture")) {
JSONObject pictureObj = object.getJSONObject("picture");
@@ -58,16 +64,31 @@ public class AuthFacebookRequest extends BaseAuthRequest {
picture = pictureObj.getString("url");
}
}
return AuthUser.builder()
.uuid(object.getString("id"))
.username(object.getString("name"))
.nickname(object.getString("name"))
.avatar(picture)
.location(object.getString("locale"))
.email(object.getString("email"))
.gender(AuthUserGender.getRealGender(object.getString("gender")))
.token(authToken)
.source(AuthSource.FACEBOOK)
.build();
return picture;
}
/**
* 返回获取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("fields", "id,name,birthday,gender,hometown,email,devices,picture.width(400)")
.build();
}
/**
* 检查响应内容是否正确
*
* @param object 请求响应内容
*/
private void checkResponse(JSONObject object) {
if (object.containsKey("error")) {
throw new AuthException(object.getJSONObject("error").getString("message"));
}
}
}
@@ -1,15 +1,14 @@
package me.zhyd.oauth.request;
import cn.hutool.http.HttpRequest;
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.AuthSource;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.model.AuthUserGender;
import me.zhyd.oauth.utils.UrlBuilder;
/**
* Gitee登录
@@ -18,44 +17,56 @@ import me.zhyd.oauth.utils.UrlBuilder;
* @version 1.0
* @since 1.8
*/
public class AuthGiteeRequest extends BaseAuthRequest {
public class AuthGiteeRequest extends AuthDefaultRequest {
public AuthGiteeRequest(AuthConfig config) {
super(config, AuthSource.GITEE);
}
@Override
protected AuthToken getAccessToken(String code) {
String accessTokenUrl = UrlBuilder.getGiteeAccessTokenUrl(config.getClientId(), config.getClientSecret(), code, config.getRedirectUri());
HttpResponse response = HttpRequest.post(accessTokenUrl).execute();
protected AuthToken getAccessToken(AuthCallback authCallback) {
HttpResponse response = doPostAuthorizationCode(authCallback.getCode());
JSONObject accessTokenObject = JSONObject.parseObject(response.body());
if (accessTokenObject.containsKey("error")) {
throw new AuthException("Unable to get token from gitee using code [" + code + "]");
}
this.checkResponse(accessTokenObject);
return AuthToken.builder()
.accessToken(accessTokenObject.getString("access_token"))
.build();
.accessToken(accessTokenObject.getString("access_token"))
.refreshToken(accessTokenObject.getString("refresh_token"))
.scope(accessTokenObject.getString("scope"))
.tokenType(accessTokenObject.getString("token_type"))
.expireIn(accessTokenObject.getIntValue("expires_in"))
.build();
}
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
String accessToken = authToken.getAccessToken();
HttpResponse response = HttpRequest.get(UrlBuilder.getGiteeUserInfoUrl(accessToken)).execute();
HttpResponse response = doGetUserInfo(authToken);
String userInfo = response.body();
JSONObject object = JSONObject.parseObject(userInfo);
this.checkResponse(object);
return AuthUser.builder()
.uuid(object.getString("id"))
.username(object.getString("login"))
.avatar(object.getString("avatar_url"))
.blog(object.getString("blog"))
.nickname(object.getString("name"))
.company(object.getString("company"))
.location(object.getString("address"))
.email(object.getString("email"))
.remark(object.getString("bio"))
.gender(AuthUserGender.UNKNOW)
.token(authToken)
.source(AuthSource.GITEE)
.build();
.uuid(object.getString("id"))
.username(object.getString("login"))
.avatar(object.getString("avatar_url"))
.blog(object.getString("blog"))
.nickname(object.getString("name"))
.company(object.getString("company"))
.location(object.getString("address"))
.email(object.getString("email"))
.remark(object.getString("bio"))
.gender(AuthUserGender.UNKNOWN)
.token(authToken)
.source(source)
.build();
}
/**
* 检查响应内容是否正确
*
* @param object 请求响应内容
*/
private void checkResponse(JSONObject object) {
if (object.containsKey("error")) {
throw new AuthException(object.getString("error_description"));
}
}
}
@@ -1,16 +1,15 @@
package me.zhyd.oauth.request;
import cn.hutool.http.HttpRequest;
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.AuthSource;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.model.AuthUserGender;
import me.zhyd.oauth.utils.GlobalAuthUtil;
import me.zhyd.oauth.utils.UrlBuilder;
import java.util.Map;
@@ -21,44 +20,55 @@ import java.util.Map;
* @version 1.0
* @since 1.8
*/
public class AuthGithubRequest extends BaseAuthRequest {
public class AuthGithubRequest extends AuthDefaultRequest {
public AuthGithubRequest(AuthConfig config) {
super(config, AuthSource.GITHUB);
}
@Override
protected AuthToken getAccessToken(String code) {
String accessTokenUrl = UrlBuilder.getGithubAccessTokenUrl(config.getClientId(), config.getClientSecret(), code, config.getRedirectUri());
HttpResponse response = HttpRequest.post(accessTokenUrl).execute();
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"));
}
return AuthToken.builder()
.accessToken(res.get("access_token"))
.build();
.accessToken(res.get("access_token"))
.scope(res.get("scope"))
.tokenType(res.get("token_type"))
.build();
}
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
String accessToken = authToken.getAccessToken();
HttpResponse response = HttpRequest.get(UrlBuilder.getGithubUserInfoUrl(accessToken)).execute();
String userInfo = response.body();
JSONObject object = JSONObject.parseObject(userInfo);
HttpResponse response = doGetUserInfo(authToken);
JSONObject object = JSONObject.parseObject(response.body());
if (object.containsKey("error")) {
throw new AuthException(object.getString("error_description"));
}
return AuthUser.builder()
.uuid(object.getString("id"))
.username(object.getString("login"))
.avatar(object.getString("avatar_url"))
.blog(object.getString("blog"))
.nickname(object.getString("name"))
.company(object.getString("company"))
.location(object.getString("location"))
.email(object.getString("email"))
.remark(object.getString("bio"))
.gender(AuthUserGender.UNKNOW)
.token(authToken)
.source(AuthSource.GITHUB)
.build();
.uuid(object.getString("id"))
.username(object.getString("login"))
.avatar(object.getString("avatar_url"))
.blog(object.getString("blog"))
.nickname(object.getString("name"))
.company(object.getString("company"))
.location(object.getString("location"))
.email(object.getString("email"))
.remark(object.getString("bio"))
.gender(AuthUserGender.UNKNOWN)
.token(authToken)
.source(source)
.build();
}
/**
* 检查响应内容是否正确
*
* @param object 请求响应内容
*/
private void checkResponse(JSONObject object) {
}
}
@@ -4,11 +4,12 @@ import cn.hutool.http.HttpRequest;
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.AuthSource;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.model.AuthUserGender;
import me.zhyd.oauth.utils.UrlBuilder;
/**
@@ -18,49 +19,83 @@ import me.zhyd.oauth.utils.UrlBuilder;
* @version 1.3
* @since 1.3
*/
public class AuthGoogleRequest extends BaseAuthRequest {
public class AuthGoogleRequest extends AuthDefaultRequest {
public AuthGoogleRequest(AuthConfig config) {
super(config, AuthSource.GOOGLE);
}
@Override
protected AuthToken getAccessToken(String code) {
String accessTokenUrl = UrlBuilder.getGoogleAccessTokenUrl(config.getClientId(), config.getClientSecret(), code, config
.getRedirectUri());
HttpResponse response = HttpRequest.post(accessTokenUrl).execute();
JSONObject object = JSONObject.parseObject(response.body());
if (object.containsKey("error") || object.containsKey("error_description")) {
throw new AuthException("get google access_token has error:[" + object.getString("error") + "], error_description:[" + object
.getString("error_description") + "]");
}
protected AuthToken getAccessToken(AuthCallback authCallback) {
HttpResponse response = doPostAuthorizationCode(authCallback.getCode());
JSONObject accessTokenObject = JSONObject.parseObject(response.body());
this.checkResponse(accessTokenObject);
return AuthToken.builder()
.accessToken(object.getString("access_token"))
.expireIn(object.getIntValue("expires_in"))
.scope(object.getString("scope"))
.tokenType(object.getString("token_type"))
.idToken(object.getString("id_token"))
.build();
.accessToken(accessTokenObject.getString("access_token"))
.expireIn(accessTokenObject.getIntValue("expires_in"))
.scope(accessTokenObject.getString("scope"))
.tokenType(accessTokenObject.getString("token_type"))
.idToken(accessTokenObject.getString("id_token"))
.build();
}
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
String accessToken = authToken.getIdToken();
HttpResponse response = HttpRequest.get(UrlBuilder.getGoogleUserInfoUrl(accessToken)).execute();
HttpResponse response = HttpRequest.post(userInfoUrl(authToken))
.header("Authorization", "Bearer " + authToken.getAccessToken())
.execute();
String userInfo = response.body();
JSONObject object = JSONObject.parseObject(userInfo);
this.checkResponse(object);
return AuthUser.builder()
.uuid(object.getString("sub"))
.username(object.getString("name"))
.avatar(object.getString("picture"))
.nickname(object.getString("name"))
.location(object.getString("locale"))
.email(object.getString("email"))
.gender(AuthUserGender.UNKNOW)
.token(authToken)
.source(AuthSource.GOOGLE)
.build();
.uuid(object.getString("sub"))
.username(object.getString("email"))
.avatar(object.getString("picture"))
.nickname(object.getString("name"))
.location(object.getString("locale"))
.email(object.getString("email"))
.gender(AuthUserGender.UNKNOWN)
.token(authToken)
.source(source)
.build();
}
/**
* 返回认证url,可自行跳转页面
* https://openidconnect.googleapis.com/v1/userinfo
*
* @return 返回授权地址
*/
@Override
public String authorize() {
return UrlBuilder.fromBaseUrl(source.authorize())
.queryParam("response_type", "code")
.queryParam("client_id", config.getClientId())
.queryParam("scope", "openid%20email%20profile")
.queryParam("redirect_uri", config.getRedirectUri())
.queryParam("state", getRealState(config.getState()))
.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()).build();
}
/**
* 检查响应内容是否正确
*
* @param object 请求响应内容
*/
private void checkResponse(JSONObject object) {
if (object.containsKey("error") || object.containsKey("error_description")) {
throw new AuthException(object.containsKey("error") + ":" + object.getString("error_description"));
}
}
}
@@ -4,7 +4,10 @@ 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 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.*;
import me.zhyd.oauth.utils.StringUtils;
@@ -18,31 +21,55 @@ import me.zhyd.oauth.utils.UrlBuilder;
* @version 1.0
* @since 1.8
*/
public class AuthLinkedinRequest extends BaseAuthRequest {
public class AuthLinkedinRequest extends AuthDefaultRequest {
public AuthLinkedinRequest(AuthConfig config) {
super(config, AuthSource.LINKEDIN);
}
@Override
protected AuthToken getAccessToken(String code) {
String accessTokenUrl = UrlBuilder.getLinkedinAccessTokenUrl(config.getClientId(), config.getClientSecret(), code, config.getRedirectUri());
return this.getToken(accessTokenUrl);
protected AuthToken getAccessToken(AuthCallback authCallback) {
return this.getToken(accessTokenUrl(authCallback.getCode()));
}
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
String accessToken = authToken.getAccessToken();
HttpResponse response = HttpRequest.get(UrlBuilder.getLinkedinUserInfoUrl())
.header("Host", "api.linkedin.com")
.header("Connection", "Keep-Alive")
.header("Authorization", "Bearer " + accessToken)
.execute();
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());
this.checkResponse(userInfoObject);
// 组装用户名
String userName = getUserName(userInfoObject);
// 获取用户头像
String avatar = this.getAvatar(userInfoObject);
// 获取用户邮箱地址
String email = this.getUserEmail(accessToken);
return AuthUser.builder()
.uuid(userInfoObject.getString("id"))
.username(userName)
.nickname(userName)
.avatar(avatar)
.email(email)
.token(authToken)
.gender(AuthUserGender.UNKNOWN)
.source(AuthSource.LINKEDIN)
.build();
}
/**
* 获取用户的真实名
*
* @param userInfoObject 用户json对象
* @return 用户名
*/
private String getUserName(JSONObject userInfoObject) {
String firstName, lastName;
// 获取firstName
if (userInfoObject.containsKey("localizedFirstName")) {
@@ -56,46 +83,46 @@ public class AuthLinkedinRequest extends BaseAuthRequest {
} else {
lastName = getUserName(userInfoObject, "lastName");
}
String userName = firstName + " " + lastName;
return firstName + " " + lastName;
}
// 获取用户头像
/**
* 获取用户的头像
*
* @param userInfoObject 用户json对象
* @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");
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");
}
}
// 获取用户邮箱地址
String email = this.getUserEmail(accessToken);
return AuthUser.builder()
.uuid(userInfoObject.getString("id"))
.username(userName)
.nickname(userName)
.avatar(avatar)
.email(email)
.token(authToken)
.gender(AuthUserGender.UNKNOW)
.source(AuthSource.LINKEDIN)
.build();
return avatar;
}
/**
* 获取用户的email
*
* @param accessToken 用户授权后返回的token
* @return 用户的邮箱地址
*/
private String getUserEmail(String accessToken) {
String email = null;
HttpResponse emailResponse = HttpRequest.get("https://api.linkedin.com/v2/emailAddress?q=members&projection=(elements*(handle~))")
.header("Host", "api.linkedin.com")
.header("Connection", "Keep-Alive")
.header("Authorization", "Bearer " + accessToken)
.execute();
System.out.println(emailResponse.body());
.header("Host", "api.linkedin.com")
.header("Connection", "Keep-Alive")
.header("Authorization", "Bearer " + accessToken)
.execute();
JSONObject emailObj = JSONObject.parseObject(emailResponse.body());
if (emailObj.containsKey("elements")) {
email = emailObj.getJSONArray("elements").getJSONObject(0).getJSONObject("handle~").getString("emailAddress");
}
return email;
this.checkResponse(emailObj);
Object obj = JSONPath.eval(emailObj, "$['elements'][0]['handle~']['emailAddress']");
return null == obj ? null : (String) obj;
}
private String getUserName(JSONObject userInfoObject, String nameKey) {
@@ -109,19 +136,25 @@ public class AuthLinkedinRequest extends BaseAuthRequest {
@Override
public AuthResponse refresh(AuthToken oldToken) {
if (StringUtils.isEmpty(oldToken.getRefreshToken())) {
throw new AuthException(ResponseStatus.UNSUPPORTED);
String refreshToken = oldToken.getRefreshToken();
if (StringUtils.isEmpty(refreshToken)) {
throw new AuthException(AuthResponseStatus.UNSUPPORTED);
}
String refreshTokenUrl = UrlBuilder.getLinkedinRefreshUrl(config.getClientId(), config.getClientSecret(), oldToken.getRefreshToken());
String refreshTokenUrl = refreshTokenUrl(refreshToken);
return AuthResponse.builder()
.code(ResponseStatus.SUCCESS.getCode())
.data(this.getToken(refreshTokenUrl))
.build();
.code(AuthResponseStatus.SUCCESS.getCode())
.data(this.getToken(refreshTokenUrl))
.build();
}
private void checkResponse(JSONObject userInfoObject) {
if (userInfoObject.containsKey("error")) {
throw new AuthException(userInfoObject.getString("error_description"));
/**
* 检查响应内容是否正确
*
* @param object 请求响应内容
*/
private void checkResponse(JSONObject object) {
if (object.containsKey("error")) {
throw new AuthException(object.getString("error_description"));
}
}
@@ -133,18 +166,47 @@ public class AuthLinkedinRequest extends BaseAuthRequest {
*/
private AuthToken getToken(String accessTokenUrl) {
HttpResponse response = HttpRequest.post(accessTokenUrl)
.header("Host", "www.linkedin.com")
.header("Content-Type", "application/x-www-form-urlencoded")
.execute();
.header("Host", "www.linkedin.com")
.contentType("application/x-www-form-urlencoded")
.execute();
String accessTokenStr = response.body();
JSONObject accessTokenObject = JSONObject.parseObject(accessTokenStr);
this.checkResponse(accessTokenObject);
return AuthToken.builder()
.accessToken(accessTokenObject.getString("access_token"))
.expireIn(accessTokenObject.getIntValue("expires_in"))
.refreshToken(accessTokenObject.getString("refresh_token"))
.build();
.accessToken(accessTokenObject.getString("access_token"))
.expireIn(accessTokenObject.getIntValue("expires_in"))
.refreshToken(accessTokenObject.getString("refresh_token"))
.build();
}
/**
* 返回认证url,可自行跳转页面
*
* @return 返回授权地址
*/
@Override
public String authorize() {
return UrlBuilder.fromBaseUrl(source.authorize())
.queryParam("response_type", "code")
.queryParam("client_id", config.getClientId())
.queryParam("redirect_uri", config.getRedirectUri())
.queryParam("state", getRealState(config.getState()))
.queryParam("scope", "r_liteprofile%20r_emailaddress%20w_member_social")
.build();
}
/**
* 返回获取userInfo的url
*
* @param authToken 用户授权后的token
* @return 返回获取userInfo的url
*/
@Override
protected String userInfoUrl(AuthToken authToken) {
return UrlBuilder.fromBaseUrl(source.userInfo())
.queryParam("projection", "(id,firstName,lastName,profilePicture(displayImage~:playableStreams))")
.build();
}
}
@@ -4,7 +4,10 @@ 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.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.*;
import me.zhyd.oauth.utils.UrlBuilder;
@@ -18,7 +21,8 @@ import java.text.MessageFormat;
* @version 1.5
* @since 1.5
*/
public class AuthMiRequest extends BaseAuthRequest {
@Slf4j
public class AuthMiRequest extends AuthDefaultRequest {
private static final String PREFIX = "&&&START&&&";
public AuthMiRequest(AuthConfig config) {
@@ -26,65 +30,65 @@ public class AuthMiRequest extends BaseAuthRequest {
}
@Override
protected AuthToken getAccessToken(String code) {
String accessTokenUrl = UrlBuilder.getMiAccessTokenUrl(config.getClientId(), config.getClientSecret(), config.getRedirectUri(), code);
return getToken(accessTokenUrl);
protected AuthToken getAccessToken(AuthCallback authCallback) {
return getToken(accessTokenUrl(authCallback.getCode()));
}
private AuthToken getToken(String accessTokenUrl) {
HttpResponse response = HttpRequest.get(accessTokenUrl).execute();
String jsonStr = StrUtil.replace(response.body(), PREFIX, StrUtil.EMPTY);
JSONObject object = JSONObject.parseObject(jsonStr);
JSONObject accessTokenObject = JSONObject.parseObject(jsonStr);
if (object.containsKey("error")) {
throw new AuthException(object.getString("error_description"));
if (accessTokenObject.containsKey("error")) {
throw new AuthException(accessTokenObject.getString("error_description"));
}
return AuthToken.builder()
.accessToken(object.getString("access_token"))
.expireIn(object.getIntValue("expires_in"))
.scope(object.getString("scope"))
.tokenType(object.getString("token_type"))
.refreshToken(object.getString("refresh_token"))
.openId(object.getString("openId"))
.macAlgorithm(object.getString("mac_algorithm"))
.macKey(object.getString("mac_key"))
.build();
.accessToken(accessTokenObject.getString("access_token"))
.expireIn(accessTokenObject.getIntValue("expires_in"))
.scope(accessTokenObject.getString("scope"))
.tokenType(accessTokenObject.getString("token_type"))
.refreshToken(accessTokenObject.getString("refresh_token"))
.openId(accessTokenObject.getString("openId"))
.macAlgorithm(accessTokenObject.getString("mac_algorithm"))
.macKey(accessTokenObject.getString("mac_key"))
.build();
}
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
// 获取用户信息
HttpResponse userResponse = HttpRequest.get(UrlBuilder.getMiUserInfoUrl(config.getClientId(), authToken.getAccessToken()))
.execute();
HttpResponse userResponse = doGetUserInfo(authToken);
JSONObject userProfile = JSONObject.parseObject(userResponse.body());
if (StrUtil.equalsIgnoreCase(userProfile.getString("result"), "error")) {
if ("error".equalsIgnoreCase(userProfile.getString("result"))) {
throw new AuthException(userProfile.getString("description"));
}
JSONObject user = userProfile.getJSONObject("data");
AuthUser authUser = AuthUser.builder()
.uuid(authToken.getOpenId())
.username(user.getString("miliaoNick"))
.nickname(user.getString("miliaoNick"))
.avatar(user.getString("miliaoIcon"))
.email(user.getString("mail"))
.gender(AuthUserGender.UNKNOW)
.token(authToken)
.source(AuthSource.MI)
.build();
.uuid(authToken.getOpenId())
.username(user.getString("miliaoNick"))
.nickname(user.getString("miliaoNick"))
.avatar(user.getString("miliaoIcon"))
.email(user.getString("mail"))
.gender(AuthUserGender.UNKNOWN)
.token(authToken)
.source(source)
.build();
// 获取用户邮箱手机号等信息
String emailPhoneUrl = MessageFormat.format("{0}?clientId={1}&token={2}", "https://open.account.xiaomi.com/user/phoneAndEmail", config
.getClientId(), authToken.getAccessToken());
.getClientId(), authToken.getAccessToken());
HttpResponse emailResponse = HttpRequest.get(emailPhoneUrl).execute();
JSONObject userEmailPhone = JSONObject.parseObject(emailResponse.body());
if (!StrUtil.equalsIgnoreCase(userEmailPhone.getString("result"), "error")) {
if (!"error".equalsIgnoreCase(userEmailPhone.getString("result"))) {
JSONObject emailPhone = userEmailPhone.getJSONObject("data");
authUser.setEmail(emailPhone.getString("email"));
} else {
log.warn("小米开发平台暂时不对外开放用户手机及邮箱信息的获取");
}
return authUser;
@@ -98,9 +102,40 @@ public class AuthMiRequest extends BaseAuthRequest {
*/
@Override
public AuthResponse refresh(AuthToken authToken) {
String miRefreshUrl = UrlBuilder.getMiRefreshUrl(config.getClientId(), config.getClientSecret(), config.getRedirectUri(), authToken
.getRefreshToken());
return AuthResponse.builder()
.code(AuthResponseStatus.SUCCESS.getCode())
.data(getToken(refreshTokenUrl(authToken.getRefreshToken())))
.build();
}
return AuthResponse.builder().code(ResponseStatus.SUCCESS.getCode()).data(getToken(miRefreshUrl)).build();
/**
* 返回认证url,可自行跳转页面
*
* @return 返回授权地址
*/
@Override
public String authorize() {
return UrlBuilder.fromBaseUrl(source.authorize())
.queryParam("response_type", "code")
.queryParam("client_id", config.getClientId())
.queryParam("redirect_uri", config.getRedirectUri())
.queryParam("state", getRealState(config.getState()))
.queryParam("scope", "user/profile%20user/openIdV2%20user/phoneAndEmail")
.queryParam("skip_confirm", "false")
.build();
}
/**
* 返回获取userInfo的url
*
* @param authToken 用户授权后的token
* @return 返回获取userInfo的url
*/
@Override
protected String userInfoUrl(AuthToken authToken) {
return UrlBuilder.fromBaseUrl(source.userInfo())
.queryParam("clientId", config.getClientId())
.queryParam("token", authToken.getAccessToken())
.build();
}
}
@@ -2,15 +2,15 @@ package me.zhyd.oauth.request;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
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.*;
import me.zhyd.oauth.utils.UrlBuilder;
import java.util.HashMap;
import java.util.Map;
import static me.zhyd.oauth.utils.GlobalAuthUtil.parseQueryToMap;
/**
* 微软登录
@@ -19,17 +19,14 @@ import java.util.Map;
* @version 1.5
* @since 1.5
*/
public class AuthMicrosoftRequest extends BaseAuthRequest {
public class AuthMicrosoftRequest extends AuthDefaultRequest {
public AuthMicrosoftRequest(AuthConfig config) {
super(config, AuthSource.MICROSOFT);
}
@Override
protected AuthToken getAccessToken(String code) {
String accessTokenUrl = UrlBuilder.getMicrosoftAccessTokenUrl(config.getClientId(), config.getClientSecret(), config
.getRedirectUri(), code);
return getToken(accessTokenUrl);
protected AuthToken getAccessToken(AuthCallback authCallback) {
return getToken(accessTokenUrl(authCallback.getCode()));
}
/**
@@ -39,30 +36,33 @@ public class AuthMicrosoftRequest extends BaseAuthRequest {
* @return token对象
*/
private AuthToken getToken(String accessTokenUrl) {
Map<String, Object> paramMap = new HashMap<>(6);
HttpUtil.decodeParamMap(accessTokenUrl, "UTF-8").forEach(paramMap::put);
HttpResponse response = HttpRequest.post(accessTokenUrl)
.header("Host", "https://login.microsoftonline.com")
.header("Content-Type", "application/x-www-form-urlencoded")
.form(paramMap)
.execute();
.header("Host", "https://login.microsoftonline.com")
.contentType("application/x-www-form-urlencoded")
.form(parseQueryToMap(accessTokenUrl))
.execute();
String accessTokenStr = response.body();
JSONObject object = JSONObject.parseObject(accessTokenStr);
JSONObject accessTokenObject = JSONObject.parseObject(accessTokenStr);
this.checkResponse(object);
this.checkResponse(accessTokenObject);
return AuthToken.builder()
.accessToken(object.getString("access_token"))
.expireIn(object.getIntValue("expires_in"))
.scope(object.getString("scope"))
.tokenType(object.getString("token_type"))
.refreshToken(object.getString("refresh_token"))
.build();
.accessToken(accessTokenObject.getString("access_token"))
.expireIn(accessTokenObject.getIntValue("expires_in"))
.scope(accessTokenObject.getString("scope"))
.tokenType(accessTokenObject.getString("token_type"))
.refreshToken(accessTokenObject.getString("refresh_token"))
.build();
}
private void checkResponse(JSONObject response) {
if (response.containsKey("error")) {
throw new AuthException(response.getString("error_description"));
/**
* 检查响应内容是否正确
*
* @param object 请求响应内容
*/
private void checkResponse(JSONObject object) {
if (object.containsKey("error")) {
throw new AuthException(object.getString("error_description"));
}
}
@@ -71,21 +71,20 @@ public class AuthMicrosoftRequest extends BaseAuthRequest {
String token = authToken.getAccessToken();
String tokenType = authToken.getTokenType();
String jwt = tokenType + " " + token;
HttpResponse response = HttpRequest.get(UrlBuilder.getMicrosoftUserInfoUrl())
.header("Authorization", jwt)
.execute();
HttpResponse response = HttpRequest.get(userInfoUrl(authToken)).header("Authorization", jwt).execute();
String userInfo = response.body();
JSONObject object = JSONObject.parseObject(userInfo);
this.checkResponse(object);
return AuthUser.builder()
.uuid(object.getString("id"))
.username(object.getString("userPrincipalName"))
.nickname(object.getString("displayName"))
.location(object.getString("officeLocation"))
.email(object.getString("mail"))
.gender(AuthUserGender.UNKNOW)
.token(authToken)
.source(AuthSource.MICROSOFT)
.build();
.uuid(object.getString("id"))
.username(object.getString("userPrincipalName"))
.nickname(object.getString("displayName"))
.location(object.getString("officeLocation"))
.email(object.getString("mail"))
.gender(AuthUserGender.UNKNOWN)
.token(authToken)
.source(source)
.build();
}
/**
@@ -96,9 +95,73 @@ public class AuthMicrosoftRequest extends BaseAuthRequest {
*/
@Override
public AuthResponse refresh(AuthToken authToken) {
String refreshTokenUrl = UrlBuilder.getMicrosoftRefreshUrl(config.getClientId(), config.getClientSecret(), config
.getRedirectUri(), authToken.getRefreshToken());
return AuthResponse.builder()
.code(AuthResponseStatus.SUCCESS.getCode())
.data(getToken(refreshTokenUrl(authToken.getRefreshToken())))
.build();
}
return AuthResponse.builder().code(ResponseStatus.SUCCESS.getCode()).data(getToken(refreshTokenUrl)).build();
/**
* 返回认证url,可自行跳转页面
*
* @return 返回授权地址
*/
@Override
public String authorize() {
return UrlBuilder.fromBaseUrl(source.authorize())
.queryParam("response_type", "code")
.queryParam("client_id", config.getClientId())
.queryParam("redirect_uri", config.getRedirectUri())
.queryParam("response_mode", "query")
.queryParam("scope", "offline_access%20user.read%20mail.read")
.queryParam("state", getRealState(config.getState()))
.build();
}
/**
* 返回获取accessToken的url
*
* @param code
* @return 返回获取accessToken的url
*/
@Override
protected String accessTokenUrl(String code) {
return UrlBuilder.fromBaseUrl(source.accessToken())
.queryParam("code", code)
.queryParam("client_id", config.getClientId())
.queryParam("client_secret", config.getClientSecret())
.queryParam("grant_type", "authorization_code")
.queryParam("scope", "user.read%20mail.read")
.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()).build();
}
/**
* 返回获取accessToken的url
*
* @param refreshToken 用户授权后的token
* @return 返回获取accessToken的url
*/
@Override
protected String refreshTokenUrl(String refreshToken) {
return UrlBuilder.fromBaseUrl(source.refresh())
.queryParam("client_id", config.getClientId())
.queryParam("client_secret", config.getClientSecret())
.queryParam("refresh_token", refreshToken)
.queryParam("grant_type", "refresh_token")
.queryParam("scope", "user.read%20mail.read")
.queryParam("redirect_uri", config.getRedirectUri())
.build();
}
}
@@ -1,14 +1,14 @@
package me.zhyd.oauth.request;
import cn.hutool.http.HttpRequest;
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.AuthSource;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.model.AuthUserGender;
import me.zhyd.oauth.utils.UrlBuilder;
/**
@@ -18,44 +18,84 @@ import me.zhyd.oauth.utils.UrlBuilder;
* @version 1.0
* @since 1.8
*/
public class AuthOschinaRequest extends BaseAuthRequest {
public class AuthOschinaRequest extends AuthDefaultRequest {
public AuthOschinaRequest(AuthConfig config) {
super(config, AuthSource.OSCHINA);
}
@Override
protected AuthToken getAccessToken(String code) {
String accessTokenUrl = UrlBuilder.getOschinaAccessTokenUrl(config.getClientId(), config.getClientSecret(), code, config.getRedirectUri());
HttpResponse response = HttpRequest.post(accessTokenUrl).execute();
protected AuthToken getAccessToken(AuthCallback authCallback) {
HttpResponse response = doPostAuthorizationCode(authCallback.getCode());
JSONObject accessTokenObject = JSONObject.parseObject(response.body());
if (accessTokenObject.containsKey("error")) {
throw new AuthException("Unable to get token from oschina using code [" + code + "]");
}
this.checkResponse(accessTokenObject);
return AuthToken.builder()
.accessToken(accessTokenObject.getString("access_token"))
.build();
.accessToken(accessTokenObject.getString("access_token"))
.refreshToken(accessTokenObject.getString("refresh_token"))
.uid(accessTokenObject.getString("uid"))
.expireIn(accessTokenObject.getIntValue("expires_in"))
.build();
}
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
String accessToken = authToken.getAccessToken();
HttpResponse response = HttpRequest.get(UrlBuilder.getOschinaUserInfoUrl(accessToken)).execute();
HttpResponse response = doGetUserInfo(authToken);
JSONObject object = JSONObject.parseObject(response.body());
this.checkResponse(object);
return AuthUser.builder()
.uuid(object.getString("id"))
.username(object.getString("name"))
.nickname(object.getString("name"))
.avatar(object.getString("avatar"))
.blog(object.getString("url"))
.location(object.getString("location"))
.gender(AuthUserGender.getRealGender(object.getString("gender")))
.email(object.getString("email"))
.token(authToken)
.source(source)
.build();
}
/**
* 返回获取accessToken的url
*
* @param code
* @return 返回获取accessToken的url
*/
@Override
protected String accessTokenUrl(String code) {
return UrlBuilder.fromBaseUrl(source.accessToken())
.queryParam("code", code)
.queryParam("client_id", config.getClientId())
.queryParam("client_secret", config.getClientSecret())
.queryParam("grant_type", "authorization_code")
.queryParam("redirect_uri", config.getRedirectUri())
.queryParam("dataType", "json")
.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("dataType", "json")
.build();
}
/**
* 检查响应内容是否正确
*
* @param object 请求响应内容
*/
private void checkResponse(JSONObject object) {
if (object.containsKey("error")) {
throw new AuthException(object.getString("error_description"));
}
return AuthUser.builder()
.uuid(object.getString("id"))
.username(object.getString("name"))
.nickname(object.getString("name"))
.avatar(object.getString("avatar"))
.blog(object.getString("url"))
.location(object.getString("location"))
.gender(AuthUserGender.getRealGender(object.getString("gender")))
.email(object.getString("email"))
.token(authToken)
.source(AuthSource.OSCHINA)
.build();
}
}
@@ -0,0 +1,107 @@
package me.zhyd.oauth.request;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import com.alibaba.fastjson.JSONObject;
import me.zhyd.oauth.config.AuthConfig;
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;
import java.util.Objects;
import static me.zhyd.oauth.config.AuthSource.PINTEREST;
/**
* Pinterest登录
*
* @author hongwei.peng (pengisgood(at)gmail(dot)com)
* @version 1.9.0
* @since 1.8
*/
public class AuthPinterestRequest extends AuthDefaultRequest {
private static final String FAILURE = "failure";
public AuthPinterestRequest(AuthConfig config) {
super(config, PINTEREST);
}
@Override
protected AuthToken getAccessToken(AuthCallback authCallback) {
HttpResponse response = doPostAuthorizationCode(authCallback.getCode());
JSONObject accessTokenObject = JSONObject.parseObject(response.body());
this.checkResponse(accessTokenObject);
return AuthToken.builder()
.accessToken(accessTokenObject.getString("access_token"))
.tokenType(accessTokenObject.getString("token_type"))
.build();
}
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
String userinfoUrl = userInfoUrl(authToken);
HttpResponse response = HttpRequest.get(userinfoUrl).setFollowRedirects(true).execute();
JSONObject object = JSONObject.parseObject(response.body());
this.checkResponse(object);
JSONObject userObj = object.getJSONObject("data");
return AuthUser.builder()
.uuid(userObj.getString("id"))
.avatar(getAvatarUrl(userObj))
.username(userObj.getString("username"))
.nickname(userObj.getString("first_name") + " " + userObj.getString("last_name"))
.gender(AuthUserGender.UNKNOWN)
.remark(userObj.getString("bio"))
.token(authToken)
.source(source)
.build();
}
private String getAvatarUrl(JSONObject userObj) {
// image is a map data structure
JSONObject jsonObject = userObj.getJSONObject("image");
if (Objects.isNull(jsonObject)) {
return null;
}
return jsonObject.getJSONObject("60x60").getString("url");
}
@Override
public String authorize() {
return UrlBuilder.fromBaseUrl(source.authorize())
.queryParam("response_type", "code")
.queryParam("client_id", config.getClientId())
.queryParam("redirect_uri", config.getRedirectUri())
.queryParam("state", getRealState(config.getState()))
.queryParam("scope", "read_public")
.build();
}
/**
* 返回获取userInfo的url
*
* @param authToken token
* @return 返回获取userInfo的url
*/
protected String userInfoUrl(AuthToken authToken) {
return UrlBuilder.fromBaseUrl(source.userInfo())
.queryParam("access_token", authToken.getAccessToken())
.queryParam("fields", "id,username,first_name,last_name,bio,image")
.build();
}
/**
* 检查响应内容是否正确
*
* @param object 请求响应内容
*/
private void checkResponse(JSONObject object) {
if (!object.containsKey("status") && FAILURE.equals(object.getString("status"))) {
throw new AuthException(object.getString("message"));
}
}
}
@@ -5,11 +5,10 @@ import cn.hutool.http.HttpRequest;
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.AuthSource;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.model.AuthUserGender;
import me.zhyd.oauth.model.*;
import me.zhyd.oauth.utils.GlobalAuthUtil;
import me.zhyd.oauth.utils.StringUtils;
import me.zhyd.oauth.utils.UrlBuilder;
@@ -24,33 +23,30 @@ import java.util.Map;
* @version 1.0
* @since 1.8
*/
public class AuthQqRequest extends BaseAuthRequest {
public class AuthQqRequest extends AuthDefaultRequest {
public AuthQqRequest(AuthConfig config) {
super(config, AuthSource.QQ);
}
@Override
protected AuthToken getAccessToken(String code) {
String accessTokenUrl = UrlBuilder.getQqAccessTokenUrl(config.getClientId(), config.getClientSecret(), code, config
.getRedirectUri());
HttpResponse response = HttpRequest.get(accessTokenUrl).execute();
Map<String, String> accessTokenObject = GlobalAuthUtil.parseStringToMap(response.body());
if (!accessTokenObject.containsKey("access_token")) {
throw new AuthException("Unable to get token from qq using code [" + code + "]");
}
return AuthToken.builder()
.accessToken(accessTokenObject.get("access_token"))
.expireIn(Integer.valueOf(accessTokenObject.get("expires_in")))
.refreshToken(accessTokenObject.get("refresh_token"))
.build();
protected AuthToken getAccessToken(AuthCallback authCallback) {
HttpResponse 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();
}
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
String accessToken = authToken.getAccessToken();
String openId = this.getOpenId(authToken);
HttpResponse response = HttpRequest.get(UrlBuilder.getQqUserInfoUrl(config.getClientId(), accessToken, openId))
.execute();
HttpResponse response = doGetUserInfo(authToken);
JSONObject object = JSONObject.parseObject(response.body());
if (object.getIntValue("ret") != 0) {
throw new AuthException(object.getString("msg"));
@@ -62,21 +58,22 @@ public class AuthQqRequest extends BaseAuthRequest {
String location = String.format("%s-%s", object.getString("province"), object.getString("city"));
return AuthUser.builder()
.username(object.getString("nickname"))
.nickname(object.getString("nickname"))
.avatar(avatar)
.location(location)
.uuid(openId)
.gender(AuthUserGender.getRealGender(object.getString("gender")))
.token(authToken)
.source(AuthSource.QQ)
.build();
.username(object.getString("nickname"))
.nickname(object.getString("nickname"))
.avatar(avatar)
.location(location)
.uuid(openId)
.gender(AuthUserGender.getRealGender(object.getString("gender")))
.token(authToken)
.source(source)
.build();
}
private String getOpenId(AuthToken authToken) {
String accessToken = authToken.getAccessToken();
HttpResponse response = HttpRequest.get(UrlBuilder.getQqOpenidUrl("https://graph.qq.com/oauth2.0/me", accessToken))
.execute();
HttpResponse response = HttpRequest.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(", "");
@@ -87,10 +84,39 @@ public class AuthQqRequest extends BaseAuthRequest {
throw new AuthException(object.get("error") + ":" + object.get("error_description"));
}
authToken.setOpenId(object.getString("openid"));
authToken.setUnionId(object.getString("unionid"));
if (object.containsKey("unionid")) {
authToken.setUnionId(object.getString("unionid"));
}
return StringUtils.isEmpty(authToken.getUnionId()) ? authToken.getOpenId() : authToken.getUnionId();
}
throw new AuthException("request error");
}
/**
* 返回获取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("oauth_consumer_key", config.getClientId())
.queryParam("openid", authToken.getOpenId())
.build();
}
private AuthToken getAuthToken(HttpResponse response) {
Map<String, String> accessTokenObject = GlobalAuthUtil.parseStringToMap(response.body());
if (!accessTokenObject.containsKey("access_token") || accessTokenObject.containsKey("code")) {
throw new AuthException(accessTokenObject.get("msg"));
}
return AuthToken.builder()
.accessToken(accessTokenObject.get("access_token"))
.expireIn(Integer.valueOf(accessTokenObject.get("expires_in")))
.refreshToken(accessTokenObject.get("refresh_token"))
.build();
}
}
@@ -0,0 +1,113 @@
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 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.utils.UrlBuilder;
import java.util.Objects;
import static me.zhyd.oauth.config.AuthSource.RENREN;
import static me.zhyd.oauth.model.AuthResponseStatus.SUCCESS;
/**
* 人人登录
*
* @author hongwei.peng (pengisgood(at)gmail(dot)com)
* @version 1.9.0
* @since 1.8
*/
public class AuthRenrenRequest extends AuthDefaultRequest {
public AuthRenrenRequest(AuthConfig config) {
super(config, RENREN);
}
@Override
protected AuthToken getAccessToken(AuthCallback authCallback) {
return this.getToken(accessTokenUrl(authCallback.getCode()));
}
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
HttpResponse response = doGetUserInfo(authToken);
JSONObject userObj = JSONObject.parseObject(response.body()).getJSONObject("response");
return AuthUser.builder()
.uuid(userObj.getString("id"))
.avatar(getAvatarUrl(userObj))
.nickname(userObj.getString("name"))
.company(getCompany(userObj))
.gender(getGender(userObj))
.token(authToken)
.source(source)
.build();
}
@Override
public AuthResponse refresh(AuthToken authToken) {
return AuthResponse.builder()
.code(SUCCESS.getCode())
.data(getToken(this.refreshTokenUrl(authToken.getRefreshToken())))
.build();
}
private AuthToken getToken(String url) {
HttpResponse response = HttpRequest.post(url).execute();
JSONObject jsonObject = JSONObject.parseObject(response.body());
if (jsonObject.containsKey("error")) {
throw new AuthException("Failed to get token from Renren: " + jsonObject);
}
return AuthToken.builder()
.tokenType(jsonObject.getString("token_type"))
.expireIn(jsonObject.getIntValue("expires_in"))
.accessToken(jsonObject.getString("access_token"))
.refreshToken(jsonObject.getString("refresh_token"))
.openId(jsonObject.getJSONObject("user").getString("id"))
.build();
}
private String getAvatarUrl(JSONObject userObj) {
JSONArray jsonArray = userObj.getJSONArray("avatar");
if (Objects.isNull(jsonArray) || jsonArray.isEmpty()) {
return null;
}
return jsonArray.getJSONObject(0).getString("url");
}
private AuthUserGender getGender(JSONObject userObj) {
JSONObject basicInformation = userObj.getJSONObject("basicInformation");
if (Objects.isNull(basicInformation)) {
return AuthUserGender.UNKNOWN;
}
return AuthUserGender.getRealGender(basicInformation.getString("sex"));
}
private String getCompany(JSONObject userObj) {
JSONArray jsonArray = userObj.getJSONArray("work");
if (Objects.isNull(jsonArray) || jsonArray.isEmpty()) {
return null;
}
return jsonArray.getJSONObject(0).getString("name");
}
/**
* 返回获取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("userId", authToken.getOpenId())
.build();
}
}
@@ -1,7 +1,9 @@
package me.zhyd.oauth.request;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthResponse;
import me.zhyd.oauth.model.AuthResponseStatus;
import me.zhyd.oauth.model.AuthToken;
/**
@@ -17,17 +19,17 @@ public interface AuthRequest {
* @return 返回授权地址
*/
default String authorize() {
throw new AuthException(ResponseStatus.NOT_IMPLEMENTED);
throw new AuthException(AuthResponseStatus.NOT_IMPLEMENTED);
}
/**
* 第三方登录
*
* @param code 通过authorize换回的code
* @param authCallback 用于接收回调参数的实体
* @return 返回登录成功后的用户信息
*/
default AuthResponse login(String code) {
throw new AuthException(ResponseStatus.NOT_IMPLEMENTED);
default AuthResponse login(AuthCallback authCallback) {
throw new AuthException(AuthResponseStatus.NOT_IMPLEMENTED);
}
/**
@@ -37,7 +39,7 @@ public interface AuthRequest {
* @return AuthResponse
*/
default AuthResponse revoke(AuthToken authToken) {
throw new AuthException(ResponseStatus.NOT_IMPLEMENTED);
throw new AuthException(AuthResponseStatus.NOT_IMPLEMENTED);
}
/**
@@ -47,6 +49,6 @@ public interface AuthRequest {
* @return AuthResponse
*/
default AuthResponse refresh(AuthToken authToken) {
throw new AuthException(ResponseStatus.NOT_IMPLEMENTED);
throw new AuthException(AuthResponseStatus.NOT_IMPLEMENTED);
}
}
@@ -0,0 +1,91 @@
package me.zhyd.oauth.request;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import com.alibaba.fastjson.JSONObject;
import me.zhyd.oauth.config.AuthConfig;
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;
import static me.zhyd.oauth.config.AuthSource.STACK_OVERFLOW;
import static me.zhyd.oauth.utils.GlobalAuthUtil.parseQueryToMap;
/**
* Stack Overflow登录
*
* @author hongwei.peng (pengisgood(at)gmail(dot)com)
* @version 1.9.0
* @since 1.8
*/
public class AuthStackOverflowRequest extends AuthDefaultRequest {
public AuthStackOverflowRequest(AuthConfig config) {
super(config, STACK_OVERFLOW);
}
@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());
this.checkResponse(accessTokenObject);
return AuthToken.builder()
.accessToken(accessTokenObject.getString("access_token"))
.expireIn(accessTokenObject.getIntValue("expires"))
.build();
}
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
String userInfoUrl = UrlBuilder.fromBaseUrl(this.source.userInfo())
.queryParam("access_token", authToken.getAccessToken())
.queryParam("site", "stackoverflow")
.queryParam("key", this.config.getStackOverflowKey())
.build();
HttpResponse response = HttpRequest.get(userInfoUrl).execute();
JSONObject object = JSONObject.parseObject(response.body());
this.checkResponse(object);
JSONObject userObj = object.getJSONArray("items").getJSONObject(0);
return AuthUser.builder()
.uuid(userObj.getString("user_id"))
.avatar(userObj.getString("profile_image"))
.location(userObj.getString("location"))
.nickname(userObj.getString("display_name"))
.blog(userObj.getString("website_url"))
.gender(AuthUserGender.UNKNOWN)
.token(authToken)
.source(source)
.build();
}
@Override
public String authorize() {
return UrlBuilder.fromBaseUrl(source.authorize())
.queryParam("response_type", "code")
.queryParam("client_id", config.getClientId())
.queryParam("redirect_uri", config.getRedirectUri())
.queryParam("state", getRealState(config.getState()))
.queryParam("scope", "read_inbox")
.build();
}
/**
* 检查响应内容是否正确
*
* @param object 请求响应内容
*/
private void checkResponse(JSONObject object) {
if (object.containsKey("error")) {
throw new AuthException(object.getString("error_description"));
}
}
}
@@ -1,14 +1,14 @@
package me.zhyd.oauth.request;
import cn.hutool.http.HttpRequest;
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.exception.AuthException;
import me.zhyd.oauth.model.AuthSource;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.model.AuthUserGender;
import me.zhyd.oauth.enums.AuthUserGender;
import me.zhyd.oauth.utils.GlobalAuthUtil;
import me.zhyd.oauth.utils.UrlBuilder;
@@ -19,41 +19,54 @@ import me.zhyd.oauth.utils.UrlBuilder;
* @version 1.0
* @since 1.8
*/
public class AuthTaobaoRequest extends BaseAuthRequest {
public class AuthTaobaoRequest extends AuthDefaultRequest {
public AuthTaobaoRequest(AuthConfig config) {
super(config, AuthSource.TAOBAO);
}
@Override
protected AuthToken getAccessToken(String code) {
return AuthToken.builder()
.accessCode(code)
.build();
protected AuthToken getAccessToken(AuthCallback authCallback) {
return AuthToken.builder().accessCode(authCallback.getCode()).build();
}
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
String accessCode = authToken.getAccessCode();
HttpResponse response = HttpRequest.post(UrlBuilder.getTaobaoAccessTokenUrl(this.config.getClientId(), this.config.getClientSecret(), accessCode, this.config.getRedirectUri())).execute();
JSONObject object = JSONObject.parseObject(response.body());
if (object.containsKey("error")) {
throw new AuthException(ResponseStatus.FAILURE + ":" + object.getString("error_description"));
HttpResponse response = doPostAuthorizationCode(authToken.getAccessCode());
JSONObject accessTokenObject = JSONObject.parseObject(response.body());
if (accessTokenObject.containsKey("error")) {
throw new AuthException(accessTokenObject.getString("error_description"));
}
authToken.setAccessToken(object.getString("access_token"));
authToken.setRefreshToken(object.getString("refresh_token"));
authToken.setExpireIn(object.getIntValue("expires_in"));
authToken.setUid(object.getString("taobao_user_id"));
authToken.setOpenId(object.getString("taobao_open_uid"));
authToken.setAccessToken(accessTokenObject.getString("access_token"));
authToken.setRefreshToken(accessTokenObject.getString("refresh_token"));
authToken.setExpireIn(accessTokenObject.getIntValue("expires_in"));
authToken.setUid(accessTokenObject.getString("taobao_user_id"));
authToken.setOpenId(accessTokenObject.getString("taobao_open_uid"));
String nick = GlobalAuthUtil.urlDecode(object.getString("taobao_user_nick"));
String nick = GlobalAuthUtil.urlDecode(accessTokenObject.getString("taobao_user_nick"));
return AuthUser.builder()
.uuid(object.getString("taobao_user_id"))
.username(nick)
.nickname(nick)
.gender(AuthUserGender.UNKNOW)
.token(authToken)
.source(AuthSource.TAOBAO)
.build();
.uuid(accessTokenObject.getString("taobao_user_id"))
.username(nick)
.nickname(nick)
.gender(AuthUserGender.UNKNOWN)
.token(authToken)
.source(source)
.build();
}
/**
* 返回认证url,可自行跳转页面
*
* @return 返回授权地址
*/
@Override
public String authorize() {
return UrlBuilder.fromBaseUrl(source.authorize())
.queryParam("response_type", "code")
.queryParam("client_id", config.getClientId())
.queryParam("redirect_uri", config.getRedirectUri())
.queryParam("state", getRealState(config.getState()))
.queryParam("view", "web")
.build();
}
}
@@ -0,0 +1,105 @@
package me.zhyd.oauth.request;
import cn.hutool.http.HttpRequest;
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.*;
/**
* Teambition授权登录
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0
* @since 1.8
*/
public class AuthTeambitionRequest extends AuthDefaultRequest {
public AuthTeambitionRequest(AuthConfig config) {
super(config, AuthSource.TEAMBITION);
}
/**
* @param authCallback 回调返回的参数
* @return 所有信息
*/
@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());
this.checkResponse(accessTokenObject);
return AuthToken.builder()
.accessToken(accessTokenObject.getString("access_token"))
.refreshToken(accessTokenObject.getString("refresh_token"))
.build();
}
@Override
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());
this.checkResponse(object);
authToken.setUid(object.getString("_id"));
return AuthUser.builder()
.uuid(object.getString("_id"))
.username(object.getString("name"))
.nickname(object.getString("name"))
.avatar(object.getString("avatarUrl"))
.blog(object.getString("website"))
.location(object.getString("location"))
.email(object.getString("email"))
.gender(AuthUserGender.UNKNOWN)
.token(authToken)
.source(source)
.build();
}
@Override
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());
this.checkResponse(refreshTokenObject);
return AuthResponse.builder()
.code(AuthResponseStatus.SUCCESS.getCode())
.data(AuthToken.builder()
.accessToken(refreshTokenObject.getString("access_token"))
.refreshToken(refreshTokenObject.getString("refresh_token"))
.build())
.build();
}
/**
* 检查响应内容是否正确
*
* @param object 请求响应内容
*/
private void checkResponse(JSONObject object) {
if ((object.containsKey("message") && object.containsKey("name"))) {
throw new AuthException(object.getString("name") + ", " + object.getString("message"));
}
}
}
@@ -1,14 +1,14 @@
package me.zhyd.oauth.request;
import cn.hutool.http.HttpRequest;
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.AuthSource;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.model.AuthUserGender;
import me.zhyd.oauth.utils.UrlBuilder;
/**
@@ -18,47 +18,71 @@ import me.zhyd.oauth.utils.UrlBuilder;
* @version 1.0
* @since 1.8
*/
public class AuthTencentCloudRequest extends BaseAuthRequest {
public class AuthTencentCloudRequest extends AuthDefaultRequest {
public AuthTencentCloudRequest(AuthConfig config) {
super(config, AuthSource.TENCENT_CLOUD);
}
@Override
protected AuthToken getAccessToken(String code) {
String accessTokenUrl = UrlBuilder.getTencentCloudAccessTokenUrl(config.getClientId(), config.getClientSecret(), code);
HttpResponse response = HttpRequest.get(accessTokenUrl).execute();
JSONObject object = JSONObject.parseObject(response.body());
if (object.getIntValue("code") != 0) {
throw new AuthException("Unable to get token from tencent cloud using code [" + code + "]: " + object.get("msg"));
}
protected AuthToken getAccessToken(AuthCallback authCallback) {
HttpResponse response = doGetAuthorizationCode(authCallback.getCode());
JSONObject accessTokenObject = JSONObject.parseObject(response.body());
this.checkResponse(accessTokenObject);
return AuthToken.builder()
.accessToken(object.getString("access_token"))
.build();
.accessToken(accessTokenObject.getString("access_token"))
.expireIn(accessTokenObject.getIntValue("expires_in"))
.refreshToken(accessTokenObject.getString("refresh_token"))
.build();
}
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
String accessToken = authToken.getAccessToken();
HttpResponse response = HttpRequest.get(UrlBuilder.getTencentCloudUserInfoUrl(accessToken)).execute();
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"));
}
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(AuthSource.TENCENT_CLOUD)
.build();
}
/**
* 返回认证url,可自行跳转页面
*
* @return 返回授权地址
*/
@Override
public String authorize() {
return UrlBuilder.fromBaseUrl(source.authorize())
.queryParam("response_type", "code")
.queryParam("client_id", config.getClientId())
.queryParam("redirect_uri", config.getRedirectUri())
.queryParam("scope", "user")
.queryParam("state", getRealState(config.getState()))
.build();
}
}
@@ -1,11 +1,15 @@
package me.zhyd.oauth.request;
import cn.hutool.http.HttpRequest;
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.AuthToutiaoErrorCode;
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.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.utils.UrlBuilder;
/**
@@ -15,38 +19,33 @@ import me.zhyd.oauth.utils.UrlBuilder;
* @version 1.5
* @since 1.5
*/
public class AuthToutiaoRequest extends BaseAuthRequest {
public class AuthToutiaoRequest extends AuthDefaultRequest {
public AuthToutiaoRequest(AuthConfig config) {
super(config, AuthSource.TOUTIAO);
}
@Override
protected AuthToken getAccessToken(String code) {
String accessTokenUrl = UrlBuilder.getToutiaoAccessTokenUrl(config.getClientId(), config.getClientSecret(), code);
HttpResponse response = HttpRequest.get(accessTokenUrl).execute();
JSONObject object = JSONObject.parseObject(response.body());
protected AuthToken getAccessToken(AuthCallback authCallback) {
HttpResponse response = doGetAuthorizationCode(authCallback.getCode());
JSONObject accessTokenObject = JSONObject.parseObject(response.body());
if (object.containsKey("error_code")) {
throw new AuthException(AuthToutiaoErrorCode.getErrorCode(object.getIntValue("error_code")).getDesc());
}
this.checkResponse(accessTokenObject);
return AuthToken.builder()
.accessToken(object.getString("access_token"))
.expireIn(object.getIntValue("expires_in"))
.openId(object.getString("open_id"))
.build();
.accessToken(accessTokenObject.getString("access_token"))
.expireIn(accessTokenObject.getIntValue("expires_in"))
.openId(accessTokenObject.getString("open_id"))
.build();
}
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
HttpResponse userResponse = HttpRequest.get(UrlBuilder.getToutiaoUserInfoUrl(config.getClientId(), authToken.getAccessToken())).execute();
HttpResponse userResponse = doGetUserInfo(authToken);
JSONObject userProfile = JSONObject.parseObject(userResponse.body());
if (userProfile.containsKey("error_code")) {
throw new AuthException(AuthToutiaoErrorCode.getErrorCode(userProfile.getIntValue("error_code")).getDesc());
}
this.checkResponse(userProfile);
JSONObject user = userProfile.getJSONObject("data");
@@ -54,14 +53,73 @@ public class AuthToutiaoRequest extends BaseAuthRequest {
String anonymousUserName = "匿名用户";
return AuthUser.builder()
.uuid(user.getString("uid"))
.username(isAnonymousUser ? anonymousUserName : user.getString("screen_name"))
.nickname(isAnonymousUser ? anonymousUserName : user.getString("screen_name"))
.avatar(user.getString("avatar_url"))
.remark(user.getString("description"))
.gender(AuthUserGender.getRealGender(user.getString("gender")))
.token(authToken)
.source(AuthSource.TOUTIAO)
.build();
.uuid(user.getString("uid"))
.username(isAnonymousUser ? anonymousUserName : user.getString("screen_name"))
.nickname(isAnonymousUser ? anonymousUserName : user.getString("screen_name"))
.avatar(user.getString("avatar_url"))
.remark(user.getString("description"))
.gender(AuthUserGender.getRealGender(user.getString("gender")))
.token(authToken)
.source(source)
.build();
}
/**
* 返回认证url,可自行跳转页面
*
* @return 返回授权地址
*/
@Override
public String authorize() {
return UrlBuilder.fromBaseUrl(source.authorize())
.queryParam("response_type", "code")
.queryParam("client_key", config.getClientId())
.queryParam("redirect_uri", config.getRedirectUri())
.queryParam("state", getRealState(config.getState()))
.queryParam("auth_only", 1)
.queryParam("display", 0)
.build();
}
/**
* 返回获取accessToken的url
*
* @param code 授权码
* @return 返回获取accessToken的url
*/
@Override
protected String accessTokenUrl(String code) {
return UrlBuilder.fromBaseUrl(source.accessToken())
.queryParam("code", code)
.queryParam("client_key", config.getClientId())
.queryParam("client_secret", config.getClientSecret())
.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("client_key", config.getClientId())
.queryParam("access_token", authToken.getAccessToken())
.build();
}
/**
* 检查响应内容是否正确
*
* @param object 请求响应内容
*/
private void checkResponse(JSONObject object) {
if (object.containsKey("error_code")) {
throw new AuthException(AuthToutiaoErrorCode.getErrorCode(object.getIntValue("error_code"))
.getDesc());
}
}
}
@@ -4,6 +4,8 @@ import cn.hutool.http.HttpRequest;
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.*;
import me.zhyd.oauth.utils.UrlBuilder;
@@ -15,7 +17,7 @@ import me.zhyd.oauth.utils.UrlBuilder;
* @version 1.0
* @since 1.8
*/
public class AuthWeChatRequest extends BaseAuthRequest {
public class AuthWeChatRequest extends AuthDefaultRequest {
public AuthWeChatRequest(AuthConfig config) {
super(config, AuthSource.WECHAT);
}
@@ -23,45 +25,47 @@ public class AuthWeChatRequest extends BaseAuthRequest {
/**
* 微信的特殊性,此时返回的信息同时包含 openid 和 access_token
*
* @param code 授权码
* @param authCallback 回调返回的参数
* @return 所有信息
*/
@Override
protected AuthToken getAccessToken(String code) {
String accessTokenUrl = UrlBuilder.getWeChatAccessTokenUrl(config.getClientId(), config.getClientSecret(), code);
return this.getToken(accessTokenUrl);
protected AuthToken getAccessToken(AuthCallback authCallback) {
return this.getToken(accessTokenUrl(authCallback.getCode()));
}
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
String accessToken = authToken.getAccessToken();
String openId = authToken.getOpenId();
HttpResponse response = HttpRequest.get(UrlBuilder.getWeChatUserInfoUrl(accessToken, openId)).execute();
HttpResponse response = doGetUserInfo(authToken);
JSONObject object = JSONObject.parseObject(response.body());
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()
.username(object.getString("nickname"))
.nickname(object.getString("nickname"))
.avatar(object.getString("headimgurl"))
.location(location)
.uuid(openId)
.gender(AuthUserGender.getRealGender(object.getString("sex")))
.token(authToken)
.source(AuthSource.WECHAT)
.build();
.username(object.getString("nickname"))
.nickname(object.getString("nickname"))
.avatar(object.getString("headimgurl"))
.location(location)
.uuid(openId)
.gender(AuthUserGender.getRealGender(object.getString("sex")))
.token(authToken)
.source(source)
.build();
}
@Override
public AuthResponse refresh(AuthToken oldToken) {
String refreshTokenUrl = UrlBuilder.getWeChatRefreshUrl(config.getClientId(), oldToken.getRefreshToken());
return AuthResponse.builder()
.code(ResponseStatus.SUCCESS.getCode())
.data(this.getToken(refreshTokenUrl))
.build();
.code(AuthResponseStatus.SUCCESS.getCode())
.data(this.getToken(refreshTokenUrl(oldToken.getRefreshToken())))
.build();
}
/**
@@ -83,15 +87,77 @@ public class AuthWeChatRequest extends BaseAuthRequest {
*/
private AuthToken getToken(String accessTokenUrl) {
HttpResponse response = HttpRequest.get(accessTokenUrl).execute();
JSONObject object = JSONObject.parseObject(response.body());
JSONObject accessTokenObject = JSONObject.parseObject(response.body());
this.checkResponse(object);
this.checkResponse(accessTokenObject);
return AuthToken.builder()
.accessToken(object.getString("access_token"))
.refreshToken(object.getString("refresh_token"))
.expireIn(object.getIntValue("expires_in"))
.openId(object.getString("openid"))
.build();
.accessToken(accessTokenObject.getString("access_token"))
.refreshToken(accessTokenObject.getString("refresh_token"))
.expireIn(accessTokenObject.getIntValue("expires_in"))
.openId(accessTokenObject.getString("openid"))
.build();
}
/**
* 返回认证url,可自行跳转页面
*
* @return 返回授权地址
*/
@Override
public String authorize() {
return UrlBuilder.fromBaseUrl(source.authorize())
.queryParam("response_type", "code")
.queryParam("appid", config.getClientId())
.queryParam("redirect_uri", config.getRedirectUri())
.queryParam("scope", "snsapi_login")
.queryParam("state", getRealState(config.getState()).concat("#wechat_redirect"))
.build();
}
/**
* 返回获取accessToken的url
*
* @param code 授权码
* @return 返回获取accessToken的url
*/
@Override
protected String accessTokenUrl(String code) {
return UrlBuilder.fromBaseUrl(source.accessToken())
.queryParam("code", code)
.queryParam("appid", config.getClientId())
.queryParam("secret", config.getClientSecret())
.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("refresh_token", refreshToken)
.queryParam("grant_type", "refresh_token")
.build();
}
}
@@ -4,11 +4,12 @@ import cn.hutool.http.HttpRequest;
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.AuthSource;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.model.AuthUserGender;
import me.zhyd.oauth.utils.IpUtils;
import me.zhyd.oauth.utils.StringUtils;
import me.zhyd.oauth.utils.UrlBuilder;
@@ -21,26 +22,26 @@ import me.zhyd.oauth.utils.UrlBuilder;
* @version 1.0
* @since 1.8
*/
public class AuthWeiboRequest extends BaseAuthRequest {
public class AuthWeiboRequest extends AuthDefaultRequest {
public AuthWeiboRequest(AuthConfig config) {
super(config, AuthSource.WEIBO);
}
@Override
protected AuthToken getAccessToken(String code) {
String accessTokenUrl = UrlBuilder.getWeiboAccessTokenUrl(config.getClientId(), config.getClientSecret(), code, config.getRedirectUri());
HttpResponse response = HttpRequest.post(accessTokenUrl).execute();
protected AuthToken getAccessToken(AuthCallback authCallback) {
HttpResponse response = doPostAuthorizationCode(authCallback.getCode());
String accessTokenStr = response.body();
JSONObject accessTokenObject = JSONObject.parseObject(accessTokenStr);
if (accessTokenObject.containsKey("error")) {
throw new AuthException("Unable to get token from weibo using code [" + code + "]:" + accessTokenObject.getString("error_description"));
throw new AuthException(accessTokenObject.getString("error_description"));
}
return AuthToken.builder()
.accessToken(accessTokenObject.getString("access_token"))
.uid(accessTokenObject.getString("uid"))
.expireIn(accessTokenObject.getIntValue("remind_in"))
.build();
.accessToken(accessTokenObject.getString("access_token"))
.uid(accessTokenObject.getString("uid"))
.openId(accessTokenObject.getString("uid"))
.expireIn(accessTokenObject.getIntValue("expires_in"))
.build();
}
@Override
@@ -48,26 +49,41 @@ public class AuthWeiboRequest extends BaseAuthRequest {
String accessToken = authToken.getAccessToken();
String uid = authToken.getUid();
String oauthParam = String.format("uid=%s&access_token=%s", uid, accessToken);
HttpResponse response = HttpRequest.get(UrlBuilder.getWeiboUserInfoUrl(oauthParam))
.header("Authorization", "OAuth2 " + oauthParam)
.header("API-RemoteIP", IpUtils.getIp())
.execute();
HttpResponse response = HttpRequest.get(userInfoUrl(authToken))
.header("Authorization", "OAuth2 " + oauthParam)
.header("API-RemoteIP", IpUtils.getIp())
.execute();
String userInfo = response.body();
JSONObject object = JSONObject.parseObject(userInfo);
if (object.containsKey("error")) {
throw new AuthException(object.getString("error"));
}
return AuthUser.builder()
.uuid(object.getString("id"))
.username(object.getString("name"))
.avatar(object.getString("profile_image_url"))
.blog(StringUtils.isEmpty(object.getString("url")) ? "https://weibo.com/" + object.getString("profile_url") : object.getString("url"))
.nickname(object.getString("screen_name"))
.location(object.getString("location"))
.remark(object.getString("description"))
.gender(AuthUserGender.getRealGender(object.getString("gender")))
.token(authToken)
.source(AuthSource.WEIBO)
.build();
.uuid(object.getString("id"))
.username(object.getString("name"))
.avatar(object.getString("profile_image_url"))
.blog(StringUtils.isEmpty(object.getString("url")) ? "https://weibo.com/" + object.getString("profile_url") : object
.getString("url"))
.nickname(object.getString("screen_name"))
.location(object.getString("location"))
.remark(object.getString("description"))
.gender(AuthUserGender.getRealGender(object.getString("gender")))
.token(authToken)
.source(source)
.build();
}
/**
* 返回获取userInfo的url
*
* @param authToken authToken
* @return 返回获取userInfo的url
*/
@Override
protected String userInfoUrl(AuthToken authToken) {
return UrlBuilder.fromBaseUrl(source.userInfo())
.queryParam("access_token", authToken.getAccessToken())
.queryParam("uid", authToken.getUid())
.build();
}
}
@@ -1,60 +0,0 @@
package me.zhyd.oauth.request;
import lombok.Data;
import me.zhyd.oauth.authorization.AuthorizationFactory;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.AuthResponse;
import me.zhyd.oauth.model.AuthSource;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.utils.AuthConfigChecker;
/**
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0
* @since 1.8
*/
@Data
public abstract class BaseAuthRequest implements AuthRequest {
protected AuthConfig config;
protected AuthSource source;
public BaseAuthRequest(AuthConfig config, AuthSource source) {
this.config = config;
this.source = source;
if (!AuthConfigChecker.isSupportedAuth(config, source)) {
throw new AuthException(ResponseStatus.PARAMETER_INCOMPLETE);
}
// 校验配置合法性
AuthConfigChecker.check(config, source);
}
protected abstract AuthToken getAccessToken(String code);
protected abstract AuthUser getUserInfo(AuthToken authToken);
@Override
public AuthResponse login(String code) {
try {
AuthToken authToken = this.getAccessToken(code);
AuthUser user = this.getUserInfo(authToken);
return AuthResponse.builder().code(ResponseStatus.SUCCESS.getCode()).data(user).build();
} catch (Exception e) {
return this.responseError(e);
}
}
private AuthResponse responseError(Exception e) {
int errorCode = ResponseStatus.FAILURE.getCode();
if (e instanceof AuthException) {
errorCode = ((AuthException) e).getErrorCode();
}
return AuthResponse.builder().code(errorCode).msg(e.getMessage()).build();
}
@Override
public String authorize() {
return AuthorizationFactory.getAuthorize(source).getAuthorizeUrl(config);
}
}
@@ -0,0 +1,87 @@
package me.zhyd.oauth.utils;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.config.AuthSource;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.AuthResponseStatus;
/**
* 授权配置类的校验器
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0
* @since 1.8
*/
public class AuthChecker {
/**
* 是否支持第三方登录
*
* @param config config
* @param source source
* @return true or false
*/
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) {
isSupported = StringUtils.isNotEmpty(config.getAlipayPublicKey());
}
if (isSupported && AuthSource.STACK_OVERFLOW == source) {
isSupported = StringUtils.isNotEmpty(config.getStackOverflowKey());
}
return isSupported;
}
/**
* 检查配置合法性。针对部分平台, 对redirect uri有特定要求。一般来说redirect uri都是http://,而对于facebook平台, redirect uri 必须是https的链接
*
* @param config config
* @param source source
*/
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);
}
// facebook的回调地址必须为https的链接
if (AuthSource.FACEBOOK == source && !GlobalAuthUtil.isHttpsProtocol(redirectUri)) {
throw new AuthException(AuthResponseStatus.ILLEGAL_REDIRECT_URI);
}
// 支付宝在创建回调地址时,不允许使用localhost或者127.0.0.1
if (AuthSource.ALIPAY == source && GlobalAuthUtil.isLocalHost(redirectUri)) {
throw new AuthException(AuthResponseStatus.ILLEGAL_REDIRECT_URI);
}
}
/**
* 校验回调传回的code
*
* @param code 回调时传回的code
*/
public static void checkCode(String code) {
if (StringUtils.isEmpty(code)) {
throw new AuthException(AuthResponseStatus.ILLEGAL_CODE);
}
}
/**
* 校验state的合法性防止被CSRF
*
* @param newState 新的state,一般为回调时传回的state(可能被篡改)
* @param originalState 原始的state,发起授权时向第三方平台传递的state
*/
public static void checkState(String newState, String originalState) {
// 如果原始state为空,表示当前平台未使用state
if (StringUtils.isEmpty(originalState)) {
return;
}
// 如果授权之前使用了state,但是回调时未返回state,则表示当前请求为非法的请求,可能正在被CSRF攻击
if (StringUtils.isEmpty(newState)) {
throw new AuthException(AuthResponseStatus.ILLEGAL_REQUEST);
}
// 如果授权前后的state不一致,则表示当前请求为非法的请求,新的state可能为伪造
if (!newState.equals(originalState)) {
throw new AuthException(AuthResponseStatus.ILLEGAL_REQUEST);
}
}
}
@@ -1,52 +0,0 @@
package me.zhyd.oauth.utils;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.AuthSource;
import me.zhyd.oauth.request.ResponseStatus;
/**
* 授权配置类的校验器
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0
* @since 1.8
*/
public class AuthConfigChecker {
/**
* 是否支持第三方登录
*
* @param config config
* @param source source
* @return true or false
*/
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) {
isSupported = StringUtils.isNotEmpty(config.getAlipayPublicKey());
}
return isSupported;
}
/**
* 检查配置合法性。针对部分平台, 对redirect uri有特定要求。一般来说redirect uri都是http://,而对于facebook平台, redirect uri 必须是https的链接
*
* @param config config
* @param source source
*/
public static void check(AuthConfig config, AuthSource source) {
String redirectUri = config.getRedirectUri();
if (!GlobalAuthUtil.isHttpProtocol(redirectUri) && !GlobalAuthUtil.isHttpsProtocol(redirectUri)) {
throw new AuthException(ResponseStatus.ILLEGAL_REDIRECT_URI);
}
// facebook的回调地址必须为https的链接
if (AuthSource.FACEBOOK == source && !GlobalAuthUtil.isHttpsProtocol(redirectUri)) {
throw new AuthException(ResponseStatus.ILLEGAL_REDIRECT_URI);
}
// 支付宝在创建回调地址时,不允许使用localhost或者127.0.0.1
if (AuthSource.ALIPAY == source && GlobalAuthUtil.isLocalHost(redirectUri)) {
throw new AuthException(ResponseStatus.ILLEGAL_REDIRECT_URI);
}
}
}
@@ -0,0 +1,185 @@
package me.zhyd.oauth.utils;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.RandomUtil;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import me.zhyd.oauth.config.AuthSource;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.AuthResponseStatus;
import java.nio.charset.Charset;
import java.util.concurrent.ConcurrentHashMap;
/**
* state工具,负责创建、获取和删除state
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0
* @since 1.8
*/
@Slf4j
public class AuthState {
/**
* 空字符串
*/
private static final String EMPTY_STR = "";
/**
* state存储器
*/
private static ConcurrentHashMap<String, String> stateBucket = new ConcurrentHashMap<>();
/**
* 生成随机的state
*
* @param source oauth平台
* @return state
*/
public static String create(AuthSource source) {
return create(source.name());
}
/**
* 生成随机的state
*
* @param source oauth平台
* @return state
*/
public static String create(String source) {
return create(source, RandomUtil.randomString(4));
}
/**
* 创建state
*
* @param source oauth平台
* @param body 希望加密到state的消息体
* @return state
*/
public static String create(String source, Object body) {
return create(source, JSON.toJSONString(body));
}
/**
* 创建state
*
* @param source oauth平台
* @param body 希望加密到state的消息体
* @return state
*/
public static String create(String source, String body) {
String currentIp = getCurrentIp();
String simpleKey = ((source + currentIp));
String key = Base64.encode(simpleKey.getBytes(Charset.forName("UTF-8")));
log.debug("Create the state: ip={}, platform={}, simpleKey={}, key={}, body={}", currentIp, source, simpleKey, key, body);
if (stateBucket.containsKey(key)) {
log.debug("Get from bucket: {}", stateBucket.get(key));
return stateBucket.get(key);
}
String simpleState = source + "_" + currentIp + "_" + body;
String state = Base64.encode(simpleState.getBytes(Charset.forName("UTF-8")));
log.debug("Create a new state: {}", state, simpleState);
stateBucket.put(key, state);
return state;
}
/**
* 获取state
*
* @param source oauth平台
* @return state
*/
public static String get(String source) {
String currentIp = getCurrentIp();
String simpleKey = ((source + currentIp));
String key = Base64.encode(simpleKey.getBytes(Charset.forName("UTF-8")));
log.debug("Get state by the key[{}], current ip[{}]", key, currentIp);
return stateBucket.get(key);
}
/**
* 获取state中保存的body内容
*
* @param source oauth平台
* @param state 加密后的state
* @param clazz body的实际类型
* @param <T> 需要转换的具体的class类型
* @return state
*/
public static <T> T getBody(String source, String state, Class<T> clazz) {
if (StringUtils.isEmpty(state) || null == clazz) {
return null;
}
log.debug("Get body from the state[{}] of the {} and convert it to {}", state, source, clazz.toString());
String currentIp = getCurrentIp();
String decodedState = Base64.decodeStr(state);
log.debug("The decoded state is [{}]", decodedState);
if (!decodedState.startsWith(source)) {
return null;
}
String noneSourceState = decodedState.substring(source.length() + 1);
if (!noneSourceState.startsWith(currentIp)) {
// ip不相同,可能为非法的请求
throw new AuthException(AuthResponseStatus.ILLEGAL_REQUEST);
}
String body = noneSourceState.substring(currentIp.length() + 1);
log.debug("body is [{}]", body);
if (clazz == String.class) {
return (T) body;
}
if (clazz == Integer.class) {
return (T) Integer.valueOf(Integer.parseInt(body));
}
if (clazz == Long.class) {
return (T) Long.valueOf(Long.parseLong(body));
}
if (clazz == Short.class) {
return (T) Short.valueOf(Short.parseShort(body));
}
if (clazz == Double.class) {
return (T) Double.valueOf(Double.parseDouble(body));
}
if (clazz == Float.class) {
return (T) Float.valueOf(Float.parseFloat(body));
}
if (clazz == Boolean.class) {
return (T) Boolean.valueOf(Boolean.parseBoolean(body));
}
if (clazz == Byte.class) {
return (T) Byte.valueOf(Byte.parseByte(body));
}
return JSON.parseObject(body, clazz);
}
/**
* 登录成功后,清除state
*
* @param source oauth平台
*/
public static void delete(String source) {
String currentIp = getCurrentIp();
String simpleKey = ((source + currentIp));
String key = Base64.encode(simpleKey.getBytes(Charset.forName("UTF-8")));
log.debug("Delete used state[{}] by the key[{}], current ip[{}]", stateBucket.get(key), key, currentIp);
stateBucket.remove(key);
}
/**
* 登录成功后,清除state
*
* @param source oauth平台
*/
public static void delete(AuthSource source) {
delete(source.name());
}
private static String getCurrentIp() {
String currentIp = IpUtils.getIp();
return StringUtils.isEmpty(currentIp) ? EMPTY_STR : currentIp;
}
}
@@ -1,6 +1,9 @@
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;
@@ -8,11 +11,11 @@ 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.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.*;
/**
* 全局的工具类
@@ -22,16 +25,12 @@ import java.util.Map;
* @since 1.8
*/
public class GlobalAuthUtil {
private static final String DEFAULT_ENCODING = "UTF-8";
private static final Charset DEFAULT_ENCODING = StandardCharsets.UTF_8;
private static final String ALGORITHM = "HmacSHA256";
public static String generateDingTalkSignature(String secretKey, String timestamp) {
try {
byte[] signData = sign(secretKey.getBytes(DEFAULT_ENCODING), timestamp.getBytes(DEFAULT_ENCODING));
return urlEncode(new String(Base64.encode(signData, false)));
} catch (UnsupportedEncodingException ex) {
throw new AuthException("Unsupported algorithm: " + DEFAULT_ENCODING, ex);
}
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) {
@@ -46,15 +45,14 @@ public class GlobalAuthUtil {
}
}
private static String urlEncode(String value) {
public static String urlEncode(String value) {
if (value == null) {
return "";
}
try {
String encoded = URLEncoder.encode(value, GlobalAuthUtil.DEFAULT_ENCODING);
return encoded.replace("+", "%20").replace("*", "%2A")
.replace("~", "%7E").replace("/", "%2F");
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);
}
@@ -65,7 +63,7 @@ public class GlobalAuthUtil {
return "";
}
try {
return URLDecoder.decode(value, GlobalAuthUtil.DEFAULT_ENCODING);
return URLDecoder.decode(value, GlobalAuthUtil.DEFAULT_ENCODING.displayName());
} catch (UnsupportedEncodingException e) {
throw new AuthException("Failed To Decode Uri", e);
}
@@ -85,6 +83,26 @@ public class GlobalAuthUtil {
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;
+46 -784
View File
@@ -1,817 +1,79 @@
package me.zhyd.oauth.utils;
import me.zhyd.oauth.consts.ApiUrl;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import lombok.Setter;
import java.text.MessageFormat;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* Url构建工具类
* <p>
* 构造URL
* </p>
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @author yangkai.shen (https://xkcoding.com)
* @version 1.0
* @since 1.0
* @since 1.8
*/
@Setter
public class UrlBuilder {
private static final String GITHUB_ACCESS_TOKEN_PATTERN = "{0}?client_id={1}&client_secret={2}&code={3}&redirect_uri={4}";
private static final String GITHUB_USER_INFO_PATTERN = "{0}?access_token={1}";
private static final String GITHUB_AUTHORIZE_PATTERN = "{0}?client_id={1}&state=1&redirect_uri={2}";
private final Map<String, Object> params = new LinkedHashMap<>(7);
private String baseUrl;
private static final String GOOGLE_AUTHORIZE_PATTERN = "{0}?client_id={1}&response_type=code&scope=openid%20email%20profile&redirect_uri={2}&state={3}";
private static final String GOOGLE_ACCESS_TOKEN_PATTERN = "{0}?client_id={1}&client_secret={2}&code={3}&redirect_uri={4}&grant_type=authorization_code";
private static final String GOOGLE_USER_INFO_PATTERN = "{0}?id_token={1}";
private UrlBuilder() {
private static final String WEIBO_ACCESS_TOKEN_PATTERN = "{0}?client_id={1}&client_secret={2}&grant_type=authorization_code&code={3}&redirect_uri={4}";
private static final String WEIBO_USER_INFO_PATTERN = "{0}?{1}";
private static final String WEIBO_AUTHORIZE_PATTERN = "{0}?client_id={1}&response_type=code&redirect_uri={2}";
private static final String GITEE_ACCESS_TOKEN_PATTERN = "{0}?client_id={1}&client_secret={2}&grant_type=authorization_code&code={3}&redirect_uri={4}";
private static final String GITEE_USER_INFO_PATTERN = "{0}?access_token={1}";
private static final String GITEE_AUTHORIZE_PATTERN = "{0}?client_id={1}&response_type=code&redirect_uri={2}";
private static final String DING_TALK_QRCONNECT_PATTERN = "{0}?appid={1}&response_type=code&scope=snsapi_login&state=STATE&redirect_uri={2}";
private static final String DING_TALK_USER_INFO_PATTERN = "{0}?signature={1}&timestamp={2}&accessKey={3}";
private static final String BAIDU_ACCESS_TOKEN_PATTERN = "{0}?client_id={1}&client_secret={2}&grant_type=authorization_code&code={3}&redirect_uri={4}";
private static final String BAIDU_USER_INFO_PATTERN = "{0}?access_token={1}";
private static final String BAIDU_AUTHORIZE_PATTERN = "{0}?client_id={1}&response_type=code&redirect_uri={2}&display=popup";
private static final String BAIDU_REVOKE_PATTERN = "{0}?access_token={1}";
private static final String CSDN_ACCESS_TOKEN_PATTERN = "{0}?client_id={1}&client_secret={2}&grant_type=authorization_code&code={3}&redirect_uri={4}";
private static final String CSDN_USER_INFO_PATTERN = "{0}?access_token={1}";
private static final String CSDN_AUTHORIZE_PATTERN = "{0}?client_id={1}&response_type=code&redirect_uri={2}";
private static final String CODING_ACCESS_TOKEN_PATTERN = "{0}?client_id={1}&client_secret={2}&grant_type=authorization_code&code={3}";
private static final String CODING_USER_INFO_PATTERN = "{0}?access_token={1}";
private static final String CODING_AUTHORIZE_PATTERN = "{0}?client_id={1}&response_type=code&redirect_uri={2}&scope=user";
private static final String TENCENT_ACCESS_TOKEN_PATTERN = "{0}?client_id={1}&client_secret={2}&grant_type=authorization_code&code={3}";
private static final String TENCENT_USER_INFO_PATTERN = "{0}?access_token={1}";
private static final String TENCENT_AUTHORIZE_PATTERN = "{0}?client_id={1}&response_type=code&redirect_uri={2}&scope=user";
private static final String OSCHINA_ACCESS_TOKEN_PATTERN = "{0}?client_id={1}&client_secret={2}&grant_type=authorization_code&code={3}&redirect_uri={4}&dataType=json";
private static final String OSCHINA_USER_INFO_PATTERN = "{0}?access_token={1}&dataType=json";
private static final String OSCHINA_AUTHORIZE_PATTERN = "{0}?client_id={1}&response_type=code&redirect_uri={2}";
private static final String ALIPAY_AUTHORIZE_PATTERN = "{0}?app_id={1}&scope=auth_user&redirect_uri={2}&state=init";
private static final String QQ_ACCESS_TOKEN_PATTERN = "{0}?client_id={1}&client_secret={2}&grant_type=authorization_code&code={3}&redirect_uri={4}";
private static final String QQ_USER_INFO_PATTERN = "{0}?oauth_consumer_key={1}&access_token={2}&openid={3}";
private static final String QQ_AUTHORIZE_PATTERN = "{0}?client_id={1}&response_type=code&redirect_uri={2}&state={3}";
private static final String QQ_OPENID_PATTERN = "{0}?access_token={1}&unionid=1";
private static final String WECHAT_AUTHORIZE_PATTERN = "{0}?appid={1}&redirect_uri={2}&response_type=code&scope=snsapi_login&state={3}#wechat_redirect";
private static final String WECHAT_ACCESS_TOKEN_PATTERN = "{0}?appid={1}&secret={2}&code={3}&grant_type=authorization_code";
private static final String WECHAT_REFRESH_TOKEN_PATTERN = "{0}?appid={1}&grant_type=refresh_token&refresh_token={2}";
private static final String WECHAT_USER_INFO_PATTERN = "{0}?access_token={1}&openid={2}&lang=zh_CN";
private static final String TAOBAO_AUTHORIZE_PATTERN = "{0}?response_type=code&client_id={1}&redirect_uri={2}&state={3}&view=web";
private static final String TAOBAO_ACCESS_TOKEN_PATTERN = "{0}?client_id={1}&client_secret={2}&code={3}&redirect_uri={4}&grant_type=authorization_code";
private static final String FACEBOOK_AUTHORIZE_PATTERN = "{0}?client_id={1}&redirect_uri={2}&state={3}&response_type=code&scope=";
private static final String FACEBOOK_ACCESS_TOKEN_PATTERN = "{0}?client_id={1}&client_secret={2}&code={3}&redirect_uri={4}&grant_type=authorization_code";
private static final String FACEBOOK_USER_INFO_PATTERN = "{0}?access_token={1}&fields=id,name,birthday,gender,hometown,email,devices,picture.width(400)";
private static final String DOUYIN_AUTHORIZE_PATTERN = "{0}?client_key={1}&redirect_uri={2}&state={3}&response_type=code&scope=user_info";
private static final String DOUYIN_ACCESS_TOKEN_PATTERN = "{0}?client_id={1}&client_secret={2}&code={3}&grant_type=authorization_code";
private static final String DOUYIN_USER_INFO_PATTERN = "{0}?access_token={1}&open_id={2}";
private static final String DOUYIN_REFRESH_TOKEN_PATTERN = "{0}?client_key={1}&refresh_token={2}&grant_type=refresh_token";
private static final String LINKEDIN_AUTHORIZE_PATTERN = "{0}?client_id={1}&redirect_uri={2}&state={3}&response_type=code&scope=r_liteprofile%20r_emailaddress%20w_member_social";
private static final String LINKEDIN_ACCESS_TOKEN_PATTERN = "{0}?client_id={1}&client_secret={2}&code={3}&redirect_uri={4}&grant_type=authorization_code";
private static final String LINKEDIN_USER_INFO_PATTERN = "{0}?projection=(id,firstName,lastName,profilePicture(displayImage~:playableStreams))";
private static final String LINKEDIN_REFRESH_TOKEN_PATTERN = "{0}?client_id={1}&client_secret={2}&refresh_token={3}&grant_type=refresh_token";
private static final String MICROSOFT_AUTHORIZE_PATTERN = "{0}?client_id={1}&response_type=code&redirect_uri={2}&response_mode=query&scope=offline_access%20user.read%20mail.read&state={3}";
private static final String MICROSOFT_ACCESS_TOKEN_PATTERN = "{0}?client_id={1}&client_secret={2}&scope=user.read%20mail.read&redirect_uri={3}&code={4}&grant_type=authorization_code";
private static final String MICROSOFT_USER_INFO_PATTERN = "{0}";
private static final String MICROSOFT_REFRESH_TOKEN_PATTERN = "{0}?client_id={1}&client_secret={2}&scope=user.read%20mail.read&redirect_uri={3}&refresh_token={4}&grant_type=refresh_token";
private static final String MI_AUTHORIZE_PATTERN = "{0}?client_id={1}&redirect_uri={2}&response_type=code&scope=user/profile%20user/openIdV2%20user/phoneAndEmail&state={3}&skip_confirm=false";
private static final String MI_ACCESS_TOKEN_PATTERN = "{0}?client_id={1}&client_secret={2}&redirect_uri={3}&code={4}&grant_type=authorization_code";
private static final String MI_USER_INFO_PATTERN = "{0}?clientId={1}&token={2}";
private static final String MI_REFRESH_TOKEN_PATTERN = "{0}?client_id={1}&client_secret={2}&redirect_uri={3}&refresh_token={4}&grant_type=refresh_token";
private static final String TOUTIAO_ACCESS_TOKEN_PATTERN = "{0}?client_key={1}&client_secret={2}&code={3}&grant_type=authorize_code";
private static final String TOUTIAO_USER_INFO_PATTERN = "{0}?client_key={1}&access_token={2}";
private static final String TOUTIAO_AUTHORIZE_PATTERN = "{0}?client_key={1}&redirect_uri={2}&state={3}&response_type=code&auth_only=1&display=0";
/**
* 获取githubtoken的接口地址
*
* @param clientId github 应用的Client ID
* @param clientSecret github 应用的Client Secret
* @param code github 授权前的code,用来换token
* @param redirectUri 待跳转的页面
* @return full url
*/
public static String getGithubAccessTokenUrl(String clientId, String clientSecret, String code, String redirectUri) {
return MessageFormat.format(GITHUB_ACCESS_TOKEN_PATTERN, ApiUrl.GITHUB.accessToken(), clientId, clientSecret, code, redirectUri);
}
/**
* 获取github用户详情的接口地址
*
* @param token github 应用的token
* @return full url
* @param baseUrl 基础路径
* @return the new {@code UrlBuilder}
*/
public static String getGithubUserInfoUrl(String token) {
return MessageFormat.format(GITHUB_USER_INFO_PATTERN, ApiUrl.GITHUB.userInfo(), token);
public static UrlBuilder fromBaseUrl(String baseUrl) {
UrlBuilder builder = new UrlBuilder();
builder.setBaseUrl(baseUrl);
return builder;
}
/**
* 获取github授权地址
* 添加参数
*
* @param clientId github 应用的Client ID
* @param redirectUrl github 应用授权成功后的回调地址
* @return full url
* @param key 参数名称
* @param value 参数值
* @return this UrlBuilder
*/
public static String getGithubAuthorizeUrl(String clientId, String redirectUrl) {
return MessageFormat.format(GITHUB_AUTHORIZE_PATTERN, ApiUrl.GITHUB.authorize(), clientId, redirectUrl);
public UrlBuilder queryParam(String key, Object value) {
Assert.notBlank(key, "参数名不能为空");
String valueAsString = (value != null ? value.toString() : null);
this.params.put(key, valueAsString);
return this;
}
/**
* 获取weibo token的接口地址
* 构造url
*
* @param clientId weibo 应用的App Key
* @param clientSecret weibo 应用的App Secret
* @param code weibo 授权前的code,用来换token
* @param redirectUri 待跳转的页面
* @return full url
* @return url
*/
public static String getWeiboAccessTokenUrl(String clientId, String clientSecret, String code, String redirectUri) {
return MessageFormat.format(WEIBO_ACCESS_TOKEN_PATTERN, ApiUrl.WEIBO.accessToken(), clientId, clientSecret, code, redirectUri);
public String build() {
return this.build(false);
}
/**
* 获取weibo用户详情的接口地址
* 构造url
*
* @param token weibo 应用的token
* @return full url
* @param encode 转码
* @return url
*/
public static String getWeiboUserInfoUrl(String token) {
return MessageFormat.format(WEIBO_USER_INFO_PATTERN, ApiUrl.WEIBO.userInfo(), token);
}
/**
* 获取weibo授权地址
*
* @param clientId weibo 应用的Client ID
* @param redirectUrl weibo 应用授权成功后的回调地址
* @return full url
*/
public static String getWeiboAuthorizeUrl(String clientId, String redirectUrl) {
return MessageFormat.format(WEIBO_AUTHORIZE_PATTERN, ApiUrl.WEIBO.authorize(), clientId, redirectUrl);
}
/**
* 获取gitee token的接口地址
*
* @param clientId gitee 应用的Client ID
* @param clientSecret gitee 应用的Client Secret
* @param code gitee 授权前的code,用来换token
* @param redirectUri 待跳转的页面
* @return full url
*/
public static String getGiteeAccessTokenUrl(String clientId, String clientSecret, String code, String redirectUri) {
return MessageFormat.format(GITEE_ACCESS_TOKEN_PATTERN, ApiUrl.GITEE.accessToken(), clientId, clientSecret, code, redirectUri);
}
/**
* 获取gitee用户详情的接口地址
*
* @param token gitee 应用的token
* @return full url
*/
public static String getGiteeUserInfoUrl(String token) {
return MessageFormat.format(GITEE_USER_INFO_PATTERN, ApiUrl.GITEE.userInfo(), token);
}
/**
* 获取gitee授权地址
*
* @param clientId gitee 应用的Client ID
* @param redirectUrl gitee 应用授权成功后的回调地址
* @return json
*/
public static String getGiteeAuthorizeUrl(String clientId, String redirectUrl) {
return MessageFormat.format(GITEE_AUTHORIZE_PATTERN, ApiUrl.GITEE.authorize(), clientId, redirectUrl);
}
/**
* 获取钉钉登录二维码的地址
*
* @param clientId 钉钉 应用的App Id
* @param redirectUrl 钉钉 应用授权成功后的回调地址
* @return full url
*/
public static String getDingTalkQrConnectUrl(String clientId, String redirectUrl) {
return MessageFormat.format(DING_TALK_QRCONNECT_PATTERN, ApiUrl.DINGTALK.authorize(), clientId, redirectUrl);
}
/**
* 获取钉钉用户信息的地址
*
* @param signature 通过appSecret计算出来的签名值,签名计算方法:https://open-doc.dingtalk.com/microapp/faquestions/hxs5v9
* @param timestamp 当前时间戳,单位是毫秒
* @param accessKey 钉钉 应用的App Id
* @return full url
*/
public static String getDingTalkUserInfoUrl(String signature, String timestamp, String accessKey) {
return MessageFormat.format(DING_TALK_USER_INFO_PATTERN, ApiUrl.DINGTALK.userInfo(), signature, timestamp, accessKey);
}
/**
* 获取baidu token的接口地址
*
* @param clientId baidu 应用的API Key
* @param clientSecret baidu 应用的Secret Key
* @param code baidu 授权前的code,用来换token
* @param redirectUri 待跳转的页面
* @return full url
*/
public static String getBaiduAccessTokenUrl(String clientId, String clientSecret, String code, String redirectUri) {
return MessageFormat.format(BAIDU_ACCESS_TOKEN_PATTERN, ApiUrl.BAIDU.accessToken(), clientId, clientSecret, code, redirectUri);
}
/**
* 获取baidu用户详情的接口地址
*
* @param token baidu 应用的token
* @return full url
*/
public static String getBaiduUserInfoUrl(String token) {
return MessageFormat.format(BAIDU_USER_INFO_PATTERN, ApiUrl.BAIDU.userInfo(), token);
}
/**
* 获取baidu授权地址
*
* @param clientId baidu 应用的API Key
* @param redirectUrl baidu 应用授权成功后的回调地址
* @return json
*/
public static String getBaiduAuthorizeUrl(String clientId, String redirectUrl) {
return MessageFormat.format(BAIDU_AUTHORIZE_PATTERN, ApiUrl.BAIDU.authorize(), clientId, redirectUrl);
}
/**
* 获取收回baidu授权的地址
*
* @param accessToken baidu 授权登录后的token
* @return json
*/
public static String getBaiduRevokeUrl(String accessToken) {
return MessageFormat.format(BAIDU_REVOKE_PATTERN, ApiUrl.BAIDU.revoke(), accessToken);
}
/**
* 获取csdn token的接口地址
*
* @param clientId csdn 应用的App Key
* @param clientSecret csdn 应用的App Secret
* @param code csdn 授权前的code,用来换token
* @param redirectUri 待跳转的页面
* @return full url
*/
public static String getCsdnAccessTokenUrl(String clientId, String clientSecret, String code, String redirectUri) {
return MessageFormat.format(CSDN_ACCESS_TOKEN_PATTERN, ApiUrl.CSDN.accessToken(), clientId, clientSecret, code, redirectUri);
}
/**
* 获取csdn用户详情的接口地址
*
* @param token csdn 应用的token
* @return full url
*/
public static String getCsdnUserInfoUrl(String token) {
return MessageFormat.format(CSDN_USER_INFO_PATTERN, ApiUrl.CSDN.userInfo(), token);
}
/**
* 获取csdn授权地址
*
* @param clientId csdn 应用的Client ID
* @param redirectUrl csdn 应用授权成功后的回调地址
* @return full url
*/
public static String getCsdnAuthorizeUrl(String clientId, String redirectUrl) {
return MessageFormat.format(CSDN_AUTHORIZE_PATTERN, ApiUrl.CSDN.authorize(), clientId, redirectUrl);
}
/**
* 获取coding token的接口地址
*
* @param clientId coding 应用的App Key
* @param clientSecret coding 应用的App Secret
* @param code coding 授权前的code,用来换token
* @return full url
*/
public static String getCodingAccessTokenUrl(String clientId, String clientSecret, String code) {
return MessageFormat.format(CODING_ACCESS_TOKEN_PATTERN, ApiUrl.CODING.accessToken(), clientId, clientSecret, code);
}
/**
* 获取coding用户详情的接口地址
*
* @param token coding 应用的token
* @return full url
*/
public static String getCodingUserInfoUrl(String token) {
return MessageFormat.format(CODING_USER_INFO_PATTERN, ApiUrl.CODING.userInfo(), token);
}
/**
* 获取coding授权地址
*
* @param clientId coding 应用的Client ID
* @param redirectUrl coding 应用授权成功后的回调地址
* @return full url
*/
public static String getCodingAuthorizeUrl(String clientId, String redirectUrl) {
return MessageFormat.format(CODING_AUTHORIZE_PATTERN, ApiUrl.CODING.authorize(), clientId, redirectUrl);
}
/**
* 获取腾讯云开发者平台 token的接口地址
*
* @param clientId coding 应用的App Key
* @param clientSecret coding 应用的App Secret
* @param code coding 授权前的code,用来换token
* @return full url
*/
public static String getTencentCloudAccessTokenUrl(String clientId, String clientSecret, String code) {
return MessageFormat.format(TENCENT_ACCESS_TOKEN_PATTERN, ApiUrl.TENCENTCLOUD.accessToken(), clientId, clientSecret, code);
}
/**
* 获取腾讯云开发者平台用户详情的接口地址
*
* @param token coding 应用的token
* @return full url
*/
public static String getTencentCloudUserInfoUrl(String token) {
return MessageFormat.format(TENCENT_USER_INFO_PATTERN, ApiUrl.TENCENTCLOUD.userInfo(), token);
}
/**
* 获取腾讯云开发者平台授权地址
*
* @param clientId coding 应用的Client ID
* @param redirectUrl coding 应用授权成功后的回调地址
* @return full url
*/
public static String getTencentCloudAuthorizeUrl(String clientId, String redirectUrl) {
return MessageFormat.format(TENCENT_AUTHORIZE_PATTERN, ApiUrl.TENCENTCLOUD.authorize(), clientId, redirectUrl);
}
/**
* 获取oschina token的接口地址
*
* @param clientId oschina 应用的App Key
* @param clientSecret oschina 应用的App Secret
* @param code oschina 授权前的code,用来换token
* @param redirectUri 待跳转的页面
* @return full url
*/
public static String getOschinaAccessTokenUrl(String clientId, String clientSecret, String code, String redirectUri) {
return MessageFormat.format(OSCHINA_ACCESS_TOKEN_PATTERN, ApiUrl.OSCHINA.accessToken(), clientId, clientSecret, code, redirectUri);
}
/**
* 获取oschina用户详情的接口地址
*
* @param token oschina 应用的token
* @return full url
*/
public static String getOschinaUserInfoUrl(String token) {
return MessageFormat.format(OSCHINA_USER_INFO_PATTERN, ApiUrl.OSCHINA.userInfo(), token);
}
/**
* 获取oschina授权地址
*
* @param clientId oschina 应用的Client ID
* @param redirectUrl oschina 应用授权成功后的回调地址
* @return full url
*/
public static String getOschinaAuthorizeUrl(String clientId, String redirectUrl) {
return MessageFormat.format(OSCHINA_AUTHORIZE_PATTERN, ApiUrl.OSCHINA.authorize(), clientId, redirectUrl);
}
/**
* 获取qq token的接口地址
*
* @param clientId qq 应用的App Key
* @param clientSecret qq 应用的App Secret
* @param code qq 授权前的code,用来换token
* @param redirectUri 待跳转的页面
* @return full url
*/
public static String getQqAccessTokenUrl(String clientId, String clientSecret, String code, String redirectUri) {
return MessageFormat.format(QQ_ACCESS_TOKEN_PATTERN, ApiUrl.QQ.accessToken(), clientId, clientSecret, code, redirectUri);
}
/**
* 获取qq用户详情的接口地址
*
* @param clientId qq 应用的clientId
* @param token qq 应用的token
* @param openId qq 应用的openId
* @return full url
*/
public static String getQqUserInfoUrl(String clientId, String token, String openId) {
return MessageFormat.format(QQ_USER_INFO_PATTERN, ApiUrl.QQ.userInfo(), clientId, token, openId);
}
/**
* 获取qq授权地址
*
* @param clientId qq 应用的Client ID
* @param redirectUrl qq 应用授权成功后的回调地址
* @return full url
*/
public static String getQqAuthorizeUrl(String clientId, String redirectUrl) {
return MessageFormat.format(QQ_AUTHORIZE_PATTERN, ApiUrl.QQ.authorize(), clientId, redirectUrl, System.currentTimeMillis());
}
/**
* 获取qq授权地址
*
* @param url 获取qqopenid的api接口地址
* @param token qq 应用授权的token
* @return full url
*/
public static String getQqOpenidUrl(String url, String token) {
return MessageFormat.format(QQ_OPENID_PATTERN, url, token);
}
/**
* 获取alipay授权地址
*
* @param clientId alipay 应用的Client ID
* @param redirectUrl alipay 应用授权成功后的回调地址
* @return full url
*/
public static String getAlipayAuthorizeUrl(String clientId, String redirectUrl) {
return MessageFormat.format(ALIPAY_AUTHORIZE_PATTERN, ApiUrl.ALIPAY.authorize(), clientId, redirectUrl);
}
/**
* 获取微信 授权地址
*
* @param clientId 微信 应用的appid
* @param redirectUrl 微信 应用授权成功后的回调地址
* @return full url
*/
public static String getWeChatAuthorizeUrl(String clientId, String redirectUrl) {
return MessageFormat.format(WECHAT_AUTHORIZE_PATTERN, ApiUrl.WECHAT.authorize(), clientId, redirectUrl, System.currentTimeMillis());
}
/**
* 获取微信 token的接口地址
*
* @param clientId 微信 应用的appid
* @param clientSecret 微信 应用的secret
* @param code 微信 授权前的code,用来换token
* @return full url
*/
public static String getWeChatAccessTokenUrl(String clientId, String clientSecret, String code) {
return MessageFormat.format(WECHAT_ACCESS_TOKEN_PATTERN, ApiUrl.WECHAT.accessToken(), clientId, clientSecret, code);
}
/**
* 获取微信 用户详情的接口地址
*
* @param token 微信 应用返回的 access token
* @param openId 微信 应用返回的openId
* @return full url
*/
public static String getWeChatUserInfoUrl(String token, String openId) {
return MessageFormat.format(WECHAT_USER_INFO_PATTERN, ApiUrl.WECHAT.userInfo(), token, openId);
}
/**
* 获取微信 刷新令牌 地址
*
* @param clientId 微信 应用的appid
* @param refreshToken 微信 应用返回的刷新token
* @return full url
*/
public static String getWeChatRefreshUrl(String clientId, String refreshToken) {
return MessageFormat.format(WECHAT_REFRESH_TOKEN_PATTERN, ApiUrl.WECHAT.refresh(), clientId, refreshToken);
}
/**
* 获取Taobao token的接口地址: 淘宝的授权登录,在这一步就会返回用户信息
*
* @param clientId taobao 应用的App Key
* @param clientSecret taobao 应用的App Secret
* @param code taobao 授权前的code,用来换token
* @param redirectUri 待跳转的页面
* @return full url
*/
public static String getTaobaoAccessTokenUrl(String clientId, String clientSecret, String code, String redirectUri) {
return MessageFormat.format(TAOBAO_ACCESS_TOKEN_PATTERN, ApiUrl.TAOBAO.accessToken(), clientId, clientSecret, code, redirectUri);
}
/**
* 获取Taobao授权地址
*
* @param clientId Taobao 应用的Client ID
* @param redirectUrl Taobao 应用授权成功后的回调地址
* @return full url
*/
public static String getTaobaoAuthorizeUrl(String clientId, String redirectUrl) {
return MessageFormat.format(TAOBAO_AUTHORIZE_PATTERN, ApiUrl.TAOBAO.authorize(), clientId, redirectUrl, System.currentTimeMillis());
}
/**
* 获取Google授权地址
*
* @param clientId google 应用的Client ID
* @param redirectUrl google 应用授权成功后的回调地址
* @return full url
*/
public static String getGoogleAuthorizeUrl(String clientId, String redirectUrl) {
return MessageFormat.format(GOOGLE_AUTHORIZE_PATTERN, ApiUrl.GOOGLE.authorize(), clientId, redirectUrl, System.currentTimeMillis());
}
/**
* 获取Google token的接口地址
*
* @param clientId google 应用的Client ID
* @param clientSecret google 应用的Client Secret
* @param code google 授权前的code,用来换token
* @param redirectUri 待跳转的页面
* @return full url
*/
public static String getGoogleAccessTokenUrl(String clientId, String clientSecret, String code, String redirectUri) {
return MessageFormat.format(GOOGLE_ACCESS_TOKEN_PATTERN, ApiUrl.GOOGLE.accessToken(), clientId, clientSecret, code, redirectUri);
}
/**
* 获取Google用户详情的接口地址
*
* @param token google 应用的token
* @return full url
*/
public static String getGoogleUserInfoUrl(String token) {
return MessageFormat.format(GOOGLE_USER_INFO_PATTERN, ApiUrl.GOOGLE.userInfo(), token);
}
/**
* 获取Facebook授权地址
*
* @param clientId Facebook 应用的Client ID
* @param redirectUrl Facebook 应用授权成功后的回调地址
* @return full url
*/
public static String getFacebookAuthorizeUrl(String clientId, String redirectUrl) {
return MessageFormat.format(FACEBOOK_AUTHORIZE_PATTERN, ApiUrl.FACEBOOK.authorize(), clientId, redirectUrl, System
.currentTimeMillis());
}
/**
* 获取Facebook token的接口地址
*
* @param clientId Facebook 应用的Client ID
* @param clientSecret Facebook 应用的Client Secret
* @param code Facebook 授权前的code,用来换token
* @param redirectUri 待跳转的页面
* @return full url
*/
public static String getFacebookAccessTokenUrl(String clientId, String clientSecret, String code, String redirectUri) {
return MessageFormat.format(FACEBOOK_ACCESS_TOKEN_PATTERN, ApiUrl.FACEBOOK.accessToken(), clientId, clientSecret, code, redirectUri);
}
/**
* 获取Facebook用户详情的接口地址
*
* @param token Facebook 应用的token
* @return full url
*/
public static String getFacebookUserInfoUrl(String token) {
return MessageFormat.format(FACEBOOK_USER_INFO_PATTERN, ApiUrl.FACEBOOK.userInfo(), token);
}
/**
* 获取Douyin授权地址
*
* @param clientId Douyin 应用的Client ID
* @param redirectUrl Douyin 应用授权成功后的回调地址
* @return full url
*/
public static String getDouyinAuthorizeUrl(String clientId, String redirectUrl) {
return MessageFormat.format(DOUYIN_AUTHORIZE_PATTERN, ApiUrl.DOUYIN.authorize(), clientId, redirectUrl, System.currentTimeMillis());
}
/**
* 获取Douyin token的接口地址
*
* @param clientId Douyin 应用的Client ID
* @param clientSecret Douyin 应用的Client Secret
* @param code Douyin 授权前的code,用来换token
* @return full url
*/
public static String getDouyinAccessTokenUrl(String clientId, String clientSecret, String code) {
return MessageFormat.format(DOUYIN_ACCESS_TOKEN_PATTERN, ApiUrl.DOUYIN.accessToken(), clientId, clientSecret, code);
}
/**
* 获取Douyin用户详情的接口地址
*
* @param token Douyin 应用的token
* @param openId 用户在当前应用的唯一标识 通过token接口获取
* @return full url
*/
public static String getDouyinUserInfoUrl(String token, String openId) {
return MessageFormat.format(DOUYIN_USER_INFO_PATTERN, ApiUrl.DOUYIN.userInfo(), token, openId);
}
/**
* 获取Douyin 刷新令牌 地址
*
* @param clientId Douyin 应用的client_key
* @param refreshToken Douyin 应用返回的refresh_token
* @return full url
*/
public static String getDouyinRefreshUrl(String clientId, String refreshToken) {
return MessageFormat.format(DOUYIN_REFRESH_TOKEN_PATTERN, ApiUrl.DOUYIN.refresh(), clientId, refreshToken);
}
/**
* 获取Linkedin授权地址
*
* @param clientId Linkedin 应用的Client ID
* @param redirectUrl Linkedin 应用授权成功后的回调地址
* @return full url
*/
public static String getLinkedinAuthorizeUrl(String clientId, String redirectUrl) {
return MessageFormat.format(LINKEDIN_AUTHORIZE_PATTERN, ApiUrl.LINKEDIN.authorize(), clientId, redirectUrl, System
.currentTimeMillis());
}
/**
* 获取Linkedin token的接口地址
*
* @param clientId Linkedin 应用的Client ID
* @param clientSecret Linkedin 应用的Client Secret
* @param code Linkedin 授权前的code,用来换token
* @param redirectUrl Linkedin 应用授权成功后的回调地址
* @return full url
*/
public static String getLinkedinAccessTokenUrl(String clientId, String clientSecret, String code, String redirectUrl) {
return MessageFormat.format(LINKEDIN_ACCESS_TOKEN_PATTERN, ApiUrl.LINKEDIN.accessToken(), clientId, clientSecret, code, redirectUrl);
}
/**
* 获取Linkedin用户详情的接口地址
*
* @return full url
*/
public static String getLinkedinUserInfoUrl() {
return MessageFormat.format(LINKEDIN_USER_INFO_PATTERN, ApiUrl.LINKEDIN.userInfo());
}
/**
* 获取Linkedin 刷新令牌 地址
*
* @param clientId Linkedin 应用的client_key
* @param clientSecret Linkedin 应用的Client Secret
* @param refreshToken Linkedin 应用返回的refresh_token
* @return full url
*/
public static String getLinkedinRefreshUrl(String clientId, String clientSecret, String refreshToken) {
return MessageFormat.format(LINKEDIN_REFRESH_TOKEN_PATTERN, ApiUrl.LINKEDIN.refresh(), clientId, clientSecret, refreshToken);
}
/**
* 获取微软授权地址
*
* @param clientId 微软 应用的Client ID
* @param redirectUrl 微软 应用授权成功后的回调地址
* @return full url
*/
public static String getMicrosoftAuthorizeUrl(String clientId, String redirectUrl) {
return MessageFormat.format(MICROSOFT_AUTHORIZE_PATTERN, ApiUrl.MICROSOFT.authorize(), clientId, redirectUrl, System
.currentTimeMillis());
}
/**
* 获取微软 token的接口地址
*
* @param clientId 微软 应用的Client ID
* @param clientSecret 微软 应用的Client Secret
* @param redirectUrl 微软 应用授权成功后的回调地址
* @param code 微软 授权前的code,用来换token
* @return full url
*/
public static String getMicrosoftAccessTokenUrl(String clientId, String clientSecret, String redirectUrl, String code) {
return MessageFormat.format(MICROSOFT_ACCESS_TOKEN_PATTERN, ApiUrl.MICROSOFT.accessToken(), clientId, clientSecret, redirectUrl, code);
}
/**
* 获取微软用户详情的接口地址
*
* @return full url
*/
public static String getMicrosoftUserInfoUrl() {
return MessageFormat.format(MICROSOFT_USER_INFO_PATTERN, ApiUrl.MICROSOFT.userInfo());
}
/**
* 获取微软 刷新令牌 地址
*
* @param clientId 微软 应用的client_key
* @param clientSecret 微软 应用的Client Secret
* @param redirectUrl 微软 应用授权成功后的回调地址
* @param refreshToken 微软 应用返回的refresh_token
* @return full url
*/
public static String getMicrosoftRefreshUrl(String clientId, String clientSecret, String redirectUrl, String refreshToken) {
return MessageFormat.format(MICROSOFT_REFRESH_TOKEN_PATTERN, ApiUrl.MICROSOFT.refresh(), clientId, clientSecret, redirectUrl, refreshToken);
}
/**
* 获取小米授权地址
*
* @param clientId 小米 应用的Client ID
* @param redirectUrl 小米 应用授权成功后的回调地址
* @return full url
*/
public static String getMiAuthorizeUrl(String clientId, String redirectUrl) {
return MessageFormat.format(MI_AUTHORIZE_PATTERN, ApiUrl.MI.authorize(), clientId, redirectUrl, System.currentTimeMillis());
}
/**
* 获取小米 token的接口地址
*
* @param clientId 小米 应用的Client ID
* @param clientSecret 小米 应用的Client Secret
* @param redirectUrl 小米 应用授权成功后的回调地址
* @param code 小米 授权前的code,用来换token
* @return full url
*/
public static String getMiAccessTokenUrl(String clientId, String clientSecret, String redirectUrl, String code) {
return MessageFormat.format(MI_ACCESS_TOKEN_PATTERN, ApiUrl.MI.accessToken(), clientId, clientSecret, redirectUrl, code);
}
/**
* 获取小米用户详情的接口地址
*
* @param clientId 小米 应用的client_key
* @param token token
* @return full url
*/
public static String getMiUserInfoUrl(String clientId, String token) {
return MessageFormat.format(MI_USER_INFO_PATTERN, ApiUrl.MI.userInfo(), clientId, token);
}
/**
* 获取小米 刷新令牌 地址
*
* @param clientId 小米 应用的client_key
* @param clientSecret 小米 应用的Client Secret
* @param redirectUrl 小米 应用授权成功后的回调地址
* @param refreshToken 小米 应用返回的refresh_token
* @return full url
*/
public static String getMiRefreshUrl(String clientId, String clientSecret, String redirectUrl, String refreshToken) {
return MessageFormat.format(MI_REFRESH_TOKEN_PATTERN, ApiUrl.MI.refresh(), clientId, clientSecret, redirectUrl, refreshToken);
}
/**
* 获取今日头条授权地址
*
* @param clientId 今日头条 应用的Client ID
* @param redirectUrl 今日头条 应用授权成功后的回调地址
* @return full url
*/
public static String getToutiaoAuthorizeUrl(String clientId, String redirectUrl) {
return MessageFormat.format(TOUTIAO_AUTHORIZE_PATTERN, ApiUrl.TOUTIAO.authorize(), clientId, redirectUrl, System.currentTimeMillis());
}
/**
* 获取今日头条 token的接口地址
*
* @param clientId 今日头条 应用的Client ID
* @param clientSecret 今日头条 应用的Client Secret
* @param code 今日头条 授权前的code,用来换token
* @return full url
*/
public static String getToutiaoAccessTokenUrl(String clientId, String clientSecret, String code) {
return MessageFormat.format(TOUTIAO_ACCESS_TOKEN_PATTERN, ApiUrl.TOUTIAO.accessToken(), clientId, clientSecret, code);
}
/**
* 获取今日头条用户详情的接口地址
*
* @param clientId 今日头条 应用的client_key
* @param token token
* @return full url
*/
public static String getToutiaoUserInfoUrl(String clientId, String token) {
return MessageFormat.format(TOUTIAO_USER_INFO_PATTERN, ApiUrl.TOUTIAO.userInfo(), clientId, token);
public String build(boolean encode) {
if (MapUtil.isEmpty(this.params)) {
return this.baseUrl;
}
String baseUrl = StrUtil.addSuffixIfNot(this.baseUrl, "?");
String paramString = GlobalAuthUtil.parseMapToString(this.params, encode);
return baseUrl + paramString;
}
}
+127 -42
View File
@@ -1,6 +1,7 @@
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;
@@ -18,11 +19,12 @@ public class AuthRequestTest {
.clientId("clientId")
.clientSecret("clientSecret")
.redirectUri("redirectUri")
.state("state")
.build());
// 返回授权页面,可自行调整
// 返回授权页面,可自行跳转
authRequest.authorize();
// 授权登录后会返回一个code,用这个code进行登录
authRequest.login("code");
// 授权登录后会返回code(auth_code(仅限支付宝))、state1.8.0版本后,可以用AuthCallback类作为回调接口的入参
authRequest.login(new AuthCallback());
}
@Test
@@ -31,11 +33,12 @@ public class AuthRequestTest {
.clientId("clientId")
.clientSecret("clientSecret")
.redirectUri("redirectUri")
.state("state")
.build());
// 返回授权页面,可自行调整
// 返回授权页面,可自行跳转
authRequest.authorize();
// 授权登录后会返回一个code,用这个code进行登录
authRequest.login("code");
// 授权登录后会返回code(auth_code(仅限支付宝))、state1.8.0版本后,可以用AuthCallback类作为回调接口的入参
authRequest.login(new AuthCallback());
}
@Test
@@ -45,10 +48,10 @@ public class AuthRequestTest {
.clientSecret("clientSecret")
.redirectUri("redirectUri")
.build());
// 返回授权页面,可自行调整
// 返回授权页面,可自行跳转
authRequest.authorize();
// 授权登录后会返回一个code,用这个code进行登录
authRequest.login("code");
// 授权登录后会返回code(auth_code(仅限支付宝))、state1.8.0版本后,可以用AuthCallback类作为回调接口的入参
authRequest.login(new AuthCallback());
}
@Test
@@ -57,11 +60,12 @@ public class AuthRequestTest {
.clientId("clientId")
.clientSecret("clientSecret")
.redirectUri("redirectUri")
.state("state")
.build());
// 返回授权页面,可自行调整
// 返回授权页面,可自行跳转
String url = authRequest.authorize();
// 授权登录后会返回一个code,用这个code进行登录
authRequest.login("code");
// 授权登录后会返回code(auth_code(仅限支付宝))、state1.8.0版本后,可以用AuthCallback类作为回调接口的入参
authRequest.login(new AuthCallback());
}
@Test
@@ -70,11 +74,12 @@ public class AuthRequestTest {
.clientId("clientId")
.clientSecret("clientSecret")
.redirectUri("redirectUri")
.state("state")
.build());
// 返回授权页面,可自行调整
// 返回授权页面,可自行跳转
String url = authRequest.authorize();
// 授权登录后会返回一个code,用这个code进行登录
authRequest.login("code");
// 授权登录后会返回code(auth_code(仅限支付宝))、state1.8.0版本后,可以用AuthCallback类作为回调接口的入参
authRequest.login(new AuthCallback());
}
@Test
@@ -83,11 +88,12 @@ public class AuthRequestTest {
.clientId("clientId")
.clientSecret("clientSecret")
.redirectUri("redirectUri")
.state("state")
.build());
// 返回授权页面,可自行调整
// 返回授权页面,可自行跳转
String url = authRequest.authorize();
// 授权登录后会返回一个code,用这个code进行登录
authRequest.login("code");
// 授权登录后会返回code(auth_code(仅限支付宝))、state1.8.0版本后,可以用AuthCallback类作为回调接口的入参
authRequest.login(new AuthCallback());
}
@Test
@@ -96,11 +102,12 @@ public class AuthRequestTest {
.clientId("clientId")
.clientSecret("clientSecret")
.redirectUri("redirectUri")
.state("state")
.build());
// 返回授权页面,可自行调整
// 返回授权页面,可自行跳转
String url = authRequest.authorize();
// 授权登录后会返回一个code,用这个code进行登录
authRequest.login("code");
// 授权登录后会返回code(auth_code(仅限支付宝))、state1.8.0版本后,可以用AuthCallback类作为回调接口的入参
authRequest.login(new AuthCallback());
}
@Test
@@ -109,11 +116,27 @@ public class AuthRequestTest {
.clientId("clientId")
.clientSecret("clientSecret")
.redirectUri("redirectUri")
.state("state")
.build());
// 返回授权页面,可自行调整
// 返回授权页面,可自行跳转
String url = authRequest.authorize();
// 授权登录后会返回一个code,用这个code进行登录
authRequest.login("code");
// 授权登录后会返回code(auth_code(仅限支付宝))、state1.8.0版本后,可以用AuthCallback类作为回调接口的入参
authRequest.login(new AuthCallback());
}
@Test
public void alipayTest() {
AuthRequest authRequest = new AuthAlipayRequest(AuthConfig.builder()
.clientId("clientId")
.clientSecret("clientSecret")
.redirectUri("redirectUri")
.alipayPublicKey("publicKey")
.state("state")
.build());
// 返回授权页面,可自行跳转
String url = authRequest.authorize();
// 授权登录后会返回code(auth_code(仅限支付宝))、state1.8.0版本后,可以用AuthCallback类作为回调接口的入参
AuthResponse login = authRequest.login(new AuthCallback());
}
@Test
@@ -122,11 +145,12 @@ public class AuthRequestTest {
.clientId("clientId")
.clientSecret("clientSecret")
.redirectUri("redirectUri")
.state("state")
.build());
// 返回授权页面,可自行调整
// 返回授权页面,可自行跳转
String url = authRequest.authorize();
// 授权登录后会返回一个code,用这个code进行登录
AuthResponse login = authRequest.login("code");
// 授权登录后会返回code(auth_code(仅限支付宝))、state1.8.0版本后,可以用AuthCallback类作为回调接口的入参
AuthResponse login = authRequest.login(new AuthCallback());
}
@Test
@@ -135,11 +159,26 @@ public class AuthRequestTest {
.clientId("clientId")
.clientSecret("clientSecret")
.redirectUri("redirectUri")
.state("state")
.build());
// 返回授权页面,可自行调整
// 返回授权页面,可自行跳转
String url = authRequest.authorize();
// 授权登录后会返回一个code,用这个code进行登录
AuthResponse login = authRequest.login("code");
// 授权登录后会返回code(auth_code(仅限支付宝))、state1.8.0版本后,可以用AuthCallback类作为回调接口的入参
AuthResponse login = authRequest.login(new AuthCallback());
}
@Test
public void taobaoTest() {
AuthRequest authRequest = new AuthTaobaoRequest(AuthConfig.builder()
.clientId("clientId")
.clientSecret("clientSecret")
.redirectUri("redirectUri")
.state("state")
.build());
// 返回授权页面,可自行跳转
String url = authRequest.authorize();
// 授权登录后会返回code(auth_code(仅限支付宝))、state1.8.0版本后,可以用AuthCallback类作为回调接口的入参
AuthResponse login = authRequest.login(new AuthCallback());
}
@Test
@@ -148,11 +187,12 @@ public class AuthRequestTest {
.clientId("clientId")
.clientSecret("clientSecret")
.redirectUri("redirectUri")
.state("state")
.build());
// 返回授权页面,可自行调整
// 返回授权页面,可自行跳转
String url = authRequest.authorize();
// 授权登录后会返回一个code,用这个code进行登录
AuthResponse login = authRequest.login("code");
// 授权登录后会返回code(auth_code(仅限支付宝))、state1.8.0版本后,可以用AuthCallback类作为回调接口的入参
AuthResponse login = authRequest.login(new AuthCallback());
}
@Test
@@ -161,11 +201,40 @@ public class AuthRequestTest {
.clientId("clientId")
.clientSecret("clientSecret")
.redirectUri("redirectUri")
.state("state")
.build());
// 返回授权页面,可自行调整
// 返回授权页面,可自行跳转
String url = authRequest.authorize();
// 授权登录后会返回一个code,用这个code进行登录
AuthResponse login = authRequest.login("code");
// 授权登录后会返回code(auth_code(仅限支付宝))、state1.8.0版本后,可以用AuthCallback类作为回调接口的入参
AuthResponse login = authRequest.login(new AuthCallback());
}
@Test
public void douyinTest() {
AuthRequest authRequest = new AuthDouyinRequest(AuthConfig.builder()
.clientId("clientId")
.clientSecret("clientSecret")
.redirectUri("redirectUri")
.state("state")
.build());
// 返回授权页面,可自行跳转
String url = authRequest.authorize();
// 授权登录后会返回code(auth_code(仅限支付宝))、state1.8.0版本后,可以用AuthCallback类作为回调接口的入参
AuthResponse login = authRequest.login(new AuthCallback());
}
@Test
public void linkedinTest() {
AuthRequest authRequest = new AuthLinkedinRequest(AuthConfig.builder()
.clientId("clientId")
.clientSecret("clientSecret")
.redirectUri("redirectUri")
.state("state")
.build());
// 返回授权页面,可自行跳转
String url = authRequest.authorize();
// 授权登录后会返回code(auth_code(仅限支付宝))、state1.8.0版本后,可以用AuthCallback类作为回调接口的入参
AuthResponse login = authRequest.login(new AuthCallback());
}
@Test
@@ -174,11 +243,12 @@ public class AuthRequestTest {
.clientId("clientId")
.clientSecret("clientSecret")
.redirectUri("redirectUri")
.state("state")
.build());
// 返回授权页面,可自行调整
// 返回授权页面,可自行跳转
String url = authRequest.authorize();
// 授权登录后会返回一个code,用这个code进行登录
AuthResponse login = authRequest.login("code");
// 授权登录后会返回code(auth_code(仅限支付宝))、state1.8.0版本后,可以用AuthCallback类作为回调接口的入参
AuthResponse login = authRequest.login(new AuthCallback());
}
@Test
@@ -187,10 +257,25 @@ public class AuthRequestTest {
.clientId("clientId")
.clientSecret("clientSecret")
.redirectUri("redirectUri")
.state("state")
.build());
// 返回授权页面,可自行调整
// 返回授权页面,可自行跳转
String url = authRequest.authorize();
// 授权登录后会返回一个code,用这个code进行登录
AuthResponse login = authRequest.login("code");
// 授权登录后会返回code(auth_code(仅限支付宝))、state1.8.0版本后,可以用AuthCallback类作为回调接口的入参
AuthResponse login = authRequest.login(new AuthCallback());
}
@Test
public void toutiaoTest() {
AuthRequest authRequest = new AuthToutiaoRequest(AuthConfig.builder()
.clientId("clientId")
.clientSecret("clientSecret")
.redirectUri("redirectUri")
.state("state")
.build());
// 返回授权页面,可自行跳转
String url = authRequest.authorize();
// 授权登录后会返回code(auth_code(仅限支付宝))、state1.8.0版本后,可以用AuthCallback类作为回调接口的入参
AuthResponse login = authRequest.login(new AuthCallback());
}
}
@@ -0,0 +1,231 @@
package me.zhyd.oauth.utils;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import me.zhyd.oauth.config.AuthConfig;
import org.junit.Assert;
import org.junit.Test;
import java.util.*;
public class AuthStateTest {
/**
* step1 生成state: 预期创建一个新的state...
* Z2l0aHViXzE5Mi4xNjguMTkuMV9yM3ll
*
* step2 重复生成state: 预期从bucket中返回一个可用的state...
* Z2l0aHViXzE5Mi4xNjguMTkuMV9yM3ll
*
* step3 获取state: 预期获取上面生成的state...
* Z2l0aHViXzE5Mi4xNjguMTkuMV9yM3ll
*
* step4 删除state: 预期删除掉上面创建的state...
*
* step5 重新获取state: 预期返回null...
* null
*/
@Test
public void usage() {
String source = "github";
System.out.println("\nstep1 生成state: 预期创建一个新的state...");
String state = AuthState.create(source);
System.out.println(state);
System.out.println("\nstep2 重复生成state: 预期从bucket中返回一个可用的state...");
String recreateState = AuthState.create(source);
System.out.println(recreateState);
Assert.assertEquals(state, recreateState);
System.out.println("\nstep3 获取state: 预期获取上面生成的state...");
String stateByBucket = AuthState.get(source);
System.out.println(stateByBucket);
Assert.assertEquals(state, stateByBucket);
System.out.println("\nstep4 删除state: 预期删除掉上面创建的state...");
AuthState.delete(source);
System.out.println("\nstep5 重新获取state: 预期返回null...");
String deletedState = AuthState.get(source);
System.out.println(deletedState);
Assert.assertNull(deletedState);
}
/**
* 通过随机字符串生成state...
* Z2l0aHViXzE5Mi4xNjguMTkuMV9wdnAy
*
* 通过传入自定义的字符串生成state...
* Z2l0aHViXzE5Mi4xNjguMTkuMV/ov5nmmK/kuIDkuKrlrZfnrKbkuLI=
*
* 通过传入数字生成state...
* Z2l0aHViXzE5Mi4xNjguMTkuMV8xMTE=
*
* 通过传入日期生成state...
* Z2l0aHViXzE5Mi4xNjguMTkuMV8xNTQ2MzE1OTMyMDAw
*
* 通过传入map生成state...
* Z2l0aHViXzE5Mi4xNjguMTkuMV97InVzZXJUb2tlbiI6Inh4eHh4IiwidXNlcklkIjoxfQ==
*
* 通过传入List生成state...
* Z2l0aHViXzE5Mi4xNjguMTkuMV9bInh4eHgiLCJ4eHh4eHh4eCJd
*
* 通过传入实体类生成state...
* Z2l0aHViXzE5Mi4xNjguMTkuMV97ImNsaWVudElkIjoieHh4eHgiLCJjbGllbnRTZWNyZXQiOiJ4eHh4eCIsInVuaW9uSWQiOmZhbHNlfQ==
*/
@Test
public void create() {
String source = "github";
System.out.println("\n通过随机字符串生成state...");
String state = AuthState.create(source);
System.out.println(state);
AuthState.delete(source);
System.out.println("\n通过传入自定义的字符串生成state...");
String stringBody = "这是一个字符串";
String stringState = AuthState.create(source, stringBody);
System.out.println(stringState);
AuthState.delete(source);
System.out.println("\n通过传入数字生成state...");
Integer numberBody = 111;
String numberState = AuthState.create(source, numberBody);
System.out.println(numberState);
AuthState.delete(source);
System.out.println("\n通过传入日期生成state...");
Date dateBody = DateUtil.parse("2019-01-01 12:12:12", DatePattern.NORM_DATETIME_PATTERN);
String dateState = AuthState.create(source, dateBody);
System.out.println(dateState);
AuthState.delete(source);
System.out.println("\n通过传入map生成state...");
Map<String, Object> mapBody = new HashMap<>();
mapBody.put("userId", 1);
mapBody.put("userToken", "xxxxx");
String mapState = AuthState.create(source, mapBody);
System.out.println(mapState);
AuthState.delete(source);
System.out.println("\n通过传入List生成state...");
List<String> listBody = new ArrayList<>();
listBody.add("xxxx");
listBody.add("xxxxxxxx");
String listState = AuthState.create(source, listBody);
System.out.println(listState);
AuthState.delete(source);
System.out.println("\n通过传入实体类生成state...");
AuthConfig entityBody = AuthConfig.builder()
.clientId("xxxxx")
.clientSecret("xxxxx")
.build();
String entityState = AuthState.create(source, entityBody);
System.out.println(entityState);
AuthState.delete(source);
}
/**
* 通过随机字符串生成state...
* Z2l0aHViXzE5Mi4xNjguMTkuMV9kaWNn
* dicg
*
* 通过传入自定义的字符串生成state...
* Z2l0aHViXzE5Mi4xNjguMTkuMV/ov5nmmK/kuIDkuKrlrZfnrKbkuLI=
* 这是一个字符串
*
* 通过传入数字生成state...
* Z2l0aHViXzE5Mi4xNjguMTkuMV8xMTE=
* 111
*
* 通过传入日期生成state...
* Z2l0aHViXzE5Mi4xNjguMTkuMV8xNTQ2MzE1OTMyMDAw
* Tue Jan 01 12:12:12 CST 2019
*
* 通过传入map生成state...
* Z2l0aHViXzE5Mi4xNjguMTkuMV97InVzZXJUb2tlbiI6Inh4eHh4IiwidXNlcklkIjoxfQ==
* {userToken=xxxxx, userId=1}
*
* 通过传入List生成state...
* Z2l0aHViXzE5Mi4xNjguMTkuMV9bInh4eHgiLCJ4eHh4eHh4eCJd
* [xxxx, xxxxxxxx]
*
* 通过传入实体类生成state...
* Z2l0aHViXzE5Mi4xNjguMTkuMV97ImNsaWVudElkIjoieHh4eHgiLCJjbGllbnRTZWNyZXQiOiJ4eHh4eCIsInVuaW9uSWQiOmZhbHNlfQ==
* me.zhyd.oauth.config.AuthConfig@725bef66
*/
@Test
public void getBody() {
String source = "github";
System.out.println("\n通过随机字符串生成state...");
String state = AuthState.create(source);
System.out.println(state);
String body = AuthState.getBody(source, state, String.class);
System.out.println(body);
AuthState.delete(source);
System.out.println("\n通过传入自定义的字符串生成state...");
String stringBody = "这是一个字符串";
String stringState = AuthState.create(source, stringBody);
System.out.println(stringState);
stringBody = AuthState.getBody(source, stringState, String.class);
System.out.println(stringBody);
AuthState.delete(source);
System.out.println("\n通过传入数字生成state...");
Integer numberBody = 111;
String numberState = AuthState.create(source, numberBody);
System.out.println(numberState);
numberBody = AuthState.getBody(source, numberState, Integer.class);
System.out.println(numberBody);
AuthState.delete(source);
System.out.println("\n通过传入日期生成state...");
Date dateBody = DateUtil.parse("2019-01-01 12:12:12", DatePattern.NORM_DATETIME_PATTERN);
String dateState = AuthState.create(source, dateBody);
System.out.println(dateState);
dateBody = AuthState.getBody(source, dateState, Date.class);
System.out.println(dateBody);
AuthState.delete(source);
System.out.println("\n通过传入map生成state...");
Map<String, Object> mapBody = new HashMap<>();
mapBody.put("userId", 1);
mapBody.put("userToken", "xxxxx");
String mapState = AuthState.create(source, mapBody);
System.out.println(mapState);
mapBody = AuthState.getBody(source, mapState, Map.class);
System.out.println(mapBody);
AuthState.delete(source);
System.out.println("\n通过传入List生成state...");
List<String> listBody = new ArrayList<>();
listBody.add("xxxx");
listBody.add("xxxxxxxx");
String listState = AuthState.create(source, listBody);
System.out.println(listState);
listBody = AuthState.getBody(source, listState, List.class);
System.out.println(listBody);
AuthState.delete(source);
System.out.println("\n通过传入实体类生成state...");
AuthConfig entityBody = AuthConfig.builder()
.clientId("xxxxx")
.clientSecret("xxxxx")
.build();
String entityState = AuthState.create(source, entityBody);
System.out.println(entityState);
entityBody = AuthState.getBody(source, entityState, AuthConfig.class);
System.out.println(entityBody);
AuthState.delete(source);
}
@Test
public void getErrorStateBody() {
String source = "github";
String state = "1111111111111111111111111111111";
String body = AuthState.getBody(source, state, String.class);
System.out.println(body);
AuthState.delete(source);
}
}
@@ -0,0 +1,90 @@
package me.zhyd.oauth.utils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
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)
* @version 1.0
* @website https://www.zhyd.me
* @date 2019/7/19 15:52
* @since 1.8
*/
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);
}
}
@@ -0,0 +1,61 @@
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,38 @@
package me.zhyd.oauth.utils;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.config.AuthSource;
import me.zhyd.oauth.request.AuthWeChatRequest;
import org.junit.Assert;
import org.junit.Test;
/**
* <p>
* UrlBuilder测试类
* </p>
*
* @author yangkai.shen (https://xkcoding.com)
* @date Created in 2019-07-18 16:36
*/
public class UrlBuilderTest {
@Test
public void testUrlBuilder() {
AuthConfig config = AuthConfig.builder()
.clientId("appid-110110110")
.clientSecret("secret-110110110")
.redirectUri("https://xkcoding.com")
.state(AuthState.create(AuthSource.WECHAT))
.build();
String build = UrlBuilder.fromBaseUrl(AuthSource.WECHAT.authorize())
.queryParam("appid", config.getClientId())
.queryParam("redirect_uri", config.getRedirectUri())
.queryParam("response_type", "code")
.queryParam("scope", "snsapi_login")
.queryParam("state", config.getState().concat("#wechat_redirect"))
.build(false);
AuthWeChatRequest request = new AuthWeChatRequest(config);
String authorize = request.authorize();
Assert.assertEquals(build, authorize);
AuthState.delete(AuthSource.WECHAT);
}
}
+60 -2
View File
@@ -1,10 +1,68 @@
### 2019/06/18
### 2019/07/19
1. 增加`stackoverflow`参数校验
2. 解决`Pinterest`获取用户失败的问题
3. 添加注释
### 2019/07/19
1. 合并github上[@dyc12ii](https://github.com/dyc12ii) 的[pr#25](https://github.com/zhangyd-c/JustAuth/pull/25),升级fastjson版本至1.2.58,避免安全漏洞
2. `AuthUserGender`枚举类挪到`enums`包下
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),修复小米回调错误问题 同时 支持微信获取
### 2019/07/15
1. 新增 `AuthState` 类,内置默认的state生成规则和校验规则
### 2019/07/12
1. 合并[Braavos96](https://github.com/Braavos96)提交的[PR#16](https://github.com/zhangyd-c/JustAuth/pull/16)
### 2019/06/28
1. 修复百度登录获取不到token失效时间的问题
2. 增加state参数校验,预防CSRF。**强烈建议启用state**
### 2019/06/27
1. 修改login方法的参数为AuthCallback,封装回调返回的参数
2. 支持state参数
3. 增加code和state参数校验
### 2019/06/25
qq授权登录时,需要获取`openId`作为`uuid`,在`1.6.1-beta``1.7.0`版本中,引入了`unionId`这一属性。获取`unionid`需要单独向qq团队**发送邮件**申请权限,鉴于这一申请权限的步骤比较麻烦(需要填写的内容比较多),所以在`AuthConfig`中增加了一个`unionId`属性,当为**true**时才会获取unionid,当为false时只获取openId。如果你需要该功能, 则在自行申请了相关权限后,将该属性置为true即可。关于unionId的参考链接:[UnionID介绍](http://wiki.connect.qq.com/unionid%E4%BB%8B%E7%BB%8D)
### 2019/06/19
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
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)
6. 合并[skqing](https://gitee.com/skqing)提交的[PR](https://gitee.com/yadong.zhang/JustAuth/pulls/2),解决一些BUG
### 2019/06/06
1. 增加今日头条的授权登陆