1
0
mirror of synced 2026-05-22 13:43:20 +00:00

Compare commits

...

219 Commits

Author SHA1 Message Date
yadong.zhang 8b3bc42678 Merge branch 'dev'
# Conflicts:
#	docs/references.md
2020-06-30 16:09:33 +08:00
yadong.zhang 164e12de1f 🔇 doc 文档迁移到 https://justauth.wiki 2020-06-30 16:08:31 +08:00
yadong.zhang 369388c11d 🎨 新增文档 + 增加阿里云登录刷新token的接口和支持忽略state校验的逻辑 2020-06-30 12:18:39 +08:00
yadong.zhang a1200b2510 📝 添加 微信企业版 授权登录的文档 2020-06-29 01:07:43 +08:00
yadong.zhang 6ea1d41211 📝 更新文档 2020-06-29 00:44:04 +08:00
yadong.zhang ddde4ef1f5 🔖 发布 1.15.6 2020-06-29 00:20:15 +08:00
yadong.zhang 26884f7218 📝 添加 Facebook 授权登录的文档 2020-06-28 22:43:39 +08:00
yadong.zhang 7f9c12d5bf 🔥 移除领英刷新token的接口。参考官网:To refresh an access token, go through the authorization process again to fetch a new token. 2020-06-28 22:10:38 +08:00
yadong.zhang fbeb4c68ab 👽 AuthConfig 增加忽略校验 state 的参数 2020-06-28 22:09:12 +08:00
yadong.zhang 179287f720 👽 增加阿里云授权登录中刷新授权token的接口 2020-06-28 22:08:31 +08:00
yadong.zhang 8a47735208 📝 补充使用者文档 2020-06-28 21:54:08 +08:00
yadong.zhang 0feb859f68 📝 增加 Google 授权登录的文档 2020-06-28 21:53:46 +08:00
yadong.zhang 441dda3970 📝 增加 领英 授权登录的文档 2020-06-28 17:39:09 +08:00
yadong.zhang b564ce8a89 📝 增加 开源中国 授权登录的文档 2020-06-28 16:22:04 +08:00
yadong.zhang 5e0065508a 📝 增加 dingtalk 授权登录的文档 2020-06-28 13:03:34 +08:00
yadong.zhang cf6f42b586 📝 增加 baidu 授权登录的文档 2020-06-28 12:22:13 +08:00
yadong.zhang 89b5fe4543 Merge branch 'master' of https://gitee.com/yadong.zhang/JustAuth 2020-06-24 17:48:29 +08:00
yadong.zhang 6f313fa91a 🔖 发布 v1.15.5 2020-06-24 17:47:24 +08:00
yadong.zhang 36bcebdd46 📝 编写文档 2020-06-10 19:06:16 +08:00
yadong.zhang 99b8680fd2 更新抖音的链接 2020-06-10 17:20:17 +08:00
yadong.zhang 2717d9cf7a 🐛 解决抖音登录获取用户地址异常的问题 2020-06-10 17:18:35 +08:00
yadong.zhang 166ddffaf4 📝 Writing docs. 2020-06-07 18:48:55 +08:00
yadong.zhang 33385fd30a 🍻 AuthUser 中新增 rawUserInfo,用来存放第三方平台返回的原始用户数据 2020-06-07 18:46:00 +08:00
yadong.zhang 8e5a679129 Merge branch 'dev' of https://github.com/justauth/JustAuth into dev 2020-06-07 16:50:13 +08:00
yadong.zhang 5ab2e87157 Merge pull request #81 from snippet0809/master
支持阿里云授权登录
2020-06-07 16:49:40 +08:00
yadong.zhang c34e226a54 🔀 合并 Gitee PR !15 2020-06-07 16:30:28 +08:00
yadong.zhang e4985c63f3 Merge branch 'dev' of https://gitee.com/yadong.zhang/JustAuth into dev 2020-06-07 16:28:41 +08:00
yadong.zhang e93ad07c5f 🔖 1.15.5-alpha 2020-06-07 16:28:27 +08:00
yadong.zhang 4ca1f46722 !15 修复人人网返回token中有竖线(|)没有进行URL Encode操作导致的url解析失败的bug
Merge pull request !15 from jingoo/dev
2020-06-07 16:23:25 +08:00
yadong.zhang 571c3438e4 📝 编写文档 2020-06-02 22:54:59 +08:00
yadong.zhang 2497519ec1 !17 fastjson 漏洞修复
Merge pull request !17 from harrylee/dev
2020-06-02 22:49:56 +08:00
yadong.zhang 278a435cdb 🔖 发布1.15.4-alpha(1.15.3-alpha不可用) 2020-06-02 22:44:29 +08:00
harrylee e75df92080 🔒 fastjson 漏洞修复 2020-06-01 10:18:20 +08:00
崔子健 9e6a4c61b0 修复人人网返回token中有竖线(|)没有进行URL Encode操作导致的url解析失败的bug 2020-05-28 14:48:41 +08:00
Spet d355699cc3 支持阿里云授权登录 2020-05-26 14:37:01 +08:00
yadong.zhang 28466f8ab5 📝 更新在线文档内容 2020-05-13 02:19:20 +08:00
yadong.zhang c6bd5d9e5d 🔖 发布 1.15.3-alpha 解决推特登录异常的BUG 2020-05-13 00:58:03 +08:00
yadong.zhang d0ae0f2c7a 🔖 临时发布 1.15.2-alpha 解决领英获取token发生异常的问题 2020-05-10 22:54:58 +08:00
yadong.zhang 521a423e26 🔖 临时发布 1.15.2-alpha 解决领英获取token发生异常的问题 2020-05-10 22:48:56 +08:00
yadong.zhang 1f1d61fe4e 🐛 修复 Gitee Issues#I1GPIB 2020-05-10 22:47:42 +08:00
yadong.zhang ec87ea145c 🐛 解决 领英 获取token 401的问题 2020-05-10 22:43:15 +08:00
yadong.zhang b1121ba545 📝 更新README 2020-05-02 21:21:11 +08:00
yadong.zhang 41d0d01329 📝 更新README 2020-05-02 21:20:45 +08:00
yadong.zhang 77603cf7db 📝 更新文档、facebook添加获取link属性 2020-04-17 23:51:59 +08:00
yadong.zhang 7be51f1d33 📝 更新文档 2020-04-10 17:37:20 +08:00
yadong.zhang e71af6b2e6 📝 更新文档 2020-04-10 17:35:04 +08:00
yadong.zhang 54779ab26a 📝 更新文档 2020-04-10 16:42:04 +08:00
yadong.zhang be1e23e2ab 📝 更新文档 2020-04-10 16:02:29 +08:00
yadong.zhang 739e7dfd39 🔖 发布一个版本, 1.15.1,1.15.0版有问题,请不要使用。抱歉 2020-04-10 15:53:14 +08:00
yadong.zhang 58c8aa9010 🔖 发布一个版本, 1.15.0 2020-04-10 15:46:29 +08:00
yadong.zhang 93aae33f0a 🔖 发布一个版本, 1.15.0 2020-04-10 15:45:27 +08:00
yadong.zhang 268a9b477b 🔖 发布一个版本, 1.15.0 2020-04-10 15:18:19 +08:00
yadong.zhang adaf9d59cd Merge pull request #69 from dengliming/master
Upgrade fastjson version
2020-04-10 15:16:01 +08:00
yadong.zhang 7f0f1e17d2 👽 等待simple-http发版, 解决使用apache-httpclient时的403bug 2020-04-09 22:58:34 +08:00
yadong.zhang 65b2db32b5 📝 编写文档 2020-04-08 01:06:56 +08:00
yadong.zhang 9e79dd7cb3 Merge branch 'master' into dev 2020-04-08 01:01:58 +08:00
yadong.zhang 30f29a49d1 📝 编写文档 2020-04-08 00:59:51 +08:00
yadong.zhang 129d1d57ed !14 【轻量级 PR】:update README.md.
Merge pull request !14 from shimingxy/N/A
2020-04-06 22:24:20 +08:00
shimingxy 3da90736c4 update README.md.
MaxKey使用JustAuth集成OAuth第三方认证
2020-04-06 22:05:01 +08:00
yadong.zhang 05891e48e9 公告 2020-04-05 00:29:55 +08:00
dengliming 9ef19b1d54 Upgrade fastjson version 2020-04-03 17:51:58 +08:00
yadong.zhang 2de19a3824 合并https://gitee.com/yadong.zhang/JustAuth/pulls/10,增加京东登录 2020-03-28 21:01:41 +08:00
yadong.zhang bccf3279af 完善单测 2020-03-28 21:00:28 +08:00
yadong.zhang 6b4a8b78b1 📝 Writing docs. 2020-03-21 13:08:11 +08:00
yadong.zhang 5a94ff82c1 🍻 gitter 2020-03-21 12:44:09 +08:00
yadong.zhang 7b03751a8d 📝 Writing docs. 2020-03-21 12:39:55 +08:00
yadong.zhang 823a43b1e2 🔧 修改pom 2020-03-18 01:33:31 +08:00
yadong.zhang db6e88173a 🔧 修改pom 2020-03-18 00:54:46 +08:00
yadong.zhang c6481a681e 修改测试用例 2020-03-17 23:27:36 +08:00
yadong.zhang 1e1038fbd4 修改测试用例,增加scope为test的http工具依赖 2020-03-17 23:26:12 +08:00
yadong.zhang 7cb0f8dbb6 修改测试用例 2020-03-17 23:18:07 +08:00
yadong.zhang 65c60a61b3 🔖 发布1.14.0,解耦HTTP工具 2020-03-17 22:57:53 +08:00
yadong.zhang d5a8ffcab2 Merge pull request #61 from githubeacon/master
增加飞书授权登录
2020-01-26 21:08:43 +08:00
yadong.zhang c95c118f88 Merge pull request #65 from runningzyp/dev
更新文档
2020-01-26 21:07:13 +08:00
YunpengZhan fec9a3c214 更新文档 2020-01-19 18:05:03 +08:00
yadong.zhang 6439058bda 📝 更新文档 2020-01-07 20:43:44 +08:00
beacon a1d31018d3 增加飞书授权登录 2019-12-30 21:16:48 +08:00
yadong.zhang 2a9d8e81a1 Merge branch 'master' into dev
# Conflicts:
#	pom.xml
2019-12-26 12:36:52 +08:00
yadong.zhang dda873ca11 Merge pull request #59 from xkcoding/extract-http
♻️ 抽取 HTTP,具体实现交给使用者,解耦 hutool-http
2019-12-26 12:35:52 +08:00
Yangkai.Shen 8dc4b5d7d6 ♻️ 抽取 HTTP,具体实现交给使用者,解耦 hutool-http 2019-12-25 18:28:18 +08:00
yadong.zhang 2038f24b3b Merge branch 'dev' 2019-12-24 19:37:33 +08:00
yadong.zhang f8f13fdb10 Merge branch 'dev' of https://gitee.com/yadong.zhang/JustAuth into dev 2019-12-24 19:36:41 +08:00
yadong.zhang 697c07cccc 📝 更新文档,加入发版日期 2019-12-24 19:35:39 +08:00
yadong.zhang e868d43ca3 🥚 添加实用工具,方便每次发版批量更新依赖版本 2019-12-24 19:34:39 +08:00
yadong.zhang 0700d7ee25 🔖 更新版本 2019-12-24 19:33:17 +08:00
yadong.zhang 822b4806a1 🍻 调整微信平台获取用户性别的代码逻辑,单独适配微信平台 2019-12-24 19:22:38 +08:00
yadong.zhang ab8c24ee5c 🍻 调整过获取用户性别的逻辑 2019-12-24 18:47:57 +08:00
yadong.zhang cd24cbdd58 Merge pull request #57 from xkcoding/wechat-mp
 集成微信公众号登录,修改之前的微信登录为微信开放平台登录
2019-12-24 11:00:18 +08:00
Yangkai.Shen 4bc9c09610 📝 修改文档,区分公众平台和开放平台 2019-12-23 13:54:27 +08:00
Yangkai.Shen 4d2518e21a 集成微信公众号登录,修改之前的微信登录为微信开放平台登录 2019-12-23 13:47:40 +08:00
yadong.zhang f47dc074e7 update docs/index.html. 2019-12-23 09:54:52 +08:00
yadong.zhang b7f01549c1 update docs/index.html. 2019-12-23 09:49:15 +08:00
yadong.zhang 264294caee 📝 添加微博授权登录的帮助文档 2019-12-22 20:03:22 +08:00
yadong.zhang 19ec565004 🍻 增加微博授权登录的revoke方法,支持手动回收授权 2019-12-22 20:02:26 +08:00
yadong.zhang 028924ecbf 📝 完善文档 2019-12-22 16:58:34 +08:00
yadong.zhang d2d3232501 📝 完善文档 2019-12-22 16:52:32 +08:00
yadong.zhang 1206847576 📝 添加Gittalk 2019-12-22 16:34:42 +08:00
yadong.zhang 2490c0f1ff 📝 自定义404页面 2019-12-22 16:34:14 +08:00
yadong.zhang 2fd7255ad4 📝 支付宝登录文档的图片 2019-12-22 14:56:45 +08:00
yadong.zhang f7873d71b2 📝 完善文档 2019-12-12 15:05:26 +08:00
yadong.zhang 602b1eaf58 📝 添加支付宝授权登录的帮助文档 2019-12-12 15:03:39 +08:00
yadong.zhang 7c2bc82814 🎨 优化 2019-12-12 14:47:57 +08:00
yadong.zhang 28b764f0d2 📝 添加qq授权登录的帮助文档 2019-12-12 11:48:27 +08:00
yadong.zhang 2442661291 📝 添加qq授权登录的帮助文档 2019-12-12 11:48:07 +08:00
yadong.zhang abb121a57c 📝 添加qq授权登录的帮助文档 2019-12-12 11:47:54 +08:00
yadong.zhang 51603e18ad 📝 添加微信授权登录的帮助文档 2019-12-12 11:30:54 +08:00
yadong.zhang 77c9680333 📝 编写文档 2019-12-10 17:21:09 +08:00
yadong.zhang 525c406c92 🐛 修复抖音登录取值取错层级的问题(issue#I15SIG@Gitee) 2019-12-10 17:15:19 +08:00
yadong.zhang e65d87daa3 Merge branch 'dev' of https://gitee.com/yadong.zhang/JustAuth into dev 2019-12-05 17:39:48 +08:00
yadong.zhang 8639359f53 ⬆️ 依赖升级 2019-12-05 17:39:08 +08:00
yadong.zhang 9eda5aaf2b !11 注解更新、包版本升级
Merge pull request !11 from 燃點/master
2019-12-05 17:34:34 +08:00
yadong.zhang 9d6ceb3898 💩 2019-12-05 17:20:31 +08:00
yadong.zhang 7bb9864799 !12 注解更新、包版本升级
Merge pull request !12 from 燃點/master
2019-11-15 17:56:36 +08:00
刘勋 95e4db2315 alipay-sdk-version 升级 4.8.10.ALL 2019-11-12 18:17:10 +08:00
刘勋 fc4606dad3 fastjson 升级到 1.2.62 2019-11-12 18:15:21 +08:00
刘勋 31330a366d hutool-http 升级到5.0.5 2019-11-12 18:14:51 +08:00
刘勋 7364c203ab 增加无参构造 2019-11-12 18:10:26 +08:00
yadong.zhang c730072f07 📝 编写文档 2019-11-12 12:15:02 +08:00
yadong.zhang 3c3e9b63d5 🐛 拿我祭天前,先让我升级一下版本 2019-11-12 12:06:19 +08:00
yadong.zhang 100dacac91 🐛 拿我祭天吧 2019-11-12 12:04:00 +08:00
yadong.zhang 41dee559df 🐛 拿我祭天吧 2019-11-12 12:02:06 +08:00
yadong.zhang b0769d09b3 Merge branch 'dev' of https://github.com/zhangyd-c/JustAuth into dev 2019-11-12 11:50:41 +08:00
yadong.zhang c77c74be3c Merge pull request #54 from jnan88/dev
fix#52 AuthCallback在spring中接收参数异常
2019-11-12 11:09:03 +08:00
qizai 66fdec3e2e fix#52 AuthCallback在spring中接收参数异常,需要有默认的构造方法,增加注解@AllArgsConstructor和@NoArgsConstructor 2019-11-08 12:06:21 +08:00
yadong.zhang c674457f2f 📝 补充文档 2019-11-01 22:56:31 +08:00
yadong.zhang 0a0f6a34e8 📝 补充文档 2019-11-01 22:32:35 +08:00
yadong.zhang 366a6ac886 Merge branch 'dev' 2019-11-01 22:29:15 +08:00
yadong.zhang 15d03fcd07 📝 补充文档 2019-11-01 22:27:53 +08:00
yadong.zhang c0b9010d84 google-site-verification 2019-10-31 11:29:30 +08:00
yadong.zhang 3cb7c0dd82 📝 补充文档 2019-10-26 17:46:25 +08:00
yadong.zhang a23610c8b8 📝 补充文档:自定义state缓存、自定义实现oauth等 2019-10-26 17:43:21 +08:00
yadong.zhang 4e0fe3ba33 📝 补充文档:自定义state缓存、自定义实现oauth等 2019-10-26 17:41:59 +08:00
yadong.zhang 9f341389d2 🍻 集成推特 2019-10-22 18:16:31 +08:00
yadong.zhang 43459c3192 Merge pull request #49 from pengisgood/dev
Integrate Twitter
2019-10-09 10:01:19 +08:00
Hongwei Peng de038b852d update doc 2019-09-30 10:19:53 +08:00
Hongwei Peng da8ec82e74 refactor 2019-09-30 08:22:25 +08:00
Hongwei Peng 5ed7e1563b integrate twitter login 2019-09-30 08:11:21 +08:00
yadong.zhang 3e9fdf7c35 !9 增加序列化标识 解决直接引用Model序列化失败问题
Merge pull request !9 from -苏若年-/master
2019-09-25 15:23:58 +08:00
-苏若年- 427c5b6d38 序列化 2019-09-25 13:05:05 +08:00
-苏若年- ea4beb71bc 序列化 2019-09-25 13:04:37 +08:00
-苏若年- 1334cd7378 序列化 2019-09-25 13:04:11 +08:00
-苏若年- 81eaca75ab 序列化 2019-09-25 13:03:41 +08:00
yadong.zhang 9e4815cde7 📝 修改demo链接 2019-09-23 09:16:21 +08:00
yadong.zhang 17630ea1bf 📝 修复文档错误 2019-09-22 01:31:18 +08:00
yadong.zhang b8240ac210 🍻 文档、删除AuthRequestTest 2019-09-20 19:33:52 +08:00
yadong.zhang 3d7d37b651 🍻 文档、注释 2019-09-17 19:28:46 +08:00
yadong.zhang 94550e406f 🍻 文档 2019-09-17 19:17:55 +08:00
yadong.zhang fdc78212ff 🍻 文档 2019-09-17 18:38:35 +08:00
yadong.zhang 51087658a4 🍻 完善饿了么授权登录 2019-09-11 19:46:16 +08:00
harrylee 469558bddc 集成京东 2019-09-11 15:06:50 +08:00
yadong.zhang 6567f17513 Merge branch 'master' into dev 2019-09-11 15:06:38 +08:00
yadong.zhang 6a3e061921 🍻 错别字 2019-09-11 11:18:35 +08:00
yadong.zhang 3a5a312b68 📝 编写文档 2019-09-11 11:16:31 +08:00
yadong.zhang 3ff0b36b49 Merge branch 'master' into dev 2019-09-11 09:20:10 +08:00
yadong.zhang faae8816be 🍻 添加Nutzboot版Demo 2019-09-11 09:10:41 +08:00
yadong.zhang 6cf6a7d7dc Merge branch 'master' of https://gitee.com/yadong.zhang/JustAuth 2019-09-11 09:07:04 +08:00
harrylee d9952968d1 📝 集成京东 2019-09-10 23:51:08 +08:00
yadong.zhang c424f9c9c0 !6 添加Nutzboot版本的Demo地址
Merge pull request !6 from 蛋蛋的忧伤/master
2019-09-10 20:17:55 +08:00
王庆华 726018031f add: 添加NutzBoot版本的Demo,修改README 2019-09-10 19:28:56 +08:00
yadong.zhang 80f2dbdad7 提取公共的Source接口,支持自定义扩展第三方平台的授权登录,具体扩展例子可参考AuthExtendRequest 2019-09-06 22:21:35 +08:00
yadong.zhang 02f9f833e6 提取公共的Source接口,支持自定义扩展第三方平台的授权登录,具体扩展例子可参考AuthExtendRequest 2019-09-06 22:19:02 +08:00
yadong.zhang 05374e7a1f 集成“饿了么”授权登录(未测试) 2019-09-06 19:22:49 +08:00
yadong.zhang 1eacc41959 📝 编写文档 2019-09-06 16:56:29 +08:00
yadong.zhang 66c7455d30 集成美团,待发布1.12.0 2019-09-06 16:54:08 +08:00
yadong.zhang 055807d75f !5 fix: Fastjson < 1.2.60 远程拒绝服务漏洞预警
Merge pull request !5 from harrylee/master
2019-09-06 13:18:52 +08:00
harrylee 9b4e149953 fix: Fastjson < 1.2.60 远程拒绝服务漏洞预警
link: https://card.weibo.com/article/m/show/id/2309404413257925394542
2019-09-06 11:27:55 +08:00
yadong.zhang 2e16556968 📝 编写文档 2019-09-04 09:44:14 +08:00
yadong.zhang c94b123a4b 集成Gitlab 2019-09-03 20:39:19 +08:00
yadong.zhang ee1b6c8c5f 集成酷家乐 2019-09-03 09:14:50 +08:00
yadong.zhang 2be048ef0e Merge pull request #45 from wuweiqi1993/kujialeAuth
Kujiale auth
2019-09-02 21:29:16 +08:00
shahuang 55fd335511 Merge branch 'kujialeAuth' of https://github.com/wuweiqi1993/JustAuth into kujialeAuth 2019-09-02 11:25:40 +08:00
shahuang bb975f8eb3 移除用户额外信息 2019-09-02 11:24:59 +08:00
wuweiqi1993 13a388fb0f Update README.md 2019-08-30 16:37:29 +08:00
wuweiqi1993 ff48e107c8 Update README.md 2019-08-30 16:36:21 +08:00
shahuang de273d0482 add kujiale auth 2019-08-30 16:28:43 +08:00
yadong.zhang 16f77ec770 📝 编写文档 2019-08-23 20:05:26 +08:00
yadong.zhang 10df9f05f3 🔖 AuthUser添加构造函数,支持反序列化。发布1.10.1 2019-08-17 16:37:15 +08:00
yadong.zhang fd183afd4a 📝 Writing docs. 2019-08-08 19:16:19 +08:00
yadong.zhang d48a62d5cb Merge branch 'master' into dev 2019-08-07 11:02:12 +08:00
yadong.zhang b466dd8de3 🍻 add package-info 2019-08-07 10:54:36 +08:00
yadong.zhang 4d8411e978 📝 编写文档 2019-08-06 18:26:50 +08:00
yadong.zhang cf77e4d0e0 🔀 合并分支 2019-08-06 18:14:35 +08:00
yadong.zhang 4f303705d9 格式化代码 2019-08-06 18:01:18 +08:00
yadong.zhang 1fb8be6c82 Merge branch 'master' of https://github.com/zhangyd-c/JustAuth 2019-08-06 17:49:35 +08:00
yadong.zhang 48a368b516 合并pr,增加单测以及集成企业微信 2019-08-06 17:46:51 +08:00
yadong.zhang 6594d90a71 Merge pull request #35 from xkcoding/wechat-enterprise
 企业微信完成
2019-08-06 17:22:49 +08:00
yadong.zhang 31c983234f Merge pull request #34 from Diffblue-benchmarks/add-tests-for-stringutils-2
Add tests for me.zhyd.oauth.utils.StringUtils
2019-08-06 17:14:41 +08:00
Yangkai.Shen 52682debfb 企业微信完成 2019-08-06 15:44:02 +08:00
yadong.zhang d5e161eaef 📝 集成华为 2019-08-06 07:41:35 +08:00
yadong.zhang 1ab1cc124a 📝 集成华为 2019-08-05 22:32:35 +08:00
Chris Smowton 050686d85f Add unit tests for me.zhyd.oauth.utils.StringUtils
These tests were written using Diffblue Cover.
2019-08-05 15:12:37 +01:00
Chris Smowton 05f95b86b4 Prevent illegal redirect URI error in testing
Redirect URIs are now verified to ensure they contain "http://" or "https://"
prefixes, so we must supply a plausible URI in testing. This allows us to get
a step further when running `mvn test`.
2019-08-05 15:12:37 +01:00
yadong.zhang 665daa37b2 📝 集成华为 2019-08-05 22:05:58 +08:00
yadong.zhang 576402eec3 📝 文档 2019-08-05 18:34:09 +08:00
yadong.zhang ec6c7a92b6 📝 删除根目录下的更新记录文档,挪到https://docs.justauth.whnb.wang中 2019-08-03 14:52:54 +08:00
yadong.zhang d44cbd6e2b 📝 帮助文档 2019-08-03 14:47:36 +08:00
yadong.zhang 15caef01d3 📝 帮助文档 2019-08-03 14:45:18 +08:00
yadong.zhang ad2e7c8231 📝 帮助文档 2019-08-03 14:41:11 +08:00
yadong.zhang 57a7b7ff17 📝 帮助文档 2019-08-03 14:35:51 +08:00
yadong.zhang 7a40159d15 📝 帮助文档 2019-08-03 14:28:31 +08:00
yadong.zhang bcb27e4118 📝 帮助文档 2019-08-03 14:28:14 +08:00
yadong.zhang 0b9cb95103 📝 帮助文档 2019-08-03 14:27:54 +08:00
yadong.zhang d61bb58b93 Merge branch 'master' into dev 2019-08-03 11:22:12 +08:00
yadong.zhang d034722232 📝 修改文档 2019-08-03 11:21:58 +08:00
yadong.zhang 197f15dc4f Merge branch 'master' of https://gitee.com/yadong.zhang/JustAuth 2019-08-03 10:08:35 +08:00
yadong.zhang fcb6fd9937 📝 Writing docs. 2019-08-03 10:07:56 +08:00
yadong.zhang ce5c437289 🍻 规范代码注释 2019-08-03 09:49:49 +08:00
yadong.zhang fabfff60c9 Merge pull request #32 from xkcoding/extract-cache
抽取缓存接口
2019-08-03 09:35:37 +08:00
yadong.zhang d4296d160e Merge branch 'dev' into extract-cache 2019-08-03 09:35:29 +08:00
yadong.zhang d9967b2814 💡 添加源码注释 2019-08-02 20:47:10 +08:00
yadong.zhang 267b74bed7 📝 增加缓存配置、去掉slf4j依赖、增加Log工具类等 2019-08-02 20:41:06 +08:00
Yangkai.Shen 3e8c475d3f ♻️ 添加 Request 的 AuthStateCache 构造器 2019-08-02 14:57:13 +08:00
Yangkai.Shen c1f9e96a92 修改 cache 测试类 2019-08-02 14:21:20 +08:00
Yangkai.Shen bed01eef6d ♻️ 抽取 state 校验方法 2019-08-02 14:19:42 +08:00
Yangkai.Shen 909702e4da 添加私服仓库,开发时方便发布测试 2019-08-02 11:22:46 +08:00
Yangkai.Shen 57cb7fb0d1 抽取 cache 接口,方便用户自行集成 cache 2019-08-02 11:15:37 +08:00
yadong.zhang 6474c46505 Merge branch 'master' into dev 2019-08-01 17:13:34 +08:00
yadong.zhang 92bc4ab34a 测试hutool版本 2019-07-31 14:49:04 +08:00
yadong.zhang 4fab7561a4 测试hutool版本 2019-07-31 11:04:48 +08:00
97 changed files with 5492 additions and 1961 deletions
+6 -7
View File
@@ -1,13 +1,12 @@
### 哪个平台?
为更快的帮您定位问题,推荐您用以下模板反馈问题:
### 1. 出现问题时,您做了哪些操作?
### 2. 在哪个步骤出现了问题?
### 重现步骤
### 报错信息
### 3. 您希望得到什么结果?
### 4. 您实际得到什么结果?
### 5. 请附上您出现问题的整屏截图或者整个异常堆栈信息
+23 -9
View File
@@ -1,15 +1,29 @@
### 该Pull Request关联的Issue
- [ ] 是否为解决Issue
### 修改描述
### 您做了哪些更新?
- 新增
- 修改
- 修复
- 其他
### 是否做了充分测试?
- [ ] 是,已经做过测试,并且测试通过
- [ ] 否,还没做测试,需要作者自测
注:测试demo可以使用:
- [simple版](https://github.com/justauth/JustAuth-demo)
- [jFinal版](https://github.com/xkcoding/jfinal-justauth-demo)
- [ActFramework版](https://github.com/xkcoding/act-justauth-demo)
- [Nutzboot版](https://github.com/EggsBlue/nutzboot-justauth-demo)
- [Blade版](https://github.com/justauth/blade-justauth-demo)
### 测试用例
### 修复效果的截屏
+8 -2
View File
@@ -25,6 +25,12 @@ hs_err_pid*
# exclude idea files
.idea
*.iml
*.sh
bin/codecov.sh
bin/deploy.sh
bin/docsify-cli.sh
bin/push.sh
bin/push-dev.sh
target
target
/pom.xml.versionsBackup
/gpg
+181
View File
@@ -0,0 +1,181 @@
<p align="center">
<a href="https://justauth.wiki"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/Justauth.png" width="400"></a>
</p>
<p align="center">
<strong>Login, so easy.</strong>
</p>
<p align="center">
<a target="_blank" href="https://search.maven.org/search?q=JustAuth">
<img src="https://img.shields.io/badge/Maven%20Central--1.15.6-blue" ></img>
</a>
<a target="_blank" href="https://gitee.com/yadong.zhang/JustAuth/blob/master/LICENSE">
<img src="https://img.shields.io/apm/l/vim-mode.svg?color=yellow" ></img>
</a>
<a target="_blank" href="https://www.oracle.com/technetwork/java/javase/downloads/index.html">
<img src="https://img.shields.io/badge/JDK-1.8+-green.svg" ></img>
</a>
<a target="_blank" href="https://apidoc.gitee.com/yadong.zhang/JustAuth/" title="API文档">
<img src="https://img.shields.io/badge/Api%20Docs--1.15.6-latest-orange" ></img>
</a>
<a target="_blank" href="https://justauth.wiki" title="参考文档">
<img src="https://img.shields.io/badge/Docs-latest-blueviolet.svg" ></img>
</a>
<a href="https://codecov.io/gh/zhangyd-c/JustAuth">
<img src="https://codecov.io/gh/zhangyd-c/JustAuth/branch/master/graph/badge.svg" />
</a>
<a href='https://gitee.com/yadong.zhang/JustAuth/stargazers'>
<img src='https://gitee.com/yadong.zhang/JustAuth/badge/star.svg?theme=white' alt='star'></img>
</a>
<a target="_blank" href='https://github.com/zhangyd-c/JustAuth'>
<img src="https://img.shields.io/github/stars/zhangyd-c/JustAuth.svg?style=social" alt="github star"></img>
</a>
</p>
<center>
<table>
<tr>
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/gitee.png" width="20"></td>
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/github.png" width="20"></td>
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/weibo.png" width="20"></td>
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/dingtalk.png" width="20"></td>
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/baidu.png" width="20"></td>
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/coding.png" width="20"></td>
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/tencentCloud.png" width="20"></td>
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/oschina.png" width="20"></td>
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/alipay.png" width="20"></td>
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/qq.png" width="20"></td>
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/wechat.png" width="20" title="微信开放平台"></td>
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/taobao.png" width="20"></td>
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/google.png" width="20"></td>
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/facebook.png" width="20"></td>
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/douyin.png" width="20"></td>
</tr>
</table>
<table>
<tr>
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/linkedin.png" width="20"></td>
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/microsoft.png" width="20"></td>
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/mi.png" width="20"></td>
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/toutiao.png" width="20"></td>
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/teambition.png" width="20"></td>
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/renren.png" width="20"></td>
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/pinterest.png" width="20"></td>
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/stackoverflow.png" width="20"></td>
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/huawei.png" width="20"></td>
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/wechat.png" width="20" title="微信企业版"></td>
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/csdn.png" width="20"></td>
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/kujiale.png" width="20"></td>
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/gitlab.png" width="20"></td>
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/meituan.png" width="20"></td>
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/eleme.png" width="20"></td>
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/twitter.png" width="20"></td>
</tr>
</table>
<center><a href="https://justauth.wiki/#/?id=%E5%B7%B2%E9%9B%86%E6%88%90%E7%9A%84%E5%B9%B3%E5%8F%B0" target="_blank">查看更多</a></center>
</center>
-------------------------------------------------------------------------------
`JustAuth`, as you see, It is just a Java library of third-party authorized login, It's smaller and easier to use. JustAuth is the best third-party login tool written in JAVA.
Source Code[gitee](https://gitee.com/yadong.zhang/JustAuth) | [github](https://github.com/zhangyd-c/JustAuth)
Docs[Reference Doc](https://justauth.wiki)
## Features
1. **Multiple platform**: Has integrated more than a dozen third-party platforms.([plan](https://gitee.com/yadong.zhang/JustAuth/issues/IUGRK))
2. **Minimalist**: The minimalist design is very simple to use.
## Quick start
- Add maven dependency
These artifacts are available from Maven Central:
```xml
<dependency>
<groupId>me.zhyd.oauth</groupId>
<artifactId>JustAuth</artifactId>
<version>1.15.6</version>
</dependency>
```
- Using JustAuth
```java
// Create authorization request
AuthRequest authRequest = new AuthGiteeRequest(AuthConfig.builder()
.clientId("clientId")
.clientSecret("clientSecret")
.redirectUri("redirectUri")
.build());
// Generate authorization url
authRequest.authorize("state");
// After authorization to login, it will return: code(auth_code(Alipay only)),state, After version 1.8.0, you can use the AuthCallback as a parameter to the callback interface
// Note: JustAuth saves state for 3 minutes by default. If it is not used within 3 minutes, the expired state will be cleared automatically.
authRequest.login(callback);
```
Note, that since [v1.14.0](https://gitee.com/yadong.zhang/JustAuth/releases/v1.14.0) JustAuth has been integrated by default with [simple-http](https://github.com/xkcoding/simple-http) as the HTTP general interface (see the update [JustAuth 1.14.0 release! Perfect decoupling of HTTP tools](https://mp.weixin.qq.com/s?__biz=MzA3NDk3OTIwMg==&mid=2450633197&idx=1&sn=11e625b307db62b2f1c4e82f7744b2a2&chksm=88929300bfe51a16562b45592a264482ae2c74c6dbfa4a3aa9611ad4fea4a9be5b1f0545527d&token=1093833287&lang=zh_CN#rd)). Since most projects already integrate HTTP tools such as OkHttp3, apache HttpClient, and hutool-http), in order to reduce unnecessary dependencies,Starting from [v1.14.0](https://gitee.com/yadong.zhang/JustAuth/releases/v1.14.0), JustAuth will not integrate hutool-http by default. If the developer's project is new or there is no integrated HTTP implementation tool in the project, please add the corresponding HTTP implementation class by yourself. Alternative dependencies are as follows:
- hutool-http
```xml
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-http</artifactId>
<version>5.2.5</version>
</dependency>
```
- httpclient
```xml
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.12</version>
</dependency>
```
- okhttp
```xml
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.4.1</version>
</dependency>
```
## Contributions
1. Fork this project to your repository
2. Clone the project after fork.
3. Modify the code (either to fix issue, or to add new features)
4. Commit and push code to a remote repository
5. Create a new PR (pull request), and select `dev` branch
6. Waiting for author to merge
I look forward to your joining us.
## Contributors
[contributors](https://justauth.wiki/#/contributors)
## Change Logs
[CHANGELOGS](https://justauth.wiki/#/update)
## Recommend
- `spring-boot-demo` In-depth study and actual combat of spring boot projects: [https://github.com/xkcoding/spring-boot-demo](https://github.com/xkcoding/spring-boot-demo)
- `mica` Efficient Development of scaffolding by Spring Cloud: [https://github.com/lets-mica/mica](https://github.com/lets-mica/mica)
- `pig` Cosmic strongest Micro Services Certified authorized scaffolding (essential for Architects): [https://gitee.com/log4j/pig](https://gitee.com/log4j/pig)
- `SpringBlade` Complete online solution (necessary for enterprise development): https://gitee.com/smallc/SpringBlade
## References
- [The OAuth 2.0 Authorization Framework](https://tools.ietf.org/html/rfc6749)
- [OAuth 2.0](https://oauth.net/2/)
+92 -86
View File
@@ -1,12 +1,12 @@
<p align="center">
<a href="https://www.justauth.cn/"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/logo.png" width="400"></a>
<a href="https://justauth.wiki"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/Justauth.png" width="400"></a>
</p>
<p align="center">
<strong>Login, so easy.</strong>
</p>
<p align="center">
<a target="_blank" href="https://search.maven.org/search?q=JustAuth">
<img src="https://img.shields.io/badge/Maven Central-1.9.5-blue.svg" ></img>
<img src="https://img.shields.io/badge/Maven%20Central--1.15.6-blue" ></img>
</a>
<a target="_blank" href="https://gitee.com/yadong.zhang/JustAuth/blob/master/LICENSE">
<img src="https://img.shields.io/apm/l/vim-mode.svg?color=yellow" ></img>
@@ -14,8 +14,11 @@
<a target="_blank" href="https://www.oracle.com/technetwork/java/javase/downloads/index.html">
<img src="https://img.shields.io/badge/JDK-1.8+-green.svg" ></img>
</a>
<a target="_blank" href="https://apidoc.gitee.com/yadong.zhang/JustAuth/">
<img src="https://img.shields.io/badge/Docs-1.9.5-orange.svg" ></img>
<a target="_blank" href="https://apidoc.gitee.com/yadong.zhang/JustAuth/" title="API文档">
<img src="https://img.shields.io/badge/Api%20Docs-1.15.6-latest-orange" ></img>
</a>
<a target="_blank" href="https://justauth.wiki" title="参考文档">
<img src="https://img.shields.io/badge/Docs-latest-blueviolet.svg" ></img>
</a>
<a href="https://codecov.io/gh/zhangyd-c/JustAuth">
<img src="https://codecov.io/gh/zhangyd-c/JustAuth/branch/master/graph/badge.svg" />
@@ -31,51 +34,59 @@
<center>
<table>
<tr>
<td align="center" width="200"><a href="#授权gitee"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/gitee.png" width="20"></a></td>
<td align="center" width="200"><a href="#授权github"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/github.png" width="20"></a></td>
<td align="center" width="200"><a href="#授权weibo"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/weibo.png" width="20"></a></td>
<td align="center" width="200"><a href="#授权钉钉"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/dingtalk.png" width="20"></a></td>
<td align="center" width="200"><a href="#授权百度"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/baidu.png" width="20"></a></td>
<td align="center" width="200"><a href="#授权coding"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/coding.png" width="20"></a></td>
<td align="center" width="200"><a href="#授权腾讯云开发者平台"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/tencentCloud.png" width="20"></a></td>
<td align="center" width="200"><a href="#授权oschina"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/oschina.png" width="20"></a></td>
<td align="center" width="200"><a href="#授权支付宝"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/alipay.png" width="20"></a></td>
<td align="center" width="200"><a href="#授权qq"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/qq.png" width="20"></a></td>
<td align="center" width="200"><a href="#授权微信"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/wechat.png" width="20"></a></td>
<td align="center" width="200"><a href="#授权淘宝"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/taobao.png" width="20"></a></td>
<td align="center" width="200"><a href="#授权google"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/google.png" width="20"></a></td>
<td align="center" width="200"><a href="#授权facebook"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/facebook.png" width="20"></a></td>
<td align="center" width="200"><a href="#授权抖音"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/douyin.png" width="20"></a></td>
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/gitee.png" width="20"></td>
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/github.png" width="20"></td>
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/weibo.png" width="20"></td>
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/dingtalk.png" width="20"></td>
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/baidu.png" width="20"></td>
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/coding.png" width="20"></td>
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/tencentCloud.png" width="20"></td>
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/oschina.png" width="20"></td>
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/alipay.png" width="20"></td>
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/qq.png" width="20"></td>
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/wechat.png" width="20" title="微信开放平台"></td>
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/taobao.png" width="20"></td>
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/google.png" width="20"></td>
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/facebook.png" width="20"></td>
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/douyin.png" width="20"></td>
</tr>
</table>
<table>
<tr>
<td align="center" width="200"><a href="#授权领英"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/linkedin.png" width="20"></a></td>
<td align="center" width="200"><a href="#授权微软"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/microsoft.png" width="20"></a></td>
<td align="center" width="200"><a href="#授权小米"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/mi.png" width="20"></a></td>
<td align="center" width="200"><a href="#授权今日头条"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/toutiao.png" width="20"></a></td>
<td align="center" width="200"><a href="#授权Teambition"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/teambition.png" width="20"></a></td>
<td align="center" width="200"><a href="#授权人人"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/renren.png" width="20"></a></td>
<td align="center" width="200"><a href="#授权Pinterest"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/pinterest.png" width="20"></a></td>
<td align="center" width="200"><a href="#授权Stack Overflow"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/stackoverflow.png" width="20"></a></td>
<td align="center" width="200"><a href="#授权csdn"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/csdn.png" width="20"></a></td>
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/linkedin.png" width="20"></td>
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/microsoft.png" width="20"></td>
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/mi.png" width="20"></td>
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/toutiao.png" width="20"></td>
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/teambition.png" width="20"></td>
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/renren.png" width="20"></td>
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/pinterest.png" width="20"></td>
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/stackoverflow.png" width="20"></td>
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/huawei.png" width="20"></td>
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/wechat.png" width="20" title="微信企业版"></td>
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/csdn.png" width="20"></td>
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/kujiale.png" width="20"></td>
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/gitlab.png" width="20"></td>
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/meituan.png" width="20"></td>
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/eleme.png" width="20"></td>
<td align="center" width="200"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/twitter.png" width="20"></td>
</tr>
</table>
<center><a href="https://justauth.wiki/#/?id=%E5%B7%B2%E9%9B%86%E6%88%90%E7%9A%84%E5%B9%B3%E5%8F%B0" target="_blank">查看更多</a></center>
</center>
-------------------------------------------------------------------------------
JustAuth,如你所见,它仅仅是一个**第三方授权登录**的**工具类库**,它可以让我们脱离繁琐的第三方登录SDK,让登录变得**So easy!**
项目开源地址:[gitee](https://gitee.com/yadong.zhang/JustAuth) | [github](https://github.com/zhangyd-c/JustAuth)
项目开源地址:[gitee](https://gitee.com/yadong.zhang/JustAuth) | [github](https://github.com/zhangyd-c/JustAuth)
项目文档:[参考文档](https://justauth.wiki)
## 特点
废话不多说,就俩字:
1. **全**:已集成十多家第三方平台(国内外常用的基本都已包含),后续依然还有扩展计划
1. **全**:已集成十多家第三方平台(国内外常用的基本都已包含),仍然还在持续扩展中([开发计划](https://gitee.com/yadong.zhang/JustAuth/issues/IUGRK)
2. **简**API就是奔着最简单去设计的(见后面`快速开始`),尽量让您用起来没有障碍感!
## 快速开始
@@ -85,7 +96,7 @@ JustAuth,如你所见,它仅仅是一个**第三方授权登录**的**工具
<dependency>
<groupId>me.zhyd.oauth</groupId>
<artifactId>JustAuth</artifactId>
<version>1.9.5</version>
<version>1.15.6</version>
</dependency>
```
- 调用api
@@ -97,60 +108,43 @@ AuthRequest authRequest = new AuthGiteeRequest(AuthConfig.builder()
.redirectUri("redirectUri")
.build());
// 生成授权页面
authRequest.authorize();
authRequest.authorize("state");
// 授权登录后会返回code(auth_code(仅限支付宝))、state1.8.0版本后,可以用AuthCallback类作为回调接口的参数
// 注:JustAuth默认保存state的时效为3分钟,3分钟内未使用则会自动清除过期的state
authRequest.login(callback);
```
**配套Demo**
- [Springboot版](https://gitee.com/yadong.zhang/JustAuth-demo)
- [jFinal版](https://github.com/xkcoding/jfinal-justauth-demo)
- [ActFramework版](https://github.com/xkcoding/act-justauth-demo)
如下**任选一种** HTTP 工具 依赖,_项目内如果已有,请忽略_
**扩展工具**
- hutool-http
- [justauth-spring-boot-starter](https://github.com/xkcoding/justauth-spring-boot-starter): Spring Boot 集成 JustAuth 的最佳实践
```xml
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-http</artifactId>
<version>5.2.5</version>
</dependency>
```
**配套SpringBoot starter**
- httpclient
[justauth-spring-boot-starter](https://github.com/xkcoding/justauth-spring-boot-starter)
```xml
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.12</version>
</dependency>
```
具体的例子可以参考:
- okhttp
- [实现Gitee授权登录](http://t.cn/ExDKxQs)
- [实现Github授权登录](http://t.cn/EJ0Fxqo)
- [Spring Boot 快速集成第三方登录功能](http://t.cn/AiWWx5kH)
#### API列表
| :computer: 平台 | :coffee: API类 | :page_facing_up: SDK |
|:------:|:-------:|:-------:|
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/gitee.png" width="20"> | [AuthGiteeRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthGiteeRequest.java) | <a href="https://gitee.com/api/v5/oauth_doc#list_1" target="_blank">参考文档</a> |
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/github.png" width="20"> | [AuthGithubRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthGiteeRequest.java) | <a href="https://developer.github.com/apps/building-oauth-apps/authorizing-oauth-apps/" target="_blank">参考文档</a> |
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/weibo.png" width="20"> | [AuthWeiboRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthGiteeRequest.java) | <a href="https://open.weibo.com/wiki/%E6%8E%88%E6%9D%83%E6%9C%BA%E5%88%B6%E8%AF%B4%E6%98%8E" target="_blank">参考文档</a> |
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/dingtalk.png" width="20"> | [AuthDingTalkRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthDingTalkRequest.java) | <a href="https://open-doc.dingtalk.com/microapp/serverapi2/kymkv6" target="_blank">参考文档</a> |
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/baidu.png" width="20"> | [AuthBaiduRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthBaiduRequest.java) | <a href="http://developer.baidu.com/wiki/index.php?title=docs/oauth" target="_blank">参考文档</a> |
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/coding.png" width="25"> | [AuthCodingRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthCodingRequest.java) | <a href="https://open.coding.net/references/oauth/" target="_blank">参考文档</a> |
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/tencentCloud.png" width="25"> | [AuthTencentCloudRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthTencentCloudRequest.java) | <a href="https://dev.tencent.com/help/doc/faq/b4e5b7aee786/oauth" target="_blank">参考文档</a> |
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/oschina.png" width="20"> | [AuthOschinaRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthOschinaRequest.java) | <a href="https://www.oschina.net/openapi/docs/oauth2_authorize" target="_blank">参考文档</a> |
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/alipay.png" width="20"> | [AuthAlipayRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthAlipayRequest.java) | <a href="https://alipay.open.taobao.com/docs/doc.htm?spm=a219a.7629140.0.0.336d4b70GUKXOl&treeId=193&articleId=105809&docType=1" target="_blank">参考文档</a> |
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/qq.png" width="20"> | [AuthQqRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthQqRequest.java) | <a href="https://wiki.connect.qq.com/%E4%BD%BF%E7%94%A8authorization_code%E8%8E%B7%E5%8F%96access_token" target="_blank">参考文档</a> |
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/wechat.png" width="20"> | [AuthWeChatRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthWeChatRequest.java) | <a href="https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1419316505&token=&lang=zh_CN" target="_blank">参考文档</a> |
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/taobao.png" width="20"> | [AuthTaobaoRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthTaobaoRequest.java) | <a href="https://open.taobao.com/doc.htm?spm=a219a.7386797.0.0.4e00669acnkQy6&source=search&docId=105590&docType=1" target="_blank">参考文档</a> |
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/google.png" width="20"> | [AuthGoogleRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthGoogleRequest.java) | <a href="https://developers.google.com/identity/protocols/OpenIDConnect" target="_blank">参考文档</a> |
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/facebook.png" width="20"> | [AuthFacebookRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthFacebookRequest.java) | <a href="https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow" target="_blank">参考文档</a> |
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/douyin.png" width="20"> | [AuthDouyinRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthDouyinRequest.java) | <a href="https://www.douyin.com/platform/doc/m-2-1-1" target="_blank">参考文档</a> |
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/linkedin.png" width="20"> | [AuthLinkedinRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthLinkedinRequest.java) | <a href="https://docs.microsoft.com/zh-cn/linkedin/shared/authentication/authorization-code-flow?context=linkedin/context" target="_blank">参考文档</a> |
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/microsoft.png" width="20"> | [AuthMicrosoftRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthMicrosoftRequest.java) | <a href="https://docs.microsoft.com/zh-cn/graph/auth/" target="_blank">参考文档</a> |
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/mi.png" width="20"> | [AuthMiRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthMiRequest.java) | <a href="https://dev.mi.com/console/doc/detail?pId=711" target="_blank">参考文档</a> |
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/toutiao.png" width="20"> | [AuthToutiaoRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthToutiaoRequest.java) | <a href="https://open.mp.toutiao.com/#/resource?_k=y7mfgk" target="_blank">参考文档</a> |
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/teambition.png" width="20"> | [AuthTeambitionRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthTeambitionRequest.java) | <a href="https://docs.teambition.com/" target="_blank">参考文档</a> |
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/renren.png" width="20"> | [AuthRenrenRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthRenrenRequest.java) | <a href="http://open.renren.com/wiki/OAuth2.0" target="_blank">参考文档</a> |
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/pinterest.png" width="20"> | [AuthPinterestRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthPinterestRequest.java) | <a href="https://developers.pinterest.com/docs/api/overview/?" target="_blank">参考文档</a> |
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/stackoverflow.png" width="20"> | [AuthStackOverflowRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthStackOverflowRequest.java) | <a href="https://api.stackexchange.com/docs/authentication" target="_blank">参考文档</a> |
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/csdn.png" width="20"> | [AuthCsdnRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthCsdnRequest.java) | 无 |
_请知悉:经咨询CSDN官方客服得知,CSDN的授权开放平台已经下线。如果以前申请过的应用,可以继续使用,但是不再支持申请新的应用。so, 本项目中的CSDN登录只能针对少部分用户使用了_
```xml
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.4.1</version>
</dependency>
```
## 后续开发计划
@@ -167,23 +161,36 @@ _请知悉:经咨询CSDN官方客服得知,CSDN的授权开放平台已经
5. 发起PRpull request 请求,提交到`dev`分支
6. 等待作者合并
## 贡献者名单
[contributors](https://justauth.wiki/#/contributors)
## 更新记录
[CHANGELOGS](https://justauth.wiki/#/update)
## 致谢
在项目立项初期,也对当前开源圈的一些相同类型的项目作过调研,同时本项目也参考过这些项目,再次感谢开源圈内的朋友。
[YurunOAuthLogin](https://gitee.com/yurunsoft/YurunOAuthLogin): PHP 第三方登录授权 SDK
- [YurunOAuthLogin](https://gitee.com/yurunsoft/YurunOAuthLogin): PHP 第三方登录授权 SDK
- [阿里妈妈MUX倾力打造的矢量图标库-iconfont](https://www.iconfont.cn/search/index): 本文档中的图标大部分取自该平台
- [mica](https://github.com/lets-mica/mica)Spring Cloud 微服务开发核心包,支持 `web `和 `webflux`。注:JustAuth项目中的[UuidUtils](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/utils/UuidUtils.java)就是直接使用的mica提供的高性能的uuid创建工具类源码[StringUtil.java](https://github.com/lets-mica/mica/blob/master/mica-core/src/main/java/net/dreamlu/mica/core/utils/StringUtil.java#L335)
- 感谢 JetBrains 提供的免费开源 License
<img src="https://images.gitee.com/uploads/images/2020/0406/220236_f5275c90_5531506.png" alt="图片引用自lets-mica" style="float:left;">
[阿里妈妈MUX倾力打造的矢量图标库-iconfont](https://www.iconfont.cn/search/index): 本文档中的图标大部分取自该平台
<a href="https://www.producthunt.com/posts/justauth?utm_source=badge-featured&utm_medium=badge&utm_souce=badge-justauth" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=196886&theme=dark" alt="JustAuth - Login, so easy! | Product Hunt Embed" style="width: 250px; height: 54px;" width="250px" height="54px" /></a>
[mica](https://github.com/lets-mica/mica)Spring Cloud 微服务开发核心包,支持 `web ``webflux`。注:JustAuth项目中的[UuidUtils](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/utils/UuidUtils.java)就是直接使用的mica提供的高性能的uuid创建工具类源码[StringUtil.java](https://github.com/lets-mica/mica/blob/master/mica-core/src/main/java/net/dreamlu/mica/core/utils/StringUtil.java#L335)
## 关于OAuth
[The OAuth 2.0 Authorization Framework](https://tools.ietf.org/html/rfc6749)
## 开源推荐
- `spring-boot-demo` 深度学习并实战 spring boot 的项目: [https://github.com/xkcoding/spring-boot-demo](https://github.com/xkcoding/spring-boot-demo)
- `mica` SpringBoot 微服务高效开发工具集: [https://github.com/lets-mica/mica](https://github.com/lets-mica/mica)
- `pig` 宇宙最强微服务认证授权脚手架(架构师必备): [https://gitee.com/log4j/pig](https://gitee.com/log4j/pig)
- `SpringBlade` 完整的线上解决方案(企业开发必备): [https://gitee.com/smallc/SpringBlade](https://gitee.com/smallc/SpringBlade)
- `MaxKey` 马克思的钥匙,寓意是最大钥匙,是用户单点登录认证系统(Sigle Sign On System,OAuth 2.0/OpenID Connect、SAML 2.0、JWT、CAS等标准化的开放协议,使用JustAuth集成OAuth第三方认证。: [https://shimingxy.github.io/MaxKey/](https://shimingxy.github.io/MaxKey/)
## 关注&交流
| 公众号 | 微信(备注:加群) |
| 公众号 | 微信(备注:JustAuth) |
| :------------: | :------------: |
| <img src="https://gitee.com/yadong.zhang/static/raw/master/wx/wechat_account.jpg" width="200" /> | <img src="https://gitee.com/yadong.zhang/static/raw/master/wx/wx.png" width="170"/> |
@@ -191,11 +198,10 @@ _请知悉:经咨询CSDN官方客服得知,CSDN的授权开放平台已经
- JustAuth交流群 230017570):专业交流该项目
- 开源总群 (190886500):各个开源项目的都有,也有博客建设等方面的朋友。(注意,该群需付费进入,防止发垃圾广告、垃圾推广等人士)
## 请喝咖啡
| 支付宝 | 微信 |
| :------------: | :------------: |
| <img src="https://gitee.com/yadong.zhang/static/raw/master/qrcode/zfb_code.png" width="200"/> | <img src="https://gitee.com/yadong.zhang/static/raw/master/qrcode/wx_code.png" width="200" /> |
通过“[爱发电](https://afdian.net/@zhangyadong)”赞助,感谢您的支持
+35
View File
@@ -0,0 +1,35 @@
#!/bin/bash
#-----------------------------------------------------------
# 参考自 hutool 工具
# 此脚本用于每次升级justauth时替换相应位置的版本号
#-----------------------------------------------------------
pwd=$(pwd)
echo "当前路径:$pwd"
if [ -n "$1" ];then
new_version="$1"
old_version=`cat $pwd/bin/version.txt`
echo "$old_version 替换为新版本 $new_version"
else
# 参数错误,退出
echo "ERROR: 请指定新版本!"
exit
fi
if [ ! -n "$old_version" ]; then
echo "ERROR: 旧版本不存在,请确认bin/version.txt中信息正确"
exit
fi
# 替换README.md等文件中的版本
sed -i "s/${old_version}/${new_version}/g" $pwd/README.md
sed -i "s/${old_version}/${new_version}/g" $pwd/README.en-US.md
sed -i "s/${old_version}/${new_version}/g" $pwd/docs/README.md
sed -i "s/${old_version}/${new_version}/g" $pwd/docs/_coverpage.md
# 替换pom.xml中的版本
sed -i "s/${old_version}/${new_version}/g" $pwd/pom.xml
# 保留新版本号
echo "$new_version" > $pwd/bin/version.txt
+19
View File
@@ -0,0 +1,19 @@
#!/bin/bash
#------------------------------------------------
# 参考自 hutool 工具
# 升级justauth版本,包括:
# 1. 升级pom.xml中的版本号
# 2. 替换README.md中的版本号
#------------------------------------------------
if [ ! -n "$1" ]; then
echo "ERROR: 新版本不存在,请指定参数1"
exit
fi
# 替换所有模块pom.xml中的版本
mvn versions:set -DnewVersion=$1
# 替换其它地方的版本
source $(pwd)/bin/repVersion.sh "$1"
+1
View File
@@ -0,0 +1 @@
1.15.6
-7
View File
@@ -1,7 +0,0 @@
# 项目贡献者名单
- <img src="https://avatar.gitee.com/uploads/99/784199_yadong.zhang.png!avatar100?1462325358" width="20"> · yadong.zhang : <a href="https://github.com/zhangyd-c" target="_blank">[Github]</a> | <a href="https://gitee.com/yadong.zhang" target="_blank">[Gitee]</a> | <a href="https://www.zhyd.me" target="_blank">[个人网站]</a>
- <img src="https://avatars0.githubusercontent.com/u/10429917?s=460&v=4" width="20"> · yangkai.shen : <a href="https://github.com/xkcoding" target="_blank">[Github]</a> | <a href="https://xkcoding.com" target="_blank">[个人网站]</a>
- <img src="https://avatar.gitee.com/uploads/51/1651_dolphinboy.png!avatar100?1479346570" width="20"> · skqing : <a href="https://gitee.com/skqing" target="_blank">[Gitee]</a> | <a href="https://my.oschina.net/dolphinboy" target="_blank">[个人网站]</a>
- <img src="https://avatars2.githubusercontent.com/u/2988765?s=115&v=4" width="20"> · pengisgood : <a href="https://github.com/pengisgood" target="_blank">[Github]</a> | <a href="https://pengisgood.github.io" target="_blank">[个人网站]</a>
- 千年等一回,我只为等你...
View File
+22
View File
@@ -0,0 +1,22 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta name="google-site-verification" content="DJSx-fAGWci-EWueWy2_RfafHJX1iv5XhfftGSg-SR0" />
<meta charset="UTF-8">
<title>JustAuth - 史上最全的整合第三方登录的开源库</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta name="description" content="JustAuth,史上最全的整合第三方登录的开源库" />
<meta name="keywords" content="JustAuth,第三方授权登录,OAuth" />
<meta itemprop="name" content="JustAuth,史上最全的整合第三方登录的开源库" />
<meta itemprop="description" content="JustAuth,如你所见,它仅仅是一个第三方授权登录的工具类库,它可以让我们脱离繁琐的第三方登录SDK,让登录变得So easy!" />
</head>
<body>
<script>
window.location.href = "https://justauth.wiki";
</script>
</body>
</html>
+1 -1
View File
@@ -98,7 +98,7 @@ _注:非全部平台,部分平台可能不存在图例_
#### 授权Twitter
暂无
![授权Twitter]( "授权Twitter")
#### 授权csdn
+20
View File
@@ -0,0 +1,20 @@
#!/bin/bash
# 参考自 hutool 工具
help(){
echo "--------------------------------------------------------------------------"
echo ""
echo "usage: ./ja.sh [updv]"
echo ""
echo "-updv [version num] Update all justauth related versions."
echo ""
echo "--------------------------------------------------------------------------"
}
case "$1" in
'updv')
bin/updVersion.sh $2
;;
*)
help
esac
+101 -74
View File
@@ -6,12 +6,14 @@
<groupId>me.zhyd.oauth</groupId>
<artifactId>JustAuth</artifactId>
<version>1.9.5</version>
<version>1.15.6</version>
<name>JustAuth</name>
<url>https://gitee.com/yadong.zhang/JustAuth</url>
<description>
史上最全的整合第三方登录的工具,目前已支持Github、Gitee、微博、钉钉、百度、Coding、腾讯云开发者平台、OSChina、支付宝、QQ、微信、淘宝、Google、Facebook、抖音、领英、小米、微软和今日头条等第三方平台的授权登录。
史上最全的整合第三方登录的开源库。目前已支持Github、Gitee、微博、钉钉、百度、Coding、腾讯云开发者平台、OSChina、支付宝、
QQ、微信开放平台、微信公众平台、淘宝、Google、Facebook、抖音、领英、小米、微软、今日头条、Teambition、StackOverflow、Pinterest、人人、华为、
企业微信、酷家乐、Gitlab、美团、饿了么和推特等第三方平台的授权登录。
Login, so easy!
</description>
@@ -58,12 +60,11 @@
<maven-surefire-version>2.20</maven-surefire-version>
<maven-gpg-version>1.6</maven-gpg-version>
<maven.test.skip>false</maven.test.skip>
<hutool-version>4.6.1</hutool-version>
<lombok-version>1.18.4</lombok-version>
<simple-http.version>1.0.2</simple-http.version>
<lombok-version>1.18.10</lombok-version>
<junit-version>4.11</junit-version>
<fastjson-version>1.2.58</fastjson-version>
<alipay-sdk-version>3.7.4.ALL</alipay-sdk-version>
<slf4j-version>1.7.25</slf4j-version>
<fastjson-version>1.2.71</fastjson-version>
<alipay-sdk-version>4.8.10.ALL</alipay-sdk-version>
<jacoco-version>0.8.2</jacoco-version>
</properties>
@@ -75,9 +76,9 @@
<optional>true</optional>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-http</artifactId>
<version>${hutool-version}</version>
<groupId>com.xkcoding.http</groupId>
<artifactId>simple-http</artifactId>
<version>${simple-http.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
@@ -96,11 +97,6 @@
<version>${alipay-sdk-version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>${slf4j-version}</version>
</dependency>
</dependencies>
<build>
@@ -165,65 +161,65 @@
<id>release</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>${maven-source.version}</version>
<inherited>true</inherited>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>cobertura-maven-plugin</artifactId>
<version>${cobertura-version}</version>
<configuration>
<formats>
<format>html</format>
<format>xml</format>
</formats>
<check/>
</configuration>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>${jacoco-version}</version>
<executions>
<execution>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>report</id>
<phase>test</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>${maven-gpg-version}</version>
<executions>
<execution>
<phase>verify</phase>
<goals>
<goal>sign</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>${maven-source.version}</version>
<inherited>true</inherited>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>cobertura-maven-plugin</artifactId>
<version>${cobertura-version}</version>
<configuration>
<formats>
<format>html</format>
<format>xml</format>
</formats>
<check/>
</configuration>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>${jacoco-version}</version>
<executions>
<execution>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>report</id>
<phase>test</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>${maven-gpg-version}</version>
<executions>
<execution>
<phase>verify</phase>
<goals>
<goal>sign</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<distributionManagement>
<snapshotRepository>
@@ -236,5 +232,36 @@
</repository>
</distributionManagement>
</profile>
<!--私服-->
<profile>
<id>nexus</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>${maven-gpg-version}</version>
<executions>
<execution>
<phase>verify</phase>
<goals>
<goal>sign</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<distributionManagement>
<repository>
<id>xkcoding-nexus</id>
<url>https://nexus.xkcoding.com/repository/maven-releases/</url>
</repository>
<snapshotRepository>
<id>xkcoding-nexus</id>
<url>https://nexus.xkcoding.com/repository/maven-snapshots/</url>
</snapshotRepository>
</distributionManagement>
</profile>
</profiles>
</project>
+22
View File
@@ -0,0 +1,22 @@
package me.zhyd.oauth.cache;
/**
* AuthCache配置类
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @since 1.10.0
*/
public class AuthCacheConfig {
/**
* 默认缓存过期时间:3分钟
* 鉴于授权过程中,根据个人的操作习惯,或者授权平台的不同(google等),每个授权流程的耗时也有差异,不过单个授权流程一般不会太长
* 本缓存工具默认的过期时间设置为3分钟,即程序默认认为3分钟内的授权有效,超过3分钟则默认失效,失效后删除
*/
public static long timeout = 3 * 60 * 1000;
/**
* 是否开启定时{@link AuthDefaultCache#pruneCache()}的任务
*/
public static boolean schedulePrune = true;
}
@@ -13,6 +13,9 @@ import java.util.concurrent.atomic.AtomicInteger;
*/
public enum AuthCacheScheduler {
/**
* 当前实例
*/
INSTANCE;
private AtomicInteger cacheTaskNumber = new AtomicInteger(1);
+4 -8
View File
@@ -18,12 +18,6 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
*/
public class AuthDefaultCache implements AuthCache {
/**
* 默认缓存过期时间:3分钟
* 鉴于授权过程中,根据个人的操作习惯,或者授权平台的不同(google等),每个授权流程的耗时也有差异,不过单个授权流程一般不会太长
* 本缓存工具默认的过期时间设置为3分钟,即程序默认认为3分钟内的授权有效,超过3分钟则默认失效,失效后删除
*/
private static final long DEF_TIMEOUT = 3 * 60 * 1000;
/**
* state cache
*/
@@ -33,7 +27,9 @@ public class AuthDefaultCache implements AuthCache {
private final Lock readLock = cacheLock.readLock();
public AuthDefaultCache() {
this.schedulePrune(DEF_TIMEOUT);
if (AuthCacheConfig.schedulePrune) {
this.schedulePrune(AuthCacheConfig.timeout);
}
}
/**
@@ -44,7 +40,7 @@ public class AuthDefaultCache implements AuthCache {
*/
@Override
public void set(String key, String value) {
set(key, value, DEF_TIMEOUT);
set(key, value, AuthCacheConfig.timeout);
}
/**
@@ -0,0 +1,66 @@
package me.zhyd.oauth.cache;
/**
* 默认的state缓存实现
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @since 1.10.0
*/
public enum AuthDefaultStateCache implements AuthStateCache {
/**
* 当前实例
*/
INSTANCE;
private AuthCache authCache;
AuthDefaultStateCache() {
authCache = new AuthDefaultCache();
}
/**
* 存入缓存
*
* @param key 缓存key
* @param value 缓存内容
*/
@Override
public void cache(String key, String value) {
authCache.set(key, value);
}
/**
* 存入缓存
*
* @param key 缓存key
* @param value 缓存内容
* @param timeout 指定缓存过期时间(毫秒)
*/
@Override
public void cache(String key, String value, long timeout) {
authCache.set(key, value, timeout);
}
/**
* 获取缓存内容
*
* @param key 缓存key
* @return 缓存内容
*/
@Override
public String get(String key) {
return authCache.get(key);
}
/**
* 是否存在key,如果对应key的value值已过期,也返回false
*
* @param key 缓存key
* @return true:存在key,并且value没过期;falsekey不存在或者已过期
*/
@Override
public boolean containsKey(String key) {
return authCache.containsKey(key);
}
}
+11 -18
View File
@@ -1,22 +1,21 @@
package me.zhyd.oauth.cache;
/**
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0
* @since 1.8
* <p>
* State缓存接口,方便用户扩展
* </p>
*
* @author yangkai.shen
* @since 1.10.0
*/
public class AuthStateCache {
private static AuthCache authCache = new AuthDefaultCache();
public interface AuthStateCache {
/**
* 存入缓存
*
* @param key 缓存key
* @param value 缓存内容
*/
public static void cache(String key, String value) {
authCache.set(key, value);
}
void cache(String key, String value);
/**
* 存入缓存
@@ -25,9 +24,7 @@ public class AuthStateCache {
* @param value 缓存内容
* @param timeout 指定缓存过期时间(毫秒)
*/
public static void cache(String key, String value, long timeout) {
authCache.set(key, value, timeout);
}
void cache(String key, String value, long timeout);
/**
* 获取缓存内容
@@ -35,9 +32,7 @@ public class AuthStateCache {
* @param key 缓存key
* @return 缓存内容
*/
public static String get(String key) {
return authCache.get(key);
}
String get(String key);
/**
* 是否存在key,如果对应key的value值已过期,也返回false
@@ -45,7 +40,5 @@ public class AuthStateCache {
* @param key 缓存key
* @return true:存在key,并且value没过期;falsekey不存在或者已过期
*/
public static boolean containsKey(String key) {
return authCache.containsKey(key);
}
boolean containsKey(String key);
}
+5
View File
@@ -0,0 +1,5 @@
/**
* JustAuth 缓存实现, 提供基础的基于ConcurrentHashMap + ScheduledExecutorService 实现的定时缓存。
* 同时对外暴露{@code AuthStateCache}接口,可进行对缓存实现的自定义。
*/
package me.zhyd.oauth.cache;
@@ -1,6 +1,8 @@
package me.zhyd.oauth.config;
import com.xkcoding.http.config.HttpConfig;
import lombok.*;
import me.zhyd.oauth.model.AuthCallback;
/**
* JustAuth配置类
@@ -16,12 +18,12 @@ import lombok.*;
public class AuthConfig {
/**
* 客户端id:对应平台的appKey
* 客户端id:对应平台的appKey
*/
private String clientId;
/**
* 客户端Secret:对应平台的appSecret
* 客户端Secret:对应平台的appSecret
*/
private String clientSecret;
@@ -32,6 +34,7 @@ public class AuthConfig {
/**
* 支付宝公钥:当选择支付宝登录时,该值可用
* 对应“RSA2(SHA256)密钥”中的“支付宝公钥”
*/
private String alipayPublicKey;
@@ -47,7 +50,52 @@ public class AuthConfig {
/**
* Stack Overflow Key
* <p>
* 1.9.0版本新增参数
*
* @since 1.9.0
*/
private String stackOverflowKey;
/**
* 企业微信,授权方的网页应用ID
*
* @since 1.10.0
*/
private String agentId;
/**
* 使用 Coding 登录时,需要传该值。
*
* 团队域名前缀,比如以“ https://justauth.coding.net/ ”为例,{@code codingGroupName} = justauth
*
* @since 1.15.5
*/
private String codingGroupName;
/**
* 针对国外服务可以单独设置代理
* HttpConfig config = new HttpConfig();
* config.setProxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("127.0.0.1", 10080)));
* config.setTimeout(15000);
*
* @since 1.15.5
*/
private HttpConfig httpConfig;
/**
* 忽略校验 {@code state} 参数,默认不开启。当 {@code ignoreCheckState} 为 {@code true} 时,
* {@link me.zhyd.oauth.request.AuthDefaultRequest#login(AuthCallback)} 将不会校验 {@code state} 的合法性。
*
* 使用场景:当且仅当使用自实现 {@code state} 校验逻辑时开启
*
* 以下场景使用方案仅作参考:
* 1. 授权、登录为同端,并且全部使用 JustAuth 实现时,该值建议设为 {@code false};
* 2. 授权和登录为不同端实现时,比如前端页面拼装 {@code authorizeUrl},并且前端自行对{@code state}进行校验,
* 后端只负责使用{@code code}获取用户信息时,该值建议设为 {@code true};
*
* <strong>如非特殊需要,不建议开启这个配置</strong>
*
* 该方案主要为了解决以下类似场景的问题:
* @see <a href="https://github.com/justauth/JustAuth/issues/83">https://github.com/justauth/JustAuth/issues/83</a>
*/
private boolean ignoreCheckState;
}
@@ -0,0 +1,791 @@
package me.zhyd.oauth.config;
import me.zhyd.oauth.enums.AuthResponseStatus;
import me.zhyd.oauth.exception.AuthException;
/**
* JustAuth内置的各api需要的url, 用枚举类分平台类型管理
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @since 1.0
*/
public enum AuthDefaultSource implements AuthSource {
/**
* Github
*/
GITHUB {
@Override
public String authorize() {
return "https://github.com/login/oauth/authorize";
}
@Override
public String accessToken() {
return "https://github.com/login/oauth/access_token";
}
@Override
public String userInfo() {
return "https://api.github.com/user";
}
},
/**
* 新浪微博
*/
WEIBO {
@Override
public String authorize() {
return "https://api.weibo.com/oauth2/authorize";
}
@Override
public String accessToken() {
return "https://api.weibo.com/oauth2/access_token";
}
@Override
public String userInfo() {
return "https://api.weibo.com/2/users/show.json";
}
@Override
public String revoke() {
return "https://api.weibo.com/oauth2/revokeoauth2";
}
},
/**
* gitee
*/
GITEE {
@Override
public String authorize() {
return "https://gitee.com/oauth/authorize";
}
@Override
public String accessToken() {
return "https://gitee.com/oauth/token";
}
@Override
public String userInfo() {
return "https://gitee.com/api/v5/user";
}
},
/**
* 钉钉
*/
DINGTALK {
@Override
public String authorize() {
return "https://oapi.dingtalk.com/connect/qrconnect";
}
@Override
public String accessToken() {
throw new AuthException(AuthResponseStatus.UNSUPPORTED);
}
@Override
public String userInfo() {
return "https://oapi.dingtalk.com/sns/getuserinfo_bycode";
}
},
/**
* 百度
*/
BAIDU {
@Override
public String authorize() {
return "https://openapi.baidu.com/oauth/2.0/authorize";
}
@Override
public String accessToken() {
return "https://openapi.baidu.com/oauth/2.0/token";
}
@Override
public String userInfo() {
return "https://openapi.baidu.com/rest/2.0/passport/users/getInfo";
}
@Override
public String revoke() {
return "https://openapi.baidu.com/rest/2.0/passport/auth/revokeAuthorization";
}
@Override
public String refresh() {
return "https://openapi.baidu.com/oauth/2.0/token";
}
},
/**
* csdn
*/
CSDN {
@Override
public String authorize() {
return "https://api.csdn.net/oauth2/authorize";
}
@Override
public String accessToken() {
return "https://api.csdn.net/oauth2/access_token";
}
@Override
public String userInfo() {
return "https://api.csdn.net/user/getinfo";
}
},
/**
* Coding
*
* 参考 https://help.coding.net/docs/project/open/oauth.html#%E7%94%A8%E6%88%B7%E6%8E%88%E6%9D%83 中的说明,
* 新版的 coding API 地址需要传入用户团队名,这儿使用动态参数,方便在 request 中使用
*/
CODING {
@Override
public String authorize() {
return "https://%s.coding.net/oauth_authorize.html";
}
@Override
public String accessToken() {
return "https://%s.coding.net/api/oauth/access_token";
}
@Override
public String userInfo() {
return "https://%s.coding.net/api/account/current_user";
}
},
/**
* oschina 开源中国
*/
OSCHINA {
@Override
public String authorize() {
return "https://www.oschina.net/action/oauth2/authorize";
}
@Override
public String accessToken() {
return "https://www.oschina.net/action/openapi/token";
}
@Override
public String userInfo() {
return "https://www.oschina.net/action/openapi/user";
}
},
/**
* 支付宝
*/
ALIPAY {
@Override
public String authorize() {
return "https://openauth.alipay.com/oauth2/publicAppAuthorize.htm";
}
@Override
public String accessToken() {
return "https://openapi.alipay.com/gateway.do";
}
@Override
public String userInfo() {
return "https://openapi.alipay.com/gateway.do";
}
},
/**
* QQ
*/
QQ {
@Override
public String authorize() {
return "https://graph.qq.com/oauth2.0/authorize";
}
@Override
public String accessToken() {
return "https://graph.qq.com/oauth2.0/token";
}
@Override
public String userInfo() {
return "https://graph.qq.com/user/get_user_info";
}
@Override
public String refresh() {
return "https://graph.qq.com/oauth2.0/token";
}
},
/**
* 微信开放平台
*/
WECHAT_OPEN {
@Override
public String authorize() {
return "https://open.weixin.qq.com/connect/qrconnect";
}
@Override
public String accessToken() {
return "https://api.weixin.qq.com/sns/oauth2/access_token";
}
@Override
public String userInfo() {
return "https://api.weixin.qq.com/sns/userinfo";
}
@Override
public String refresh() {
return "https://api.weixin.qq.com/sns/oauth2/refresh_token";
}
},
/**
* 微信公众平台
*/
WECHAT_MP {
@Override
public String authorize() {
return "https://open.weixin.qq.com/connect/oauth2/authorize";
}
@Override
public String accessToken() {
return "https://api.weixin.qq.com/sns/oauth2/access_token";
}
@Override
public String userInfo() {
return "https://api.weixin.qq.com/sns/userinfo";
}
@Override
public String refresh() {
return "https://api.weixin.qq.com/sns/oauth2/refresh_token";
}
},
/**
* 淘宝
*/
TAOBAO {
@Override
public String authorize() {
return "https://oauth.taobao.com/authorize";
}
@Override
public String accessToken() {
return "https://oauth.taobao.com/token";
}
@Override
public String userInfo() {
throw new AuthException(AuthResponseStatus.UNSUPPORTED);
}
},
/**
* Google
*/
GOOGLE {
@Override
public String authorize() {
return "https://accounts.google.com/o/oauth2/v2/auth";
}
@Override
public String accessToken() {
return "https://www.googleapis.com/oauth2/v4/token";
}
@Override
public String userInfo() {
return "https://www.googleapis.com/oauth2/v3/userinfo";
}
},
/**
* Facebook
*/
FACEBOOK {
@Override
public String authorize() {
return "https://www.facebook.com/v3.3/dialog/oauth";
}
@Override
public String accessToken() {
return "https://graph.facebook.com/v3.3/oauth/access_token";
}
@Override
public String userInfo() {
return "https://graph.facebook.com/v3.3/me";
}
},
/**
* 抖音
*/
DOUYIN {
@Override
public String authorize() {
return "https://open.douyin.com/platform/oauth/connect";
}
@Override
public String accessToken() {
return "https://open.douyin.com/oauth/access_token/";
}
@Override
public String userInfo() {
return "https://open.douyin.com/oauth/userinfo/";
}
@Override
public String refresh() {
return "https://open.douyin.com/oauth/refresh_token/";
}
},
/**
* 领英
*/
LINKEDIN {
@Override
public String authorize() {
return "https://www.linkedin.com/oauth/v2/authorization";
}
@Override
public String accessToken() {
return "https://www.linkedin.com/oauth/v2/accessToken";
}
@Override
public String userInfo() {
return "https://api.linkedin.com/v2/me";
}
@Override
public String refresh() {
return "https://www.linkedin.com/oauth/v2/accessToken";
}
},
/**
* 微软
*/
MICROSOFT {
@Override
public String authorize() {
return "https://login.microsoftonline.com/common/oauth2/v2.0/authorize";
}
@Override
public String accessToken() {
return "https://login.microsoftonline.com/common/oauth2/v2.0/token";
}
@Override
public String userInfo() {
return "https://graph.microsoft.com/v1.0/me";
}
@Override
public String refresh() {
return "https://login.microsoftonline.com/common/oauth2/v2.0/token";
}
},
/**
* 小米
*/
MI {
@Override
public String authorize() {
return "https://account.xiaomi.com/oauth2/authorize";
}
@Override
public String accessToken() {
return "https://account.xiaomi.com/oauth2/token";
}
@Override
public String userInfo() {
return "https://open.account.xiaomi.com/user/profile";
}
@Override
public String refresh() {
return "https://account.xiaomi.com/oauth2/token";
}
},
/**
* 今日头条
*/
TOUTIAO {
@Override
public String authorize() {
return "https://open.snssdk.com/auth/authorize";
}
@Override
public String accessToken() {
return "https://open.snssdk.com/auth/token";
}
@Override
public String userInfo() {
return "https://open.snssdk.com/data/user_profile";
}
},
/**
* Teambition
*/
TEAMBITION {
@Override
public String authorize() {
return "https://account.teambition.com/oauth2/authorize";
}
@Override
public String accessToken() {
return "https://account.teambition.com/oauth2/access_token";
}
@Override
public String refresh() {
return "https://account.teambition.com/oauth2/refresh_token";
}
@Override
public String userInfo() {
return "https://api.teambition.com/users/me";
}
},
/**
* 人人网
*/
RENREN {
@Override
public String authorize() {
return "https://graph.renren.com/oauth/authorize";
}
@Override
public String accessToken() {
return "https://graph.renren.com/oauth/token";
}
@Override
public String refresh() {
return "https://graph.renren.com/oauth/token";
}
@Override
public String userInfo() {
return "https://api.renren.com/v2/user/get";
}
},
/**
* Pinterest
*/
PINTEREST {
@Override
public String authorize() {
return "https://api.pinterest.com/oauth";
}
@Override
public String accessToken() {
return "https://api.pinterest.com/v1/oauth/token";
}
@Override
public String userInfo() {
return "https://api.pinterest.com/v1/me";
}
},
/**
* Stack Overflow
*/
STACK_OVERFLOW {
@Override
public String authorize() {
return "https://stackoverflow.com/oauth";
}
@Override
public String accessToken() {
return "https://stackoverflow.com/oauth/access_token/json";
}
@Override
public String userInfo() {
return "https://api.stackexchange.com/2.2/me";
}
},
/**
* 华为
*
* @since 1.10.0
*/
HUAWEI {
@Override
public String authorize() {
return "https://oauth-login.cloud.huawei.com/oauth2/v2/authorize";
}
@Override
public String accessToken() {
return "https://oauth-login.cloud.huawei.com/oauth2/v2/token";
}
@Override
public String userInfo() {
return "https://api.vmall.com/rest.php";
}
@Override
public String refresh() {
return "https://oauth-login.cloud.huawei.com/oauth2/v2/token";
}
},
/**
* 企业微信
*
* @since 1.10.0
*/
WECHAT_ENTERPRISE {
@Override
public String authorize() {
return "https://open.work.weixin.qq.com/wwopen/sso/qrConnect";
}
@Override
public String accessToken() {
return "https://qyapi.weixin.qq.com/cgi-bin/gettoken";
}
@Override
public String userInfo() {
return "https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo";
}
},
/**
* 酷家乐
*
* @since 1.11.0
*/
KUJIALE {
@Override
public String authorize() {
return "https://oauth.kujiale.com/oauth2/show";
}
@Override
public String accessToken() {
return "https://oauth.kujiale.com/oauth2/auth/token";
}
@Override
public String userInfo() {
return "https://oauth.kujiale.com/oauth2/openapi/user";
}
@Override
public String refresh() {
return "https://oauth.kujiale.com/oauth2/auth/token/refresh";
}
},
/**
* Gitlab
*
* @since 1.11.0
*/
GITLAB {
@Override
public String authorize() {
return "https://gitlab.com/oauth/authorize";
}
@Override
public String accessToken() {
return "https://gitlab.com/oauth/token";
}
@Override
public String userInfo() {
return "https://gitlab.com/api/v4/user";
}
},
/**
* 美团
*
* @since 1.12.0
*/
MEITUAN {
@Override
public String authorize() {
return "https://openapi.waimai.meituan.com/oauth/authorize";
}
@Override
public String accessToken() {
return "https://openapi.waimai.meituan.com/oauth/access_token";
}
@Override
public String userInfo() {
return "https://openapi.waimai.meituan.com/oauth/userinfo";
}
@Override
public String refresh() {
return "https://openapi.waimai.meituan.com/oauth/refresh_token";
}
},
/**
* 饿了么
* <p>
* 注:集成的是正式环境,非沙箱环境
*
* @since 1.12.0
*/
ELEME {
@Override
public String authorize() {
return "https://open-api.shop.ele.me/authorize";
}
@Override
public String accessToken() {
return "https://open-api.shop.ele.me/token";
}
@Override
public String userInfo() {
return "https://open-api.shop.ele.me/api/v1/";
}
@Override
public String refresh() {
return "https://open-api.shop.ele.me/token";
}
},
/**
* Twitter
*
* @since 1.13.0
*/
TWITTER {
@Override
public String authorize() {
return "https://api.twitter.com/oauth/authenticate";
}
@Override
public String accessToken() {
return "https://api.twitter.com/oauth/access_token";
}
@Override
public String userInfo() {
return "https://api.twitter.com/1.1/users/show.json";
}
},
/**
* 飞书
* 注意:该平台暂时存在问题,请不要使用。待修复完成后会重新发版
*
* @since 1.14.0
*/
FEISHU {
@Override
public String authorize() {
return "https://open.feishu.cn/connect/qrconnect/page/sso/";
}
@Override
public String accessToken() {
return "https://open.feishu.cn/connect/qrconnect/oauth2/access_token/";
}
@Override
public String userInfo() {
return "https://open.feishu.cn/connect/qrconnect/oauth2/user_info/";
}
@Override
public String refresh() {
return "https://open.feishu.cn/connect/qrconnect/oauth2/access_token/";
}
},
/**
* 京东
*
* @since 1.15.0
*/
JD {
@Override
public String authorize() {
return "https://open-oauth.jd.com/oauth2/to_login";
}
@Override
public String accessToken() {
return "https://open-oauth.jd.com/oauth2/access_token";
}
@Override
public String userInfo() {
return "https://api.jd.com/routerjson";
}
@Override
public String refresh() {
return "https://open-oauth.jd.com/oauth2/refresh_token";
}
},
/**
* 阿里云
*/
ALIYUN {
@Override
public String authorize() {
return "https://signin.aliyun.com/oauth2/v1/auth";
}
@Override
public String accessToken() {
return "https://oauth.aliyun.com/v1/token";
}
@Override
public String userInfo() {
return "https://oauth.aliyun.com/v1/userinfo";
}
@Override
public String refresh() {
return "https://oauth.aliyun.com/v1/token";
}
}
}
@@ -1,552 +1,55 @@
package me.zhyd.oauth.config;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.enums.AuthResponseStatus;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.AuthCallback;
/**
* 各api需要的url, 用枚举类分平台类型管理
* OAuth平台的API地址的统一接口,提供以下方法:
* 1) {@link AuthSource#authorize()}: 获取授权url. 必须实现
* 2) {@link AuthSource#accessToken()}: 获取accessToken的url. 必须实现
* 3) {@link AuthSource#userInfo()}: 获取用户信息的url. 必须实现
* 4) {@link AuthSource#revoke()}: 获取取消授权的url. 非必须实现接口(部分平台不支持)
* 5) {@link AuthSource#refresh()}: 获取刷新授权的url. 非必须实现接口(部分平台不支持)
* <p>
* 注:
* ①、如需通过JustAuth扩展实现第三方授权,请参考{@link AuthDefaultSource}自行创建对应的枚举类并实现{@link AuthSource}接口
* ②、如果不是使用的枚举类,那么在授权成功后获取用户信息时,需要单独处理source字段的赋值
* ③、如果扩展了对应枚举类时,在{@link me.zhyd.oauth.request.AuthRequest#login(AuthCallback)}中可以通过{@code xx.toString()}获取对应的source
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @since 1.0
* @version 1.0
* @since 1.12.0
*/
public enum AuthSource {
/**
* Github
*/
GITHUB {
@Override
public String authorize() {
return "https://github.com/login/oauth/authorize";
}
@Override
public String accessToken() {
return "https://github.com/login/oauth/access_token";
}
@Override
public String userInfo() {
return "https://api.github.com/user";
}
},
/**
* 新浪微博
*/
WEIBO {
@Override
public String authorize() {
return "https://api.weibo.com/oauth2/authorize";
}
@Override
public String accessToken() {
return "https://api.weibo.com/oauth2/access_token";
}
@Override
public String userInfo() {
return "https://api.weibo.com/2/users/show.json";
}
},
/**
* gitee
*/
GITEE {
@Override
public String authorize() {
return "https://gitee.com/oauth/authorize";
}
@Override
public String accessToken() {
return "https://gitee.com/oauth/token";
}
@Override
public String userInfo() {
return "https://gitee.com/api/v5/user";
}
},
/**
* 钉钉
*/
DINGTALK {
@Override
public String authorize() {
return "https://oapi.dingtalk.com/connect/qrconnect";
}
@Override
public String accessToken() {
throw new AuthException(AuthResponseStatus.UNSUPPORTED);
}
@Override
public String userInfo() {
return "https://oapi.dingtalk.com/sns/getuserinfo_bycode";
}
},
/**
* 百度
*/
BAIDU {
@Override
public String authorize() {
return "https://openapi.baidu.com/oauth/2.0/authorize";
}
@Override
public String accessToken() {
return "https://openapi.baidu.com/oauth/2.0/token";
}
@Override
public String userInfo() {
return "https://openapi.baidu.com/rest/2.0/passport/users/getInfo";
}
@Override
public String revoke() {
return "https://openapi.baidu.com/rest/2.0/passport/auth/revokeAuthorization";
}
@Override
public String refresh() {
return "https://openapi.baidu.com/oauth/2.0/token";
}
},
/**
* csdn
*/
CSDN {
@Override
public String authorize() {
return "https://api.csdn.net/oauth2/authorize";
}
@Override
public String accessToken() {
return "https://api.csdn.net/oauth2/access_token";
}
@Override
public String userInfo() {
return "https://api.csdn.net/user/getinfo";
}
},
/**
* Coding
*/
CODING {
@Override
public String authorize() {
return "https://coding.net/oauth_authorize.html";
}
@Override
public String accessToken() {
return "https://coding.net/api/oauth/access_token";
}
@Override
public String userInfo() {
return "https://coding.net/api/account/current_user";
}
},
/**
* 腾讯云开发者平台(coding升级后就变成腾讯云开发者平台了)
*/
TENCENT_CLOUD {
@Override
public String authorize() {
return "https://dev.tencent.com/oauth_authorize.html";
}
@Override
public String accessToken() {
return "https://dev.tencent.com/api/oauth/access_token";
}
@Override
public String userInfo() {
return "https://dev.tencent.com/api/account/current_user";
}
},
/**
* oschina 开源中国
*/
OSCHINA {
@Override
public String authorize() {
return "https://www.oschina.net/action/oauth2/authorize";
}
@Override
public String accessToken() {
return "https://www.oschina.net/action/openapi/token";
}
@Override
public String userInfo() {
return "https://www.oschina.net/action/openapi/user";
}
},
/**
* 支付宝
*/
ALIPAY {
@Override
public String authorize() {
return "https://openauth.alipay.com/oauth2/publicAppAuthorize.htm";
}
@Override
public String accessToken() {
return "https://openapi.alipay.com/gateway.do";
}
@Override
public String userInfo() {
return "https://openapi.alipay.com/gateway.do";
}
},
/**
* QQ
*/
QQ {
@Override
public String authorize() {
return "https://graph.qq.com/oauth2.0/authorize";
}
@Override
public String accessToken() {
return "https://graph.qq.com/oauth2.0/token";
}
@Override
public String userInfo() {
return "https://graph.qq.com/user/get_user_info";
}
@Override
public String refresh() {
return "https://graph.qq.com/oauth2.0/token";
}
},
/**
* 微信
*/
WECHAT {
@Override
public String authorize() {
return "https://open.weixin.qq.com/connect/qrconnect";
}
@Override
public String accessToken() {
return "https://api.weixin.qq.com/sns/oauth2/access_token";
}
@Override
public String userInfo() {
return "https://api.weixin.qq.com/sns/userinfo";
}
@Override
public String refresh() {
return "https://api.weixin.qq.com/sns/oauth2/refresh_token";
}
},
/**
* 淘宝
*/
TAOBAO {
@Override
public String authorize() {
return "https://oauth.taobao.com/authorize";
}
@Override
public String accessToken() {
return "https://oauth.taobao.com/token";
}
@Override
public String userInfo() {
throw new AuthException(AuthResponseStatus.UNSUPPORTED);
}
},
/**
* Google
*/
GOOGLE {
@Override
public String authorize() {
return "https://accounts.google.com/o/oauth2/v2/auth";
}
@Override
public String accessToken() {
return "https://www.googleapis.com/oauth2/v4/token";
}
@Override
public String userInfo() {
return "https://www.googleapis.com/oauth2/v3/userinfo";
}
},
/**
* Facebook
*/
FACEBOOK {
@Override
public String authorize() {
return "https://www.facebook.com/v3.3/dialog/oauth";
}
@Override
public String accessToken() {
return "https://graph.facebook.com/v3.3/oauth/access_token";
}
@Override
public String userInfo() {
return "https://graph.facebook.com/v3.3/me";
}
},
/**
* 抖音
*/
DOUYIN {
@Override
public String authorize() {
return "https://open.douyin.com/platform/oauth/connect";
}
@Override
public String accessToken() {
return "https://open.douyin.com/oauth/access_token/";
}
@Override
public String userInfo() {
return "https://open.douyin.com/oauth/userinfo/";
}
@Override
public String refresh() {
return "https://open.douyin.com/oauth/refresh_token/";
}
},
/**
* 领英
*/
LINKEDIN {
@Override
public String authorize() {
return "https://www.linkedin.com/oauth/v2/authorization";
}
@Override
public String accessToken() {
return "https://www.linkedin.com/oauth/v2/accessToken";
}
@Override
public String userInfo() {
return "https://api.linkedin.com/v2/me";
}
@Override
public String refresh() {
return "https://www.linkedin.com/oauth/v2/accessToken";
}
},
/**
* 微软
*/
MICROSOFT {
@Override
public String authorize() {
return "https://login.microsoftonline.com/common/oauth2/v2.0/authorize";
}
@Override
public String accessToken() {
return "https://login.microsoftonline.com/common/oauth2/v2.0/token";
}
@Override
public String userInfo() {
return "https://graph.microsoft.com/v1.0/me";
}
@Override
public String refresh() {
return "https://login.microsoftonline.com/common/oauth2/v2.0/token";
}
},
/**
* 小米
*/
MI {
@Override
public String authorize() {
return "https://account.xiaomi.com/oauth2/authorize";
}
@Override
public String accessToken() {
return "https://account.xiaomi.com/oauth2/token";
}
@Override
public String userInfo() {
return "https://open.account.xiaomi.com/user/profile";
}
@Override
public String refresh() {
return "https://account.xiaomi.com/oauth2/token";
}
},
/**
* 今日头条
*/
TOUTIAO {
@Override
public String authorize() {
return "https://open.snssdk.com/auth/authorize";
}
@Override
public String accessToken() {
return "https://open.snssdk.com/auth/token";
}
@Override
public String userInfo() {
return "https://open.snssdk.com/data/user_profile";
}
},
/**
* Teambition
*/
TEAMBITION {
@Override
public String authorize() {
return "https://account.teambition.com/oauth2/authorize";
}
@Override
public String accessToken() {
return "https://account.teambition.com/oauth2/access_token";
}
@Override
public String refresh() {
return "https://account.teambition.com/oauth2/refresh_token";
}
@Override
public String userInfo() {
return "https://api.teambition.com/users/me";
}
},
/**
* 人人网
*/
RENREN {
@Override
public String authorize() {
return "https://graph.renren.com/oauth/authorize";
}
@Override
public String accessToken() {
return "https://graph.renren.com/oauth/token";
}
@Override
public String refresh() {
return "https://graph.renren.com/oauth/token";
}
@Override
public String userInfo() {
return "https://api.renren.com/v2/user/get";
}
},
/**
* Pinterest
*/
PINTEREST {
@Override
public String authorize() {
return "https://api.pinterest.com/oauth";
}
@Override
public String accessToken() {
return "https://api.pinterest.com/v1/oauth/token";
}
@Override
public String userInfo() {
return "https://api.pinterest.com/v1/me";
}
},
/**
* Stack Overflow
*/
STACK_OVERFLOW {
@Override
public String authorize() {
return "https://stackoverflow.com/oauth";
}
@Override
public String accessToken() {
return "https://stackoverflow.com/oauth/access_token/json";
}
@Override
public String userInfo() {
return "https://api.stackexchange.com/2.2/me";
}
};
public interface AuthSource {
/**
* 授权的api
*
* @return url
*/
public abstract String authorize();
String authorize();
/**
* 获取accessToken的api
*
* @return url
*/
public abstract String accessToken();
String accessToken();
/**
* 获取用户信息的api
*
* @return url
*/
public abstract String userInfo();
String userInfo();
/**
* 取消授权的api
*
* @return url
*/
public String revoke() {
default String revoke() {
throw new AuthException(AuthResponseStatus.UNSUPPORTED);
}
@@ -555,8 +58,19 @@ public enum AuthSource {
*
* @return url
*/
public String refresh() {
default String refresh() {
throw new AuthException(AuthResponseStatus.UNSUPPORTED);
}
/**
* 获取Source的字符串名字
*
* @return name
*/
default String getName() {
if (this instanceof Enum) {
return String.valueOf(this);
}
return this.getClass().getSimpleName();
}
}
@@ -0,0 +1,4 @@
/**
* JustAuth 核心配置相关,包括{@code AuthConfig}和{@code AuthSource}
*/
package me.zhyd.oauth.config;
@@ -21,11 +21,13 @@ public enum AuthResponseStatus {
NOT_IMPLEMENTED(5001, "Not Implemented"),
PARAMETER_INCOMPLETE(5002, "Parameter incomplete"),
UNSUPPORTED(5003, "Unsupported operation"),
NO_AUTH_SOURCE(5004, "AuthSource cannot be null"),
NO_AUTH_SOURCE(5004, "AuthDefaultSource cannot be null"),
UNIDENTIFIED_PLATFORM(5005, "Unidentified platform"),
ILLEGAL_REDIRECT_URI(5006, "Illegal redirect uri"),
ILLEGAL_REQUEST(5007, "Illegal request"),
ILLEGAL_CODE(5008, "Illegal code"),
ILLEGAL_STATUS(5009, "Illegal state"),
REQUIRED_REFRESH_TOKEN(5010, "The refresh token is required; it must not be null"),
;
private int code;
@@ -2,6 +2,7 @@ package me.zhyd.oauth.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
import me.zhyd.oauth.utils.StringUtils;
import java.util.Arrays;
@@ -18,25 +19,41 @@ public enum AuthUserGender {
* MALE/FAMALE为正常值,通过{@link AuthUserGender#getRealGender(String)}方法获取真实的性别
* UNKNOWN为容错值,部分平台不会返回用户性别,为了方便统一,使用UNKNOWN标记所有未知或不可测的用户性别信息
*/
MALE(1, ""),
FEMALE(0, ""),
UNKNOWN(-1, "未知");
MALE("1", ""),
FEMALE("0", ""),
UNKNOWN("-1", "未知");
private int code;
private String code;
private String desc;
public static AuthUserGender getRealGender(String code) {
if (code == null) {
/**
* 获取用户的实际性别,常规网站
*
* @param originalGender 用户第三方标注的原始性别
* @return 用户性别
*/
public static AuthUserGender getRealGender(String originalGender) {
if (null == originalGender || UNKNOWN.getCode().equals(originalGender)) {
return UNKNOWN;
}
String[] males = {"m", "", "1", "male"};
if (Arrays.asList(males).contains(code.toLowerCase())) {
if (Arrays.asList(males).contains(originalGender.toLowerCase())) {
return MALE;
}
String[] females = {"f", "", "0", "female"};
if (Arrays.asList(females).contains(code.toLowerCase())) {
return FEMALE;
return FEMALE;
}
/**
* 获取微信平台用户的实际性别,0表示未定义,1表示男性,2表示女性
*
* @param originalGender 用户第三方标注的原始性别
* @return 用户性别
* @since 1.13.2
*/
public static AuthUserGender getWechatRealGender(String originalGender) {
if (StringUtils.isEmpty(originalGender) || "0".equals(originalGender)) {
return AuthUserGender.UNKNOWN;
}
return UNKNOWN;
return getRealGender(originalGender);
}
}
@@ -0,0 +1,4 @@
/**
* 提供一些必要的枚举类
*/
package me.zhyd.oauth.enums;
@@ -1,5 +1,6 @@
package me.zhyd.oauth.exception;
import me.zhyd.oauth.config.AuthSource;
import me.zhyd.oauth.enums.AuthResponseStatus;
/**
@@ -17,6 +18,10 @@ public class AuthException extends RuntimeException {
this(AuthResponseStatus.FAILURE.getCode(), errorMsg);
}
public AuthException(String errorMsg, AuthSource source) {
this(AuthResponseStatus.FAILURE.getCode(), errorMsg, source);
}
public AuthException(int errorCode, String errorMsg) {
super(errorMsg);
this.errorCode = errorCode;
@@ -24,7 +29,15 @@ public class AuthException extends RuntimeException {
}
public AuthException(AuthResponseStatus status) {
super(status.getMsg());
this(status.getCode(), status.getMsg());
}
public AuthException(int errorCode, String errorMsg, AuthSource source) {
this(errorCode, String.format("%s [%s]", errorMsg, source.getName()));
}
public AuthException(AuthResponseStatus status, AuthSource source) {
this(status.getCode(), status.getMsg(), source);
}
public AuthException(String message, Throwable cause) {
@@ -0,0 +1,4 @@
/**
* JustAuth专用异常封装
*/
package me.zhyd.oauth.exception;
+150
View File
@@ -0,0 +1,150 @@
package me.zhyd.oauth.log;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.io.PrintStream;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
/**
* 针对JustAuth提供的轻量级的日志打印工具
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @see Log#debug(String)
* @see Log#debug(String, Throwable)
* @see Log#warn(String)
* @see Log#warn(String, Throwable)
* @see Log#error(String)
* @see Log#error(String, Throwable)
* @since 1.10.0
*/
public class Log {
public static void debug(String msg) {
debug(msg, null);
}
public static void warn(String msg) {
warn(msg, null);
}
public static void error(String msg) {
error(msg, null);
}
public static void debug(String msg, Throwable t) {
print(Level.DEBUG, msg, t, System.out);
}
public static void warn(String msg, Throwable t) {
print(Level.WARN, msg, t, System.out);
}
public static void error(String msg, Throwable t) {
print(Level.ERROR, msg, t, System.err);
}
/**
* 打印日志内容,格式:2019-08-02 20:44:07 main me.zhyd.oauth.log.Log(debug:39) [DEBUG] - xxxx
*
* @param level 日志级别
* @param msg 日志内容
* @param t 异常信息
* @param ps 实际执行打印的PrintStream
*/
private static void print(Level level, String msg, Throwable t, PrintStream ps) {
if (Config.enable) {
if (level.getLevelNum() >= Config.level.getLevelNum()) {
ps.println(String.format("%s %s %s [%s] - %s", getDate(), Thread.currentThread().getName(), getCaller(), level, msg));
writeThrowable(t, ps);
ps.flush();
}
}
}
/**
* 获取调用方的信息
*
* @return 返回调用方的信息,格式:class(method:lineNumber)
*/
private static String getCaller() {
int offset = 2;
StackTraceElement[] stackTraceArr = (new Throwable()).getStackTrace();
StackTraceElement stackTrace = null;
if (offset >= stackTraceArr.length) {
offset = offset - 1;
}
stackTrace = stackTraceArr[offset];
return stackTrace.getClassName() +
"(" +
stackTrace.getMethodName() +
':' +
stackTrace.getLineNumber() +
")";
}
/**
* 获取格式化后的日期
*
* @return string
*/
private static String getDate() {
return LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
}
/**
* 打印异常信息
*
* @param t 异常
* @param targetStream 实际执行打印的PrintStream
*/
private static void writeThrowable(Throwable t, PrintStream targetStream) {
if (t != null) {
t.printStackTrace(targetStream);
}
}
/**
* 日志级别
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @since 1.10.0
*/
@Getter
@AllArgsConstructor
public enum Level {
/**
* DEBUG: 普通级别
*/
DEBUG(10),
/**
* WARN: 警告级别
*/
WARN(30),
/**
* ERROR: 异常级别
*/
ERROR(40);
private int levelNum;
}
/**
* 日志配置
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @since 1.10.0
*/
static class Config {
/**
* 需要打印的日志级别
*/
static Level level = Level.DEBUG;
/**
* 是否启用日志打印功能,默认启用
*/
static boolean enable = true;
}
}
@@ -0,0 +1,4 @@
/**
* 针对JustAuth简单封装的日志打印工具,可用过{@link me.zhyd.oauth.log.Log.Config}开关日志和指定日志级别
*/
package me.zhyd.oauth.log;
@@ -1,8 +1,13 @@
package me.zhyd.oauth.model;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import java.io.Serializable;
/**
* 授权回调时的参数类
*
@@ -11,7 +16,10 @@ import lombok.Setter;
*/
@Getter
@Setter
public class AuthCallback {
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class AuthCallback implements Serializable {
/**
* 访问AuthorizeUrl后回调时带的参数code
@@ -27,4 +35,26 @@ public class AuthCallback {
* 访问AuthorizeUrl后回调时带的参数state,用于和请求AuthorizeUrl前的state比较,防止CSRF攻击
*/
private String state;
/**
* 华为授权登录接受code的参数名
*
* @since 1.10.0
*/
private String authorization_code;
/**
* Twitter回调后返回的oauth_token
*
* @since 1.13.0
*/
private String oauth_token;
/**
* Twitter回调后返回的oauth_verifier
*
* @since 1.13.0
*/
private String oauth_verifier;
}
@@ -1,9 +1,10 @@
package me.zhyd.oauth.model;
import lombok.Builder;
import lombok.Getter;
import lombok.*;
import me.zhyd.oauth.enums.AuthResponseStatus;
import java.io.Serializable;
/**
* JustAuth统一授权响应类
*
@@ -11,8 +12,11 @@ import me.zhyd.oauth.enums.AuthResponseStatus;
* @since 1.8
*/
@Getter
@Setter
@Builder
public class AuthResponse<T> {
@NoArgsConstructor
@AllArgsConstructor
public class AuthResponse<T> implements Serializable {
/**
* 授权响应状态码
*/
@@ -1,9 +1,8 @@
package me.zhyd.oauth.model;
import lombok.Builder;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import lombok.*;
import java.io.Serializable;
/**
* 授权所需的token
@@ -14,7 +13,9 @@ import lombok.Setter;
@Getter
@Setter
@Builder
public class AuthToken {
@NoArgsConstructor
@AllArgsConstructor
public class AuthToken implements Serializable {
private String accessToken;
private int expireIn;
private String refreshToken;
@@ -36,4 +37,22 @@ public class AuthToken {
private String macAlgorithm;
private String macKey;
/**
* 企业微信附带属性
*
* @since 1.10.0
*/
private String code;
/**
* Twitter附带属性
*
* @since 1.13.0
*/
private String oauthToken;
private String oauthTokenSecret;
private String userId;
private String screenName;
private Boolean oauthCallbackConfirmed;
}
@@ -1,10 +1,9 @@
package me.zhyd.oauth.model;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import me.zhyd.oauth.config.AuthSource;
import com.alibaba.fastjson.JSONObject;
import lombok.*;
import me.zhyd.oauth.enums.AuthUserGender;
import java.io.Serializable;
/**
* 授权成功后的用户信息,根据授权平台的不同,获取的数据完整性也不同
@@ -15,9 +14,11 @@ import me.zhyd.oauth.enums.AuthUserGender;
@Getter
@Setter
@Builder
public class AuthUser {
@NoArgsConstructor
@AllArgsConstructor
public class AuthUser implements Serializable {
/**
* 用户第三方系统的唯一id。在调用方集成组件时,可以用uuid + source唯一确定一个用户
* 用户第三方系统的唯一id。在调用方集成组件时,可以用uuid + source唯一确定一个用户
*
* @since 1.3.3
*/
@@ -61,9 +62,14 @@ public class AuthUser {
/**
* 用户来源
*/
private AuthSource source;
private String source;
/**
* 用户授权的token信息
*/
private AuthToken token;
/**
* 第三方平台返回的原始用户信息
*/
private JSONObject rawUserInfo;
}
@@ -0,0 +1,4 @@
/**
* JustAuth核心模型类,封装了用户、token、响应和callback等实体类
*/
package me.zhyd.oauth.model;
@@ -0,0 +1,8 @@
/**
* JustAuth,如你所见,它仅仅是一个第三方授权登录的工具类库,它可以让我们脱离繁琐的第三方登录SDK,让登录变得So easy!
* <p>
* 史上最全的整合第三方登录的开源库。目前已支持Github、Gitee、微博、钉钉、百度、Coding、腾讯云开发者平台、OSChina、
* 支付宝、QQ、微信、淘宝、Google、Facebook、抖音、领英、小米、微软、今日头条、Teambition、StackOverflow、Pinterest、
* 人人、华为和企业微信等第三方平台的授权登录。 Login, so easy!
*/
package me.zhyd.oauth;
@@ -1,5 +1,6 @@
package me.zhyd.oauth.request;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
@@ -7,11 +8,14 @@ import com.alipay.api.request.AlipaySystemOauthTokenRequest;
import com.alipay.api.request.AlipayUserInfoShareRequest;
import com.alipay.api.response.AlipaySystemOauthTokenResponse;
import com.alipay.api.response.AlipayUserInfoShareResponse;
import me.zhyd.oauth.cache.AuthStateCache;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.config.AuthSource;
import me.zhyd.oauth.config.AuthDefaultSource;
import me.zhyd.oauth.enums.AuthResponseStatus;
import me.zhyd.oauth.enums.AuthUserGender;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthResponse;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.utils.StringUtils;
@@ -28,8 +32,14 @@ public class AuthAlipayRequest extends AuthDefaultRequest {
private AlipayClient alipayClient;
public AuthAlipayRequest(AuthConfig config) {
super(config, AuthSource.ALIPAY);
this.alipayClient = new DefaultAlipayClient(AuthSource.ALIPAY.accessToken(), config.getClientId(), config.getClientSecret(), "json", "UTF-8", config
super(config, AuthDefaultSource.ALIPAY);
this.alipayClient = new DefaultAlipayClient(AuthDefaultSource.ALIPAY.accessToken(), config.getClientId(), config.getClientSecret(), "json", "UTF-8", config
.getAlipayPublicKey(), "RSA2");
}
public AuthAlipayRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.ALIPAY, authStateCache);
this.alipayClient = new DefaultAlipayClient(AuthDefaultSource.ALIPAY.accessToken(), config.getClientId(), config.getClientSecret(), "json", "UTF-8", config
.getAlipayPublicKey(), "RSA2");
}
@@ -55,6 +65,37 @@ public class AuthAlipayRequest extends AuthDefaultRequest {
.build();
}
/**
* 刷新access token (续期)
*
* @param authToken 登录成功后返回的Token信息
* @return AuthResponse
*/
@Override
public AuthResponse refresh(AuthToken authToken) {
AlipaySystemOauthTokenRequest request = new AlipaySystemOauthTokenRequest();
request.setGrantType("refresh_token");
request.setRefreshToken(authToken.getRefreshToken());
AlipaySystemOauthTokenResponse response = null;
try {
response = this.alipayClient.execute(request);
} catch (Exception e) {
throw new AuthException(e);
}
if (!response.isSuccess()) {
throw new AuthException(response.getSubMsg());
}
return AuthResponse.builder()
.code(AuthResponseStatus.SUCCESS.getCode())
.data(AuthToken.builder()
.accessToken(response.getAccessToken())
.uid(response.getUserId())
.expireIn(Integer.parseInt(response.getExpiresIn()))
.refreshToken(response.getRefreshToken())
.build())
.build();
}
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
String accessToken = authToken.getAccessToken();
@@ -73,6 +114,7 @@ public class AuthAlipayRequest extends AuthDefaultRequest {
String location = String.format("%s %s", StringUtils.isEmpty(province) ? "" : province, StringUtils.isEmpty(city) ? "" : city);
return AuthUser.builder()
.rawUserInfo(JSONObject.parseObject(JSONObject.toJSONString(response)))
.uuid(response.getUserId())
.username(StringUtils.isEmpty(response.getUserName()) ? response.getNickName() : response.getUserName())
.nickname(response.getNickName())
@@ -80,7 +122,7 @@ public class AuthAlipayRequest extends AuthDefaultRequest {
.location(location)
.gender(AuthUserGender.getRealGender(response.getGender()))
.token(authToken)
.source(source)
.source(source.toString())
.build();
}
@@ -0,0 +1,57 @@
package me.zhyd.oauth.request;
import com.alibaba.fastjson.JSONObject;
import me.zhyd.oauth.cache.AuthStateCache;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.config.AuthDefaultSource;
import me.zhyd.oauth.enums.AuthUserGender;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.utils.UrlBuilder;
/**
* 阿里云登录
*
* @author snippet0809 (https://github.com/snippet0809)
* @since 1.15.5
*/
public class AuthAliyunRequest extends AuthDefaultRequest {
public AuthAliyunRequest(AuthConfig config) {
super(config, AuthDefaultSource.ALIYUN);
}
public AuthAliyunRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.ALIYUN, authStateCache);
}
@Override
protected AuthToken getAccessToken(AuthCallback authCallback) {
String response = doPostAuthorizationCode(authCallback.getCode());
JSONObject accessTokenObject = JSONObject.parseObject(response);
return AuthToken.builder()
.accessToken(accessTokenObject.getString("access_token"))
.expireIn(accessTokenObject.getIntValue("expires_in"))
.tokenType(accessTokenObject.getString("token_type"))
.idToken(accessTokenObject.getString("id_token"))
.refreshToken(accessTokenObject.getString("refresh_token"))
.build();
}
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
String userInfo = doGetUserInfo(authToken);
JSONObject object = JSONObject.parseObject(userInfo);
return AuthUser.builder()
.rawUserInfo(object)
.uuid(object.getString("sub"))
.username(object.getString("login_name"))
.nickname(object.getString("name"))
.gender(AuthUserGender.UNKNOWN)
.token(authToken)
.source(source.toString())
.build();
}
}
@@ -1,14 +1,17 @@
package me.zhyd.oauth.request;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import com.alibaba.fastjson.JSONObject;
import me.zhyd.oauth.utils.HttpUtils;
import me.zhyd.oauth.cache.AuthStateCache;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.config.AuthSource;
import me.zhyd.oauth.config.AuthDefaultSource;
import me.zhyd.oauth.enums.AuthResponseStatus;
import me.zhyd.oauth.enums.AuthUserGender;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.*;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthResponse;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.utils.StringUtils;
import me.zhyd.oauth.utils.UrlBuilder;
@@ -21,30 +24,41 @@ import me.zhyd.oauth.utils.UrlBuilder;
public class AuthBaiduRequest extends AuthDefaultRequest {
public AuthBaiduRequest(AuthConfig config) {
super(config, AuthSource.BAIDU);
super(config, AuthDefaultSource.BAIDU);
}
public AuthBaiduRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.BAIDU, authStateCache);
}
@Override
protected AuthToken getAccessToken(AuthCallback authCallback) {
HttpResponse response = doPostAuthorizationCode(authCallback.getCode());
String response = doPostAuthorizationCode(authCallback.getCode());
return getAuthToken(response);
}
/**
* https://openapi.baidu.com/rest/2.0/passport/users/getInfo?access_token=121.c86e87cc0828cc1dabb8faee540531d4.YsUIAWvYbgqVni1VhkgKgyLh8nEyELbDOEZs_OA.OgDgmA
* https://openapi.baidu.com/rest/2.0/passport/users/getInfo?access_token=121.2907d9facf9fb97adf7287fa75496eda.Y3NSjR3-3HKt1RgT0HEl7GgxRXT5gOOVdngXezY.OcC_7g
* 新旧应用返回的用户信息不一致
* @param authToken token信息
* @return AuthUser
*/
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
HttpResponse response = doGetUserInfo(authToken);
String userInfo = response.body();
String userInfo = doGetUserInfo(authToken);
JSONObject object = JSONObject.parseObject(userInfo);
this.checkResponse(object);
return AuthUser.builder()
.uuid(object.getString("userid"))
.rawUserInfo(object)
.uuid(object.containsKey("userid") ? object.getString("userid") : object.getString("openid"))
.username(object.getString("username"))
.nickname(object.getString("username"))
.avatar(getAvatar(object))
.remark(object.getString("userdetail"))
.gender(AuthUserGender.getRealGender(object.getString("sex")))
.token(authToken)
.source(source)
.source(source.toString())
.build();
}
@@ -55,8 +69,8 @@ public class AuthBaiduRequest extends AuthDefaultRequest {
@Override
public AuthResponse revoke(AuthToken authToken) {
HttpResponse response = doGetRevoke(authToken);
JSONObject object = JSONObject.parseObject(response.body());
String response = doGetRevoke(authToken);
JSONObject object = JSONObject.parseObject(response);
this.checkResponse(object);
// 返回1表示取消授权成功,否则失败
AuthResponseStatus status = object.getIntValue("result") == 1 ? AuthResponseStatus.SUCCESS : AuthResponseStatus.FAILURE;
@@ -71,7 +85,7 @@ public class AuthBaiduRequest extends AuthDefaultRequest {
.queryParam("client_id", this.config.getClientId())
.queryParam("client_secret", this.config.getClientSecret())
.build();
HttpResponse response = HttpRequest.get(refreshUrl).execute();
String response = new HttpUtils(config.getHttpConfig()).get(refreshUrl);
return AuthResponse.builder()
.code(AuthResponseStatus.SUCCESS.getCode())
.data(this.getAuthToken(response))
@@ -108,8 +122,8 @@ public class AuthBaiduRequest extends AuthDefaultRequest {
}
}
private AuthToken getAuthToken(HttpResponse response) {
JSONObject accessTokenObject = JSONObject.parseObject(response.body());
private AuthToken getAuthToken(String response) {
JSONObject accessTokenObject = JSONObject.parseObject(response);
this.checkResponse(accessTokenObject);
return AuthToken.builder()
.accessToken(accessTokenObject.getString("access_token"))
@@ -1,9 +1,9 @@
package me.zhyd.oauth.request;
import cn.hutool.http.HttpResponse;
import com.alibaba.fastjson.JSONObject;
import me.zhyd.oauth.cache.AuthStateCache;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.config.AuthSource;
import me.zhyd.oauth.config.AuthDefaultSource;
import me.zhyd.oauth.enums.AuthUserGender;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.AuthCallback;
@@ -20,13 +20,17 @@ import me.zhyd.oauth.utils.UrlBuilder;
public class AuthCodingRequest extends AuthDefaultRequest {
public AuthCodingRequest(AuthConfig config) {
super(config, AuthSource.CODING);
super(config, AuthDefaultSource.CODING);
}
public AuthCodingRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.CODING, authStateCache);
}
@Override
protected AuthToken getAccessToken(AuthCallback authCallback) {
HttpResponse response = doGetAuthorizationCode(authCallback.getCode());
JSONObject accessTokenObject = JSONObject.parseObject(response.body());
String response = doGetAuthorizationCode(authCallback.getCode());
JSONObject accessTokenObject = JSONObject.parseObject(response);
this.checkResponse(accessTokenObject);
return AuthToken.builder()
.accessToken(accessTokenObject.getString("access_token"))
@@ -37,16 +41,17 @@ public class AuthCodingRequest extends AuthDefaultRequest {
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
HttpResponse response = doGetUserInfo(authToken);
JSONObject object = JSONObject.parseObject(response.body());
String response = doGetUserInfo(authToken);
JSONObject object = JSONObject.parseObject(response);
this.checkResponse(object);
object = object.getJSONObject("data");
return AuthUser.builder()
.rawUserInfo(object)
.uuid(object.getString("id"))
.username(object.getString("name"))
.avatar("https://coding.net/" + object.getString("avatar"))
.blog("https://coding.net/" + object.getString("path"))
.avatar("https://coding.net" + object.getString("avatar"))
.blog("https://coding.net" + object.getString("path"))
.nickname(object.getString("name"))
.company(object.getString("company"))
.location(object.getString("location"))
@@ -54,7 +59,7 @@ public class AuthCodingRequest extends AuthDefaultRequest {
.email(object.getString("email"))
.remark(object.getString("slogan"))
.token(authToken)
.source(source)
.source(source.toString())
.build();
}
@@ -78,7 +83,7 @@ public class AuthCodingRequest extends AuthDefaultRequest {
*/
@Override
public String authorize(String state) {
return UrlBuilder.fromBaseUrl(source.authorize())
return UrlBuilder.fromBaseUrl(String.format(source.authorize(), config.getCodingGroupName()))
.queryParam("response_type", "code")
.queryParam("client_id", config.getClientId())
.queryParam("redirect_uri", config.getRedirectUri())
@@ -86,4 +91,33 @@ public class AuthCodingRequest extends AuthDefaultRequest {
.queryParam("state", getRealState(state))
.build();
}
/**
* 返回获取accessToken的url
*
* @param code 授权码
* @return 返回获取accessToken的url
*/
@Override
public String accessTokenUrl(String code) {
return UrlBuilder.fromBaseUrl(String.format(source.accessToken(), config.getCodingGroupName()))
.queryParam("code", code)
.queryParam("client_id", config.getClientId())
.queryParam("client_secret", config.getClientSecret())
.queryParam("grant_type", "authorization_code")
.queryParam("redirect_uri", config.getRedirectUri())
.build();
}
/**
* 返回获取userInfo的url
*
* @param authToken token
* @return 返回获取userInfo的url
*/
@Override
public String userInfoUrl(AuthToken authToken) {
return UrlBuilder.fromBaseUrl(String.format(source.userInfo(), config.getCodingGroupName()))
.queryParam("access_token", authToken.getAccessToken())
.build();
}
}
@@ -1,9 +1,9 @@
package me.zhyd.oauth.request;
import cn.hutool.http.HttpResponse;
import com.alibaba.fastjson.JSONObject;
import me.zhyd.oauth.cache.AuthStateCache;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.config.AuthSource;
import me.zhyd.oauth.config.AuthDefaultSource;
import me.zhyd.oauth.enums.AuthUserGender;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.AuthCallback;
@@ -20,30 +20,35 @@ import me.zhyd.oauth.model.AuthUser;
public class AuthCsdnRequest extends AuthDefaultRequest {
public AuthCsdnRequest(AuthConfig config) {
super(config, AuthSource.CSDN);
super(config, AuthDefaultSource.CSDN);
}
public AuthCsdnRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.CSDN, authStateCache);
}
@Override
protected AuthToken getAccessToken(AuthCallback authCallback) {
HttpResponse response = doPostAuthorizationCode(authCallback.getCode());
JSONObject accessTokenObject = JSONObject.parseObject(response.body());
String response = doPostAuthorizationCode(authCallback.getCode());
JSONObject accessTokenObject = JSONObject.parseObject(response);
this.checkResponse(accessTokenObject);
return AuthToken.builder().accessToken(accessTokenObject.getString("access_token")).build();
}
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
HttpResponse response = doGetUserInfo(authToken);
JSONObject object = JSONObject.parseObject(response.body());
String response = doGetUserInfo(authToken);
JSONObject object = JSONObject.parseObject(response);
this.checkResponse(object);
return AuthUser.builder()
.rawUserInfo(object)
.uuid(object.getString("username"))
.username(object.getString("username"))
.remark(object.getString("description"))
.blog(object.getString("website"))
.gender(AuthUserGender.UNKNOWN)
.token(authToken)
.source(source)
.source(source.toString())
.build();
}
@@ -1,13 +1,13 @@
package me.zhyd.oauth.request;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import lombok.extern.slf4j.Slf4j;
import me.zhyd.oauth.utils.HttpUtils;
import me.zhyd.oauth.cache.AuthDefaultStateCache;
import me.zhyd.oauth.cache.AuthStateCache;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.config.AuthSource;
import me.zhyd.oauth.enums.AuthResponseStatus;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.log.Log;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthResponse;
import me.zhyd.oauth.model.AuthToken;
@@ -24,16 +24,21 @@ import me.zhyd.oauth.utils.UuidUtils;
* @author yangkai.shen (https://xkcoding.com)
* @since 1.0.0
*/
@Slf4j
public abstract class AuthDefaultRequest implements AuthRequest {
protected AuthConfig config;
protected AuthSource source;
protected AuthStateCache authStateCache;
public AuthDefaultRequest(AuthConfig config, AuthSource source) {
this(config, source, AuthDefaultStateCache.INSTANCE);
}
public AuthDefaultRequest(AuthConfig config, AuthSource source, AuthStateCache authStateCache) {
this.config = config;
this.source = source;
this.authStateCache = authStateCache;
if (!AuthChecker.isSupportedAuth(config, source)) {
throw new AuthException(AuthResponseStatus.PARAMETER_INCOMPLETE);
throw new AuthException(AuthResponseStatus.PARAMETER_INCOMPLETE, source);
}
// 校验配置合法性
AuthChecker.checkConfig(config, source);
@@ -68,14 +73,16 @@ public abstract class AuthDefaultRequest implements AuthRequest {
@Override
public AuthResponse login(AuthCallback authCallback) {
try {
AuthChecker.checkCode(source == AuthSource.ALIPAY ? authCallback.getAuth_code() : authCallback.getCode());
AuthChecker.checkState(authCallback.getState());
AuthChecker.checkCode(source, authCallback);
if (!config.isIgnoreCheckState()) {
AuthChecker.checkState(authCallback.getState(), source, authStateCache);
}
AuthToken authToken = this.getAccessToken(authCallback);
AuthUser user = this.getUserInfo(authToken);
return AuthResponse.builder().code(AuthResponseStatus.SUCCESS.getCode()).data(user).build();
} catch (Exception e) {
log.error("Failed to login with oauth authorization.", e);
Log.error("Failed to login with oauth authorization.", e);
return this.responseError(e);
}
}
@@ -88,10 +95,15 @@ public abstract class AuthDefaultRequest implements AuthRequest {
*/
private AuthResponse responseError(Exception e) {
int errorCode = AuthResponseStatus.FAILURE.getCode();
String errorMsg = e.getMessage();
if (e instanceof AuthException) {
errorCode = ((AuthException) e).getErrorCode();
AuthException authException = ((AuthException) e);
errorCode = authException.getErrorCode();
if (StringUtils.isNotEmpty(authException.getErrorMsg())) {
errorMsg = authException.getErrorMsg();
}
}
return AuthResponse.builder().code(errorCode).msg(e.getMessage()).build();
return AuthResponse.builder().code(errorCode).msg(errorMsg).build();
}
/**
@@ -189,7 +201,7 @@ public abstract class AuthDefaultRequest implements AuthRequest {
state = UuidUtils.getUUID();
}
// 缓存state
AuthStateCache.cache(state, state);
authStateCache.cache(state, state);
return state;
}
@@ -197,61 +209,62 @@ public abstract class AuthDefaultRequest implements AuthRequest {
* 通用的 authorizationCode 协议
*
* @param code code码
* @return HttpResponse
* @return Response
*/
protected HttpResponse doPostAuthorizationCode(String code) {
return HttpRequest.post(accessTokenUrl(code)).execute();
protected String doPostAuthorizationCode(String code) {
return new HttpUtils(config.getHttpConfig()).post(accessTokenUrl(code));
}
/**
* 通用的 authorizationCode 协议
*
* @param code code码
* @return HttpResponse
* @return Response
*/
protected HttpResponse doGetAuthorizationCode(String code) {
return HttpRequest.get(accessTokenUrl(code)).execute();
protected String doGetAuthorizationCode(String code) {
return new HttpUtils(config.getHttpConfig()).get(accessTokenUrl(code));
}
/**
* 通用的 用户信息
*
* @param authToken token封装
* @return HttpResponse
* @return Response
*/
@Deprecated
protected HttpResponse doPostUserInfo(AuthToken authToken) {
return HttpRequest.post(userInfoUrl(authToken)).execute();
protected String doPostUserInfo(AuthToken authToken) {
return new HttpUtils(config.getHttpConfig()).post(userInfoUrl(authToken));
}
/**
* 通用的 用户信息
*
* @param authToken token封装
* @return HttpResponse
* @return Response
*/
protected HttpResponse doGetUserInfo(AuthToken authToken) {
return HttpRequest.get(userInfoUrl(authToken)).execute();
protected String doGetUserInfo(AuthToken authToken) {
return new HttpUtils(config.getHttpConfig()).get(userInfoUrl(authToken));
}
/**
* 通用的post形式的取消授权方法
*
* @param authToken token封装
* @return HttpResponse
* @return Response
*/
@Deprecated
protected HttpResponse doPostRevoke(AuthToken authToken) {
return HttpRequest.post(revokeUrl(authToken)).execute();
protected String doPostRevoke(AuthToken authToken) {
return new HttpUtils(config.getHttpConfig()).post(revokeUrl(authToken));
}
/**
* 通用的post形式的取消授权方法
*
* @param authToken token封装
* @return HttpResponse
* @return Response
*/
protected HttpResponse doGetRevoke(AuthToken authToken) {
return HttpRequest.get(revokeUrl(authToken)).execute();
protected String doGetRevoke(AuthToken authToken) {
return new HttpUtils(config.getHttpConfig()).get(revokeUrl(authToken));
}
}
@@ -1,17 +1,17 @@
package me.zhyd.oauth.request;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import me.zhyd.oauth.utils.HttpUtils;
import me.zhyd.oauth.cache.AuthStateCache;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.config.AuthSource;
import me.zhyd.oauth.config.AuthDefaultSource;
import me.zhyd.oauth.enums.AuthUserGender;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.utils.GlobalAuthUtil;
import me.zhyd.oauth.utils.GlobalAuthUtils;
import me.zhyd.oauth.utils.UrlBuilder;
/**
@@ -23,7 +23,11 @@ import me.zhyd.oauth.utils.UrlBuilder;
public class AuthDingTalkRequest extends AuthDefaultRequest {
public AuthDingTalkRequest(AuthConfig config) {
super(config, AuthSource.DINGTALK);
super(config, AuthDefaultSource.DINGTALK);
}
public AuthDingTalkRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.DINGTALK, authStateCache);
}
@Override
@@ -36,8 +40,8 @@ public class AuthDingTalkRequest extends AuthDefaultRequest {
String code = authToken.getAccessCode();
JSONObject param = new JSONObject();
param.put("tmp_auth_code", code);
HttpResponse response = HttpRequest.post(userInfoUrl(authToken)).body(param.toJSONString()).execute();
JSONObject object = JSON.parseObject(response.body());
String response = new HttpUtils(config.getHttpConfig()).post(userInfoUrl(authToken), param.toJSONString());
JSONObject object = JSON.parseObject(response);
if (object.getIntValue("errcode") != 0) {
throw new AuthException(object.getString("errmsg"));
}
@@ -47,11 +51,12 @@ public class AuthDingTalkRequest extends AuthDefaultRequest {
.unionId(object.getString("unionid"))
.build();
return AuthUser.builder()
.rawUserInfo(object)
.uuid(object.getString("unionid"))
.nickname(object.getString("nick"))
.username(object.getString("nick"))
.gender(AuthUserGender.UNKNOWN)
.source(source)
.source(source.toString())
.token(token)
.build();
}
@@ -84,7 +89,7 @@ public class AuthDingTalkRequest extends AuthDefaultRequest {
protected String userInfoUrl(AuthToken authToken) {
// 根据timestamp, appSecret计算签名值
String timestamp = System.currentTimeMillis() + "";
String urlEncodeSignature = GlobalAuthUtil.generateDingTalkSignature(config.getClientSecret(), timestamp);
String urlEncodeSignature = GlobalAuthUtils.generateDingTalkSignature(config.getClientSecret(), timestamp);
return UrlBuilder.fromBaseUrl(source.userInfo())
.queryParam("signature", urlEncodeSignature)
@@ -1,14 +1,17 @@
package me.zhyd.oauth.request;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import com.alibaba.fastjson.JSONObject;
import me.zhyd.oauth.utils.HttpUtils;
import me.zhyd.oauth.cache.AuthStateCache;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.config.AuthSource;
import me.zhyd.oauth.config.AuthDefaultSource;
import me.zhyd.oauth.enums.AuthResponseStatus;
import me.zhyd.oauth.enums.AuthUserGender;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.*;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthResponse;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.utils.UrlBuilder;
@@ -21,7 +24,11 @@ import me.zhyd.oauth.utils.UrlBuilder;
public class AuthDouyinRequest extends AuthDefaultRequest {
public AuthDouyinRequest(AuthConfig config) {
super(config, AuthSource.DOUYIN);
super(config, AuthDefaultSource.DOUYIN);
}
public AuthDouyinRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.DOUYIN, authStateCache);
}
@Override
@@ -31,18 +38,21 @@ public class AuthDouyinRequest extends AuthDefaultRequest {
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
HttpResponse response = doGetUserInfo(authToken);
JSONObject userInfoObject = JSONObject.parseObject(response.body());
String response = doGetUserInfo(authToken);
JSONObject userInfoObject = JSONObject.parseObject(response);
this.checkResponse(userInfoObject);
JSONObject object = userInfoObject.getJSONObject("data");
return AuthUser.builder()
.uuid(userInfoObject.getString("union_id"))
.username(userInfoObject.getString("nickname"))
.nickname(userInfoObject.getString("nickname"))
.avatar(userInfoObject.getString("avatar"))
.remark(userInfoObject.getString("description"))
.gender(AuthUserGender.UNKNOWN)
.rawUserInfo(object)
.uuid(object.getString("union_id"))
.username(object.getString("nickname"))
.nickname(object.getString("nickname"))
.avatar(object.getString("avatar"))
.remark(object.getString("description"))
.gender(AuthUserGender.getRealGender(object.getString("gender")))
.location(String.format("%s %s %s", object.getString("country"), object.getString("province"), object.getString("city")))
.token(authToken)
.source(source)
.source(source.toString())
.build();
}
@@ -75,16 +85,16 @@ public class AuthDouyinRequest extends AuthDefaultRequest {
* @return token对象
*/
private AuthToken getToken(String accessTokenUrl) {
HttpResponse response = HttpRequest.post(accessTokenUrl).execute();
String accessTokenStr = response.body();
JSONObject object = JSONObject.parseObject(accessTokenStr);
String response = new HttpUtils(config.getHttpConfig()).post(accessTokenUrl);
JSONObject object = JSONObject.parseObject(response);
this.checkResponse(object);
JSONObject dataObj = object.getJSONObject("data");
return AuthToken.builder()
.accessToken(object.getString("access_token"))
.openId(object.getString("open_id"))
.expireIn(object.getIntValue("expires_in"))
.refreshToken(object.getString("refresh_token"))
.scope(object.getString("scope"))
.accessToken(dataObj.getString("access_token"))
.openId(dataObj.getString("open_id"))
.expireIn(dataObj.getIntValue("expires_in"))
.refreshToken(dataObj.getString("refresh_token"))
.scope(dataObj.getString("scope"))
.build();
}
@@ -0,0 +1,178 @@
package me.zhyd.oauth.request;
import com.alibaba.fastjson.JSONObject;
import me.zhyd.oauth.utils.HttpUtils;
import com.xkcoding.http.constants.Constants;
import com.xkcoding.http.support.HttpHeader;
import me.zhyd.oauth.cache.AuthStateCache;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.config.AuthDefaultSource;
import me.zhyd.oauth.enums.AuthResponseStatus;
import me.zhyd.oauth.enums.AuthUserGender;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthResponse;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.utils.Base64Utils;
import me.zhyd.oauth.utils.GlobalAuthUtils;
import me.zhyd.oauth.utils.UrlBuilder;
import me.zhyd.oauth.utils.UuidUtils;
import java.util.HashMap;
import java.util.Map;
/**
* 饿了么
* <p>
* 注:集成的是正式环境,非沙箱环境
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @since 1.12.0
*/
public class AuthElemeRequest extends AuthDefaultRequest {
private static final String CONTENT_TYPE_FORM = "application/x-www-form-urlencoded;charset=UTF-8";
private static final String CONTENT_TYPE_JSON = "application/json; charset=utf-8";
public AuthElemeRequest(AuthConfig config) {
super(config, AuthDefaultSource.ELEME);
}
public AuthElemeRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.ELEME, authStateCache);
}
@Override
protected AuthToken getAccessToken(AuthCallback authCallback) {
Map<String, String> form = new HashMap<>(4);
form.put("client_id", config.getClientId());
form.put("redirect_uri", config.getRedirectUri());
form.put("code", authCallback.getCode());
form.put("grant_type", "authorization_code");
HttpHeader httpHeader = this.buildHeader(CONTENT_TYPE_FORM, this.getRequestId(), true);
String response = new HttpUtils(config.getHttpConfig()).post(source.accessToken(), form, httpHeader, false);
JSONObject object = JSONObject.parseObject(response);
this.checkResponse(object);
return AuthToken.builder()
.accessToken(object.getString("access_token"))
.refreshToken(object.getString("refresh_token"))
.tokenType(object.getString("token_type"))
.expireIn(object.getIntValue("expires_in"))
.build();
}
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
Map<String, Object> parameters = new HashMap<>();
// 获取商户账号信息的API接口名称
String action = "eleme.user.getUser";
// 时间戳,单位秒。API服务端允许客户端请求最大时间误差为正负5分钟。
final long timestamp = System.currentTimeMillis();
// 公共参数
Map<String, Object> metasHashMap = new HashMap<>();
metasHashMap.put("app_key", config.getClientId());
metasHashMap.put("timestamp", timestamp);
String signature = GlobalAuthUtils.generateElemeSignature(config.getClientId(), config.getClientSecret(), timestamp, action, authToken
.getAccessToken(), parameters);
String requestId = this.getRequestId();
Map<String, Object> paramsMap = new HashMap<>();
paramsMap.put("nop", "1.0.0");
paramsMap.put("id", requestId);
paramsMap.put("action", action);
paramsMap.put("token", authToken.getAccessToken());
paramsMap.put("metas", metasHashMap);
paramsMap.put("params", parameters);
paramsMap.put("signature", signature);
HttpHeader httpHeader = this.buildHeader(CONTENT_TYPE_JSON, requestId, false);
String response = new HttpUtils(config.getHttpConfig()).post(source.userInfo(), JSONObject.toJSONString(paramsMap), httpHeader);
JSONObject object = JSONObject.parseObject(response);
// 校验请求
if (object.containsKey("name")) {
throw new AuthException(object.getString("message"));
}
if (object.containsKey("error") && null != object.get("error")) {
throw new AuthException(object.getJSONObject("error").getString("message"));
}
JSONObject result = object.getJSONObject("result");
return AuthUser.builder()
.rawUserInfo(result)
.uuid(result.getString("userId"))
.username(result.getString("userName"))
.nickname(result.getString("userName"))
.gender(AuthUserGender.UNKNOWN)
.token(authToken)
.source(source.toString())
.build();
}
@Override
public AuthResponse refresh(AuthToken oldToken) {
Map<String, String> form = new HashMap<>(2);
form.put("refresh_token", oldToken.getRefreshToken());
form.put("grant_type", "refresh_token");
HttpHeader httpHeader = this.buildHeader(CONTENT_TYPE_FORM, this.getRequestId(), true);
String response = new HttpUtils(config.getHttpConfig()).post(source.refresh(), form, httpHeader, false);
JSONObject object = JSONObject.parseObject(response);
this.checkResponse(object);
return AuthResponse.builder()
.code(AuthResponseStatus.SUCCESS.getCode())
.data(AuthToken.builder()
.accessToken(object.getString("access_token"))
.refreshToken(object.getString("refresh_token"))
.tokenType(object.getString("token_type"))
.expireIn(object.getIntValue("expires_in"))
.build())
.build();
}
@Override
public String authorize(String state) {
return UrlBuilder.fromBaseUrl(super.authorize(state)).queryParam("scope", "all").build();
}
private String getBasic(String appKey, String appSecret) {
StringBuilder sb = new StringBuilder();
String encodeToString = Base64Utils.encode((appKey + ":" + appSecret).getBytes());
sb.append("Basic").append(" ").append(encodeToString);
return sb.toString();
}
private HttpHeader buildHeader(String contentType, String requestId, boolean auth) {
HttpHeader httpHeader = new HttpHeader();
httpHeader.add("Accept", "text/xml,text/javascript,text/html");
httpHeader.add(Constants.CONTENT_TYPE, contentType);
httpHeader.add("Accept-Encoding", "gzip");
httpHeader.add("User-Agent", "eleme-openapi-java-sdk");
httpHeader.add("x-eleme-requestid", requestId);
if (auth) {
httpHeader.add("Authorization", this.getBasic(config.getClientId(), config.getClientSecret()));
}
return httpHeader;
}
private String getRequestId() {
return (UuidUtils.getUUID() + "|" + System.currentTimeMillis()).toUpperCase();
}
private void checkResponse(JSONObject object) {
if (object.containsKey("error")) {
throw new AuthException(object.getString("error_description"));
}
}
}
@@ -1,9 +1,9 @@
package me.zhyd.oauth.request;
import cn.hutool.http.HttpResponse;
import com.alibaba.fastjson.JSONObject;
import me.zhyd.oauth.cache.AuthStateCache;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.config.AuthSource;
import me.zhyd.oauth.config.AuthDefaultSource;
import me.zhyd.oauth.enums.AuthUserGender;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.AuthCallback;
@@ -20,13 +20,17 @@ import me.zhyd.oauth.utils.UrlBuilder;
public class AuthFacebookRequest extends AuthDefaultRequest {
public AuthFacebookRequest(AuthConfig config) {
super(config, AuthSource.FACEBOOK);
super(config, AuthDefaultSource.FACEBOOK);
}
public AuthFacebookRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.FACEBOOK, authStateCache);
}
@Override
protected AuthToken getAccessToken(AuthCallback authCallback) {
HttpResponse response = doPostAuthorizationCode(authCallback.getCode());
JSONObject accessTokenObject = JSONObject.parseObject(response.body());
String response = doPostAuthorizationCode(authCallback.getCode());
JSONObject accessTokenObject = JSONObject.parseObject(response);
this.checkResponse(accessTokenObject);
return AuthToken.builder()
.accessToken(accessTokenObject.getString("access_token"))
@@ -37,20 +41,21 @@ public class AuthFacebookRequest extends AuthDefaultRequest {
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
HttpResponse response = doGetUserInfo(authToken);
String userInfo = response.body();
String userInfo = doGetUserInfo(authToken);
JSONObject object = JSONObject.parseObject(userInfo);
this.checkResponse(object);
return AuthUser.builder()
.rawUserInfo(object)
.uuid(object.getString("id"))
.username(object.getString("name"))
.nickname(object.getString("name"))
.blog(object.getString("link"))
.avatar(getUserPicture(object))
.location(object.getString("locale"))
.email(object.getString("email"))
.gender(AuthUserGender.getRealGender(object.getString("gender")))
.token(authToken)
.source(source)
.source(source.toString())
.build();
}
@@ -76,7 +81,7 @@ public class AuthFacebookRequest extends AuthDefaultRequest {
protected String userInfoUrl(AuthToken authToken) {
return UrlBuilder.fromBaseUrl(source.userInfo())
.queryParam("access_token", authToken.getAccessToken())
.queryParam("fields", "id,name,birthday,gender,hometown,email,devices,picture.width(400)")
.queryParam("fields", "id,name,birthday,gender,hometown,email,devices,picture.width(400),link")
.build();
}
@@ -0,0 +1,114 @@
package me.zhyd.oauth.request;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.xkcoding.http.support.HttpHeader;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.config.AuthDefaultSource;
import me.zhyd.oauth.enums.AuthResponseStatus;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthResponse;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.utils.GlobalAuthUtils;
import me.zhyd.oauth.utils.HttpUtils;
import me.zhyd.oauth.utils.UrlBuilder;
/**
* 注意:该平台暂时存在问题,请不要使用。待修复完成后会重新发版by yadong.zhang
*
* @author beacon
* @since 1.14.0
*/
@Deprecated
public class AuthFeishuRequest extends AuthDefaultRequest {
public AuthFeishuRequest(AuthConfig config) {
super(config, AuthDefaultSource.FEISHU);
throw new AuthException(AuthResponseStatus.FAILURE);
}
@Override
protected AuthToken getAccessToken(AuthCallback authCallback) {
JSONObject requestObject = new JSONObject();
requestObject.put("app_id", config.getClientId());
requestObject.put("app_secret", config.getClientSecret());
requestObject.put("grant_type", "authorization_code");
requestObject.put("code", authCallback.getCode());
String response = new HttpUtils(config.getHttpConfig()).post(source.accessToken(), requestObject.toJSONString(), new HttpHeader()
.add("Content-Type", "application/json"));
JSONObject jsonObject = JSON.parseObject(response);
this.checkResponse(jsonObject);
return AuthToken.builder()
.accessToken(jsonObject.getString("access_token"))
.refreshToken(jsonObject.getString("refresh_token"))
.expireIn(jsonObject.getIntValue("expires_in"))
.tokenType(jsonObject.getString("token_type"))
.openId(jsonObject.getString("open_id"))
.build();
}
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
String accessToken = authToken.getAccessToken();
String response = new HttpUtils(config.getHttpConfig()).get(source.userInfo(), null, new HttpHeader()
.add("Content-Type", "application/json")
.add("Authorization", "Bearer " + accessToken), false);
JSONObject object = JSON.parseObject(response);
return AuthUser.builder()
.rawUserInfo(object)
.avatar(object.getString("AvatarUrl"))
.username(object.getString("Mobile"))
.email(object.getString("Email"))
.nickname("Name")
.build();
}
@Override
public AuthResponse refresh(AuthToken authToken) {
JSONObject requestObject = new JSONObject();
requestObject.put("app_id", config.getClientId());
requestObject.put("app_secret", config.getClientSecret());
requestObject.put("grant_type", "refresh_token");
requestObject.put("refresh_token", authToken.getRefreshToken());
String response = new HttpUtils(config.getHttpConfig()).post(source.refresh(), requestObject.toJSONString(), new HttpHeader()
.add("Content-Type", "application/json"));
JSONObject jsonObject = JSON.parseObject(response);
this.checkResponse(jsonObject);
return AuthResponse.builder()
.code(AuthResponseStatus.SUCCESS.getCode())
.data(AuthToken.builder()
.accessToken(jsonObject.getString("access_token"))
.refreshToken(jsonObject.getString("refresh_token"))
.expireIn(jsonObject.getIntValue("expires_in"))
.tokenType(jsonObject.getString("token_type"))
.openId(jsonObject.getString("open_id"))
.build())
.build();
}
@Override
public String authorize(String state) {
return UrlBuilder.fromBaseUrl(source.authorize())
.queryParam("app_id", config.getClientId())
.queryParam("redirect_uri", GlobalAuthUtils.urlEncode(config.getRedirectUri()))
.queryParam("state", getRealState(state))
.build();
}
/**
* 校验响应内容是否正确
*
* @param jsonObject 响应内容
*/
private void checkResponse(JSONObject jsonObject) {
if (jsonObject.getIntValue("code") != 0) {
throw new AuthException(jsonObject.getString("message"));
}
}
}
@@ -1,9 +1,9 @@
package me.zhyd.oauth.request;
import cn.hutool.http.HttpResponse;
import com.alibaba.fastjson.JSONObject;
import me.zhyd.oauth.cache.AuthStateCache;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.config.AuthSource;
import me.zhyd.oauth.config.AuthDefaultSource;
import me.zhyd.oauth.enums.AuthUserGender;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.AuthCallback;
@@ -19,13 +19,17 @@ import me.zhyd.oauth.model.AuthUser;
public class AuthGiteeRequest extends AuthDefaultRequest {
public AuthGiteeRequest(AuthConfig config) {
super(config, AuthSource.GITEE);
super(config, AuthDefaultSource.GITEE);
}
public AuthGiteeRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.GITEE, authStateCache);
}
@Override
protected AuthToken getAccessToken(AuthCallback authCallback) {
HttpResponse response = doPostAuthorizationCode(authCallback.getCode());
JSONObject accessTokenObject = JSONObject.parseObject(response.body());
String response = doPostAuthorizationCode(authCallback.getCode());
JSONObject accessTokenObject = JSONObject.parseObject(response);
this.checkResponse(accessTokenObject);
return AuthToken.builder()
.accessToken(accessTokenObject.getString("access_token"))
@@ -38,11 +42,11 @@ public class AuthGiteeRequest extends AuthDefaultRequest {
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
HttpResponse response = doGetUserInfo(authToken);
String userInfo = response.body();
String userInfo = doGetUserInfo(authToken);
JSONObject object = JSONObject.parseObject(userInfo);
this.checkResponse(object);
return AuthUser.builder()
.rawUserInfo(object)
.uuid(object.getString("id"))
.username(object.getString("login"))
.avatar(object.getString("avatar_url"))
@@ -54,7 +58,7 @@ public class AuthGiteeRequest extends AuthDefaultRequest {
.remark(object.getString("bio"))
.gender(AuthUserGender.UNKNOWN)
.token(authToken)
.source(source)
.source(source.toString())
.build();
}
@@ -1,15 +1,15 @@
package me.zhyd.oauth.request;
import cn.hutool.http.HttpResponse;
import com.alibaba.fastjson.JSONObject;
import me.zhyd.oauth.cache.AuthStateCache;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.config.AuthSource;
import me.zhyd.oauth.config.AuthDefaultSource;
import me.zhyd.oauth.enums.AuthUserGender;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.utils.GlobalAuthUtil;
import me.zhyd.oauth.utils.GlobalAuthUtils;
import java.util.Map;
@@ -22,16 +22,20 @@ import java.util.Map;
public class AuthGithubRequest extends AuthDefaultRequest {
public AuthGithubRequest(AuthConfig config) {
super(config, AuthSource.GITHUB);
super(config, AuthDefaultSource.GITHUB);
}
public AuthGithubRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.GITHUB, authStateCache);
}
@Override
protected AuthToken getAccessToken(AuthCallback authCallback) {
HttpResponse response = doPostAuthorizationCode(authCallback.getCode());
Map<String, String> res = GlobalAuthUtil.parseStringToMap(response.body());
if (res.containsKey("error")) {
throw new AuthException(res.get("error") + ":" + res.get("error_description"));
}
String response = doPostAuthorizationCode(authCallback.getCode());
Map<String, String> res = GlobalAuthUtils.parseStringToMap(response);
this.checkResponse(res.containsKey("error"), res.get("error_description"));
return AuthToken.builder()
.accessToken(res.get("access_token"))
.scope(res.get("scope"))
@@ -41,12 +45,13 @@ public class AuthGithubRequest extends AuthDefaultRequest {
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
HttpResponse response = doGetUserInfo(authToken);
JSONObject object = JSONObject.parseObject(response.body());
if (object.containsKey("error")) {
throw new AuthException(object.getString("error_description"));
}
String response = doGetUserInfo(authToken);
JSONObject object = JSONObject.parseObject(response);
this.checkResponse(object.containsKey("error"), object.getString("error_description"));
return AuthUser.builder()
.rawUserInfo(object)
.uuid(object.getString("id"))
.username(object.getString("login"))
.avatar(object.getString("avatar_url"))
@@ -58,8 +63,14 @@ public class AuthGithubRequest extends AuthDefaultRequest {
.remark(object.getString("bio"))
.gender(AuthUserGender.UNKNOWN)
.token(authToken)
.source(source)
.source(source.toString())
.build();
}
private void checkResponse(boolean error, String error_description) {
if (error) {
throw new AuthException(error_description);
}
}
}
@@ -0,0 +1,95 @@
package me.zhyd.oauth.request;
import com.alibaba.fastjson.JSONObject;
import me.zhyd.oauth.cache.AuthStateCache;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.config.AuthDefaultSource;
import me.zhyd.oauth.enums.AuthUserGender;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.utils.UrlBuilder;
/**
* Gitlab登录
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @since 1.11.0
*/
public class AuthGitlabRequest extends AuthDefaultRequest {
public AuthGitlabRequest(AuthConfig config) {
super(config, AuthDefaultSource.GITLAB);
}
public AuthGitlabRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.GITLAB, authStateCache);
}
@Override
protected AuthToken getAccessToken(AuthCallback authCallback) {
String response = doPostAuthorizationCode(authCallback.getCode());
JSONObject object = JSONObject.parseObject(response);
this.checkResponse(object);
return AuthToken.builder()
.accessToken(object.getString("access_token"))
.refreshToken(object.getString("refresh_token"))
.idToken(object.getString("id_token"))
.tokenType(object.getString("token_type"))
.scope(object.getString("scope"))
.build();
}
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
String response = doGetUserInfo(authToken);
JSONObject object = JSONObject.parseObject(response);
this.checkResponse(object);
return AuthUser.builder()
.rawUserInfo(object)
.uuid(object.getString("id"))
.username(object.getString("username"))
.nickname(object.getString("name"))
.avatar(object.getString("avatar_url"))
.blog(object.getString("web_url"))
.company(object.getString("organization"))
.location(object.getString("location"))
.email(object.getString("email"))
.remark(object.getString("bio"))
.gender(AuthUserGender.UNKNOWN)
.token(authToken)
.source(source.toString())
.build();
}
private void checkResponse(JSONObject object) {
// oauth/token 验证异常
if (object.containsKey("error")) {
throw new AuthException(object.getString("error_description"));
}
// user 验证异常
if (object.containsKey("message")) {
throw new AuthException(object.getString("message"));
}
}
/**
* 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state}
*
* @param state state 验证授权流程的参数,可以防止csrf
* @return 返回授权地址
* @since 1.11.0
*/
@Override
public String authorize(String state) {
return UrlBuilder.fromBaseUrl(super.authorize(state))
.queryParam("scope", "read_user+openid+profile+email")
.build();
}
}
@@ -1,10 +1,11 @@
package me.zhyd.oauth.request;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import com.alibaba.fastjson.JSONObject;
import me.zhyd.oauth.utils.HttpUtils;
import com.xkcoding.http.support.HttpHeader;
import me.zhyd.oauth.cache.AuthStateCache;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.config.AuthSource;
import me.zhyd.oauth.config.AuthDefaultSource;
import me.zhyd.oauth.enums.AuthUserGender;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.AuthCallback;
@@ -21,13 +22,17 @@ import me.zhyd.oauth.utils.UrlBuilder;
public class AuthGoogleRequest extends AuthDefaultRequest {
public AuthGoogleRequest(AuthConfig config) {
super(config, AuthSource.GOOGLE);
super(config, AuthDefaultSource.GOOGLE);
}
public AuthGoogleRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.GOOGLE, authStateCache);
}
@Override
protected AuthToken getAccessToken(AuthCallback authCallback) {
HttpResponse response = doPostAuthorizationCode(authCallback.getCode());
JSONObject accessTokenObject = JSONObject.parseObject(response.body());
String response = doPostAuthorizationCode(authCallback.getCode());
JSONObject accessTokenObject = JSONObject.parseObject(response);
this.checkResponse(accessTokenObject);
return AuthToken.builder()
.accessToken(accessTokenObject.getString("access_token"))
@@ -40,13 +45,13 @@ public class AuthGoogleRequest extends AuthDefaultRequest {
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
HttpResponse response = HttpRequest.post(userInfoUrl(authToken))
.header("Authorization", "Bearer " + authToken.getAccessToken())
.execute();
String userInfo = response.body();
HttpHeader httpHeader = new HttpHeader();
httpHeader.add("Authorization", "Bearer " + authToken.getAccessToken());
String userInfo = new HttpUtils(config.getHttpConfig()).post(userInfoUrl(authToken), null, httpHeader);
JSONObject object = JSONObject.parseObject(userInfo);
this.checkResponse(object);
return AuthUser.builder()
.rawUserInfo(object)
.uuid(object.getString("sub"))
.username(object.getString("email"))
.avatar(object.getString("picture"))
@@ -55,7 +60,7 @@ public class AuthGoogleRequest extends AuthDefaultRequest {
.email(object.getString("email"))
.gender(AuthUserGender.UNKNOWN)
.token(authToken)
.source(source)
.source(source.toString())
.build();
}
@@ -0,0 +1,200 @@
package me.zhyd.oauth.request;
import com.alibaba.fastjson.JSONObject;
import me.zhyd.oauth.utils.HttpUtils;
import me.zhyd.oauth.cache.AuthStateCache;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.config.AuthDefaultSource;
import me.zhyd.oauth.enums.AuthUserGender;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthResponse;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.utils.UrlBuilder;
import java.util.HashMap;
import java.util.Map;
import static me.zhyd.oauth.enums.AuthResponseStatus.SUCCESS;
/**
* 华为授权登录
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0
* @since 1.10.0
*/
public class AuthHuaweiRequest extends AuthDefaultRequest {
public AuthHuaweiRequest(AuthConfig config) {
super(config, AuthDefaultSource.HUAWEI);
}
public AuthHuaweiRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.HUAWEI, authStateCache);
}
/**
* 获取access token
*
* @param authCallback 授权成功后的回调参数
* @return token
* @see AuthDefaultRequest#authorize()
* @see AuthDefaultRequest#authorize(String)
*/
@Override
protected AuthToken getAccessToken(AuthCallback authCallback) {
Map<String, String> form = new HashMap<>(5);
form.put("grant_type", "authorization_code");
form.put("code", authCallback.getAuthorization_code());
form.put("client_id", config.getClientId());
form.put("client_secret", config.getClientSecret());
form.put("redirect_uri", config.getRedirectUri());
String response = new HttpUtils(config.getHttpConfig()).post(source.accessToken(), form, false);
return getAuthToken(response);
}
/**
* 使用token换取用户信息
*
* @param authToken token信息
* @return 用户信息
* @see AuthDefaultRequest#getAccessToken(AuthCallback)
*/
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
Map<String, String> form = new HashMap<>(4);
form.put("nsp_ts", System.currentTimeMillis() + "");
form.put("access_token", authToken.getAccessToken());
form.put("nsp_fmt", "JS");
form.put("nsp_svc", "OpenUP.User.getInfo");
String response = new HttpUtils(config.getHttpConfig()).post(source.userInfo(), form, false);
JSONObject object = JSONObject.parseObject(response);
this.checkResponse(object);
AuthUserGender gender = getRealGender(object);
return AuthUser.builder()
.rawUserInfo(object)
.uuid(object.getString("userID"))
.username(object.getString("userName"))
.nickname(object.getString("userName"))
.gender(gender)
.avatar(object.getString("headPictureURL"))
.token(authToken)
.source(source.toString())
.build();
}
/**
* 刷新access token (续期)
*
* @param authToken 登录成功后返回的Token信息
* @return AuthResponse
*/
@Override
public AuthResponse refresh(AuthToken authToken) {
Map<String, String> form = new HashMap<>(4);
form.put("client_id", config.getClientId());
form.put("client_secret", config.getClientSecret());
form.put("refresh_token", authToken.getRefreshToken());
form.put("grant_type", "refresh_token");
String response = new HttpUtils(config.getHttpConfig()).post(source.refresh(), form, false);
return AuthResponse.builder().code(SUCCESS.getCode()).data(getAuthToken(response)).build();
}
private AuthToken getAuthToken(String response) {
JSONObject object = JSONObject.parseObject(response);
this.checkResponse(object);
return AuthToken.builder()
.accessToken(object.getString("access_token"))
.expireIn(object.getIntValue("expires_in"))
.refreshToken(object.getString("refresh_token"))
.build();
}
/**
* 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state}
*
* @param state state 验证授权流程的参数,可以防止csrf
* @return 返回授权地址
* @since 1.9.3
*/
@Override
public String authorize(String state) {
return UrlBuilder.fromBaseUrl(source.authorize())
.queryParam("response_type", "code")
.queryParam("client_id", config.getClientId())
.queryParam("redirect_uri", config.getRedirectUri())
.queryParam("access_type", "offline")
.queryParam("scope", "https%3A%2F%2Fwww.huawei.com%2Fauth%2Faccount%2Fbase.profile")
.queryParam("state", getRealState(state))
.build();
}
/**
* 返回获取accessToken的url
*
* @param code 授权码
* @return 返回获取accessToken的url
*/
@Override
protected String accessTokenUrl(String code) {
return UrlBuilder.fromBaseUrl(source.accessToken())
.queryParam("grant_type", "authorization_code")
.queryParam("code", code)
.queryParam("client_id", config.getClientId())
.queryParam("client_secret", config.getClientSecret())
.queryParam("redirect_uri", config.getRedirectUri())
.build();
}
/**
* 返回获取userInfo的url
*
* @param authToken token
* @return 返回获取userInfo的url
*/
@Override
protected String userInfoUrl(AuthToken authToken) {
return UrlBuilder.fromBaseUrl(source.userInfo())
.queryParam("nsp_ts", System.currentTimeMillis())
.queryParam("access_token", authToken.getAccessToken())
.queryParam("nsp_fmt", "JS")
.queryParam("nsp_svc", "OpenUP.User.getInfo")
.build();
}
/**
* 获取用户的实际性别。华为系统中,用户的性别:1表示女,0表示男
*
* @param object obj
* @return AuthUserGender
*/
private AuthUserGender getRealGender(JSONObject object) {
int genderCodeInt = object.getIntValue("gender");
String genderCode = genderCodeInt == 1 ? "0" : (genderCodeInt == 0) ? "1" : genderCodeInt + "";
return AuthUserGender.getRealGender(genderCode);
}
/**
* 校验响应结果
*
* @param object 接口返回的结果
*/
private void checkResponse(JSONObject object) {
if (object.containsKey("NSP_STATUS")) {
throw new AuthException(object.getString("error"));
}
if (object.containsKey("error")) {
throw new AuthException(object.getString("sub_error") + ":" + object.getString("error_description"));
}
}
}
@@ -0,0 +1,144 @@
package me.zhyd.oauth.request;
import com.alibaba.fastjson.JSONObject;
import me.zhyd.oauth.utils.HttpUtils;
import me.zhyd.oauth.cache.AuthStateCache;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.config.AuthDefaultSource;
import me.zhyd.oauth.enums.AuthResponseStatus;
import me.zhyd.oauth.enums.AuthUserGender;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthResponse;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.utils.GlobalAuthUtils;
import me.zhyd.oauth.utils.UrlBuilder;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Map;
/**
* 京东登录
*
* @author harry.lee (harryleexyz@qq.com)
* @since 1.15.0
*/
public class AuthJdRequest extends AuthDefaultRequest {
public AuthJdRequest(AuthConfig config) {
super(config, AuthDefaultSource.JD);
}
public AuthJdRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.JD, authStateCache);
}
@Override
protected AuthToken getAccessToken(AuthCallback authCallback) {
Map<String, String> params = new HashMap<>(5);
params.put("app_key", config.getClientId());
params.put("app_secret", config.getClientSecret());
params.put("grant_type", "authorization_code");
params.put("code", authCallback.getCode());
String response = new HttpUtils(config.getHttpConfig()).post(source.accessToken(), params, false);
JSONObject object = JSONObject.parseObject(response);
this.checkResponse(object);
return AuthToken.builder()
.accessToken(object.getString("access_token"))
.expireIn(object.getIntValue("expires_in"))
.refreshToken(object.getString("refresh_token"))
.scope(object.getString("scope"))
.openId(object.getString("open_id"))
.build();
}
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
UrlBuilder urlBuilder = UrlBuilder.fromBaseUrl(source.userInfo())
.queryParam("access_token", authToken.getAccessToken())
.queryParam("app_key", config.getClientId())
.queryParam("method", "jingdong.user.getUserInfoByOpenId")
.queryParam("360buy_param_json", "{\"openId\":\"" + authToken.getOpenId() + "\"}")
.queryParam("timestamp", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")))
.queryParam("v", "2.0");
urlBuilder.queryParam("sign", GlobalAuthUtils.generateJdSignature(config.getClientSecret(), urlBuilder.getReadOnlyParams()));
String response = new HttpUtils(config.getHttpConfig()).post(urlBuilder.build(true));
JSONObject object = JSONObject.parseObject(response);
this.checkResponse(object);
JSONObject data = this.getUserDataJsonObject(object);
return AuthUser.builder()
.rawUserInfo(data)
.uuid(authToken.getOpenId())
.username(data.getString("nickName"))
.nickname(data.getString("nickName"))
.avatar(data.getString("imageUrl"))
.gender(AuthUserGender.getRealGender(data.getString("gendar")))
.token(authToken)
.source(source.toString())
.build();
}
/**
* 个人用户无法申请应用
* 暂时只能参考官网给出的返回结果解析
*
* @param object 请求返回结果
* @return data JSONObject
*/
private JSONObject getUserDataJsonObject(JSONObject object) {
return object.getJSONObject("jingdong_user_getUserInfoByOpenId_response")
.getJSONObject("getuserinfobyappidandopenid_result")
.getJSONObject("data");
}
@Override
public AuthResponse refresh(AuthToken oldToken) {
Map<String, String> params = new HashMap<>(5);
params.put("app_key", config.getClientId());
params.put("app_secret", config.getClientSecret());
params.put("grant_type", "refresh_token");
params.put("refresh_token", oldToken.getRefreshToken());
String response = new HttpUtils(config.getHttpConfig()).post(source.refresh(), params, false);
JSONObject object = JSONObject.parseObject(response);
this.checkResponse(object);
return AuthResponse.builder()
.code(AuthResponseStatus.SUCCESS.getCode())
.data(AuthToken.builder()
.accessToken(object.getString("access_token"))
.expireIn(object.getIntValue("expires_in"))
.refreshToken(object.getString("refresh_token"))
.scope(object.getString("scope"))
.openId(object.getString("open_id"))
.build())
.build();
}
private void checkResponse(JSONObject object) {
if (object.containsKey("error_response")) {
throw new AuthException(object.getJSONObject("error_response").getString("zh_desc"));
}
}
@Override
public String authorize(String state) {
return UrlBuilder.fromBaseUrl(source.authorize())
.queryParam("app_key", config.getClientId())
.queryParam("response_type", "code")
.queryParam("redirect_uri", config.getRedirectUri())
.queryParam("scope", "snsapi_base")
.queryParam("state", getRealState(state))
.build();
}
}
@@ -0,0 +1,133 @@
package me.zhyd.oauth.request;
import com.alibaba.fastjson.JSONObject;
import me.zhyd.oauth.utils.HttpUtils;
import me.zhyd.oauth.cache.AuthStateCache;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.config.AuthDefaultSource;
import me.zhyd.oauth.enums.AuthResponseStatus;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthResponse;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.utils.StringUtils;
import me.zhyd.oauth.utils.UrlBuilder;
/**
* 酷家乐授权登录
*
* @author shahuang
* @since 1.11.0
*/
public class AuthKujialeRequest extends AuthDefaultRequest {
public AuthKujialeRequest(AuthConfig config) {
super(config, AuthDefaultSource.KUJIALE);
}
public AuthKujialeRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.KUJIALE, authStateCache);
}
/**
* 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state}
* 默认只向用户请求用户信息授权
*
* @param state state 验证授权流程的参数,可以防止csrf
* @return 返回授权地址
* @since 1.11.0
*/
@Override
public String authorize(String state) {
return authorize(state, "get_user_info");
}
/**
* 请求授权url
*
* @param state state 验证授权流程的参数,可以防止csrf
* @param scopeStr 请求用户授权时向用户显示的可进行授权的列表。如果要填写多个接口名称,请用逗号隔开
* 参考https://open.kujiale.com/open/apps/2/docs?doc_id=95#Step1%EF%BC%9A%E8%8E%B7%E5%8F%96Authorization%20Code参数表内的scope字段
* @return authorize url
*/
public String authorize(String state, String scopeStr) {
UrlBuilder urlBuilder = UrlBuilder.fromBaseUrl(source.authorize())
.queryParam("response_type", "code")
.queryParam("client_id", config.getClientId())
.queryParam("redirect_uri", config.getRedirectUri())
.queryParam("state", getRealState(state));
if (StringUtils.isNotEmpty(scopeStr)) {
urlBuilder.queryParam("scope", scopeStr);
}
return urlBuilder.build();
}
@Override
public AuthToken getAccessToken(AuthCallback authCallback) {
String response = doPostAuthorizationCode(authCallback.getCode());
return getAuthToken(response);
}
private AuthToken getAuthToken(String response) {
JSONObject accessTokenObject = checkResponse(response);
JSONObject resultObject = accessTokenObject.getJSONObject("d");
return AuthToken.builder()
.accessToken(resultObject.getString("accessToken"))
.refreshToken(resultObject.getString("refreshToken"))
.expireIn(resultObject.getIntValue("expiresIn"))
.build();
}
private JSONObject checkResponse(String response) {
JSONObject accessTokenObject = JSONObject.parseObject(response);
if (!"0".equals(accessTokenObject.getString("c"))) {
throw new AuthException(accessTokenObject.getString("m"));
}
return accessTokenObject;
}
@Override
public AuthUser getUserInfo(AuthToken authToken) {
String openId = this.getOpenId(authToken);
String response = new HttpUtils(config.getHttpConfig()).get(UrlBuilder.fromBaseUrl(source.userInfo())
.queryParam("access_token", authToken.getAccessToken())
.queryParam("open_id", openId)
.build());
JSONObject object = JSONObject.parseObject(response);
if (!"0".equals(object.getString("c"))) {
throw new AuthException(object.getString("m"));
}
JSONObject resultObject = object.getJSONObject("d");
return AuthUser.builder()
.rawUserInfo(resultObject)
.username(resultObject.getString("userName"))
.nickname(resultObject.getString("userName"))
.avatar(resultObject.getString("avatar"))
.uuid(resultObject.getString("openId"))
.token(authToken)
.source(source.toString())
.build();
}
/**
* 获取酷家乐的openId,此id在当前client范围内可以唯一识别授权用户
*
* @param authToken 通过{@link AuthKujialeRequest#getAccessToken(AuthCallback)}获取到的{@code authToken}
* @return openId
*/
private String getOpenId(AuthToken authToken) {
String response = new HttpUtils(config.getHttpConfig()).get(UrlBuilder.fromBaseUrl("https://oauth.kujiale.com/oauth2/auth/user")
.queryParam("access_token", authToken.getAccessToken())
.build());
JSONObject accessTokenObject = checkResponse(response);
return accessTokenObject.getString("d");
}
@Override
public AuthResponse refresh(AuthToken authToken) {
String response = new HttpUtils(config.getHttpConfig()).post(refreshTokenUrl(authToken.getRefreshToken()));
return AuthResponse.builder().code(AuthResponseStatus.SUCCESS.getCode()).data(getAuthToken(response)).build();
}
}
@@ -1,16 +1,21 @@
package me.zhyd.oauth.request;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.JSONPath;
import com.xkcoding.http.constants.Constants;
import com.xkcoding.http.support.HttpHeader;
import me.zhyd.oauth.cache.AuthStateCache;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.config.AuthSource;
import me.zhyd.oauth.config.AuthDefaultSource;
import me.zhyd.oauth.enums.AuthResponseStatus;
import me.zhyd.oauth.enums.AuthUserGender;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.*;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthResponse;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.utils.HttpUtils;
import me.zhyd.oauth.utils.StringUtils;
import me.zhyd.oauth.utils.UrlBuilder;
@@ -24,7 +29,11 @@ import me.zhyd.oauth.utils.UrlBuilder;
public class AuthLinkedinRequest extends AuthDefaultRequest {
public AuthLinkedinRequest(AuthConfig config) {
super(config, AuthSource.LINKEDIN);
super(config, AuthDefaultSource.LINKEDIN);
}
public AuthLinkedinRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.LINKEDIN, authStateCache);
}
@Override
@@ -35,12 +44,13 @@ public class AuthLinkedinRequest extends AuthDefaultRequest {
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
String accessToken = authToken.getAccessToken();
HttpResponse response = HttpRequest.get(userInfoUrl(authToken))
.header("Host", "api.linkedin.com")
.header("Connection", "Keep-Alive")
.header("Authorization", "Bearer " + accessToken)
.execute();
JSONObject userInfoObject = JSONObject.parseObject(response.body());
HttpHeader httpHeader = new HttpHeader();
httpHeader.add("Host", "api.linkedin.com");
httpHeader.add("Connection", "Keep-Alive");
httpHeader.add("Authorization", "Bearer " + accessToken);
String response = new HttpUtils(config.getHttpConfig()).get(userInfoUrl(authToken), null, httpHeader, false);
JSONObject userInfoObject = JSONObject.parseObject(response);
this.checkResponse(userInfoObject);
@@ -52,6 +62,7 @@ public class AuthLinkedinRequest extends AuthDefaultRequest {
// 获取用户邮箱地址
String email = this.getUserEmail(accessToken);
return AuthUser.builder()
.rawUserInfo(userInfoObject)
.uuid(userInfoObject.getString("id"))
.username(userName)
.nickname(userName)
@@ -59,7 +70,7 @@ public class AuthLinkedinRequest extends AuthDefaultRequest {
.email(email)
.token(authToken)
.gender(AuthUserGender.UNKNOWN)
.source(AuthSource.LINKEDIN)
.source(source.toString())
.build();
}
@@ -93,17 +104,27 @@ public class AuthLinkedinRequest extends AuthDefaultRequest {
* @return 用户的头像地址
*/
private String getAvatar(JSONObject userInfoObject) {
String avatar = null;
JSONObject profilePictureObject = userInfoObject.getJSONObject("profilePicture");
if (profilePictureObject.containsKey("displayImage~")) {
JSONArray displayImageElements = profilePictureObject.getJSONObject("displayImage~")
.getJSONArray("elements");
if (null != displayImageElements && displayImageElements.size() > 0) {
JSONObject largestImageObj = displayImageElements.getJSONObject(displayImageElements.size() - 1);
avatar = largestImageObj.getJSONArray("identifiers").getJSONObject(0).getString("identifier");
}
if (null == profilePictureObject || !profilePictureObject.containsKey("displayImage~")) {
return null;
}
return avatar;
JSONObject displayImageObject = profilePictureObject.getJSONObject("displayImage~");
if (null == displayImageObject || !displayImageObject.containsKey("elements")) {
return null;
}
JSONArray displayImageElements = displayImageObject.getJSONArray("elements");
if (null == displayImageElements || displayImageElements.isEmpty()) {
return null;
}
JSONObject largestImageObj = displayImageElements.getJSONObject(displayImageElements.size() - 1);
if (null == largestImageObj || !largestImageObj.containsKey("identifiers")) {
return null;
}
JSONArray identifiers = largestImageObj.getJSONArray("identifiers");
if (null == identifiers || identifiers.isEmpty()) {
return null;
}
return identifiers.getJSONObject(0).getString("identifier");
}
/**
@@ -113,13 +134,16 @@ public class AuthLinkedinRequest extends AuthDefaultRequest {
* @return 用户的邮箱地址
*/
private String getUserEmail(String accessToken) {
HttpResponse emailResponse = HttpRequest.get("https://api.linkedin.com/v2/emailAddress?q=members&projection=(elements*(handle~))")
.header("Host", "api.linkedin.com")
.header("Connection", "Keep-Alive")
.header("Authorization", "Bearer " + accessToken)
.execute();
JSONObject emailObj = JSONObject.parseObject(emailResponse.body());
HttpHeader httpHeader = new HttpHeader();
httpHeader.add("Host", "api.linkedin.com");
httpHeader.add("Connection", "Keep-Alive");
httpHeader.add("Authorization", "Bearer " + accessToken);
String emailResponse = new HttpUtils(config.getHttpConfig()).get("https://api.linkedin.com/v2/emailAddress?q=members&projection=(elements*(handle~))", null, httpHeader, false);
JSONObject emailObj = JSONObject.parseObject(emailResponse);
this.checkResponse(emailObj);
Object obj = JSONPath.eval(emailObj, "$['elements'][0]['handle~']['emailAddress']");
return null == obj ? null : (String) obj;
}
@@ -133,19 +157,6 @@ public class AuthLinkedinRequest extends AuthDefaultRequest {
return firstName;
}
@Override
public AuthResponse refresh(AuthToken oldToken) {
String refreshToken = oldToken.getRefreshToken();
if (StringUtils.isEmpty(refreshToken)) {
throw new AuthException(AuthResponseStatus.UNSUPPORTED);
}
String refreshTokenUrl = refreshTokenUrl(refreshToken);
return AuthResponse.builder()
.code(AuthResponseStatus.SUCCESS.getCode())
.data(this.getToken(refreshTokenUrl))
.build();
}
/**
* 检查响应内容是否正确
*
@@ -153,7 +164,7 @@ public class AuthLinkedinRequest extends AuthDefaultRequest {
*/
private void checkResponse(JSONObject object) {
if (object.containsKey("error")) {
throw new AuthException(object.getString("error_description"));
throw new AuthException(object.getString("error_description"), source);
}
}
@@ -164,12 +175,12 @@ public class AuthLinkedinRequest extends AuthDefaultRequest {
* @return token对象
*/
private AuthToken getToken(String accessTokenUrl) {
HttpResponse response = HttpRequest.post(accessTokenUrl)
.header("Host", "www.linkedin.com")
.contentType("application/x-www-form-urlencoded")
.execute();
String accessTokenStr = response.body();
JSONObject accessTokenObject = JSONObject.parseObject(accessTokenStr);
HttpHeader httpHeader = new HttpHeader();
httpHeader.add("Host", "www.linkedin.com");
httpHeader.add(Constants.CONTENT_TYPE, "application/x-www-form-urlencoded");
String response = new HttpUtils(config.getHttpConfig()).post(accessTokenUrl, null, httpHeader);
JSONObject accessTokenObject = JSONObject.parseObject(response);
this.checkResponse(accessTokenObject);
@@ -0,0 +1,120 @@
package me.zhyd.oauth.request;
import com.alibaba.fastjson.JSONObject;
import me.zhyd.oauth.utils.HttpUtils;
import me.zhyd.oauth.cache.AuthStateCache;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.config.AuthDefaultSource;
import me.zhyd.oauth.enums.AuthResponseStatus;
import me.zhyd.oauth.enums.AuthUserGender;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthResponse;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.utils.UrlBuilder;
import java.util.HashMap;
import java.util.Map;
/**
* 美团登录
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @since 1.12.0
*/
public class AuthMeituanRequest extends AuthDefaultRequest {
public AuthMeituanRequest(AuthConfig config) {
super(config, AuthDefaultSource.MEITUAN);
}
public AuthMeituanRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.MEITUAN, authStateCache);
}
@Override
protected AuthToken getAccessToken(AuthCallback authCallback) {
Map<String, String> form = new HashMap<>(4);
form.put("app_id", config.getClientId());
form.put("secret", config.getClientSecret());
form.put("code", authCallback.getCode());
form.put("grant_type", "authorization_code");
String response = new HttpUtils(config.getHttpConfig()).post(source.accessToken(), form, false);
JSONObject object = JSONObject.parseObject(response);
this.checkResponse(object);
return AuthToken.builder()
.accessToken(object.getString("access_token"))
.refreshToken(object.getString("refresh_token"))
.expireIn(object.getIntValue("expires_in"))
.build();
}
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
Map<String, String> form = new HashMap<>(3);
form.put("app_id", config.getClientId());
form.put("secret", config.getClientSecret());
form.put("access_token", authToken.getAccessToken());
String response = new HttpUtils(config.getHttpConfig()).post(source.userInfo(), form, false);
JSONObject object = JSONObject.parseObject(response);
this.checkResponse(object);
return AuthUser.builder()
.rawUserInfo(object)
.uuid(object.getString("openid"))
.username(object.getString("nickname"))
.nickname(object.getString("nickname"))
.avatar(object.getString("avatar"))
.gender(AuthUserGender.UNKNOWN)
.token(authToken)
.source(source.toString())
.build();
}
@Override
public AuthResponse refresh(AuthToken oldToken) {
Map<String, String> form = new HashMap<>(4);
form.put("app_id", config.getClientId());
form.put("secret", config.getClientSecret());
form.put("refresh_token", oldToken.getRefreshToken());
form.put("grant_type", "refresh_token");
String response = new HttpUtils(config.getHttpConfig()).post(source.refresh(), form, false);
JSONObject object = JSONObject.parseObject(response);
this.checkResponse(object);
return AuthResponse.builder()
.code(AuthResponseStatus.SUCCESS.getCode())
.data(AuthToken.builder()
.accessToken(object.getString("access_token"))
.refreshToken(object.getString("refresh_token"))
.expireIn(object.getIntValue("expires_in"))
.build())
.build();
}
private void checkResponse(JSONObject object) {
if (object.containsKey("error_code")) {
throw new AuthException(object.getString("erroe_msg"));
}
}
@Override
public String authorize(String state) {
return UrlBuilder.fromBaseUrl(source.authorize())
.queryParam("response_type", "code")
.queryParam("app_id", config.getClientId())
.queryParam("redirect_uri", config.getRedirectUri())
.queryParam("state", getRealState(state))
.queryParam("scope", "")
.build();
}
}
@@ -1,16 +1,19 @@
package me.zhyd.oauth.request;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import me.zhyd.oauth.utils.HttpUtils;
import com.xkcoding.http.constants.Constants;
import me.zhyd.oauth.cache.AuthStateCache;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.config.AuthSource;
import me.zhyd.oauth.config.AuthDefaultSource;
import me.zhyd.oauth.enums.AuthResponseStatus;
import me.zhyd.oauth.enums.AuthUserGender;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.*;
import me.zhyd.oauth.log.Log;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthResponse;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.utils.UrlBuilder;
import java.text.MessageFormat;
@@ -21,12 +24,15 @@ import java.text.MessageFormat;
* @author yangkai.shen (https://xkcoding.com)
* @since 1.5.0
*/
@Slf4j
public class AuthMiRequest extends AuthDefaultRequest {
private static final String PREFIX = "&&&START&&&";
public AuthMiRequest(AuthConfig config) {
super(config, AuthSource.MI);
super(config, AuthDefaultSource.MI);
}
public AuthMiRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.MI, authStateCache);
}
@Override
@@ -35,8 +41,8 @@ public class AuthMiRequest extends AuthDefaultRequest {
}
private AuthToken getToken(String accessTokenUrl) {
HttpResponse response = HttpRequest.get(accessTokenUrl).execute();
String jsonStr = StrUtil.replace(response.body(), PREFIX, StrUtil.EMPTY);
String response = new HttpUtils(config.getHttpConfig()).get(accessTokenUrl);
String jsonStr = response.replace(PREFIX, Constants.EMPTY);
JSONObject accessTokenObject = JSONObject.parseObject(jsonStr);
if (accessTokenObject.containsKey("error")) {
@@ -58,37 +64,38 @@ public class AuthMiRequest extends AuthDefaultRequest {
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
// 获取用户信息
HttpResponse userResponse = doGetUserInfo(authToken);
String userResponse = doGetUserInfo(authToken);
JSONObject userProfile = JSONObject.parseObject(userResponse.body());
JSONObject userProfile = JSONObject.parseObject(userResponse);
if ("error".equalsIgnoreCase(userProfile.getString("result"))) {
throw new AuthException(userProfile.getString("description"));
}
JSONObject user = userProfile.getJSONObject("data");
JSONObject object = userProfile.getJSONObject("data");
AuthUser authUser = AuthUser.builder()
.rawUserInfo(object)
.uuid(authToken.getOpenId())
.username(user.getString("miliaoNick"))
.nickname(user.getString("miliaoNick"))
.avatar(user.getString("miliaoIcon"))
.email(user.getString("mail"))
.username(object.getString("miliaoNick"))
.nickname(object.getString("miliaoNick"))
.avatar(object.getString("miliaoIcon"))
.email(object.getString("mail"))
.gender(AuthUserGender.UNKNOWN)
.token(authToken)
.source(source)
.source(source.toString())
.build();
// 获取用户邮箱手机号等信息
String emailPhoneUrl = MessageFormat.format("{0}?clientId={1}&token={2}", "https://open.account.xiaomi.com/user/phoneAndEmail", config
.getClientId(), authToken.getAccessToken());
HttpResponse emailResponse = HttpRequest.get(emailPhoneUrl).execute();
JSONObject userEmailPhone = JSONObject.parseObject(emailResponse.body());
String emailResponse = new HttpUtils(config.getHttpConfig()).get(emailPhoneUrl);
JSONObject userEmailPhone = JSONObject.parseObject(emailResponse);
if (!"error".equalsIgnoreCase(userEmailPhone.getString("result"))) {
JSONObject emailPhone = userEmailPhone.getJSONObject("data");
authUser.setEmail(emailPhone.getString("email"));
} else {
log.warn("小米开发平台暂时不对外开放用户手机及邮箱信息的获取");
Log.warn("小米开发平台暂时不对外开放用户手机及邮箱信息的获取");
}
return authUser;
@@ -1,17 +1,23 @@
package me.zhyd.oauth.request;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import com.alibaba.fastjson.JSONObject;
import me.zhyd.oauth.utils.HttpUtils;
import com.xkcoding.http.constants.Constants;
import com.xkcoding.http.support.HttpHeader;
import com.xkcoding.http.util.MapUtil;
import me.zhyd.oauth.cache.AuthStateCache;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.config.AuthSource;
import me.zhyd.oauth.config.AuthDefaultSource;
import me.zhyd.oauth.enums.AuthResponseStatus;
import me.zhyd.oauth.enums.AuthUserGender;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.*;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthResponse;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.utils.UrlBuilder;
import static me.zhyd.oauth.utils.GlobalAuthUtil.parseQueryToMap;
import java.util.Map;
/**
* 微软登录
@@ -21,7 +27,11 @@ import static me.zhyd.oauth.utils.GlobalAuthUtil.parseQueryToMap;
*/
public class AuthMicrosoftRequest extends AuthDefaultRequest {
public AuthMicrosoftRequest(AuthConfig config) {
super(config, AuthSource.MICROSOFT);
super(config, AuthDefaultSource.MICROSOFT);
}
public AuthMicrosoftRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.MICROSOFT, authStateCache);
}
@Override
@@ -36,13 +46,12 @@ public class AuthMicrosoftRequest extends AuthDefaultRequest {
* @return token对象
*/
private AuthToken getToken(String accessTokenUrl) {
HttpResponse response = HttpRequest.post(accessTokenUrl)
.header("Host", "https://login.microsoftonline.com")
.contentType("application/x-www-form-urlencoded")
.form(parseQueryToMap(accessTokenUrl))
.execute();
String accessTokenStr = response.body();
JSONObject accessTokenObject = JSONObject.parseObject(accessTokenStr);
HttpHeader httpHeader = new HttpHeader();
Map<String, String> form = MapUtil.parseStringToMap(accessTokenUrl, false);
String response = new HttpUtils(config.getHttpConfig()).post(accessTokenUrl, form, httpHeader, false);
JSONObject accessTokenObject = JSONObject.parseObject(response);
this.checkResponse(accessTokenObject);
@@ -71,11 +80,15 @@ public class AuthMicrosoftRequest extends AuthDefaultRequest {
String token = authToken.getAccessToken();
String tokenType = authToken.getTokenType();
String jwt = tokenType + " " + token;
HttpResponse response = HttpRequest.get(userInfoUrl(authToken)).header("Authorization", jwt).execute();
String userInfo = response.body();
HttpHeader httpHeader = new HttpHeader();
httpHeader.add("Authorization", jwt);
String userInfo = new HttpUtils(config.getHttpConfig()).get(userInfoUrl(authToken), null, httpHeader, false);
JSONObject object = JSONObject.parseObject(userInfo);
this.checkResponse(object);
return AuthUser.builder()
.rawUserInfo(object)
.uuid(object.getString("id"))
.username(object.getString("userPrincipalName"))
.nickname(object.getString("displayName"))
@@ -83,7 +96,7 @@ public class AuthMicrosoftRequest extends AuthDefaultRequest {
.email(object.getString("mail"))
.gender(AuthUserGender.UNKNOWN)
.token(authToken)
.source(source)
.source(source.toString())
.build();
}
@@ -115,7 +128,7 @@ public class AuthMicrosoftRequest extends AuthDefaultRequest {
.queryParam("client_id", config.getClientId())
.queryParam("redirect_uri", config.getRedirectUri())
.queryParam("response_mode", "query")
.queryParam("scope", "offline_access%20user.read%20mail.read")
.queryParam("scope", "offline_access user.read mail.read")
.queryParam("state", getRealState(state))
.build();
}
@@ -133,7 +146,7 @@ public class AuthMicrosoftRequest extends AuthDefaultRequest {
.queryParam("client_id", config.getClientId())
.queryParam("client_secret", config.getClientSecret())
.queryParam("grant_type", "authorization_code")
.queryParam("scope", "user.read%20mail.read")
.queryParam("scope", "offline_access user.read mail.read")
.queryParam("redirect_uri", config.getRedirectUri())
.build();
}
@@ -1,9 +1,9 @@
package me.zhyd.oauth.request;
import cn.hutool.http.HttpResponse;
import com.alibaba.fastjson.JSONObject;
import me.zhyd.oauth.cache.AuthStateCache;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.config.AuthSource;
import me.zhyd.oauth.config.AuthDefaultSource;
import me.zhyd.oauth.enums.AuthUserGender;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.AuthCallback;
@@ -20,13 +20,17 @@ import me.zhyd.oauth.utils.UrlBuilder;
public class AuthOschinaRequest extends AuthDefaultRequest {
public AuthOschinaRequest(AuthConfig config) {
super(config, AuthSource.OSCHINA);
super(config, AuthDefaultSource.OSCHINA);
}
public AuthOschinaRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.OSCHINA, authStateCache);
}
@Override
protected AuthToken getAccessToken(AuthCallback authCallback) {
HttpResponse response = doPostAuthorizationCode(authCallback.getCode());
JSONObject accessTokenObject = JSONObject.parseObject(response.body());
String response = doPostAuthorizationCode(authCallback.getCode());
JSONObject accessTokenObject = JSONObject.parseObject(response);
this.checkResponse(accessTokenObject);
return AuthToken.builder()
.accessToken(accessTokenObject.getString("access_token"))
@@ -38,10 +42,11 @@ public class AuthOschinaRequest extends AuthDefaultRequest {
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
HttpResponse response = doGetUserInfo(authToken);
JSONObject object = JSONObject.parseObject(response.body());
String response = doGetUserInfo(authToken);
JSONObject object = JSONObject.parseObject(response);
this.checkResponse(object);
return AuthUser.builder()
.rawUserInfo(object)
.uuid(object.getString("id"))
.username(object.getString("name"))
.nickname(object.getString("name"))
@@ -51,7 +56,7 @@ public class AuthOschinaRequest extends AuthDefaultRequest {
.gender(AuthUserGender.getRealGender(object.getString("gender")))
.email(object.getString("email"))
.token(authToken)
.source(source)
.source(source.toString())
.build();
}
@@ -1,8 +1,8 @@
package me.zhyd.oauth.request;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import com.alibaba.fastjson.JSONObject;
import me.zhyd.oauth.utils.HttpUtils;
import me.zhyd.oauth.cache.AuthStateCache;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.enums.AuthUserGender;
import me.zhyd.oauth.exception.AuthException;
@@ -13,7 +13,7 @@ import me.zhyd.oauth.utils.UrlBuilder;
import java.util.Objects;
import static me.zhyd.oauth.config.AuthSource.PINTEREST;
import static me.zhyd.oauth.config.AuthDefaultSource.PINTEREST;
/**
* Pinterest登录
@@ -29,10 +29,14 @@ public class AuthPinterestRequest extends AuthDefaultRequest {
super(config, PINTEREST);
}
public AuthPinterestRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, PINTEREST, authStateCache);
}
@Override
protected AuthToken getAccessToken(AuthCallback authCallback) {
HttpResponse response = doPostAuthorizationCode(authCallback.getCode());
JSONObject accessTokenObject = JSONObject.parseObject(response.body());
String response = doPostAuthorizationCode(authCallback.getCode());
JSONObject accessTokenObject = JSONObject.parseObject(response);
this.checkResponse(accessTokenObject);
return AuthToken.builder()
.accessToken(accessTokenObject.getString("access_token"))
@@ -43,11 +47,13 @@ public class AuthPinterestRequest extends AuthDefaultRequest {
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
String userinfoUrl = userInfoUrl(authToken);
HttpResponse response = HttpRequest.get(userinfoUrl).setFollowRedirects(true).execute();
JSONObject object = JSONObject.parseObject(response.body());
// TODO: 是否需要 .setFollowRedirects(true)
String response = new HttpUtils(config.getHttpConfig()).get(userinfoUrl);
JSONObject object = JSONObject.parseObject(response);
this.checkResponse(object);
JSONObject userObj = object.getJSONObject("data");
return AuthUser.builder()
.rawUserInfo(userObj)
.uuid(userObj.getString("id"))
.avatar(getAvatarUrl(userObj))
.username(userObj.getString("username"))
@@ -55,7 +61,7 @@ public class AuthPinterestRequest extends AuthDefaultRequest {
.gender(AuthUserGender.UNKNOWN)
.remark(userObj.getString("bio"))
.token(authToken)
.source(source)
.source(source.toString())
.build();
}
@@ -1,16 +1,18 @@
package me.zhyd.oauth.request;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import com.alibaba.fastjson.JSONObject;
import me.zhyd.oauth.utils.HttpUtils;
import me.zhyd.oauth.cache.AuthStateCache;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.config.AuthSource;
import me.zhyd.oauth.config.AuthDefaultSource;
import me.zhyd.oauth.enums.AuthResponseStatus;
import me.zhyd.oauth.enums.AuthUserGender;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.*;
import me.zhyd.oauth.utils.GlobalAuthUtil;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthResponse;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.utils.GlobalAuthUtils;
import me.zhyd.oauth.utils.StringUtils;
import me.zhyd.oauth.utils.UrlBuilder;
@@ -25,29 +27,30 @@ import java.util.Map;
*/
public class AuthQqRequest extends AuthDefaultRequest {
public AuthQqRequest(AuthConfig config) {
super(config, AuthSource.QQ);
super(config, AuthDefaultSource.QQ);
}
public AuthQqRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.QQ, authStateCache);
}
@Override
protected AuthToken getAccessToken(AuthCallback authCallback) {
HttpResponse response = doGetAuthorizationCode(authCallback.getCode());
String response = doGetAuthorizationCode(authCallback.getCode());
return getAuthToken(response);
}
@Override
public AuthResponse refresh(AuthToken authToken) {
HttpResponse response = HttpRequest.get(refreshTokenUrl(authToken.getRefreshToken())).execute();
return AuthResponse.builder()
.code(AuthResponseStatus.SUCCESS.getCode())
.data(getAuthToken(response))
.build();
String response = new HttpUtils(config.getHttpConfig()).get(refreshTokenUrl(authToken.getRefreshToken()));
return AuthResponse.builder().code(AuthResponseStatus.SUCCESS.getCode()).data(getAuthToken(response)).build();
}
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
String openId = this.getOpenId(authToken);
HttpResponse response = doGetUserInfo(authToken);
JSONObject object = JSONObject.parseObject(response.body());
String response = doGetUserInfo(authToken);
JSONObject object = JSONObject.parseObject(response);
if (object.getIntValue("ret") != 0) {
throw new AuthException(object.getString("msg"));
}
@@ -58,6 +61,7 @@ public class AuthQqRequest extends AuthDefaultRequest {
String location = String.format("%s-%s", object.getString("province"), object.getString("city"));
return AuthUser.builder()
.rawUserInfo(object)
.username(object.getString("nickname"))
.nickname(object.getString("nickname"))
.avatar(avatar)
@@ -65,39 +69,34 @@ public class AuthQqRequest extends AuthDefaultRequest {
.uuid(openId)
.gender(AuthUserGender.getRealGender(object.getString("gender")))
.token(authToken)
.source(source)
.source(source.toString())
.build();
}
/**
* 获取QQ用户的OpenId,支持自定义是否启用查询unionid的功能,如果启用查询unionid的功能,
* 那就需要调用者先通过邮件申请unionid功能,参考链接 {@see http://wiki.connect.qq.com/unionid%E4%BB%8B%E7%BB%8D}
* 那就需要开发者先通过邮件申请unionid功能,参考链接 {@see http://wiki.connect.qq.com/unionid%E4%BB%8B%E7%BB%8D}
*
* @param authToken 通过{@link AuthQqRequest#getAccessToken(AuthCallback)}获取到的{@code authToken}
* @return openId
*/
private String getOpenId(AuthToken authToken) {
HttpResponse response = HttpRequest.get(UrlBuilder.fromBaseUrl("https://graph.qq.com/oauth2.0/me")
String response = new HttpUtils(config.getHttpConfig()).get(UrlBuilder.fromBaseUrl("https://graph.qq.com/oauth2.0/me")
.queryParam("access_token", authToken.getAccessToken())
.queryParam("unionid", config.isUnionId() ? 1 : 0)
.build()).execute();
if (response.isOk()) {
String body = response.body();
String removePrefix = StrUtil.replace(body, "callback(", "");
String removeSuffix = StrUtil.replace(removePrefix, ");", "");
String openId = StrUtil.trim(removeSuffix);
JSONObject object = JSONObject.parseObject(openId);
if (object.containsKey("error")) {
throw new AuthException(object.get("error") + ":" + object.get("error_description"));
}
authToken.setOpenId(object.getString("openid"));
if (object.containsKey("unionid")) {
authToken.setUnionId(object.getString("unionid"));
}
return StringUtils.isEmpty(authToken.getUnionId()) ? authToken.getOpenId() : authToken.getUnionId();
.build());
String removePrefix = response.replace("callback(", "");
String removeSuffix = removePrefix.replace(");", "");
String openId = removeSuffix.trim();
JSONObject object = JSONObject.parseObject(openId);
if (object.containsKey("error")) {
throw new AuthException(object.get("error") + ":" + object.get("error_description"));
}
throw new AuthException("request error");
authToken.setOpenId(object.getString("openid"));
if (object.containsKey("unionid")) {
authToken.setUnionId(object.getString("unionid"));
}
return StringUtils.isEmpty(authToken.getUnionId()) ? authToken.getOpenId() : authToken.getUnionId();
}
/**
@@ -115,8 +114,8 @@ public class AuthQqRequest extends AuthDefaultRequest {
.build();
}
private AuthToken getAuthToken(HttpResponse response) {
Map<String, String> accessTokenObject = GlobalAuthUtil.parseStringToMap(response.body());
private AuthToken getAuthToken(String response) {
Map<String, String> accessTokenObject = GlobalAuthUtils.parseStringToMap(response);
if (!accessTokenObject.containsKey("access_token") || accessTokenObject.containsKey("code")) {
throw new AuthException(accessTokenObject.get("msg"));
}
@@ -1,18 +1,22 @@
package me.zhyd.oauth.request;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.xkcoding.http.util.UrlUtil;
import me.zhyd.oauth.cache.AuthStateCache;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.enums.AuthUserGender;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.*;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthResponse;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.utils.HttpUtils;
import me.zhyd.oauth.utils.UrlBuilder;
import java.util.Objects;
import static me.zhyd.oauth.config.AuthSource.RENREN;
import static me.zhyd.oauth.config.AuthDefaultSource.RENREN;
import static me.zhyd.oauth.enums.AuthResponseStatus.SUCCESS;
/**
@@ -27,6 +31,10 @@ public class AuthRenrenRequest extends AuthDefaultRequest {
super(config, RENREN);
}
public AuthRenrenRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, RENREN, authStateCache);
}
@Override
protected AuthToken getAccessToken(AuthCallback authCallback) {
return this.getToken(accessTokenUrl(authCallback.getCode()));
@@ -34,17 +42,18 @@ public class AuthRenrenRequest extends AuthDefaultRequest {
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
HttpResponse response = doGetUserInfo(authToken);
JSONObject userObj = JSONObject.parseObject(response.body()).getJSONObject("response");
String response = doGetUserInfo(authToken);
JSONObject userObj = JSONObject.parseObject(response).getJSONObject("response");
return AuthUser.builder()
.rawUserInfo(userObj)
.uuid(userObj.getString("id"))
.avatar(getAvatarUrl(userObj))
.nickname(userObj.getString("name"))
.company(getCompany(userObj))
.gender(getGender(userObj))
.token(authToken)
.source(source)
.source(source.toString())
.build();
}
@@ -57,8 +66,8 @@ public class AuthRenrenRequest extends AuthDefaultRequest {
}
private AuthToken getToken(String url) {
HttpResponse response = HttpRequest.post(url).execute();
JSONObject jsonObject = JSONObject.parseObject(response.body());
String response = new HttpUtils(config.getHttpConfig()).post(url);
JSONObject jsonObject = JSONObject.parseObject(response);
if (jsonObject.containsKey("error")) {
throw new AuthException("Failed to get token from Renren: " + jsonObject);
}
@@ -66,8 +75,8 @@ public class AuthRenrenRequest extends AuthDefaultRequest {
return AuthToken.builder()
.tokenType(jsonObject.getString("token_type"))
.expireIn(jsonObject.getIntValue("expires_in"))
.accessToken(jsonObject.getString("access_token"))
.refreshToken(jsonObject.getString("refresh_token"))
.accessToken(UrlUtil.urlEncode(jsonObject.getString("access_token")))
.refreshToken(UrlUtil.urlEncode(jsonObject.getString("refresh_token")))
.openId(jsonObject.getJSONObject("user").getString("id"))
.build();
}
@@ -1,12 +1,20 @@
package me.zhyd.oauth.request;
import me.zhyd.oauth.enums.AuthResponseStatus;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthResponse;
import me.zhyd.oauth.enums.AuthResponseStatus;
import me.zhyd.oauth.model.AuthToken;
/**
* JustAuth {@code Request}公共接口,所有平台的{@code Request}都需要实现该接口
* <p>
* {@link AuthRequest#authorize()}
* {@link AuthRequest#authorize(String)}
* {@link AuthRequest#login(AuthCallback)}
* {@link AuthRequest#revoke(AuthToken)}
* {@link AuthRequest#refresh(AuthToken)}
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @since 1.8
*/
@@ -1,8 +1,11 @@
package me.zhyd.oauth.request;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import com.alibaba.fastjson.JSONObject;
import me.zhyd.oauth.utils.HttpUtils;
import com.xkcoding.http.constants.Constants;
import com.xkcoding.http.support.HttpHeader;
import com.xkcoding.http.util.MapUtil;
import me.zhyd.oauth.cache.AuthStateCache;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.enums.AuthUserGender;
import me.zhyd.oauth.exception.AuthException;
@@ -11,8 +14,9 @@ import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.utils.UrlBuilder;
import static me.zhyd.oauth.config.AuthSource.STACK_OVERFLOW;
import static me.zhyd.oauth.utils.GlobalAuthUtil.parseQueryToMap;
import java.util.Map;
import static me.zhyd.oauth.config.AuthDefaultSource.STACK_OVERFLOW;
/**
* Stack Overflow登录
@@ -26,14 +30,19 @@ public class AuthStackOverflowRequest extends AuthDefaultRequest {
super(config, STACK_OVERFLOW);
}
public AuthStackOverflowRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, STACK_OVERFLOW, authStateCache);
}
@Override
protected AuthToken getAccessToken(AuthCallback authCallback) {
String accessTokenUrl = accessTokenUrl(authCallback.getCode());
HttpResponse response = HttpRequest.post(accessTokenUrl)
.contentType("application/x-www-form-urlencoded")
.form(parseQueryToMap(accessTokenUrl))
.execute();
JSONObject accessTokenObject = JSONObject.parseObject(response.body());
Map<String, String> form = MapUtil.parseStringToMap(accessTokenUrl, false);
HttpHeader httpHeader = new HttpHeader();
httpHeader.add(Constants.CONTENT_TYPE, "application/x-www-form-urlencoded");
String response = new HttpUtils(config.getHttpConfig()).post(accessTokenUrl, form, httpHeader, false);
JSONObject accessTokenObject = JSONObject.parseObject(response);
this.checkResponse(accessTokenObject);
return AuthToken.builder()
@@ -49,12 +58,13 @@ public class AuthStackOverflowRequest extends AuthDefaultRequest {
.queryParam("site", "stackoverflow")
.queryParam("key", this.config.getStackOverflowKey())
.build();
HttpResponse response = HttpRequest.get(userInfoUrl).execute();
JSONObject object = JSONObject.parseObject(response.body());
String response = new HttpUtils(config.getHttpConfig()).get(userInfoUrl);
JSONObject object = JSONObject.parseObject(response);
this.checkResponse(object);
JSONObject userObj = object.getJSONArray("items").getJSONObject(0);
return AuthUser.builder()
.rawUserInfo(userObj)
.uuid(userObj.getString("user_id"))
.avatar(userObj.getString("profile_image"))
.location(userObj.getString("location"))
@@ -62,7 +72,7 @@ public class AuthStackOverflowRequest extends AuthDefaultRequest {
.blog(userObj.getString("website_url"))
.gender(AuthUserGender.UNKNOWN)
.token(authToken)
.source(source)
.source(source.toString())
.build();
}
@@ -1,15 +1,15 @@
package me.zhyd.oauth.request;
import cn.hutool.http.HttpResponse;
import com.alibaba.fastjson.JSONObject;
import me.zhyd.oauth.cache.AuthStateCache;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.config.AuthSource;
import me.zhyd.oauth.config.AuthDefaultSource;
import me.zhyd.oauth.enums.AuthUserGender;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.utils.GlobalAuthUtil;
import me.zhyd.oauth.utils.GlobalAuthUtils;
import me.zhyd.oauth.utils.UrlBuilder;
/**
@@ -21,7 +21,11 @@ import me.zhyd.oauth.utils.UrlBuilder;
public class AuthTaobaoRequest extends AuthDefaultRequest {
public AuthTaobaoRequest(AuthConfig config) {
super(config, AuthSource.TAOBAO);
super(config, AuthDefaultSource.TAOBAO);
}
public AuthTaobaoRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.TAOBAO, authStateCache);
}
@Override
@@ -31,8 +35,8 @@ public class AuthTaobaoRequest extends AuthDefaultRequest {
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
HttpResponse response = doPostAuthorizationCode(authToken.getAccessCode());
JSONObject accessTokenObject = JSONObject.parseObject(response.body());
String response = doPostAuthorizationCode(authToken.getAccessCode());
JSONObject accessTokenObject = JSONObject.parseObject(response);
if (accessTokenObject.containsKey("error")) {
throw new AuthException(accessTokenObject.getString("error_description"));
}
@@ -42,14 +46,15 @@ public class AuthTaobaoRequest extends AuthDefaultRequest {
authToken.setUid(accessTokenObject.getString("taobao_user_id"));
authToken.setOpenId(accessTokenObject.getString("taobao_open_uid"));
String nick = GlobalAuthUtil.urlDecode(accessTokenObject.getString("taobao_user_nick"));
String nick = GlobalAuthUtils.urlDecode(accessTokenObject.getString("taobao_user_nick"));
return AuthUser.builder()
.rawUserInfo(new JSONObject())
.uuid(accessTokenObject.getString("taobao_user_id"))
.username(nick)
.nickname(nick)
.gender(AuthUserGender.UNKNOWN)
.token(authToken)
.source(source)
.source(source.toString())
.build();
}
@@ -1,14 +1,21 @@
package me.zhyd.oauth.request;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import com.alibaba.fastjson.JSONObject;
import me.zhyd.oauth.utils.HttpUtils;
import com.xkcoding.http.support.HttpHeader;
import me.zhyd.oauth.cache.AuthStateCache;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.config.AuthSource;
import me.zhyd.oauth.config.AuthDefaultSource;
import me.zhyd.oauth.enums.AuthResponseStatus;
import me.zhyd.oauth.enums.AuthUserGender;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.*;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthResponse;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import java.util.HashMap;
import java.util.Map;
/**
* Teambition授权登录
@@ -19,7 +26,11 @@ import me.zhyd.oauth.model.*;
public class AuthTeambitionRequest extends AuthDefaultRequest {
public AuthTeambitionRequest(AuthConfig config) {
super(config, AuthSource.TEAMBITION);
super(config, AuthDefaultSource.TEAMBITION);
}
public AuthTeambitionRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.TEAMBITION, authStateCache);
}
/**
@@ -28,13 +39,14 @@ public class AuthTeambitionRequest extends AuthDefaultRequest {
*/
@Override
protected AuthToken getAccessToken(AuthCallback authCallback) {
HttpResponse response = HttpRequest.post(source.accessToken())
.form("client_id", config.getClientId())
.form("client_secret", config.getClientSecret())
.form("code", authCallback.getCode())
.form("grant_type", "code")
.execute();
JSONObject accessTokenObject = JSONObject.parseObject(response.body());
Map<String, String> form = new HashMap<>(4);
form.put("client_id", config.getClientId());
form.put("client_secret", config.getClientSecret());
form.put("code", authCallback.getCode());
form.put("grant_type", "code");
String response = new HttpUtils(config.getHttpConfig()).post(source.accessToken(), form, false);
JSONObject accessTokenObject = JSONObject.parseObject(response);
this.checkResponse(accessTokenObject);
@@ -48,16 +60,18 @@ public class AuthTeambitionRequest extends AuthDefaultRequest {
protected AuthUser getUserInfo(AuthToken authToken) {
String accessToken = authToken.getAccessToken();
HttpResponse response = HttpRequest.get(source.userInfo())
.header("Authorization", "OAuth2 " + accessToken)
.execute();
JSONObject object = JSONObject.parseObject(response.body());
HttpHeader httpHeader = new HttpHeader();
httpHeader.add("Authorization", "OAuth2 " + accessToken);
String response = new HttpUtils(config.getHttpConfig()).get(source.userInfo(), null, httpHeader, false);
JSONObject object = JSONObject.parseObject(response);
this.checkResponse(object);
authToken.setUid(object.getString("_id"));
return AuthUser.builder()
.rawUserInfo(object)
.uuid(object.getString("_id"))
.username(object.getString("name"))
.nickname(object.getString("name"))
@@ -67,7 +81,7 @@ public class AuthTeambitionRequest extends AuthDefaultRequest {
.email(object.getString("email"))
.gender(AuthUserGender.UNKNOWN)
.token(authToken)
.source(source)
.source(source.toString())
.build();
}
@@ -75,11 +89,12 @@ public class AuthTeambitionRequest extends AuthDefaultRequest {
public AuthResponse refresh(AuthToken oldToken) {
String uid = oldToken.getUid();
String refreshToken = oldToken.getRefreshToken();
HttpResponse response = HttpRequest.post(source.refresh())
.form("_userId", uid)
.form("refresh_token", refreshToken)
.execute();
JSONObject refreshTokenObject = JSONObject.parseObject(response.body());
Map<String, String> form = new HashMap<>(2);
form.put("_userId", uid);
form.put("refresh_token", refreshToken);
String response = new HttpUtils(config.getHttpConfig()).post(source.refresh(), form, false);
JSONObject refreshTokenObject = JSONObject.parseObject(response);
this.checkResponse(refreshTokenObject);
@@ -1,89 +0,0 @@
package me.zhyd.oauth.request;
import cn.hutool.http.HttpResponse;
import com.alibaba.fastjson.JSONObject;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.config.AuthSource;
import me.zhyd.oauth.enums.AuthUserGender;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.utils.UrlBuilder;
/**
* 腾讯云登录
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @since 1.0.0
*/
public class AuthTencentCloudRequest extends AuthDefaultRequest {
public AuthTencentCloudRequest(AuthConfig config) {
super(config, AuthSource.TENCENT_CLOUD);
}
@Override
protected AuthToken getAccessToken(AuthCallback authCallback) {
HttpResponse response = doGetAuthorizationCode(authCallback.getCode());
JSONObject accessTokenObject = JSONObject.parseObject(response.body());
this.checkResponse(accessTokenObject);
return AuthToken.builder()
.accessToken(accessTokenObject.getString("access_token"))
.expireIn(accessTokenObject.getIntValue("expires_in"))
.refreshToken(accessTokenObject.getString("refresh_token"))
.build();
}
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
HttpResponse response = doGetUserInfo(authToken);
JSONObject object = JSONObject.parseObject(response.body());
this.checkResponse(object);
object = object.getJSONObject("data");
return AuthUser.builder()
.uuid(object.getString("id"))
.username(object.getString("name"))
.avatar("https://dev.tencent.com/" + object.getString("avatar"))
.blog("https://dev.tencent.com/" + object.getString("path"))
.nickname(object.getString("name"))
.company(object.getString("company"))
.location(object.getString("location"))
.gender(AuthUserGender.getRealGender(object.getString("sex")))
.email(object.getString("email"))
.remark(object.getString("slogan"))
.token(authToken)
.source(source)
.build();
}
/**
* 检查响应内容是否正确
*
* @param object 请求响应内容
*/
private void checkResponse(JSONObject object) {
if (object.getIntValue("code") != 0) {
throw new AuthException(object.getString("msg"));
}
}
/**
* 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state}
*
* @param state state 验证授权流程的参数,可以防止csrf
* @return 返回授权地址
* @since 1.9.3
*/
@Override
public String authorize(String state) {
return UrlBuilder.fromBaseUrl(source.authorize())
.queryParam("response_type", "code")
.queryParam("client_id", config.getClientId())
.queryParam("redirect_uri", config.getRedirectUri())
.queryParam("scope", "user")
.queryParam("state", getRealState(state))
.build();
}
}
@@ -1,9 +1,9 @@
package me.zhyd.oauth.request;
import cn.hutool.http.HttpResponse;
import com.alibaba.fastjson.JSONObject;
import me.zhyd.oauth.cache.AuthStateCache;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.config.AuthSource;
import me.zhyd.oauth.config.AuthDefaultSource;
import me.zhyd.oauth.enums.AuthToutiaoErrorCode;
import me.zhyd.oauth.enums.AuthUserGender;
import me.zhyd.oauth.exception.AuthException;
@@ -21,13 +21,17 @@ import me.zhyd.oauth.utils.UrlBuilder;
public class AuthToutiaoRequest extends AuthDefaultRequest {
public AuthToutiaoRequest(AuthConfig config) {
super(config, AuthSource.TOUTIAO);
super(config, AuthDefaultSource.TOUTIAO);
}
public AuthToutiaoRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.TOUTIAO, authStateCache);
}
@Override
protected AuthToken getAccessToken(AuthCallback authCallback) {
HttpResponse response = doGetAuthorizationCode(authCallback.getCode());
JSONObject accessTokenObject = JSONObject.parseObject(response.body());
String response = doGetAuthorizationCode(authCallback.getCode());
JSONObject accessTokenObject = JSONObject.parseObject(response);
this.checkResponse(accessTokenObject);
@@ -40,9 +44,9 @@ public class AuthToutiaoRequest extends AuthDefaultRequest {
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
HttpResponse userResponse = doGetUserInfo(authToken);
String userResponse = doGetUserInfo(authToken);
JSONObject userProfile = JSONObject.parseObject(userResponse.body());
JSONObject userProfile = JSONObject.parseObject(userResponse);
this.checkResponse(userProfile);
@@ -52,6 +56,7 @@ public class AuthToutiaoRequest extends AuthDefaultRequest {
String anonymousUserName = "匿名用户";
return AuthUser.builder()
.rawUserInfo(user)
.uuid(user.getString("uid"))
.username(isAnonymousUser ? anonymousUserName : user.getString("screen_name"))
.nickname(isAnonymousUser ? anonymousUserName : user.getString("screen_name"))
@@ -59,7 +64,7 @@ public class AuthToutiaoRequest extends AuthDefaultRequest {
.remark(user.getString("description"))
.gender(AuthUserGender.getRealGender(user.getString("gender")))
.token(authToken)
.source(source)
.source(source.toString())
.build();
}
@@ -119,8 +124,7 @@ public class AuthToutiaoRequest extends AuthDefaultRequest {
*/
private void checkResponse(JSONObject object) {
if (object.containsKey("error_code")) {
throw new AuthException(AuthToutiaoErrorCode.getErrorCode(object.getIntValue("error_code"))
.getDesc());
throw new AuthException(AuthToutiaoErrorCode.getErrorCode(object.getIntValue("error_code")).getDesc());
}
}
}
@@ -0,0 +1,183 @@
package me.zhyd.oauth.request;
import com.alibaba.fastjson.JSONObject;
import me.zhyd.oauth.utils.HttpUtils;
import com.xkcoding.http.constants.Constants;
import com.xkcoding.http.support.HttpHeader;
import com.xkcoding.http.util.MapUtil;
import me.zhyd.oauth.cache.AuthStateCache;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.utils.GlobalAuthUtils;
import me.zhyd.oauth.utils.UrlBuilder;
import java.util.HashMap;
import java.util.Map;
import static me.zhyd.oauth.config.AuthDefaultSource.TWITTER;
import static me.zhyd.oauth.utils.GlobalAuthUtils.generateTwitterSignature;
import static me.zhyd.oauth.utils.GlobalAuthUtils.urlEncode;
/**
* Twitter登录
*
* @author hongwei.peng (pengisgood(at)gmail(dot)com)
* @since 1.13.0
*/
public class AuthTwitterRequest extends AuthDefaultRequest {
private static final String PREAMBLE = "OAuth";
public AuthTwitterRequest(AuthConfig config) {
super(config, TWITTER);
}
public AuthTwitterRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, TWITTER, authStateCache);
}
/**
* 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state}
*
* @param state state 验证授权流程的参数,可以防止csrf
* @return 返回授权地址
* @since 1.9.3
*/
@Override
public String authorize(String state) {
AuthToken token = this.getRequestToken();
return UrlBuilder.fromBaseUrl(source.authorize())
.queryParam("oauth_token", token.getOauthToken())
.build();
}
/**
* Obtaining a request token
* https://developer.twitter.com/en/docs/twitter-for-websites/log-in-with-twitter/guides/implementing-sign-in-with-twitter
*
* @return request token
*/
public AuthToken getRequestToken() {
String baseUrl = "https://api.twitter.com/oauth/request_token";
Map<String, String> oauthParams = buildOauthParams();
oauthParams.put("oauth_callback", config.getRedirectUri());
oauthParams.put("oauth_signature", generateTwitterSignature(oauthParams, "POST", baseUrl, config.getClientSecret(), null));
String header = buildHeader(oauthParams);
HttpHeader httpHeader = new HttpHeader();
httpHeader.add("Authorization", header);
httpHeader.add("User-Agent", "themattharris' HTTP Client");
httpHeader.add("Host", "api.twitter.com");
httpHeader.add("Accept", "*/*");
String requestToken = new HttpUtils(config.getHttpConfig()).post(baseUrl, null, httpHeader);
Map<String, String> res = MapUtil.parseStringToMap(requestToken, false);
return AuthToken.builder()
.oauthToken(res.get("oauth_token"))
.oauthTokenSecret(res.get("oauth_token_secret"))
.oauthCallbackConfirmed(Boolean.valueOf(res.get("oauth_callback_confirmed")))
.build();
}
/**
* Convert request token to access token
* https://developer.twitter.com/en/docs/twitter-for-websites/log-in-with-twitter/guides/implementing-sign-in-with-twitter
*
* @return access token
*/
@Override
protected AuthToken getAccessToken(AuthCallback authCallback) {
Map<String, String> oauthParams = buildOauthParams();
oauthParams.put("oauth_token", authCallback.getOauth_token());
oauthParams.put("oauth_verifier", authCallback.getOauth_verifier());
oauthParams.put("oauth_signature", generateTwitterSignature(oauthParams, "POST", source.accessToken(), config.getClientSecret(), authCallback
.getOauth_token()));
String header = buildHeader(oauthParams);
HttpHeader httpHeader = new HttpHeader();
httpHeader.add("Authorization", header);
httpHeader.add(Constants.CONTENT_TYPE, "application/x-www-form-urlencoded");
Map<String, String> form = new HashMap<>(1);
form.put("oauth_verifier", authCallback.getOauth_verifier());
String response = new HttpUtils(config.getHttpConfig()).post(source.accessToken(), form, httpHeader, false);
Map<String, String> requestToken = MapUtil.parseStringToMap(response, false);
return AuthToken.builder()
.oauthToken(requestToken.get("oauth_token"))
.oauthTokenSecret(requestToken.get("oauth_token_secret"))
.userId(requestToken.get("user_id"))
.screenName(requestToken.get("screen_name"))
.build();
}
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
Map<String, String> queryParams = new HashMap<>();
queryParams.put("user_id", authToken.getUserId());
queryParams.put("screen_name", authToken.getScreenName());
queryParams.put("include_entities", Boolean.toString(true));
Map<String, String> oauthParams = buildOauthParams();
oauthParams.put("oauth_token", authToken.getOauthToken());
Map<String, String> params = new HashMap<>(oauthParams);
params.putAll(queryParams);
oauthParams.put("oauth_signature", generateTwitterSignature(params, "GET", source.userInfo(), config.getClientSecret(), authToken
.getOauthTokenSecret()));
String header = buildHeader(oauthParams);
HttpHeader httpHeader = new HttpHeader();
httpHeader.add("Authorization", header);
String response = new HttpUtils(config.getHttpConfig()).get(userInfoUrl(authToken), null, httpHeader, false);
JSONObject userInfo = JSONObject.parseObject(response);
return AuthUser.builder()
.rawUserInfo(userInfo)
.uuid(userInfo.getString("id_str"))
.username(userInfo.getString("screen_name"))
.nickname(userInfo.getString("name"))
.remark(userInfo.getString("description"))
.avatar(userInfo.getString("profile_image_url_https"))
.blog(userInfo.getString("url"))
.location(userInfo.getString("location"))
.avatar(userInfo.getString("profile_image_url"))
.source(source.toString())
.token(authToken)
.build();
}
@Override
protected String userInfoUrl(AuthToken authToken) {
return UrlBuilder.fromBaseUrl(source.userInfo())
.queryParam("user_id", authToken.getUserId())
.queryParam("screen_name", authToken.getScreenName())
.queryParam("include_entities", true)
.build();
}
private Map<String, String> buildOauthParams() {
Map<String, String> params = new HashMap<>(5);
params.put("oauth_consumer_key", config.getClientId());
params.put("oauth_nonce", GlobalAuthUtils.generateNonce(32));
params.put("oauth_signature_method", "HMAC-SHA1");
params.put("oauth_timestamp", GlobalAuthUtils.getTimestamp());
params.put("oauth_version", "1.0");
return params;
}
private String buildHeader(Map<String, String> oauthParams) {
final StringBuilder sb = new StringBuilder(PREAMBLE + " ");
for (Map.Entry<String, String> param : oauthParams.entrySet()) {
sb.append(param.getKey()).append("=\"").append(urlEncode(param.getValue())).append('"').append(", ");
}
return sb.deleteCharAt(sb.length() - 2).toString();
}
}
@@ -0,0 +1,155 @@
package me.zhyd.oauth.request;
import com.alibaba.fastjson.JSONObject;
import me.zhyd.oauth.utils.HttpUtils;
import me.zhyd.oauth.cache.AuthStateCache;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.config.AuthDefaultSource;
import me.zhyd.oauth.enums.AuthResponseStatus;
import me.zhyd.oauth.enums.AuthUserGender;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.utils.UrlBuilder;
/**
* <p>
* 企业微信登录
* </p>
*
* @author yangkai.shen (https://xkcoding.com)
* @since 1.10.0
*/
public class AuthWeChatEnterpriseRequest extends AuthDefaultRequest {
public AuthWeChatEnterpriseRequest(AuthConfig config) {
super(config, AuthDefaultSource.WECHAT_ENTERPRISE);
}
public AuthWeChatEnterpriseRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.WECHAT_ENTERPRISE, authStateCache);
}
/**
* 微信的特殊性,此时返回的信息同时包含 openid 和 access_token
*
* @param authCallback 回调返回的参数
* @return 所有信息
*/
@Override
protected AuthToken getAccessToken(AuthCallback authCallback) {
String response = doGetAuthorizationCode(accessTokenUrl(authCallback.getCode()));
JSONObject object = this.checkResponse(response);
return AuthToken.builder()
.accessToken(object.getString("access_token"))
.expireIn(object.getIntValue("expires_in"))
.code(authCallback.getCode())
.build();
}
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
String response = doGetUserInfo(authToken);
JSONObject object = this.checkResponse(response);
// 返回 OpenId 或其他,均代表非当前企业用户,不支持
if (!object.containsKey("UserId")) {
throw new AuthException(AuthResponseStatus.UNIDENTIFIED_PLATFORM, source);
}
String userId = object.getString("UserId");
String userDetailResponse = getUserDetail(authToken.getAccessToken(), userId);
JSONObject userDetail = this.checkResponse(userDetailResponse);
return AuthUser.builder()
.rawUserInfo(userDetail)
.username(userDetail.getString("name"))
.nickname(userDetail.getString("alias"))
.avatar(userDetail.getString("avatar"))
.location(userDetail.getString("address"))
.email(userDetail.getString("email"))
.uuid(userId)
.gender(AuthUserGender.getWechatRealGender(userDetail.getString("gender")))
.token(authToken)
.source(source.toString())
.build();
}
/**
* 校验请求结果
*
* @param response 请求结果
* @return 如果请求结果正常,则返回JSONObject
*/
private JSONObject checkResponse(String response) {
JSONObject object = JSONObject.parseObject(response);
if (object.containsKey("errcode") && object.getIntValue("errcode") != 0) {
throw new AuthException(object.getString("errmsg"), source);
}
return object;
}
/**
* 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state}
*
* @param state state 验证授权流程的参数,可以防止csrf
* @return 返回授权地址
* @since 1.9.3
*/
@Override
public String authorize(String state) {
return UrlBuilder.fromBaseUrl(source.authorize())
.queryParam("appid", config.getClientId())
.queryParam("agentid", config.getAgentId())
.queryParam("redirect_uri", config.getRedirectUri())
.queryParam("state", getRealState(state))
.build();
}
/**
* 返回获取accessToken的url
*
* @param code 授权码
* @return 返回获取accessToken的url
*/
@Override
protected String accessTokenUrl(String code) {
return UrlBuilder.fromBaseUrl(source.accessToken())
.queryParam("corpid", config.getClientId())
.queryParam("corpsecret", config.getClientSecret())
.build();
}
/**
* 返回获取userInfo的url
*
* @param authToken 用户授权后的token
* @return 返回获取userInfo的url
*/
@Override
protected String userInfoUrl(AuthToken authToken) {
return UrlBuilder.fromBaseUrl(source.userInfo())
.queryParam("access_token", authToken.getAccessToken())
.queryParam("code", authToken.getCode())
.build();
}
/**
* 用户详情
*
* @param accessToken accessToken
* @param userId 企业内用户id
* @return 用户详情
*/
private String getUserDetail(String accessToken, String userId) {
String userDetailUrl = UrlBuilder.fromBaseUrl("https://qyapi.weixin.qq.com/cgi-bin/user/get")
.queryParam("access_token", accessToken)
.queryParam("userid", userId)
.build();
return new HttpUtils(config.getHttpConfig()).get(userDetailUrl);
}
}
@@ -0,0 +1,175 @@
package me.zhyd.oauth.request;
import com.alibaba.fastjson.JSONObject;
import me.zhyd.oauth.utils.HttpUtils;
import me.zhyd.oauth.cache.AuthStateCache;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.config.AuthDefaultSource;
import me.zhyd.oauth.enums.AuthResponseStatus;
import me.zhyd.oauth.enums.AuthUserGender;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthResponse;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.utils.GlobalAuthUtils;
import me.zhyd.oauth.utils.UrlBuilder;
/**
* 微信公众平台登录
*
* @author yangkai.shen (https://xkcoding.com)
* @since 1.1.0
*/
public class AuthWeChatMpRequest extends AuthDefaultRequest {
public AuthWeChatMpRequest(AuthConfig config) {
super(config, AuthDefaultSource.WECHAT_MP);
}
public AuthWeChatMpRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.WECHAT_MP, authStateCache);
}
/**
* 微信的特殊性,此时返回的信息同时包含 openid 和 access_token
*
* @param authCallback 回调返回的参数
* @return 所有信息
*/
@Override
protected AuthToken getAccessToken(AuthCallback authCallback) {
return this.getToken(accessTokenUrl(authCallback.getCode()));
}
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
String openId = authToken.getOpenId();
String response = doGetUserInfo(authToken);
JSONObject object = JSONObject.parseObject(response);
this.checkResponse(object);
String location = String.format("%s-%s-%s", object.getString("country"), object.getString("province"), object.getString("city"));
if (object.containsKey("unionid")) {
authToken.setUnionId(object.getString("unionid"));
}
return AuthUser.builder()
.rawUserInfo(object)
.username(object.getString("nickname"))
.nickname(object.getString("nickname"))
.avatar(object.getString("headimgurl"))
.location(location)
.uuid(openId)
.gender(AuthUserGender.getWechatRealGender(object.getString("sex")))
.token(authToken)
.source(source.toString())
.build();
}
@Override
public AuthResponse refresh(AuthToken oldToken) {
return AuthResponse.builder()
.code(AuthResponseStatus.SUCCESS.getCode())
.data(this.getToken(refreshTokenUrl(oldToken.getRefreshToken())))
.build();
}
/**
* 检查响应内容是否正确
*
* @param object 请求响应内容
*/
private void checkResponse(JSONObject object) {
if (object.containsKey("errcode")) {
throw new AuthException(object.getIntValue("errcode"), object.getString("errmsg"));
}
}
/**
* 获取token,适用于获取access_token和刷新token
*
* @param accessTokenUrl 实际请求token的地址
* @return token对象
*/
private AuthToken getToken(String accessTokenUrl) {
String response = new HttpUtils(config.getHttpConfig()).get(accessTokenUrl);
JSONObject accessTokenObject = JSONObject.parseObject(response);
this.checkResponse(accessTokenObject);
return AuthToken.builder()
.accessToken(accessTokenObject.getString("access_token"))
.refreshToken(accessTokenObject.getString("refresh_token"))
.expireIn(accessTokenObject.getIntValue("expires_in"))
.openId(accessTokenObject.getString("openid"))
.scope(accessTokenObject.getString("scope"))
.build();
}
/**
* 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state}
*
* @param state state 验证授权流程的参数,可以防止csrf
* @return 返回授权地址
* @since 1.9.3
*/
@Override
public String authorize(String state) {
return UrlBuilder.fromBaseUrl(source.authorize())
.queryParam("appid", config.getClientId())
.queryParam("redirect_uri", GlobalAuthUtils.urlEncode(config.getRedirectUri()))
.queryParam("response_type", "code")
.queryParam("scope", "snsapi_userinfo")
.queryParam("state", getRealState(state).concat("#wechat_redirect"))
.build();
}
/**
* 返回获取accessToken的url
*
* @param code 授权码
* @return 返回获取accessToken的url
*/
@Override
protected String accessTokenUrl(String code) {
return UrlBuilder.fromBaseUrl(source.accessToken())
.queryParam("appid", config.getClientId())
.queryParam("secret", config.getClientSecret())
.queryParam("code", code)
.queryParam("grant_type", "authorization_code")
.build();
}
/**
* 返回获取userInfo的url
*
* @param authToken 用户授权后的token
* @return 返回获取userInfo的url
*/
@Override
protected String userInfoUrl(AuthToken authToken) {
return UrlBuilder.fromBaseUrl(source.userInfo())
.queryParam("access_token", authToken.getAccessToken())
.queryParam("openid", authToken.getOpenId())
.queryParam("lang", "zh_CN")
.build();
}
/**
* 返回获取userInfo的url
*
* @param refreshToken getAccessToken方法返回的refreshToken
* @return 返回获取userInfo的url
*/
@Override
protected String refreshTokenUrl(String refreshToken) {
return UrlBuilder.fromBaseUrl(source.refresh())
.queryParam("appid", config.getClientId())
.queryParam("grant_type", "refresh_token")
.queryParam("refresh_token", refreshToken)
.build();
}
}
@@ -1,25 +1,32 @@
package me.zhyd.oauth.request;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import com.alibaba.fastjson.JSONObject;
import me.zhyd.oauth.utils.HttpUtils;
import me.zhyd.oauth.cache.AuthStateCache;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.config.AuthSource;
import me.zhyd.oauth.config.AuthDefaultSource;
import me.zhyd.oauth.enums.AuthResponseStatus;
import me.zhyd.oauth.enums.AuthUserGender;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.*;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthResponse;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.utils.UrlBuilder;
/**
* 微信登录
* 微信开放平台登录
*
* @author yangkai.shen (https://xkcoding.com)
* @since 1.1.0
*/
public class AuthWeChatRequest extends AuthDefaultRequest {
public AuthWeChatRequest(AuthConfig config) {
super(config, AuthSource.WECHAT);
public class AuthWeChatOpenRequest extends AuthDefaultRequest {
public AuthWeChatOpenRequest(AuthConfig config) {
super(config, AuthDefaultSource.WECHAT_OPEN);
}
public AuthWeChatOpenRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.WECHAT_OPEN, authStateCache);
}
/**
@@ -37,8 +44,8 @@ public class AuthWeChatRequest extends AuthDefaultRequest {
protected AuthUser getUserInfo(AuthToken authToken) {
String openId = authToken.getOpenId();
HttpResponse response = doGetUserInfo(authToken);
JSONObject object = JSONObject.parseObject(response.body());
String response = doGetUserInfo(authToken);
JSONObject object = JSONObject.parseObject(response);
this.checkResponse(object);
@@ -49,14 +56,15 @@ public class AuthWeChatRequest extends AuthDefaultRequest {
}
return AuthUser.builder()
.rawUserInfo(object)
.username(object.getString("nickname"))
.nickname(object.getString("nickname"))
.avatar(object.getString("headimgurl"))
.location(location)
.uuid(openId)
.gender(AuthUserGender.getRealGender(object.getString("sex")))
.gender(AuthUserGender.getWechatRealGender(object.getString("sex")))
.token(authToken)
.source(source)
.source(source.toString())
.build();
}
@@ -86,8 +94,8 @@ public class AuthWeChatRequest extends AuthDefaultRequest {
* @return token对象
*/
private AuthToken getToken(String accessTokenUrl) {
HttpResponse response = HttpRequest.get(accessTokenUrl).execute();
JSONObject accessTokenObject = JSONObject.parseObject(response.body());
String response = new HttpUtils(config.getHttpConfig()).get(accessTokenUrl);
JSONObject accessTokenObject = JSONObject.parseObject(response);
this.checkResponse(accessTokenObject);
@@ -1,13 +1,16 @@
package me.zhyd.oauth.request;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import com.alibaba.fastjson.JSONObject;
import me.zhyd.oauth.utils.HttpUtils;
import com.xkcoding.http.support.HttpHeader;
import me.zhyd.oauth.cache.AuthStateCache;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.config.AuthSource;
import me.zhyd.oauth.config.AuthDefaultSource;
import me.zhyd.oauth.enums.AuthResponseStatus;
import me.zhyd.oauth.enums.AuthUserGender;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthResponse;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.utils.IpUtils;
@@ -24,14 +27,17 @@ import me.zhyd.oauth.utils.UrlBuilder;
public class AuthWeiboRequest extends AuthDefaultRequest {
public AuthWeiboRequest(AuthConfig config) {
super(config, AuthSource.WEIBO);
super(config, AuthDefaultSource.WEIBO);
}
public AuthWeiboRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.WEIBO, authStateCache);
}
@Override
protected AuthToken getAccessToken(AuthCallback authCallback) {
HttpResponse response = doPostAuthorizationCode(authCallback.getCode());
String accessTokenStr = response.body();
JSONObject accessTokenObject = JSONObject.parseObject(accessTokenStr);
String response = doPostAuthorizationCode(authCallback.getCode());
JSONObject accessTokenObject = JSONObject.parseObject(response);
if (accessTokenObject.containsKey("error")) {
throw new AuthException(accessTokenObject.getString("error_description"));
}
@@ -48,16 +54,17 @@ public class AuthWeiboRequest extends AuthDefaultRequest {
String accessToken = authToken.getAccessToken();
String uid = authToken.getUid();
String oauthParam = String.format("uid=%s&access_token=%s", uid, accessToken);
HttpResponse response = HttpRequest.get(userInfoUrl(authToken))
.header("Authorization", "OAuth2 " + oauthParam)
.header("API-RemoteIP", IpUtils.getLocalIp())
.execute();
String userInfo = response.body();
HttpHeader httpHeader = new HttpHeader();
httpHeader.add("Authorization", "OAuth2 " + oauthParam);
httpHeader.add("API-RemoteIP", IpUtils.getLocalIp());
String userInfo = new HttpUtils(config.getHttpConfig()).get(userInfoUrl(authToken), null, httpHeader, false);
JSONObject object = JSONObject.parseObject(userInfo);
if (object.containsKey("error")) {
throw new AuthException(object.getString("error"));
}
return AuthUser.builder()
.rawUserInfo(object)
.uuid(object.getString("id"))
.username(object.getString("name"))
.avatar(object.getString("profile_image_url"))
@@ -68,7 +75,7 @@ public class AuthWeiboRequest extends AuthDefaultRequest {
.remark(object.getString("description"))
.gender(AuthUserGender.getRealGender(object.getString("gender")))
.token(authToken)
.source(source)
.source(source.toString())
.build();
}
@@ -85,4 +92,19 @@ public class AuthWeiboRequest extends AuthDefaultRequest {
.queryParam("uid", authToken.getUid())
.build();
}
@Override
public AuthResponse revoke(AuthToken authToken) {
String response = doGetRevoke(authToken);
JSONObject object = JSONObject.parseObject(response);
if (object.containsKey("error")) {
return AuthResponse.builder()
.code(AuthResponseStatus.FAILURE.getCode())
.msg(object.getString("error"))
.build();
}
// 返回 result = true 表示取消授权成功,否则失败
AuthResponseStatus status = object.getBooleanValue("result") ? AuthResponseStatus.SUCCESS : AuthResponseStatus.FAILURE;
return AuthResponse.builder().code(status.getCode()).msg(status.getMsg()).build();
}
}
@@ -0,0 +1,4 @@
/**
* JustAuth核心组件,所有授权登录都是基于{@code request}实现
*/
package me.zhyd.oauth.request;
@@ -2,9 +2,11 @@ package me.zhyd.oauth.utils;
import me.zhyd.oauth.cache.AuthStateCache;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.config.AuthDefaultSource;
import me.zhyd.oauth.config.AuthSource;
import me.zhyd.oauth.enums.AuthResponseStatus;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.AuthCallback;
/**
* 授权配置类的校验器
@@ -24,12 +26,18 @@ public class AuthChecker {
*/
public static boolean isSupportedAuth(AuthConfig config, AuthSource source) {
boolean isSupported = StringUtils.isNotEmpty(config.getClientId()) && StringUtils.isNotEmpty(config.getClientSecret()) && StringUtils.isNotEmpty(config.getRedirectUri());
if (isSupported && AuthSource.ALIPAY == source) {
if (isSupported && AuthDefaultSource.ALIPAY == source) {
isSupported = StringUtils.isNotEmpty(config.getAlipayPublicKey());
}
if (isSupported && AuthSource.STACK_OVERFLOW == source) {
if (isSupported && AuthDefaultSource.STACK_OVERFLOW == source) {
isSupported = StringUtils.isNotEmpty(config.getStackOverflowKey());
}
if (isSupported && AuthDefaultSource.WECHAT_ENTERPRISE == source) {
isSupported = StringUtils.isNotEmpty(config.getAgentId());
}
if (isSupported && AuthDefaultSource.CODING == source) {
isSupported = StringUtils.isNotEmpty(config.getCodingGroupName());
}
return isSupported;
}
@@ -42,39 +50,64 @@ public class AuthChecker {
*/
public static void checkConfig(AuthConfig config, AuthSource source) {
String redirectUri = config.getRedirectUri();
if (!GlobalAuthUtil.isHttpProtocol(redirectUri) && !GlobalAuthUtil.isHttpsProtocol(redirectUri)) {
throw new AuthException(AuthResponseStatus.ILLEGAL_REDIRECT_URI);
if (!GlobalAuthUtils.isHttpProtocol(redirectUri) && !GlobalAuthUtils.isHttpsProtocol(redirectUri)) {
throw new AuthException(AuthResponseStatus.ILLEGAL_REDIRECT_URI, source);
}
// facebook的回调地址必须为https的链接
if (AuthSource.FACEBOOK == source && !GlobalAuthUtil.isHttpsProtocol(redirectUri)) {
throw new AuthException(AuthResponseStatus.ILLEGAL_REDIRECT_URI);
if (AuthDefaultSource.FACEBOOK == source && !GlobalAuthUtils.isHttpsProtocol(redirectUri)) {
// Facebook's redirect uri must use the HTTPS protocol
throw new AuthException(AuthResponseStatus.ILLEGAL_REDIRECT_URI, source);
}
// 支付宝在创建回调地址时,不允许使用localhost或者127.0.0.1
if (AuthSource.ALIPAY == source && GlobalAuthUtil.isLocalHost(redirectUri)) {
throw new AuthException(AuthResponseStatus.ILLEGAL_REDIRECT_URI);
if (AuthDefaultSource.ALIPAY == source && GlobalAuthUtils.isLocalHost(redirectUri)) {
// The redirect uri of alipay is forbidden to use localhost or 127.0.0.1
throw new AuthException(AuthResponseStatus.ILLEGAL_REDIRECT_URI, source);
}
}
/**
* 校验回调传回的code
* <p>
* {@code v1.10.0}版本中改为传入{@code source}和{@code callback},对于不同平台使用不同参数接受code的情况统一做处理
*
* @param code 回调时传回的code
* @param source 当前授权平台
* @param callback 从第三方授权回调回来时传入的参数集合
* @since 1.8.0
*/
public static void checkCode(String code) {
public static void checkCode(AuthSource source, AuthCallback callback) {
// 推特平台不支持回调 code 和 state
if (source == AuthDefaultSource.TWITTER) {
return;
}
String code = callback.getCode();
if (source == AuthDefaultSource.ALIPAY) {
code = callback.getAuth_code();
} else if (source == AuthDefaultSource.HUAWEI) {
code = callback.getAuthorization_code();
}
if (StringUtils.isEmpty(code)) {
throw new AuthException(AuthResponseStatus.ILLEGAL_CODE);
throw new AuthException(AuthResponseStatus.ILLEGAL_CODE, source);
}
}
/**
* 校验回调传回的state
* 校验回调传回的{@code state},为空或者不存在
* <p>
* {@code state}不存在的情况只有两种:
* 1. {@code state}已使用,被正常清除
* 2. {@code state}为前端伪造,本身就不存在
*
* @param state {@code state}一定不为空
* @param state {@code state}一定不为空
* @param source {@code source}当前授权平台
* @param authStateCache {@code authStateCache} state缓存实现
*/
public static void checkState(String state) {
if (StringUtils.isEmpty(state) || !AuthStateCache.containsKey(state)) {
throw new AuthException(AuthResponseStatus.ILLEGAL_REQUEST);
public static void checkState(String state, AuthSource source, AuthStateCache authStateCache) {
// 推特平台不支持回调 code 和 state
if (source == AuthDefaultSource.TWITTER) {
return;
}
if (StringUtils.isEmpty(state) || !authStateCache.containsKey(state)) {
throw new AuthException(AuthResponseStatus.ILLEGAL_STATUS, source);
}
}
}
@@ -0,0 +1,197 @@
package me.zhyd.oauth.utils;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
/**
* Base64编码
*
* @author looly
* @since 3.2.0
*/
public class Base64Utils {
private static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;
/**
* 标准编码表
*/
private static final byte[] STANDARD_ENCODE_TABLE = { //
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', //
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', //
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', //
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', //
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', //
'o', 'p', 'q', 'r', 's', 't', 'u', 'v', //
'w', 'x', 'y', 'z', '0', '1', '2', '3', //
'4', '5', '6', '7', '8', '9', '+', '/' //
};
/**
* URL安全的编码表,将 + 和 / 替换为 - 和 _
*/
private static final byte[] URL_SAFE_ENCODE_TABLE = { //
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', //
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', //
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', //
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', //
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', //
'o', 'p', 'q', 'r', 's', 't', 'u', 'v', //
'w', 'x', 'y', 'z', '0', '1', '2', '3', //
'4', '5', '6', '7', '8', '9', '-', '_' //
};
// -------------------------------------------------------------------- encode
/**
* 编码为Base64,非URL安全的
*
* @param arr 被编码的数组
* @param lineSep 在76个char之后是CRLF还是EOF
* @return 编码后的bytes
*/
public static byte[] encode(byte[] arr, boolean lineSep) {
return encode(arr, lineSep, false);
}
/**
* 编码为Base64URL安全的
*
* @param arr 被编码的数组
* @param lineSep 在76个char之后是CRLF还是EOF
* @return 编码后的bytes
* @since 3.0.6
*/
public static byte[] encodeUrlSafe(byte[] arr, boolean lineSep) {
return encode(arr, lineSep, true);
}
/**
* base64编码
*
* @param source 被编码的base64字符串
* @return 被加密后的字符串
*/
public static String encode(CharSequence source) {
return encode(source, DEFAULT_CHARSET);
}
/**
* base64编码,URL安全
*
* @param source 被编码的base64字符串
* @return 被加密后的字符串
* @since 3.0.6
*/
public static String encodeUrlSafe(CharSequence source) {
return encodeUrlSafe(source, DEFAULT_CHARSET);
}
/**
* base64编码
*
* @param source 被编码的base64字符串
* @param charset 字符集
* @return 被加密后的字符串
*/
public static String encode(CharSequence source, Charset charset) {
return encode(StringUtils.bytes(source, charset));
}
/**
* base64编码,URL安全的
*
* @param source 被编码的base64字符串
* @param charset 字符集
* @return 被加密后的字符串
* @since 3.0.6
*/
public static String encodeUrlSafe(CharSequence source, Charset charset) {
return encodeUrlSafe(StringUtils.bytes(source, charset));
}
/**
* base64编码
*
* @param source 被编码的base64字符串
* @return 被加密后的字符串
*/
public static String encode(byte[] source) {
return StringUtils.str(encode(source, false), DEFAULT_CHARSET);
}
/**
* base64编码,URL安全的
*
* @param source 被编码的base64字符串
* @return 被加密后的字符串
* @since 3.0.6
*/
public static String encodeUrlSafe(byte[] source) {
return StringUtils.str(encodeUrlSafe(source, false), DEFAULT_CHARSET);
}
/**
* 编码为Base64<br>
* 如果isMultiLine为<code>true</code>,则每76个字符一个换行符,否则在一行显示
*
* @param arr 被编码的数组
* @param isMultiLine 在76个char之后是CRLF还是EOF
* @param isUrlSafe 是否使用URL安全字符,一般为<code>false</code>
* @return 编码后的bytes
*/
public static byte[] encode(byte[] arr, boolean isMultiLine, boolean isUrlSafe) {
if (null == arr) {
return null;
}
int len = arr.length;
if (len == 0) {
return new byte[0];
}
int evenlen = (len / 3) * 3;
int cnt = ((len - 1) / 3 + 1) << 2;
int destlen = cnt + (isMultiLine ? (cnt - 1) / 76 << 1 : 0);
byte[] dest = new byte[destlen];
byte[] encodeTable = isUrlSafe ? URL_SAFE_ENCODE_TABLE : STANDARD_ENCODE_TABLE;
for (int s = 0, d = 0, cc = 0; s < evenlen; ) {
int i = (arr[s++] & 0xff) << 16 | (arr[s++] & 0xff) << 8 | (arr[s++] & 0xff);
dest[d++] = encodeTable[(i >>> 18) & 0x3f];
dest[d++] = encodeTable[(i >>> 12) & 0x3f];
dest[d++] = encodeTable[(i >>> 6) & 0x3f];
dest[d++] = encodeTable[i & 0x3f];
if (isMultiLine && ++cc == 19 && d < destlen - 2) {
dest[d++] = '\r';
dest[d++] = '\n';
cc = 0;
}
}
int left = len - evenlen;// 剩余位数
if (left > 0) {
int i = ((arr[evenlen] & 0xff) << 10) | (left == 2 ? ((arr[len - 1] & 0xff) << 2) : 0);
dest[destlen - 4] = encodeTable[i >> 12];
dest[destlen - 3] = encodeTable[(i >>> 6) & 0x3f];
if (isUrlSafe) {
// 在URL Safe模式下,=为URL中的关键字符,不需要补充。空余的byte位要去掉。
int urlSafeLen = destlen - 2;
if (2 == left) {
dest[destlen - 2] = encodeTable[i & 0x3f];
urlSafeLen += 1;
}
byte[] urlSafeDest = new byte[urlSafeLen];
System.arraycopy(dest, 0, urlSafeDest, 0, urlSafeLen);
return urlSafeDest;
} else {
dest[destlen - 2] = (left == 2) ? encodeTable[i & 0x3f] : (byte) '=';
dest[destlen - 1] = '=';
}
}
return dest;
}
}
@@ -1,123 +0,0 @@
package me.zhyd.oauth.utils;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.http.HttpUtil;
import me.zhyd.oauth.exception.AuthException;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.*;
/**
* 全局的工具类
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @since 1.0.0
*/
public class GlobalAuthUtil {
private static final Charset DEFAULT_ENCODING = StandardCharsets.UTF_8;
private static final String ALGORITHM = "HmacSHA256";
public static String generateDingTalkSignature(String secretKey, String timestamp) {
byte[] signData = sign(secretKey.getBytes(DEFAULT_ENCODING), timestamp.getBytes(DEFAULT_ENCODING));
return urlEncode(new String(Base64.encode(signData, false)));
}
private static byte[] sign(byte[] key, byte[] data) {
try {
Mac mac = Mac.getInstance(ALGORITHM);
mac.init(new SecretKeySpec(key, ALGORITHM));
return mac.doFinal(data);
} catch (NoSuchAlgorithmException ex) {
throw new AuthException("Unsupported algorithm: " + ALGORITHM, ex);
} catch (InvalidKeyException ex) {
throw new AuthException("Invalid key: " + Arrays.toString(key), ex);
}
}
public static String urlEncode(String value) {
if (value == null) {
return "";
}
try {
String encoded = URLEncoder.encode(value, GlobalAuthUtil.DEFAULT_ENCODING.displayName());
return encoded.replace("+", "%20").replace("*", "%2A").replace("~", "%7E").replace("/", "%2F");
} catch (UnsupportedEncodingException e) {
throw new AuthException("Failed To Encode Uri", e);
}
}
public static String urlDecode(String value) {
if (value == null) {
return "";
}
try {
return URLDecoder.decode(value, GlobalAuthUtil.DEFAULT_ENCODING.displayName());
} catch (UnsupportedEncodingException e) {
throw new AuthException("Failed To Decode Uri", e);
}
}
public static Map<String, String> parseStringToMap(String accessTokenStr) {
Map<String, String> res = new HashMap<>();
if (accessTokenStr.contains("&")) {
String[] fields = accessTokenStr.split("&");
for (String field : fields) {
if (field.contains("=")) {
String[] keyValue = field.split("=");
res.put(GlobalAuthUtil.urlDecode(keyValue[0]), keyValue.length == 2 ? GlobalAuthUtil.urlDecode(keyValue[1]) : null);
}
}
}
return res;
}
public static String parseMapToString(Map<String, Object> params, boolean encode) {
List<String> paramList = new ArrayList<>();
params.forEach((k, v) -> {
if (ObjectUtil.isNull(v)) {
paramList.add(k + "=");
} else {
String valueString = v.toString();
paramList.add(k + "=" + (encode ? urlEncode(valueString) : valueString));
}
});
return CollUtil.join(paramList, "&");
}
public static Map<String, Object> parseQueryToMap(String url) {
Map<String, Object> paramMap = new HashMap<>();
HttpUtil.decodeParamMap(url, "UTF-8").forEach(paramMap::put);
return paramMap;
}
public static boolean isHttpProtocol(String url) {
if (StringUtils.isEmpty(url)) {
return false;
}
return url.startsWith("http://");
}
public static boolean isHttpsProtocol(String url) {
if (StringUtils.isEmpty(url)) {
return false;
}
return url.startsWith("https://");
}
public static boolean isLocalHost(String url) {
return StringUtils.isEmpty(url) || url.contains("127.0.0.1") || url.contains("localhost");
}
}
@@ -0,0 +1,302 @@
package me.zhyd.oauth.utils;
import com.alibaba.fastjson.JSON;
import me.zhyd.oauth.exception.AuthException;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;
/**
* 全局的工具类
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @since 1.0.0
*/
public class GlobalAuthUtils {
private static final Charset DEFAULT_ENCODING = StandardCharsets.UTF_8;
private static final String HMAC_SHA1 = "HmacSHA1";
private static final String HMAC_SHA_256 = "HmacSHA256";
/**
* 生成钉钉请求的Signature
*
* @param secretKey 平台应用的授权密钥
* @param timestamp 时间戳
* @return Signature
*/
public static String generateDingTalkSignature(String secretKey, String timestamp) {
byte[] signData = sign(secretKey.getBytes(DEFAULT_ENCODING), timestamp.getBytes(DEFAULT_ENCODING), HMAC_SHA_256);
return urlEncode(new String(Base64Utils.encode(signData, false)));
}
/**
* 签名
*
* @param key key
* @param data data
* @param algorithm algorithm
* @return byte[]
*/
private static byte[] sign(byte[] key, byte[] data, String algorithm) {
try {
Mac mac = Mac.getInstance(algorithm);
mac.init(new SecretKeySpec(key, algorithm));
return mac.doFinal(data);
} catch (NoSuchAlgorithmException ex) {
throw new AuthException("Unsupported algorithm: " + algorithm, ex);
} catch (InvalidKeyException ex) {
throw new AuthException("Invalid key: " + Arrays.toString(key), ex);
}
}
/**
* 编码
*
* @param value str
* @return encode str
*/
public static String urlEncode(String value) {
if (value == null) {
return "";
}
try {
String encoded = URLEncoder.encode(value, GlobalAuthUtils.DEFAULT_ENCODING.displayName());
return encoded.replace("+", "%20").replace("*", "%2A").replace("~", "%7E").replace("/", "%2F");
} catch (UnsupportedEncodingException e) {
throw new AuthException("Failed To Encode Uri", e);
}
}
/**
* 解码
*
* @param value str
* @return decode str
*/
public static String urlDecode(String value) {
if (value == null) {
return "";
}
try {
return URLDecoder.decode(value, GlobalAuthUtils.DEFAULT_ENCODING.displayName());
} catch (UnsupportedEncodingException e) {
throw new AuthException("Failed To Decode Uri", e);
}
}
/**
* string字符串转mapstr格式为 {@code xxx=xxx&xxx=xxx}
*
* @param accessTokenStr 待转换的字符串
* @return map
*/
public static Map<String, String> parseStringToMap(String accessTokenStr) {
Map<String, String> res = new HashMap<>(6);
if (accessTokenStr.contains("&")) {
String[] fields = accessTokenStr.split("&");
for (String field : fields) {
if (field.contains("=")) {
String[] keyValue = field.split("=");
res.put(GlobalAuthUtils.urlDecode(keyValue[0]), keyValue.length == 2 ? GlobalAuthUtils.urlDecode(keyValue[1]) : null);
}
}
}
return res;
}
/**
* map转字符串,转换后的字符串格式为 {@code xxx=xxx&xxx=xxx}
*
* @param params 待转换的map
* @param encode 是否转码
* @return str
*/
public static String parseMapToString(Map<String, String> params, boolean encode) {
if (null == params || params.isEmpty()) {
return "";
}
List<String> paramList = new ArrayList<>();
params.forEach((k, v) -> {
if (null == v) {
paramList.add(k + "=");
} else {
paramList.add(k + "=" + (encode ? urlEncode(v) : v));
}
});
return String.join("&", paramList);
}
/**
* 是否为http协议
*
* @param url 待验证的url
* @return true: http协议, false: 非http协议
*/
public static boolean isHttpProtocol(String url) {
if (StringUtils.isEmpty(url)) {
return false;
}
return url.startsWith("http://");
}
/**
* 是否为https协议
*
* @param url 待验证的url
* @return true: https协议, false: 非https协议
*/
public static boolean isHttpsProtocol(String url) {
if (StringUtils.isEmpty(url)) {
return false;
}
return url.startsWith("https://");
}
/**
* 是否为本地主机(域名)
*
* @param url 待验证的url
* @return true: 本地主机(域名), false: 非本地主机(域名)
*/
public static boolean isLocalHost(String url) {
return StringUtils.isEmpty(url) || url.contains("127.0.0.1") || url.contains("localhost");
}
/**
* Generate nonce with given length
*
* @param len length
* @return nonce string
*/
public static String generateNonce(int len) {
String s = "0123456789QWERTYUIOPLKJHGFDSAZXCVBNMqwertyuioplkjhgfdsazxcvbnm";
Random rng = new Random();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < len; i++) {
int index = rng.nextInt(62);
sb.append(s, index, index + 1);
}
return sb.toString();
}
/**
* Get current timestamp
*
* @return timestamp string
*/
public static String getTimestamp() {
return String.valueOf(System.currentTimeMillis() / 1000);
}
/**
* Generate Twitter signature
* https://developer.twitter.com/en/docs/basics/authentication/guides/creating-a-signature
*
* @param params parameters including: oauth headers, query params, body params
* @param method HTTP method
* @param baseUrl base url
* @param apiSecret api key secret can be found in the developer portal by viewing the app details page
* @param tokenSecret oauth token secret
* @return BASE64 encoded signature string
*/
public static String generateTwitterSignature(Map<String, String> params, String method, String baseUrl, String apiSecret, String tokenSecret) {
TreeMap<String, String> map = new TreeMap<>(params);
String str = parseMapToString(map, true);
String baseStr = method.toUpperCase() + "&" + urlEncode(baseUrl) + "&" + urlEncode(str);
String signKey = apiSecret + "&" + (StringUtils.isEmpty(tokenSecret) ? "" : tokenSecret);
byte[] signature = sign(signKey.getBytes(DEFAULT_ENCODING), baseStr.getBytes(DEFAULT_ENCODING), HMAC_SHA1);
return new String(Base64Utils.encode(signature, false));
}
/**
* 生成饿了么请求的Signature
* <p>
* 代码copy并修改自:https://coding.net/u/napos_openapi/p/eleme-openapi-java-sdk/git/blob/master/src/main/java/eleme/openapi/sdk/utils/SignatureUtil.java
*
* @param appKey 平台应用的授权key
* @param secret 平台应用的授权密钥
* @param timestamp 时间戳,单位秒。API服务端允许客户端请求最大时间误差为正负5分钟。
* @param action 饿了么请求的api方法
* @param token 用户授权的token
* @param parameters 加密参数
* @return Signature
*/
public static String generateElemeSignature(String appKey, String secret, long timestamp, String action, String token, Map<String, Object> parameters) {
final Map<String, Object> sorted = new TreeMap<>(parameters);
sorted.put("app_key", appKey);
sorted.put("timestamp", timestamp);
StringBuffer string = new StringBuffer();
for (Map.Entry<String, Object> entry : sorted.entrySet()) {
string.append(entry.getKey()).append("=").append(JSON.toJSONString(entry.getValue()));
}
String splice = String.format("%s%s%s%s", action, token, string, secret);
String calculatedSignature = md5(splice);
return calculatedSignature.toUpperCase();
}
/**
* MD5加密
* <p>
* 代码copy并修改自:https://coding.net/u/napos_openapi/p/eleme-openapi-java-sdk/git/blob/master/src/main/java/eleme/openapi/sdk/utils/SignatureUtil.java
*
* @param str 待加密的字符串
* @return md5 str
*/
public static String md5(String str) {
MessageDigest md = null;
StringBuilder buffer = null;
try {
md = MessageDigest.getInstance("MD5");
md.update(str.getBytes(StandardCharsets.UTF_8));
byte[] byteData = md.digest();
buffer = new StringBuilder();
for (byte byteDatum : byteData) {
buffer.append(Integer.toString((byteDatum & 0xff) + 0x100, 16).substring(1));
}
} catch (Exception ignored) {
}
return null == buffer ? "" : buffer.toString();
}
/**
* 生成京东宙斯平台的签名字符串
* 宙斯签名规则过程如下:
* 将所有请求参数按照字母先后顺序排列,例如将access_token,app_key,method,timestamp,v 排序为access_token,app_key,method,timestamp,v
* 1.把所有参数名和参数值进行拼接,例如:access_tokenxxxapp_keyxxxmethodxxxxxxtimestampxxxxxxvx
* 2.把appSecret夹在字符串的两端,例如:appSecret+XXXX+appSecret
* 3.使用MD5进行加密,再转化成大写
* link: http://open.jd.com/home/home#/doc/common?listId=890
* link: https://github.com/pingjiang/jd-open-api-sdk-src/blob/master/src/main/java/com/jd/open/api/sdk/DefaultJdClient.java
*
* @param appSecret 京东应用密钥
* @param params 签名参数
* @return 签名后的字符串
* @since 1.15.0
*/
public static String generateJdSignature(String appSecret, Map<String, Object> params) {
Map<String, Object> treeMap = new TreeMap<>(params);
StringBuilder signBuilder = new StringBuilder(appSecret);
for (Map.Entry<String, Object> entry : treeMap.entrySet()) {
String name = entry.getKey();
String value = String.valueOf(entry.getValue());
if (StringUtils.isNotEmpty(name) && StringUtils.isNotEmpty(value)) {
signBuilder.append(name).append(value);
}
}
signBuilder.append(appSecret);
return md5(signBuilder.toString()).toUpperCase();
}
}
@@ -0,0 +1,105 @@
package me.zhyd.oauth.utils;
import com.xkcoding.http.HttpUtil;
import com.xkcoding.http.config.HttpConfig;
import com.xkcoding.http.support.HttpHeader;
import java.util.Map;
/**
* HttpUtil 工具,统一处理 http 请求,方便对 simple-http 做定制
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.0
*/
public class HttpUtils {
public HttpUtils(HttpConfig config) {
HttpUtil.setConfig(config);
}
public HttpUtils() {
}
/**
* GET 请求
*
* @param url URL
* @return 结果
*/
public String get(String url) {
return HttpUtil.get(url);
}
/**
* GET 请求
*
* @param url URL
* @param params 参数
* @param header 请求头
* @param encode 是否需要 url encode
* @return 结果
*/
public String get(String url, Map<String, String> params, HttpHeader header, boolean encode) {
return HttpUtil.get(url, params, header, encode);
}
/**
* POST 请求
*
* @param url URL
* @return 结果
*/
public String post(String url) {
return HttpUtil.post(url);
}
/**
* POST 请求
*
* @param url URL
* @param data JSON 参数
* @return 结果
*/
public String post(String url, String data) {
return HttpUtil.post(url, data);
}
/**
* POST 请求
*
* @param url URL
* @param data JSON 参数
* @param header 请求头
* @return 结果
*/
public String post(String url, String data, HttpHeader header) {
return HttpUtil.post(url, data, header);
}
/**
* POST 请求
*
* @param url URL
* @param params form 参数
* @param encode 是否需要 url encode
* @return 结果
*/
public String post(String url, Map<String, String> params, boolean encode) {
return HttpUtil.post(url, params, encode);
}
/**
* POST 请求
*
* @param url URL
* @param params form 参数
* @param header 请求头
* @param encode 是否需要 url encode
* @return 结果
*/
public String post(String url, Map<String, String> params, HttpHeader header, boolean encode) {
return HttpUtil.post(url, params, header, encode);
}
}
@@ -1,7 +1,6 @@
package me.zhyd.oauth.utils;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.ThreadLocalRandom;
import java.nio.charset.Charset;
/**
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
@@ -36,4 +35,40 @@ public class StringUtils {
return str.concat(appendStr);
}
/**
* 编码字符串
*
* @param str 字符串
* @param charset 字符集,如果此字段为空,则解码的结果取决于平台
* @return 编码后的字节码
*/
public static byte[] bytes(CharSequence str, Charset charset) {
if (str == null) {
return null;
}
if (null == charset) {
return str.toString().getBytes();
}
return str.toString().getBytes(charset);
}
/**
* 解码字节码
*
* @param data 字符串
* @param charset 字符集,如果此字段为空,则解码的结果取决于平台
* @return 解码后的字符串
*/
public static String str(byte[] data, Charset charset) {
if (data == null) {
return null;
}
if (null == charset) {
return new String(data);
}
return new String(data, charset);
}
}
@@ -1,10 +1,10 @@
package me.zhyd.oauth.utils;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import com.xkcoding.http.util.MapUtil;
import com.xkcoding.http.util.StringUtil;
import lombok.Setter;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
@@ -19,7 +19,7 @@ import java.util.Map;
@Setter
public class UrlBuilder {
private final Map<String, Object> params = new LinkedHashMap<>(7);
private final Map<String, String> params = new LinkedHashMap<>(7);
private String baseUrl;
private UrlBuilder() {
@@ -36,6 +36,16 @@ public class UrlBuilder {
return builder;
}
/**
* 只读的参数Map
*
* @return unmodifiable Map
* @since 1.15.0
*/
public Map<String, Object> getReadOnlyParams() {
return Collections.unmodifiableMap(params);
}
/**
* 添加参数
*
@@ -44,8 +54,9 @@ public class UrlBuilder {
* @return this UrlBuilder
*/
public UrlBuilder queryParam(String key, Object value) {
Assert.notBlank(key, "参数名不能为空");
if (StringUtil.isEmpty(key)) {
throw new RuntimeException("参数名不能为空");
}
String valueAsString = (value != null ? value.toString() : null);
this.params.put(key, valueAsString);
@@ -72,7 +83,7 @@ public class UrlBuilder {
return this.baseUrl;
}
String baseUrl = StringUtils.appendIfNotContain(this.baseUrl, "?", "&");
String paramString = GlobalAuthUtil.parseMapToString(this.params, encode);
String paramString = MapUtil.parseMapToString(this.params, encode);
return baseUrl + paramString;
}
}
@@ -0,0 +1,4 @@
/**
* 提供一些简单的工具和校验等
*/
package me.zhyd.oauth.utils;
@@ -1,280 +0,0 @@
package me.zhyd.oauth;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthResponse;
import me.zhyd.oauth.request.*;
import org.junit.Test;
/**
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
*/
public class AuthRequestTest {
@Test
public void giteeTest() {
AuthRequest authRequest = new AuthGiteeRequest(AuthConfig.builder()
.clientId("clientId")
.clientSecret("clientSecret")
.redirectUri("redirectUri")
.build());
// 返回授权页面,可自行跳转
authRequest.authorize("state");
// 授权登录后会返回code(auth_code(仅限支付宝))、state1.8.0版本后,可以用AuthCallback类作为回调接口的入参
// 注:JustAuth默认保存state的时效为3分钟,3分钟内未使用则会自动清除过期的state
authRequest.login(new AuthCallback());
}
@Test
public void githubTest() {
AuthRequest authRequest = new AuthGithubRequest(AuthConfig.builder()
.clientId("clientId")
.clientSecret("clientSecret")
.redirectUri("redirectUri")
.build());
// 返回授权页面,可自行跳转
authRequest.authorize("state");
// 授权登录后会返回code(auth_code(仅限支付宝))、state1.8.0版本后,可以用AuthCallback类作为回调接口的入参
// 注:JustAuth默认保存state的时效为3分钟,3分钟内未使用则会自动清除过期的state
authRequest.login(new AuthCallback());
}
@Test
public void weiboTest() {
AuthRequest authRequest = new AuthWeiboRequest(AuthConfig.builder()
.clientId("clientId")
.clientSecret("clientSecret")
.redirectUri("redirectUri")
.build());
// 返回授权页面,可自行跳转
authRequest.authorize("state");
// 授权登录后会返回code(auth_code(仅限支付宝))、state1.8.0版本后,可以用AuthCallback类作为回调接口的入参
// 注:JustAuth默认保存state的时效为3分钟,3分钟内未使用则会自动清除过期的state
authRequest.login(new AuthCallback());
}
@Test
public void dingdingTest() {
AuthRequest authRequest = new AuthDingTalkRequest(AuthConfig.builder()
.clientId("clientId")
.clientSecret("clientSecret")
.redirectUri("redirectUri")
.build());
// 返回授权页面,可自行跳转
authRequest.authorize("state");
// 授权登录后会返回code(auth_code(仅限支付宝))、state1.8.0版本后,可以用AuthCallback类作为回调接口的入参
// 注:JustAuth默认保存state的时效为3分钟,3分钟内未使用则会自动清除过期的state
authRequest.login(new AuthCallback());
}
@Test
public void baiduTest() {
AuthRequest authRequest = new AuthBaiduRequest(AuthConfig.builder()
.clientId("clientId")
.clientSecret("clientSecret")
.redirectUri("redirectUri")
.build());
// 返回授权页面,可自行跳转
authRequest.authorize("state");
// 授权登录后会返回code(auth_code(仅限支付宝))、state1.8.0版本后,可以用AuthCallback类作为回调接口的入参
// 注:JustAuth默认保存state的时效为3分钟,3分钟内未使用则会自动清除过期的state
authRequest.login(new AuthCallback());
}
@Test
public void codingTest() {
AuthRequest authRequest = new AuthCodingRequest(AuthConfig.builder()
.clientId("clientId")
.clientSecret("clientSecret")
.redirectUri("redirectUri")
.build());
// 返回授权页面,可自行跳转
authRequest.authorize("state");
// 授权登录后会返回code(auth_code(仅限支付宝))、state1.8.0版本后,可以用AuthCallback类作为回调接口的入参
// 注:JustAuth默认保存state的时效为3分钟,3分钟内未使用则会自动清除过期的state
authRequest.login(new AuthCallback());
}
@Test
public void tencentCloudTest() {
AuthRequest authRequest = new AuthTencentCloudRequest(AuthConfig.builder()
.clientId("clientId")
.clientSecret("clientSecret")
.redirectUri("redirectUri")
.build());
// 返回授权页面,可自行跳转
authRequest.authorize("state");
// 授权登录后会返回code(auth_code(仅限支付宝))、state1.8.0版本后,可以用AuthCallback类作为回调接口的入参
// 注:JustAuth默认保存state的时效为3分钟,3分钟内未使用则会自动清除过期的state
authRequest.login(new AuthCallback());
}
@Test
public void oschinaTest() {
AuthRequest authRequest = new AuthOschinaRequest(AuthConfig.builder()
.clientId("clientId")
.clientSecret("clientSecret")
.redirectUri("redirectUri")
.build());
// 返回授权页面,可自行跳转
authRequest.authorize("state");
// 授权登录后会返回code(auth_code(仅限支付宝))、state1.8.0版本后,可以用AuthCallback类作为回调接口的入参
// 注:JustAuth默认保存state的时效为3分钟,3分钟内未使用则会自动清除过期的state
authRequest.login(new AuthCallback());
}
@Test
public void alipayTest() {
AuthRequest authRequest = new AuthAlipayRequest(AuthConfig.builder()
.clientId("clientId")
.clientSecret("clientSecret")
.redirectUri("redirectUri")
.alipayPublicKey("publicKey")
.build());
// 返回授权页面,可自行跳转
authRequest.authorize("state");
// 授权登录后会返回code(auth_code(仅限支付宝))、state1.8.0版本后,可以用AuthCallback类作为回调接口的入参
// 注:JustAuth默认保存state的时效为3分钟,3分钟内未使用则会自动清除过期的state
AuthResponse login = authRequest.login(new AuthCallback());
}
@Test
public void qqTest() {
AuthRequest authRequest = new AuthQqRequest(AuthConfig.builder()
.clientId("clientId")
.clientSecret("clientSecret")
.redirectUri("redirectUri")
.build());
// 返回授权页面,可自行跳转
authRequest.authorize("state");
// 授权登录后会返回code(auth_code(仅限支付宝))、state1.8.0版本后,可以用AuthCallback类作为回调接口的入参
// 注:JustAuth默认保存state的时效为3分钟,3分钟内未使用则会自动清除过期的state
AuthResponse login = authRequest.login(new AuthCallback());
}
@Test
public void wechatTest() {
AuthRequest authRequest = new AuthWeChatRequest(AuthConfig.builder()
.clientId("clientId")
.clientSecret("clientSecret")
.redirectUri("redirectUri")
.build());
// 返回授权页面,可自行跳转
authRequest.authorize("state");
// 授权登录后会返回code(auth_code(仅限支付宝))、state1.8.0版本后,可以用AuthCallback类作为回调接口的入参
// 注:JustAuth默认保存state的时效为3分钟,3分钟内未使用则会自动清除过期的state
AuthResponse login = authRequest.login(new AuthCallback());
}
@Test
public void taobaoTest() {
AuthRequest authRequest = new AuthTaobaoRequest(AuthConfig.builder()
.clientId("clientId")
.clientSecret("clientSecret")
.redirectUri("redirectUri")
.build());
// 返回授权页面,可自行跳转
authRequest.authorize("state");
// 授权登录后会返回code(auth_code(仅限支付宝))、state1.8.0版本后,可以用AuthCallback类作为回调接口的入参
// 注:JustAuth默认保存state的时效为3分钟,3分钟内未使用则会自动清除过期的state
AuthResponse login = authRequest.login(new AuthCallback());
}
@Test
public void googleTest() {
AuthRequest authRequest = new AuthGoogleRequest(AuthConfig.builder()
.clientId("clientId")
.clientSecret("clientSecret")
.redirectUri("redirectUri")
.build());
// 返回授权页面,可自行跳转
authRequest.authorize("state");
// 授权登录后会返回code(auth_code(仅限支付宝))、state1.8.0版本后,可以用AuthCallback类作为回调接口的入参
// 注:JustAuth默认保存state的时效为3分钟,3分钟内未使用则会自动清除过期的state
AuthResponse login = authRequest.login(new AuthCallback());
}
@Test
public void facebookTest() {
AuthRequest authRequest = new AuthFacebookRequest(AuthConfig.builder()
.clientId("clientId")
.clientSecret("clientSecret")
.redirectUri("redirectUri")
.build());
// 返回授权页面,可自行跳转
authRequest.authorize("state");
// 授权登录后会返回code(auth_code(仅限支付宝))、state1.8.0版本后,可以用AuthCallback类作为回调接口的入参
// 注:JustAuth默认保存state的时效为3分钟,3分钟内未使用则会自动清除过期的state
AuthResponse login = authRequest.login(new AuthCallback());
}
@Test
public void douyinTest() {
AuthRequest authRequest = new AuthDouyinRequest(AuthConfig.builder()
.clientId("clientId")
.clientSecret("clientSecret")
.redirectUri("redirectUri")
.build());
// 返回授权页面,可自行跳转
authRequest.authorize("state");
// 授权登录后会返回code(auth_code(仅限支付宝))、state1.8.0版本后,可以用AuthCallback类作为回调接口的入参
// 注:JustAuth默认保存state的时效为3分钟,3分钟内未使用则会自动清除过期的state
AuthResponse login = authRequest.login(new AuthCallback());
}
@Test
public void linkedinTest() {
AuthRequest authRequest = new AuthLinkedinRequest(AuthConfig.builder()
.clientId("clientId")
.clientSecret("clientSecret")
.redirectUri("redirectUri")
.build());
// 返回授权页面,可自行跳转
authRequest.authorize("state");
// 授权登录后会返回code(auth_code(仅限支付宝))、state1.8.0版本后,可以用AuthCallback类作为回调接口的入参
// 注:JustAuth默认保存state的时效为3分钟,3分钟内未使用则会自动清除过期的state
AuthResponse login = authRequest.login(new AuthCallback());
}
@Test
public void microsoftTest() {
AuthRequest authRequest = new AuthMicrosoftRequest(AuthConfig.builder()
.clientId("clientId")
.clientSecret("clientSecret")
.redirectUri("redirectUri")
.build());
// 返回授权页面,可自行跳转
authRequest.authorize("state");
// 授权登录后会返回code(auth_code(仅限支付宝))、state1.8.0版本后,可以用AuthCallback类作为回调接口的入参
// 注:JustAuth默认保存state的时效为3分钟,3分钟内未使用则会自动清除过期的state
AuthResponse login = authRequest.login(new AuthCallback());
}
@Test
public void miTest() {
AuthRequest authRequest = new AuthMiRequest(AuthConfig.builder()
.clientId("clientId")
.clientSecret("clientSecret")
.redirectUri("redirectUri")
.build());
// 返回授权页面,可自行跳转
authRequest.authorize("state");
// 授权登录后会返回code(auth_code(仅限支付宝))、state1.8.0版本后,可以用AuthCallback类作为回调接口的入参
// 注:JustAuth默认保存state的时效为3分钟,3分钟内未使用则会自动清除过期的state
AuthResponse login = authRequest.login(new AuthCallback());
}
@Test
public void toutiaoTest() {
AuthRequest authRequest = new AuthToutiaoRequest(AuthConfig.builder()
.clientId("clientId")
.clientSecret("clientSecret")
.redirectUri("redirectUri")
.build());
// 返回授权页面,可自行跳转
authRequest.authorize("state");
// 授权登录后会返回code(auth_code(仅限支付宝))、state1.8.0版本后,可以用AuthCallback类作为回调接口的入参
// 注:JustAuth默认保存state的时效为3分钟,3分钟内未使用则会自动清除过期的state
AuthResponse login = authRequest.login(new AuthCallback());
}
}
+7 -7
View File
@@ -9,24 +9,24 @@ public class AuthStateCacheTest {
@Test
public void cache1() throws InterruptedException {
AuthStateCache.cache("key", "value");
Assert.assertEquals(AuthStateCache.get("key"), "value");
AuthDefaultStateCache.INSTANCE.cache("key", "value");
Assert.assertEquals(AuthDefaultStateCache.INSTANCE.get("key"), "value");
TimeUnit.MILLISECONDS.sleep(4);
Assert.assertEquals(AuthStateCache.get("key"), "value");
Assert.assertEquals(AuthDefaultStateCache.INSTANCE.get("key"), "value");
}
@Test
public void cache2() throws InterruptedException {
AuthStateCache.cache("key", "value", 10);
Assert.assertEquals(AuthStateCache.get("key"), "value");
AuthDefaultStateCache.INSTANCE.cache("key", "value", 10);
Assert.assertEquals(AuthDefaultStateCache.INSTANCE.get("key"), "value");
// 没过期
TimeUnit.MILLISECONDS.sleep(5);
Assert.assertEquals(AuthStateCache.get("key"), "value");
Assert.assertEquals(AuthDefaultStateCache.INSTANCE.get("key"), "value");
// 过期
TimeUnit.MILLISECONDS.sleep(6);
Assert.assertNull(AuthStateCache.get("key"));
Assert.assertNull(AuthDefaultStateCache.INSTANCE.get("key"));
}
}
@@ -0,0 +1,63 @@
package me.zhyd.oauth.config;
/**
* 测试自定义实现{@link AuthSource}接口后的枚举类
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0
* @since 1.12.0
*/
public enum AuthExtendSource implements AuthSource {
OTHER {
/**
* 授权的api
*
* @return url
*/
@Override
public String authorize() {
return "http://authorize";
}
/**
* 获取accessToken的api
*
* @return url
*/
@Override
public String accessToken() {
return "http://accessToken";
}
/**
* 获取用户信息的api
*
* @return url
*/
@Override
public String userInfo() {
return null;
}
/**
* 取消授权的api
*
* @return url
*/
@Override
public String revoke() {
return null;
}
/**
* 刷新授权的api
*
* @return url
*/
@Override
public String refresh() {
return null;
}
}
}
@@ -0,0 +1,111 @@
package me.zhyd.oauth.log;
import org.junit.Test;
/**
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0
* @date 2019/8/2 19:36
* @since 1.8
*/
public class LogTest {
public static void main(String[] args) {
// 测试正常打印
Log.debug("[1] This is a test.");
Log.debug("[1] This is a test.", new NullPointerException("npe"));
Log.warn("[1] This is a test.");
Log.warn("[1] This is a test.", new NullPointerException("npe"));
Log.error("[1] This is a test.");
Log.error("[1] This is a test.", new NullPointerException("npe"));
// 测试只打印 error级别的日志
Log.Config.level = Log.Level.ERROR;
Log.debug("[2] This is a test.");
Log.warn("[2] This is a test.");
Log.error("[2] This is a test.");
// 测试关闭日志
Log.Config.enable = false;
Log.debug("[3] This is a test.");
Log.warn("[3] This is a test.");
Log.error("[3] This is a test.");
}
/**
* 1000000: 23135ms
* 100000: 3016ms
* 10000: 328ms
* 1000: 26ms
*/
@Test
public void testByThread() {
long start = System.currentTimeMillis();
for (int i = 0; i < 1; i++) {
System.out.println(callMethodByThread());
}
long end = System.currentTimeMillis();
System.out.println((end - start) + "ms");
}
/**
* 1000000: 19058ms
* 100000: 2772ms
* 10000: 323ms
* 1000: 29ms
*/
@Test
public void testByThrowable() {
long end = System.currentTimeMillis();
for (int i = 0; i < 1; i++) {
System.out.println(callMethodByThrowable());
}
long end2 = System.currentTimeMillis();
System.out.println((end2 - end) + "ms");
}
@Test
public void testBySecurityManager() {
long end = System.currentTimeMillis();
for (int i = 0; i < 1; i++) {
System.out.println(callMethodBySecurityManager());
}
long end2 = System.currentTimeMillis();
System.out.println((end2 - end) + "ms");
}
private String callMethodByThread() {
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
for (StackTraceElement stackTraceElement : stackTrace) {
System.out.println(stackTraceElement.getMethodName());
}
return stackTrace[2].getMethodName();
}
private String callMethodByThrowable() {
StackTraceElement[] stackTrace = (new Throwable()).getStackTrace();
for (StackTraceElement stackTraceElement : stackTrace) {
System.out.println(stackTraceElement.getMethodName());
}
return stackTrace[2].getMethodName();
}
private String callMethodBySecurityManager() {
return new SecurityManager() {
String getClassName() {
for (Class clazz : getClassContext()) {
System.out.println(clazz);
}
return getClassContext()[0].getName();
}
}.getClassName();
}
}
@@ -0,0 +1,55 @@
package me.zhyd.oauth.model;
import com.alibaba.fastjson.JSON;
import me.zhyd.oauth.config.AuthDefaultSource;
import me.zhyd.oauth.config.AuthExtendSource;
import me.zhyd.oauth.config.AuthSource;
import org.junit.Assert;
import org.junit.Test;
public class AuthUserTest {
@Test
public void serialize() {
AuthUser user = AuthUser.builder()
.nickname("test")
.build();
String json = JSON.toJSONString(user);
Assert.assertEquals(json, "{\"nickname\":\"test\"}");
}
@Test
public void deserialize() {
AuthUser user = AuthUser.builder()
.nickname("test")
.build();
String json = JSON.toJSONString(user);
AuthUser deserializeUser = JSON.parseObject(json, AuthUser.class);
Assert.assertEquals(deserializeUser.getNickname(), "test");
}
@Test
public void source() {
AuthSource source = AuthDefaultSource.HUAWEI;
AuthUser user = AuthUser.builder()
.source(source.toString())
.build();
Assert.assertEquals(user.getSource(), "HUAWEI");
source = AuthExtendSource.OTHER;
user = AuthUser.builder()
.source(source.toString())
.build();
Assert.assertEquals(user.getSource(), "OTHER");
source = AuthDefaultSource.HUAWEI;
Assert.assertEquals(source, AuthDefaultSource.HUAWEI);
source = AuthExtendSource.OTHER;
Assert.assertEquals(source, AuthExtendSource.OTHER);
}
}
@@ -0,0 +1,104 @@
package me.zhyd.oauth.request;
import me.zhyd.oauth.cache.AuthStateCache;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.config.AuthExtendSource;
import me.zhyd.oauth.enums.AuthResponseStatus;
import me.zhyd.oauth.enums.AuthUserGender;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthResponse;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
/**
* 测试用自定义扩展的第三方request
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0
* @since 1.12.0
*/
public class AuthExtendRequest extends AuthDefaultRequest {
public AuthExtendRequest(AuthConfig config) {
super(config, AuthExtendSource.OTHER);
}
public AuthExtendRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthExtendSource.OTHER, authStateCache);
}
/**
* 获取access token
*
* @param authCallback 授权成功后的回调参数
* @return token
* @see AuthDefaultRequest#authorize()
* @see AuthDefaultRequest#authorize(String)
*/
@Override
protected AuthToken getAccessToken(AuthCallback authCallback) {
return AuthToken.builder()
.openId("openId")
.expireIn(1000)
.idToken("idToken")
.scope("scope")
.refreshToken("refreshToken")
.accessToken("accessToken")
.code("code")
.build();
}
/**
* 使用token换取用户信息
*
* @param authToken token信息
* @return 用户信息
* @see AuthDefaultRequest#getAccessToken(AuthCallback)
*/
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
return AuthUser.builder()
.username("test")
.nickname("test")
.gender(AuthUserGender.MALE)
.token(authToken)
.source(this.source.toString())
.build();
}
/**
* 撤销授权
*
* @param authToken 登录成功后返回的Token信息
* @return AuthResponse
*/
@Override
public AuthResponse revoke(AuthToken authToken) {
return AuthResponse.builder()
.code(AuthResponseStatus.SUCCESS.getCode())
.msg(AuthResponseStatus.SUCCESS.getMsg())
.build();
}
/**
* 刷新access token (续期)
*
* @param authToken 登录成功后返回的Token信息
* @return AuthResponse
*/
@Override
public AuthResponse refresh(AuthToken authToken) {
return AuthResponse.builder()
.code(AuthResponseStatus.SUCCESS.getCode())
.data(AuthToken.builder()
.openId("openId")
.expireIn(1000)
.idToken("idToken")
.scope("scope")
.refreshToken("refreshToken")
.accessToken("accessToken")
.code("code")
.build())
.build();
}
}
@@ -0,0 +1,82 @@
package me.zhyd.oauth.request;
import com.alibaba.fastjson.JSON;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthResponse;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.utils.AuthStateUtils;
import org.junit.Assert;
import org.junit.Test;
/**
* 自定义扩展的第三方request的测试类,用于演示具体的用法
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0
* @since 1.12.0
*/
public class AuthExtendRequestTest {
@Test
public void authorize() {
AuthRequest request = new AuthExtendRequest(AuthConfig.builder()
.clientId("clientId")
.clientSecret("clientSecret")
.redirectUri("http://redirectUri")
.build());
String authorize = request.authorize(AuthStateUtils.createState());
System.out.println(authorize);
Assert.assertNotNull(authorize);
}
@Test
public void login() {
AuthRequest request = new AuthExtendRequest(AuthConfig.builder()
.clientId("clientId")
.clientSecret("clientSecret")
.redirectUri("http://redirectUri")
.build());
String state = AuthStateUtils.createState();
request.authorize(state);
AuthCallback callback = AuthCallback.builder()
.code("code")
.state(state)
.build();
AuthResponse response = request.login(callback);
Assert.assertNotNull(response);
AuthUser user = (AuthUser) response.getData();
Assert.assertNotNull(user);
System.out.println(JSON.toJSONString(user));
}
@Test
public void revoke() {
AuthRequest request = new AuthExtendRequest(AuthConfig.builder()
.clientId("clientId")
.clientSecret("clientSecret")
.redirectUri("http://redirectUri")
.build());
AuthResponse response = request.revoke(AuthToken.builder().build());
Assert.assertNotNull(response);
System.out.println(JSON.toJSONString(response));
}
@Test
public void refresh() {
AuthRequest request = new AuthExtendRequest(AuthConfig.builder()
.clientId("clientId")
.clientSecret("clientSecret")
.redirectUri("http://redirectUri")
.build());
AuthResponse response = request.refresh(AuthToken.builder().build());
Assert.assertNotNull(response);
System.out.println(JSON.toJSONString(response.getData()));
}
}
@@ -1,87 +0,0 @@
package me.zhyd.oauth.utils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.JSONPath;
import org.junit.Test;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 其他测试方法
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
*/
public class CustomTest {
/**
* 1000000: 23135ms
* 100000: 3016ms
* 10000: 328ms
* 1000: 26ms
*/
@Test
public void test() {
long start = System.currentTimeMillis();
for (int i = 0; i < 1000; i++) {
callMethod();
}
long end = System.currentTimeMillis();
System.out.println((end - start) + "ms");
}
/**
* 1000000: 19058ms
* 100000: 2772ms
* 10000: 323ms
* 1000: 29ms
*/
@Test
public void test2() {
long end = System.currentTimeMillis();
for (int i = 0; i < 1000; i++) {
callMethod2();
}
long end2 = System.currentTimeMillis();
System.out.println((end2 - end) + "ms");
}
public String callMethod() {
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
// for (StackTraceElement stackTraceElement : stackTrace) {
// System.out.println(stackTraceElement.getMethodName());
// }
return stackTrace[2].getMethodName();
}
public String callMethod2() {
StackTraceElement[] stackTrace = (new Throwable()).getStackTrace();
// for (StackTraceElement stackTraceElement : stackTrace) {
// System.out.println(stackTraceElement.getMethodName());
// }
return stackTrace[2].getMethodName();
}
@Test
public void jsonpath() {
List<Map<String, Map<String, Object>>> list = new ArrayList<>();
Map<String, Map<String, Object>> map = new HashMap<>();
Map<String, Object> node = new HashMap<>();
node.put("emailAddress", "xxxx");
map.put("handle~", node);
list.add(map);
Map<String, Object> master = new HashMap<>();
// master.put("elements", list);
JSONObject emailObj = JSONObject.parseObject(JSON.toJSONString(master));
Object object = JSONPath.eval(emailObj, "$['elements'][0]['handle~']['emailAddress']");
System.out.println(object);
}
}
@@ -1,61 +0,0 @@
package me.zhyd.oauth.utils;
import org.junit.Assert;
import org.junit.Test;
import java.util.HashMap;
import java.util.Map;
public class GlobalAuthUtilTest {
@Test
public void testGenerateDingTalkSignature() {
Assert.assertEquals("mLTZEMqIlpAA3xtJ43KcRT0EDLwgSamFe%2FNis5lq9ik%3D",
GlobalAuthUtil.generateDingTalkSignature(
"SHA-256", "1562325753000 "));
}
@Test
public void testUrlDecode() {
Assert.assertEquals("", GlobalAuthUtil.urlDecode(null));
Assert.assertEquals("https://www.foo.bar",
GlobalAuthUtil.urlDecode("https://www.foo.bar"));
Assert.assertEquals("mLTZEMqIlpAA3xtJ43KcRT0EDLwgSamFe/Nis5lq9ik=",
GlobalAuthUtil.urlDecode(
"mLTZEMqIlpAA3xtJ43KcRT0EDLwgSamFe%2FNis5lq9ik%3D"));
}
@Test
public void testParseStringToMap() {
Map expected = new HashMap();
expected.put("bar", "baz");
Assert.assertEquals(expected,
GlobalAuthUtil.parseStringToMap("foo&bar=baz"));
}
@Test
public void testIsHttpProtocol() {
Assert.assertFalse(GlobalAuthUtil.isHttpProtocol(""));
Assert.assertFalse(GlobalAuthUtil.isHttpProtocol("foo"));
Assert.assertTrue(GlobalAuthUtil.isHttpProtocol("http://www.foo.bar"));
}
@Test
public void testIsHttpsProtocol() {
Assert.assertFalse(GlobalAuthUtil.isHttpsProtocol(""));
Assert.assertFalse(GlobalAuthUtil.isHttpsProtocol("foo"));
Assert.assertTrue(
GlobalAuthUtil.isHttpsProtocol("https://www.foo.bar"));
}
@Test
public void testIsLocalHost() {
Assert.assertFalse(GlobalAuthUtil.isLocalHost("foo"));
Assert.assertTrue(GlobalAuthUtil.isLocalHost(""));
Assert.assertTrue(GlobalAuthUtil.isLocalHost("127.0.0.1"));
Assert.assertTrue(GlobalAuthUtil.isLocalHost("localhost"));
}
}
@@ -0,0 +1,231 @@
package me.zhyd.oauth.utils;
import com.alibaba.fastjson.JSON;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthToken;
import org.junit.Assert;
import org.junit.Test;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
import static me.zhyd.oauth.config.AuthDefaultSource.TWITTER;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
public class GlobalAuthUtilsTest {
@Test
public void generateDingTalkSignature() {
assertEquals("mLTZEMqIlpAA3xtJ43KcRT0EDLwgSamFe%2FNis5lq9ik%3D", GlobalAuthUtils.generateDingTalkSignature("SHA-256", "1562325753000 "));
}
@Test
public void urlDecode() {
assertEquals("", GlobalAuthUtils.urlDecode(null));
assertEquals("https://www.foo.bar", GlobalAuthUtils.urlDecode("https://www.foo.bar"));
assertEquals("mLTZEMqIlpAA3xtJ43KcRT0EDLwgSamFe/Nis5lq9ik=", GlobalAuthUtils.urlDecode("mLTZEMqIlpAA3xtJ43KcRT0EDLwgSamFe%2FNis5lq9ik%3D"));
}
@Test
public void parseStringToMap() {
Map expected = new HashMap();
expected.put("bar", "baz");
assertEquals(expected, GlobalAuthUtils.parseStringToMap("foo&bar=baz"));
}
@Test
public void isHttpProtocol() {
Assert.assertFalse(GlobalAuthUtils.isHttpProtocol(""));
Assert.assertFalse(GlobalAuthUtils.isHttpProtocol("foo"));
Assert.assertTrue(GlobalAuthUtils.isHttpProtocol("http://www.foo.bar"));
}
@Test
public void isHttpsProtocol() {
Assert.assertFalse(GlobalAuthUtils.isHttpsProtocol(""));
Assert.assertFalse(GlobalAuthUtils.isHttpsProtocol("foo"));
Assert.assertTrue(GlobalAuthUtils.isHttpsProtocol("https://www.foo.bar"));
}
@Test
public void isLocalHost() {
Assert.assertFalse(GlobalAuthUtils.isLocalHost("foo"));
Assert.assertTrue(GlobalAuthUtils.isLocalHost(""));
Assert.assertTrue(GlobalAuthUtils.isLocalHost("127.0.0.1"));
Assert.assertTrue(GlobalAuthUtils.isLocalHost("localhost"));
}
@Test
public void testGenerateTwitterSignatureForRequestToken() {
AuthConfig config = AuthConfig.builder()
.clientId("HD0XLqzi5Wz0G08rh45Cg8mgh")
.clientSecret("0YX3RH2DnPiT77pgzLzFdfpMKX8ENLIWQKYQ7lG5TERuZNgXN5")
.redirectUri("https://codinglife.tech")
.build();
Map<String, String> params = new HashMap<>();
params.put("oauth_consumer_key", config.getClientId());
params.put("oauth_nonce", "sTj7Ivg73u052eXstpoS1AWQCynuDEPN");
params.put("oauth_signature_method", "HMAC-SHA1");
params.put("oauth_timestamp", "1569750981");
params.put("oauth_callback", config.getRedirectUri());
params.put("oauth_version", "1.0");
String baseUrl = "https://api.twitter.com/oauth/request_token";
params.put("oauth_signature", GlobalAuthUtils.generateTwitterSignature(params, "POST", baseUrl, config.getClientSecret(), null));
params.forEach((k, v) -> params.put(k, "\"" + GlobalAuthUtils.urlEncode(v) + "\""));
String actual = "OAuth " + GlobalAuthUtils.parseMapToString(params, false).replaceAll("&", ", ");
assertEquals("OAuth oauth_nonce=\"sTj7Ivg73u052eXstpoS1AWQCynuDEPN\", oauth_signature=\"%2BL5Jq%2FTaKubge04cWw%2B4yfjFlaU%3D\", oauth_callback=\"https%3A%2F%2Fcodinglife.tech\", oauth_consumer_key=\"HD0XLqzi5Wz0G08rh45Cg8mgh\", oauth_signature_method=\"HMAC-SHA1\", oauth_timestamp=\"1569750981\", oauth_version=\"1.0\"", actual);
}
@Test
public void testGenerateTwitterSignatureForAccessToken() {
AuthConfig config = AuthConfig.builder()
.clientId("HD0XLqzi5Wz0G08rh45Cg8mgh")
.clientSecret("0YX3RH2DnPiT77pgzLzFdfpMKX8ENLIWQKYQ7lG5TERuZNgXN5")
.build();
AuthCallback authCallback = AuthCallback.builder()
.oauth_token("W_KLmAAAAAAAxq5LAAABbXxJeD0")
.oauth_verifier("lYou4gxfA6S5KioUa8VF8HCShzA2nSxp")
.build();
Map<String, String> params = new HashMap<>();
params.put("oauth_consumer_key", config.getClientId());
params.put("oauth_nonce", "sTj7Ivg73u052eXstpoS1AWQCynuDEPN");
params.put("oauth_signature_method", "HMAC-SHA1");
params.put("oauth_timestamp", "1569751082");
params.put("oauth_token", authCallback.getOauth_token());
params.put("oauth_verifier", authCallback.getOauth_verifier());
params.put("oauth_version", "1.0");
params.put("oauth_signature", GlobalAuthUtils.generateTwitterSignature(params, "POST", TWITTER.accessToken(), config.getClientSecret(), authCallback
.getOauth_token()));
params.forEach((k, v) -> params.put(k, "\"" + GlobalAuthUtils.urlEncode(v) + "\""));
String actual = "OAuth " + GlobalAuthUtils.parseMapToString(params, false).replaceAll("&", ", ");
assertEquals("OAuth oauth_verifier=\"lYou4gxfA6S5KioUa8VF8HCShzA2nSxp\", oauth_nonce=\"sTj7Ivg73u052eXstpoS1AWQCynuDEPN\", oauth_signature=\"9i0lmWgvphtkl2KcCO9VyZ3K2%2F0%3D\", oauth_token=\"W_KLmAAAAAAAxq5LAAABbXxJeD0\", oauth_consumer_key=\"HD0XLqzi5Wz0G08rh45Cg8mgh\", oauth_signature_method=\"HMAC-SHA1\", oauth_timestamp=\"1569751082\", oauth_version=\"1.0\"", actual);
}
@Test
public void testGenerateTwitterSignatureForUserInfo() {
AuthConfig config = AuthConfig.builder()
.clientId("HD0XLqzi5Wz0G08rh45Cg8mgh")
.clientSecret("0YX3RH2DnPiT77pgzLzFdfpMKX8ENLIWQKYQ7lG5TERuZNgXN5")
.build();
AuthToken authToken = AuthToken.builder()
.oauthToken("1961977975-PcFQaCnpN9h9xqtqHwHlpGBXFrHJ9bOLy7OtGAL")
.oauthTokenSecret("ffyKe39GYYf8tAyhliSe3QmazpO65kZp5b49xOFX6wHho")
.userId("1961977975")
.screenName("pengisgood")
.build();
Map<String, String> oauthParams = new HashMap<>();
oauthParams.put("oauth_consumer_key", config.getClientId());
oauthParams.put("oauth_nonce", "sTj7Ivg73u052eXstpoS1AWQCynuDEPN");
oauthParams.put("oauth_signature_method", "HMAC-SHA1");
oauthParams.put("oauth_timestamp", "1569751082");
oauthParams.put("oauth_token", authToken.getOauthToken());
oauthParams.put("oauth_version", "1.0");
Map<String, String> queryParams = new HashMap<>();
queryParams.put("user_id", authToken.getUserId());
queryParams.put("screen_name", authToken.getScreenName());
queryParams.put("include_entities", Boolean.toString(true));
Map<String, String> params = new HashMap<>(queryParams);
oauthParams.put("oauth_signature", GlobalAuthUtils.generateTwitterSignature(params, "GET", TWITTER.userInfo(), config.getClientSecret(), authToken
.getOauthTokenSecret()));
oauthParams.forEach((k, v) -> oauthParams.put(k, "\"" + GlobalAuthUtils.urlEncode(v) + "\""));
String actual = "OAuth " + GlobalAuthUtils.parseMapToString(oauthParams, false).replaceAll("&", ", ");
assertEquals("OAuth oauth_nonce=\"sTj7Ivg73u052eXstpoS1AWQCynuDEPN\", oauth_signature=\"yHHq2J1W5QLAO8gGipnY1V%2Bzxqk%3D\", oauth_token=\"1961977975-PcFQaCnpN9h9xqtqHwHlpGBXFrHJ9bOLy7OtGAL\", oauth_consumer_key=\"HD0XLqzi5Wz0G08rh45Cg8mgh\", oauth_signature_method=\"HMAC-SHA1\", oauth_timestamp=\"1569751082\", oauth_version=\"1.0\"", actual);
}
@Test
public void md5() {
String str = "helloworld,iamjustauth";
String md5Str = GlobalAuthUtils.md5(str);
assertEquals("b0d923de4289b69976448cac718528b8", md5Str);
}
@Test
public void treemap() {
Map<String, Object> parameters = new HashMap<>();
parameters.put("user_id", "1");
parameters.put("screen_name", "222");
parameters.put("a", "222");
parameters.put("include_entities", Boolean.toString(true));
final Map<String, Object> sorted = new TreeMap<>(parameters);
assertEquals("{\"a\":\"222\",\"include_entities\":\"true\",\"screen_name\":\"222\",\"user_id\":\"1\"}", JSON.toJSONString(sorted));
}
@Test
public void urlEncode() {
assertEquals("", GlobalAuthUtils.urlEncode(null));
assertEquals("https%3A%2F%2Fwww.foo.bar", GlobalAuthUtils.urlEncode("https://www.foo.bar"));
assertEquals("mLTZEMqIlpAA3xtJ43KcRT0EDLwgSamFe%252FNis5lq9ik%253D", GlobalAuthUtils.urlEncode("mLTZEMqIlpAA3xtJ43KcRT0EDLwgSamFe%2FNis5lq9ik%3D"));
}
@Test
public void parseMapToString() {
Map<String, String> parameters = new HashMap<>();
parameters.put("user_id", "1");
parameters.put("screen_name", "史上最全的第三方授权登录库");
parameters.put("include_entities", Boolean.toString(true));
assertEquals("user_id=1&screen_name=史上最全的第三方授权登录库&include_entities=true", GlobalAuthUtils.parseMapToString(parameters, false));
assertEquals("user_id=1&screen_name=%E5%8F%B2%E4%B8%8A%E6%9C%80%E5%85%A8%E7%9A%84%E7%AC%AC%E4%B8%89%E6%96%B9%E6%8E%88%E6%9D%83%E7%99%BB%E5%BD%95%E5%BA%93&include_entities=true", GlobalAuthUtils.parseMapToString(parameters, true));
assertEquals("", GlobalAuthUtils.parseMapToString(null, true));
}
@Test
public void generateNonce() {
assertEquals(10, GlobalAuthUtils.generateNonce(10).length());
}
@Test
public void getTimestamp() {
assertNotNull(GlobalAuthUtils.getTimestamp());
}
@Test
public void generateTwitterSignature() {
Map<String, String> queryParams = new HashMap<>();
queryParams.put("name", "你好");
queryParams.put("gender", "male");
assertEquals("J6MAQH1kcgUdj2jmygN3rdfI4lo=", GlobalAuthUtils.generateTwitterSignature(queryParams, "GET", TWITTER.userInfo(), "xxxxx", "xxxxx"));
}
@Test
public void generateElemeSignature() {
Map<String, Object> parameters = new HashMap<>();
parameters.put("name", "你好");
parameters.put("gender", "male");
String appKey = "appKey";
String secret = "appKey";
long timestamp = 1233456789;
String action = "appKey";
String token = "appKey";
assertEquals("26FEB8BF7E84FED2619D9C5D97F421BD", GlobalAuthUtils.generateElemeSignature(appKey, secret, timestamp, action, token, parameters));
}
@Test
public void generateJdSignature() {
Map<String, Object> parameters = new HashMap<>();
parameters.put("name", "你好");
parameters.put("gender", "male");
String appSecret = "appKey";
assertEquals("FE04EC03BA8A619802CF309959C2B43F", GlobalAuthUtils.generateJdSignature(appSecret, parameters));
}
}
@@ -0,0 +1,38 @@
package me.zhyd.oauth.utils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.JSONPath;
import org.junit.Assert;
import org.junit.Test;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* JsonPath用法测试
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
*/
public class JsonPathTest {
@Test
public void jsonPath() {
List<Map<String, Map<String, Object>>> list = new ArrayList<>();
Map<String, Map<String, Object>> map = new HashMap<>();
Map<String, Object> node = new HashMap<>();
node.put("emailAddress", "xxxx");
map.put("handle~", node);
list.add(map);
Map<String, Object> master = new HashMap<>();
master.put("elements", list);
JSONObject emailObj = JSONObject.parseObject(JSON.toJSONString(master));
Object object = JSONPath.eval(emailObj, "$['elements'][0]['handle~']['emailAddress']");
Assert.assertEquals("xxxx", object);
}
}
@@ -0,0 +1,94 @@
package me.zhyd.oauth.utils;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
public class StringUtilsTest {
@Rule
public final ExpectedException thrown =
ExpectedException.none();
@Test
public void isEmptyNonEmptyInput() {
Assert.assertFalse(StringUtils.isEmpty("non-empty string"));
}
@Test
public void isEmptyEmptyInput() {
Assert.assertTrue(StringUtils.isEmpty(""));
}
@Test
public void isEmptyInputNull() {
Assert.assertTrue(StringUtils.isEmpty(null));
}
@Test
public void isNotEmptyNonEmptyInput() {
Assert.assertTrue(StringUtils.isNotEmpty("non-empty string"));
}
@Test
public void isNotEmptyEmptyInput() {
Assert.assertFalse(StringUtils.isNotEmpty(""));
}
@Test
public void isNotEmptyInputNull() {
Assert.assertFalse(StringUtils.isNotEmpty(null));
}
@Test
public void appendIfNotContainAppendedStringNotPresent() {
// (Check the case where appendStr doesn't occur in str)
final String str = "Prefix ";
final String appendStr = "suffix";
final String otherwise = "should be discarded";
final String result =
StringUtils.appendIfNotContain(str, appendStr, otherwise);
Assert.assertEquals("Prefix suffix", result);
}
@Test
public void appendIfNotContainAppendedStringPresent() {
// (Check the case where appendStr occurs in str)
final String str = "Prefix ";
final String appendStr = "Prefix";
final String otherwise = "should be appended";
final String result =
StringUtils.appendIfNotContain(str, appendStr, otherwise);
Assert.assertEquals("Prefix should be appended", result);
}
@Test
public void appendIfNotContainEmptyString() {
// (Check the special-case for str being empty)
final String str = "";
final String appendStr = "should not be appended";
final String otherwise = "should also not be appended";
final String result =
StringUtils.appendIfNotContain(str, appendStr, otherwise);
Assert.assertEquals("", result);
}
@Test
public void appendIfNotContainAppendingEmptyString() {
// (Check the special-case for appendStr being empty)
final String str = "should be kept";
final String appendStr = "";
final String otherwise = "should also not be appended";
final String result =
StringUtils.appendIfNotContain(str, appendStr, otherwise);
Assert.assertEquals("should be kept", result);
}
}
@@ -1,8 +1,8 @@
package me.zhyd.oauth.utils;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.config.AuthSource;
import me.zhyd.oauth.request.AuthWeChatRequest;
import me.zhyd.oauth.config.AuthDefaultSource;
import me.zhyd.oauth.request.AuthWeChatOpenRequest;
import org.junit.Assert;
import org.junit.Test;
@@ -22,7 +22,7 @@ public class UrlBuilderTest {
.clientSecret("secret-110110110")
.redirectUri("https://xkcoding.com")
.build();
String build = UrlBuilder.fromBaseUrl(AuthSource.WECHAT.authorize())
String build = UrlBuilder.fromBaseUrl(AuthDefaultSource.WECHAT_OPEN.authorize())
.queryParam("appid", config.getClientId())
.queryParam("redirect_uri", config.getRedirectUri())
.queryParam("response_type", "code")
@@ -30,7 +30,7 @@ public class UrlBuilderTest {
.queryParam("state", "")
.build(false);
System.out.println(build);
AuthWeChatRequest request = new AuthWeChatRequest(config);
AuthWeChatOpenRequest request = new AuthWeChatOpenRequest(config);
String authorize = request.authorize("state");
System.out.println(authorize);
}
-152
View File
@@ -1,152 +0,0 @@
### 2019/07/31 [v1.9.5](https://gitee.com/yadong.zhang/JustAuth/releases/v1.9.5)
`v1.9.4`版本发布失败,请升级到`1.9.5`版本!
由此给您带来的不便,敬请谅解!
### 2019/07/30 [v1.9.4](https://gitee.com/yadong.zhang/JustAuth/releases/v1.9.4)
1. 升级`hutool-http`版本到`v4.6.1`
2. 去除`AuthCallback`中增加的默认的校验state的方法,挪到`AuthDefaultRequest`中做统一处理
3. `alipay-sdk-java`依赖改为`provided`,如果需要使用支付宝登录,需要使用方手动引入相关依赖,具体操作方式,见项目WIKI;
4. 规范注释
### 2019/07/30 [v1.9.3](https://gitee.com/yadong.zhang/JustAuth/releases/v1.9.3)
1. 规范注释
2. 增加State缓存,`AuthCallback`中增加默认的校验state的方法
3. 增加默认的state生成方法,参考`AuthStateUtils.java``UuidUtils.java`
4. 升级`hutool-http`版本到`v4.6.0`
5. 修复其他一些问题
### 2019/07/27
1. `IpUtils.getIp`改名为`IpUtils.getLocalIp`
2. 规范注释
### 2019/07/25
1. `AuthConfig`类中去掉state参数
2. 删除`AuthState`
3. 增加`authorize(String)`方法,并且使用`@Deprecated`标记`authorize()`方法
### 2019/07/22 [v1.9.2](https://gitee.com/yadong.zhang/JustAuth/releases/v1.9.2)
1. 合并github上[xkcoding](https://github.com/xkcoding) 的[pr#26](https://github.com/zhangyd-c/JustAuth/pull/26)AuthConfig类添加lombok注解,方便 [justauth-spring-boot-starter](https://github.com/xkcoding/justauth-spring-boot-starter) 直接使用
### 2019/07/22 [v1.9.1](https://gitee.com/yadong.zhang/JustAuth/releases/v1.9.1)
1. 增加`stackoverflow`参数校验
2. 解决`Pinterest`获取用户失败的问题
3. 添加注释
### 2019/07/19 [v1.9.0](https://gitee.com/yadong.zhang/JustAuth/releases/v1.9.0)
1. 合并github上[@dyc12ii](https://github.com/dyc12ii) 的[pr#25](https://github.com/zhangyd-c/JustAuth/pull/25),升级fastjson版本至1.2.58,避免安全漏洞
2. `AuthUserGender`枚举类挪到`enums`包下
3. 删除`AuthBaiduErrorCode``AuthDingTalkErrorCode`枚举类
4. 优化百度授权流程,增加refresh token的方法
5. 优化`AuthConfig``AuthResponse`类,去掉不必要的lombonk注解,减少编译后的代码量
6. 使用lombok注解优化枚举类
7. `AuthQqRequest`增加refresh方法
8. 修复google登录无法获取用户信息的问题
9. 优化代码
### 2019/07/18
1. 合并github上[@pengisgood](https://github.com/pengisgood) 的[pr#19](https://github.com/zhangyd-c/JustAuth/pull/19),集成人人
2. 合并github上[@pengisgood](https://github.com/pengisgood) 的[pr#20](https://github.com/zhangyd-c/JustAuth/pull/20),集成Pinterest
3. 合并github上[@pengisgood](https://github.com/pengisgood) 的[pr#21](https://github.com/zhangyd-c/JustAuth/pull/21),集成StackOverflow
4. 合并github上[@xkcoding](https://github.com/xkcoding) 的[pr#23](https://github.com/zhangyd-c/JustAuth/pull/23),重构代码、新增编辑器规范,规范PR代码风格
### 2019/07/17
1. 优化代码
2. 集成Teambition登录
### 2019/07/16
1. 重构UrlBuilder类
2. 将CSDN相关的类置为`Deprecated`,后续可能会删除,也可能一直保留。毕竟CSDN的openAPI已经不对外开放了。
3. `BaseAuthRequest` 改名为 `AuthDefaultRequest`
4. `ResponseStatus` 改名为 `AuthResponseStatus` 并且移动到 `me.zhyd.oauth.model`
5. 合并github上[@xkcoding](https://github.com/xkcoding) 的[pr#18](https://github.com/zhangyd-c/JustAuth/pull/18),修复小米回调错误问题 同时 支持微信获取unionId
### 2019/07/15 [v1.8.1](https://gitee.com/yadong.zhang/JustAuth/releases/v1.8.1)
1. 新增 `AuthState` 类,内置默认的state生成规则和校验规则
### 2019/07/12
1. 合并[Braavos96](https://github.com/Braavos96)提交的[PR#16](https://github.com/zhangyd-c/JustAuth/pull/16)
### 2019/06/28 [v1.8.0](https://gitee.com/yadong.zhang/JustAuth/releases/v1.8.0)
1. 修复百度登录获取不到token失效时间的问题
2. 增加state参数校验,预防CSRF。**强烈建议启用state**
### 2019/06/27
1. 修复百度登录获取不到token失效时间的问题
2. 增加state参数校验,预防CSRF。**强烈建议启用state**
3. 修改login方法的参数为AuthCallback,封装回调返回的参数
4. 支持state参数
5. 增加code和state参数校验
由于state安全问题,1.8.0以前的版本都有隐藏的CSRF漏洞问题,所以强烈建议正在使用JustAuth的朋友升级到1.8.0版本!
### 2019/06/25 [v1.7.1](https://gitee.com/yadong.zhang/JustAuth/releases/v1.7.1)
qq授权登录时,需要获取`openId`作为`uuid`,在`1.6.1-beta``1.7.0`版本中,引入了`unionId`这一属性。获取`unionid`需要单独向qq团队**发送邮件**申请权限,鉴于这一申请权限的步骤比较麻烦(需要填写的内容比较多),所以在`AuthConfig`中增加了一个`unionId`属性,当为**true**时才会获取unionid,当为false时只获取openId。如果你需要该功能, 则在自行申请了相关权限后,将该属性置为true即可。关于unionId的参考链接:[UnionID介绍](http://wiki.connect.qq.com/unionid%E4%BB%8B%E7%BB%8D)
### 2019/06/19 [v1.7.0](https://gitee.com/yadong.zhang/JustAuth/releases/v1.7.0)
1. 合并[xkcoding](https://github.com/xkcoding)提交的[PR](https://github.com/zhangyd-c/JustAuth/pull/14),重构了部分代码,jar包由原来的`130+kb`优化到现在的`110+kb`
2. 合并[skqing](https://gitee.com/skqing)提交的[PR](https://gitee.com/yadong.zhang/JustAuth/pulls/3) 解决抖音登录失败问题
### 2019/06/18 [v1.6.1-beta](https://gitee.com/yadong.zhang/JustAuth/releases/v1.6.1-beta)
1. 解决Issue [#IY2HW](https://gitee.com/yadong.zhang/JustAuth/issues/IY2HW)
2. 解决Issue [#IY2OH](https://gitee.com/yadong.zhang/JustAuth/issues/IY2OH)
3. 解决Issue [#IY2FV](https://gitee.com/yadong.zhang/JustAuth/issues/IY2FV)
4. 修复部分注释、拼写错误
5. 解决Issue [#IY1QR](https://gitee.com/yadong.zhang/JustAuth/issues/IY1QR) 增加对Config属性的校验功能,主要校验redirect uri的合法性
6. 合并[skqing](https://gitee.com/skqing)提交的[PR](https://gitee.com/yadong.zhang/JustAuth/pulls/2),解决一些BUG
### 2019/06/06 [v1.6.0-beta](https://gitee.com/yadong.zhang/JustAuth/releases/v1.6.0-beta)
1. 增加今日头条的授权登陆
2. 发布1.6.0-beta版本,今日头条开发者暂时不能认证, 所以无法做测试,等测试通过后,正式发布release版本
### 2019/05/28 [v1.5.0](https://gitee.com/yadong.zhang/JustAuth/releases/v1.5.0)
1. 增加小米账号和微软的授权登陆
2. 发布1.5.0版本
### 2019/05/26 [v1.4.0](https://gitee.com/yadong.zhang/JustAuth/releases/v1.4.0)
1. 增加抖音和Linkedin的授权登陆
2. 修改部分图片命名
3. 优化部分代码
4. 修复`AuthSource`中腾讯云开发平台的拼写错误:`TENCEN_CLOUD`->`TENCENT_CLOUD`
5. 修复支付宝登陆时用户名为空的问题
### 2019/05/24 [v1.3.3](https://gitee.com/yadong.zhang/JustAuth/releases/v1.3.3)
1. 修复一些问题
2. 升级api,在AuthUser中增加`uuid`属性,可以通过`uuid` + `source`唯一确定一个用户,此举解决了用户身份归属的问题。
3. 发布1.3.3版本的jar包到公开仓库(1.3.2忘记发布了,( ╯□╰ ))
4. 重要:经咨询官方客服得知,CSDN的授权开放平台已经下线,如果以前申请过的应用,可以继续使用,但是不再支持申请新的应用。so, 本项目中的CSDN登录只能针对少部分用户使用了
### 2019/05/23 [v1.3.1](https://gitee.com/yadong.zhang/JustAuth/releases/v1.3.1)
1. 修复QQ登录的问题
2. 发布1.3.1版本的jar包到公开仓库
### 2019/05/21 [v1.3.0](https://gitee.com/yadong.zhang/JustAuth/releases/v1.3.0)
1. 新增google授权登录
2. 新增facebook授权登录
3. 发布1.3.0版本的jar包到公开仓库
### 2019/05/18 [v1.1.0](https://gitee.com/yadong.zhang/JustAuth/releases/v1.1.0) | [v1.2.0](https://gitee.com/yadong.zhang/JustAuth/releases/v1.2.0)
1. 发布1.1.0版本的jar包到公开仓库(支持qq和微信登录)
2. 支持淘宝登录
3. 修改`AuthUser.java`类中的`accessToken`属性,由原本的~~accessToken (String)~~改为`token (AuthToken)`
4. 修复一些bug
5. 发布1.2.0版本的jar包到公开仓库(支持淘宝登录)
### 2019/05/17
1. 增加qq和微信的授权登录
2. 修改getAccessToken方法的返回值
### 2019/03/27 [v1.0.1](https://gitee.com/yadong.zhang/JustAuth/releases/v1.0.1)
集成 支付宝授权登录
### 2019/03/25 [v1.0.0](https://gitee.com/yadong.zhang/JustAuth/releases/v1.0.0)
史上最全的整合第三方登录的工具,目前已支持Github、Gitee、微博、钉钉和百度、Coding、腾讯云开发者平台和OSChina登录。 Login, so easy!