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

Compare commits

...

226 Commits

Author SHA1 Message Date
yadong.zhang 694bbf1b01 📝 编写文档 2024-12-14 17:32:48 +08:00
yadong.zhang bbf63c6056 Merge branch 'dev' 2024-12-14 17:14:04 +08:00
yadong.zhang 44d77f3d6a Merge branch 'dev' of github.com:justauth/JustAuth into dev 2024-12-14 17:13:06 +08:00
yadong.zhang 2b6e419943 📝 编写文档 2024-12-14 17:12:00 +08:00
yadong.zhang 56bfe2803a Merge pull request #223 from hurentian/dev
feat: 支持QQ小程序
2024-12-14 17:09:49 +08:00
yadong.zhang 89d107f6da Merge pull request #222 from Yeaury/fix-doc-link
fix: Fix documentation link in README
2024-12-14 17:07:21 +08:00
yadong.zhang cdcf9a5e8d 🔥 支持支付宝证书模式登录 2024-12-14 17:02:33 +08:00
yadong.zhang 354f00660b 💩 AppleId 登录的 POM 依赖改为 provided 2024-12-14 14:31:50 +08:00
hurentian 4b07aecfcf feat: 支持QQ小程序 2024-10-17 09:52:33 +08:00
31445 9862f487b4 fix: correct documentation link in README 2024-10-13 11:57:27 +08:00
yadong.zhang 9153e97398 merge pr 2024-09-02 00:45:40 +08:00
yadong.zhang 64e1098519 💩 添加新版钉钉扫码登录能力 2024-09-02 00:24:05 +08:00
yadong.zhang e9135436f9 💩 修复企业微信扫码登录 code 的传值 2024-09-01 18:19:05 +08:00
yadong.zhang 20731ea77e 💩 添加新版华为登录能力,原AuthHuaweiRequest会在后面版本被弃用,如有使用,请切换到AuthHuaweiV3Request 2024-09-01 18:15:18 +08:00
yadong.zhang 587d2f4072 !42 修改爱发电平台的域名为https://afdian.com
Merge pull request !42 from handy/handy-0827
2024-08-27 03:44:47 +00:00
handy 602a48b3ce 修改爱发电平台的域名为https://afdian.com 2024-08-27 10:51:00 +08:00
yadong.zhang 91167bcbf7 🔥 支持微信小程序 2024-08-17 20:16:14 +08:00
yadong.zhang 8f376129d4 🔥 支持微信小程序 2024-08-17 20:11:51 +08:00
yadong.zhang 6859b8a949 🔥 Amazon PKCE 中的 code_verifier 基于 state 缓存 2024-08-04 23:28:55 +08:00
yadong.zhang b01704ff78 💩 解决单测失败的问题 2024-08-04 15:51:38 +08:00
yadong.zhang 0731ebd130 Merge remote-tracking branch 'origin/dev' into dev 2024-08-04 15:38:56 +08:00
yadong.zhang 5eb8160a42 💩 添加新版企业微信扫码登录能力(PS. 新版企业微信登录,除了支持使用移动端企业微信扫码登录之外,还支持通过企业微信桌面端进行快速登录。) 2024-08-04 15:38:40 +08:00
yadong.zhang 415c703539 💩 修复 twitter 平台在 Java11 环境下登录失败的问题。[#174](https://github.com/justauth/JustAuth/issues/174) 2024-08-04 15:33:49 +08:00
yadong.zhang 5d521e3ea4 💩 添加新版企业微信扫码登录能力(PS. 新版企业微信登录,除了支持使用移动端企业微信扫码登录之外,还支持通过企业微信桌面端进行快速登录。) 2024-08-04 15:33:37 +08:00
yadong.zhang 0eafb96a43 💩 improve: 优化 AuthCallback 获取 code 的方式 2024-08-04 14:45:30 +08:00
yadong.zhang 6d8b3b5f1d 💩 getAccessTokengetUserInfo两个方法从AuthDefaultRequest提升至AuthRequest中,方便直接使用。[Github Issue#194](https://github.com/justauth/JustAuth/issues/194) 2024-08-03 18:27:36 +08:00
yadong.zhang 15b1a974f3 💩 补全抖音平台缺失字段补全。Github#188 2024-08-03 17:57:41 +08:00
yadong.zhang 74b5b4b3bc Merge branch 'dev' of github.com:justauth/JustAuth into dev
g the commit.
2024-08-03 16:35:53 +08:00
yadong.zhang 6cc414997d Merge remote-tracking branch 'origin/dev' into dev
# Conflicts:
#	src/main/java/me/zhyd/oauth/config/AuthDefaultSource.java
2024-08-03 16:34:13 +08:00
yadong.zhang 7c6f236733 Merge branch 'master' of github.com:justauth/JustAuth 2024-08-03 16:31:02 +08:00
yadong.zhang bf5a6d1b17 Merge branch 'master' of github.com:justauth/JustAuth into dev 2024-08-03 16:29:12 +08:00
yadong.zhang 9db9fbdcc8 Merge pull request #211 from justauth/dependabot/maven/org.bouncycastle-bcpkix-jdk18on-1.78
⬆️ Bump org.bouncycastle:bcpkix-jdk18on from 1.77 to 1.78
2024-08-03 16:28:39 +08:00
yadong.zhang 0acd67e704 ⬆️ 切分 v1.16.7 版本 2024-08-03 12:15:03 +08:00
dependabot[bot] 992f4a67ac ⬆️ Bump org.bouncycastle:bcpkix-jdk18on from 1.77 to 1.78
Bumps [org.bouncycastle:bcpkix-jdk18on](https://github.com/bcgit/bc-java) from 1.77 to 1.78.
- [Changelog](https://github.com/bcgit/bc-java/blob/main/docs/releasenotes.html)
- [Commits](https://github.com/bcgit/bc-java/commits)

---
updated-dependencies:
- dependency-name: org.bouncycastle:bcpkix-jdk18on
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-03 04:08:01 +00:00
yadong.zhang 2ee2483aee Merge pull request #192 from G-XD/feat_apple
feat: sign with apple
2024-08-03 12:07:24 +08:00
yadong.zhang a1c1ab93bd !41 集成Figma平台
Merge pull request !41 from xiangqian/fork
2024-07-22 15:27:26 +00:00
xiangqian eaafd49890 feat: 集成figma平台 2024-07-22 21:40:07 +08:00
yadong.zhang 74be7d4c8b Merge pull request #198 from GitHubzhaolei/master
change:变更Google 端点地址
2024-06-23 23:06:03 +08:00
“zhaolei” d62bb6eecf change:[google oauth] api addr 2024-03-14 16:53:02 +08:00
yadong.zhang b2b007aa4d !39 补充单侧
Merge pull request !39 from handy/dev
2024-03-02 04:20:16 +00:00
handy 431adb447f 补充单侧 2024-02-02 10:12:21 +08:00
yadong.zhang 78a318e799 !38 AuthRequest 响应携带泛型,避免二次解析
Merge pull request !38 from handy/handy
2024-02-01 15:42:17 +00:00
G-XD 988d3cd293 fix: fix user info 2024-01-19 11:49:35 +08:00
G-XD 72e4a1d846 feat: support sign with apple 2024-01-19 01:42:31 +08:00
handy 65bb994012 AuthRequest 响应携带泛型,避免二次解析 2024-01-04 12:51:50 +08:00
yadong.zhang 6b758e0e1e 💡 修改注释 2023-12-03 21:50:50 +08:00
yadong.zhang 02bd057963 🔀 合并 PR#166 2023-12-03 21:05:53 +08:00
yadong.zhang 4d2a110fea Merge pull request #166 from christxlx/master
fix: bug 58-63 scope在微软最新的文档中要求必须用空格隔开,form构建时候空格会被强制转换为%20,所以此处重新覆盖
2023-12-03 21:04:10 +08:00
yadong.zhang 2ef8298278 Merge branch 'master' into dev 2023-12-03 21:01:16 +08:00
yadong.zhang 80ce57bce4 📝 编写文档 2023-12-03 21:00:45 +08:00
yadong.zhang 2fed3e5ba2 💩 修改 facebook 平台的 scope 2023-12-03 20:58:49 +08:00
yadong.zhang 1d38ce2835 💩 微软平台适配 AzureAD(目前改名为 Microsoft Entra ID)登录认证 2023-12-03 20:18:39 +08:00
yadong.zhang 14dd2b6179 💩 修复微信公众平台 scope 为 snsapi_base 登录报错的问题 [181](https://github.com/justauth/JustAuth/issues/181) 2023-12-03 20:17:47 +08:00
yadong.zhang d6ba3bba10 💩 修复 twitter 平台在 Java11 环境下登录失败的问题。[#174](https://github.com/justauth/JustAuth/issues/174) 2023-12-03 16:55:55 +08:00
yadong.zhang fe328c7375 💩 调整单元测试 2023-08-06 18:17:53 +08:00
yadong.zhang 102521a979 Merge branch 'dev' 2023-08-06 18:15:11 +08:00
yadong.zhang 924f0f552b 🔥 更新脚本 2023-08-06 18:14:42 +08:00
yadong.zhang 7167a74af2 💩 调整单元测试 2023-08-06 18:14:04 +08:00
yadong.zhang c0ac81df08 Merge remote-tracking branch 'origin/master' 2023-08-06 18:10:09 +08:00
yadong.zhang 56cdd20600 Merge branch 'master' of gitee.com:yadong.zhang/JustAuth into dev 2023-08-06 18:07:45 +08:00
yadong.zhang 87d8c1d164 Merge branch 'master' into dev 2023-08-06 17:58:24 +08:00
yadong.zhang d1f820514f ⬆️ 1.16.6 2023-08-06 17:57:47 +08:00
yadong.zhang 6d192e94ac Merge branch 'dev' of gitee.com:yadong.zhang/JustAuth into dev 2023-08-06 17:50:07 +08:00
yadong.zhang 02bec576de Merge branch 'dev'
# Conflicts:
#	README.md
2023-04-11 22:48:07 +08:00
yadong.zhang 0d599d75f0 替换 justauth.wiki 域名,该域名暂时不可用,请使用 justauth.cn 2023-04-11 22:46:53 +08:00
yadong.zhang 5171fb8148 替换 justauth.wiki 域名,该域名暂时不可用,请使用 justauth.cn 2023-04-11 22:45:58 +08:00
yadong.zhang 0025748ba7 替换 justauth.wiki 域名,该域名暂时不可用,请使用 justauth.cn 2023-04-11 22:42:47 +08:00
yadong.zhang 87cf45b675 替换 justauth.wiki 域名,该域名暂时不可用,请使用 justauth.cn 2023-04-11 22:41:18 +08:00
yadong.zhang bd1192f17f Merge branch 'dev'
# Conflicts:
#	README.md
2023-04-11 22:39:57 +08:00
yadong.zhang a7cb4aefc0 替换 justauth.wiki 域名,该域名暂时不可用,请使用 justauth.cn 2023-04-11 22:38:04 +08:00
yadong.zhang 7ffda8737c 替换 justauth.wiki 域名,该域名暂时不可用,请使用 justauth.cn 2023-04-11 22:34:18 +08:00
yadong.zhang 90a9d56fd0 替换 justauth.wiki 域名,该域名暂时不可用
Signed-off-by: yadong.zhang <yadong.zhang0415@gmail.com>
2023-04-11 14:25:54 +00:00
yadong.zhang c53c763540 !36 新增afdian授权示例图
Merge pull request !36 from ヽ米 饭/handy
2023-03-25 09:41:31 +00:00
韩帅 dc18304eee feat: 新增afdian授权示例图 2023-03-24 16:50:37 +08:00
yadong.zhang a558379680 !35 新增爱发电平台
Merge pull request !35 from ヽ米 饭/handy
2023-03-21 10:55:56 +00:00
韩帅 142846b2fa 新增爱发电平台 2023-03-21 17:42:46 +08:00
Christ 80b59fc13c fix: bug 58-63 scope在微软最新的文档中要求必须用空格隔开,form构建时候空格会被强制转换为%20,所以此处重新覆盖 2023-03-21 14:41:30 +08:00
yadong.zhang 765127e847 📝 更新文档 2023-02-26 18:09:08 +08:00
yadong.zhang 11707e6903 🔥 微信公众平台支持返回快照数据 2023-02-26 17:54:58 +08:00
yadong.zhang 3564c846f4 Merge branch 'master' of gitee.com:yadong.zhang/JustAuth into dev 2023-02-26 16:47:46 +08:00
yadong.zhang 0bc4c806bf ⬆️ Merge branch 'dev' 2023-02-26 16:47:27 +08:00
yadong.zhang 35c754f327 Merge branch 'master' of gitee.com:yadong.zhang/JustAuth 2023-02-26 16:32:19 +08:00
yadong.zhang 45ebe26f1b ⬆️ Merge branch 'dev' 2023-02-26 16:32:16 +08:00
yadong.zhang 30f4a29f48 Merge branch 'dev' of gitee.com:yadong.zhang/JustAuth 2023-02-26 16:27:49 +08:00
yadong.zhang ca8e194c4e Merge pull request #159 from cheng521521/master
添加飞书单元测试
2023-02-26 16:19:58 +08:00
yadong.zhang 81ea4b1620 Merge pull request #155 from marquis-chen/dev
企业微信网页登录--获取用户敏感信息
2023-02-26 16:19:28 +08:00
yadong.zhang cc74d4a5c8 !33 【轻量级 PR】:update pom.xml.
Merge pull request !33 from test/N/A
2023-02-24 12:15:56 +00:00
test e80304601f update pom.xml.
升级fastjson版本到1.2.83,1.2.83版本之前存在代码执行漏洞风险 ,CVE-2022-25845	

Signed-off-by: test <531648642@qq.com>
2023-02-24 11:54:47 +00:00
yadong.zhang afddf269e1 !31 修复企业微信的 redirect_uri 没有 urlEncode 问题
Merge pull request !31 from AlexChan/dev
2022-10-23 12:06:06 +00:00
alexchan bac494a7fc 修复企业微信的 redirect_uri 没有 urlEncode 问题 2022-10-21 16:22:13 +08:00
chengpengxiang 7a9d602499 test:添加飞书单元测试 2022-10-03 13:17:28 +08:00
marquis chen f3a8cf675c 企业微信网页登录增加AgentId参数,对重定向地址UrlEncode,获取用户敏感信息 2022-09-05 16:42:51 +08:00
yadong.zhang 571466f079 Merge pull request #149 from justauth/dependabot/maven/com.alibaba-fastjson-1.2.83
⬆️ Bump fastjson from 1.2.78 to 1.2.83
2022-08-07 22:59:14 +08:00
dependabot[bot] e1b912bdf6 ⬆️ Bump fastjson from 1.2.78 to 1.2.83
Bumps [fastjson](https://github.com/alibaba/fastjson) from 1.2.78 to 1.2.83.
- [Release notes](https://github.com/alibaba/fastjson/releases)
- [Commits](https://github.com/alibaba/fastjson/compare/1.2.78...1.2.83)

---
updated-dependencies:
- dependency-name: com.alibaba:fastjson
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-17 02:05:50 +00:00
yadong.zhang 02674049d0 📝 更新说明 2021-10-18 10:03:14 +08:00
yadong.zhang f1714726f5 Merge branch 'carldea/master' into dev 2021-10-18 09:55:40 +08:00
yadong.zhang a3108caccf 🐛 修复 alipay 登录失败的 BUG 2021-10-16 19:11:24 +08:00
yadong.zhang e134dd06b0 ⬆️ 升级alipay-sdk-version的依赖版本 2021-10-16 19:10:50 +08:00
yadong.zhang 172ba7be55 💩 修改用户logo的尺寸 2021-10-16 19:10:24 +08:00
Carl Dea f9e3053ecc Using TCK Tested JDK builds of OpenJDK
The AdoptOpenJDK has been discontinued since July 2021. When using Zulu you get all the latest updated (TCK Tested) builds for all versions of OpenJDK included archived major fixed versions.
2021-09-27 14:37:10 -04:00
Carl Dea 99c9ba2854 Using TCK Tested JDK builds of OpenJDK
The AdoptOpenJDK has been discontinued since July 2021. When using Zulu you get all the latest updated (TCK Tested) builds for all versions of OpenJDK included archived major fixed versions.
2021-09-27 14:36:33 -04:00
yadong.zhang 544ffd5372 💩 修复s一样的代码 2021-09-22 17:47:12 +08:00
yadong.zhang 522ebbfa7f 📝 编写文档 2021-09-22 17:16:43 +08:00
yadong.zhang 497498dbf4 Merge pull request #134 from verils/remove-provider-specific-properties
把Alipay独有的配置从AuthConfig里提取出来
2021-09-20 07:51:16 -05:00
yadong.zhang 62cd7af619 Merge pull request #133 from kang8/test
refactor: 重构 workflow
2021-09-20 07:43:28 -05:00
yadong.zhang 3a3f69692c 📝 编写文档 2021-09-20 20:39:49 +08:00
yadong.zhang c87015e34f 📝 编写文档 2021-09-20 20:37:24 +08:00
yadong.zhang 96773dd92d ⬆️ 升级第三方的依赖 2021-09-20 20:37:10 +08:00
yadong.zhang 644ef02264 ⬆️ 升级 simple-http 到 1.0.5 2021-09-18 00:35:42 +08:00
verils 4c205a9957 把第三方服务独有的配置从AuthConfig里提取出来 2021-09-01 12:42:41 +08:00
kang ea70c5fc81 ci: 快照版本切换,使用 python 脚本替换 maven 插件 2021-08-27 08:12:41 +08:00
kang 9ced525309 refactor: 重构流水线构建 2021-08-27 08:05:24 +08:00
yadong.zhang 2de0ad5013 !28 添加微软中国(世纪互联)第三方登录,新增微软方式登录的redirectUri校验
Merge pull request !28 from mroldx/dev
2021-08-26 12:06:43 +00:00
974751082@qq.com 23b7bcf43d 添加微软中国(世纪华联)第三方登录,新增微软方式登录的redirectUri校验 2021-08-25 23:06:29 +08:00
yadong.zhang 976b7d8b2b Create FUNDING.yml 2021-08-16 12:09:01 +08:00
yadong.zhang 80132b69e7 💩 修改 AuthSource 中的 [企业微信二维码第三方登录] 2021-08-15 22:57:01 +08:00
yadong.zhang 95158654a9 📝 编写文档 2021-08-15 22:54:29 +08:00
yadong.zhang f2e609192a 🔖 发布 1.16.3 版本 2021-08-15 22:52:58 +08:00
yadong.zhang 8c8f7a27a2 🙈 更新 .gitignore 文件 2021-08-15 22:52:41 +08:00
yadong.zhang 8283124c57 👽 增加 codechina 2021-08-15 22:52:24 +08:00
yadong.zhang 39b113b194 🐛 解决 Line 的 bug。Github Issue (#122) 2021-08-15 22:51:14 +08:00
yadong.zhang e45ef2ec31 🔖 修改 since 编号 2021-08-15 22:49:33 +08:00
yadong.zhang d114368a0d Merge branch 'master' into dev 2021-08-15 22:37:28 +08:00
yadong.zhang 0047cde50e Merge pull request #128 from kang8/dev
[WIP] doc: 添加快照版本对应更详细的文档
2021-08-15 09:33:21 -05:00
yadong.zhang f2c1c0f8a6 Merge pull request #127 from zheng-jx/master
企业微信第三方登录
2021-08-15 09:31:08 -05:00
kang 21e23aadb9 doc: 添加快照版本的文档编写 2021-08-14 23:57:14 +08:00
yadong.zhang db3f7da181 📝 Writing docs. 2021-08-13 18:26:23 +08:00
yadong.zhang 004e5a180f 📝 Writing docs. 2021-08-13 18:06:14 +08:00
yadong.zhang 50c31e5dea Merge pull request #126 from kang8/dev
[WIP] 添加发布快照的 workflow
2021-08-12 06:06:58 -05:00
zheng-jx 7d6049da67 企业微信第三方登录
接入链接https://open.work.weixin.qq.com/api/doc/90001/90143/91123
2021-08-11 15:50:39 +08:00
yadong.zhang 9a24553acd 🐛 预防 NPE 2021-08-11 11:03:08 +08:00
yadong.zhang d75d91db0d 📝 补充文档说明 2021-08-11 10:57:25 +08:00
yadong.zhang e1a4688ac0 📝 修复文案错误 2021-08-11 10:56:04 +08:00
yadong.zhang b1d3790ae1 author 2021-08-11 10:54:43 +08:00
yadong.zhang e5548b0173 🔖 修改版本号为 1.16.3 2021-08-11 10:51:25 +08:00
yadong.zhang ebf39627dd 添加 AuthRequestBuilder 可以便捷的创建 AuthRequest 2021-08-11 10:49:45 +08:00
yadong.zhang 74ee17b242 !27 获取三方实例 部分 进行重构,使之可以进行更简单的获取对应的实例。
Merge pull request !27 from 陈宁/master
2021-08-10 10:06:19 +00:00
ngcly b77de0bd0c 还原 2021-08-10 12:28:56 +08:00
ngcly e55033f4f5 格式化 2021-08-10 10:27:01 +08:00
ngcly 37b7784f89 格式化 2021-08-10 10:24:36 +08:00
ngcly 7cdc719166 继续进行优化,去掉多余的改动 2021-08-10 10:18:42 +08:00
ngcly 5073f82897 1 2021-08-10 00:01:12 +08:00
ngcly 9971793f0c 获取三方实例 部分 进行重构,使之可以进行更简单的获取对应的实例。
如之前获取对应的实例:
        switch (source.toLowerCase()) {
            case "dingtalk":
                authRequest = new AuthDingTalkRequest(AuthConfig.builder()
                        .clientId("")
                        .clientSecret("")
                        .redirectUri("http://localhost:8443/oauth/callback/dingtalk")
                        .build());
                break;
            case "baidu":
                authRequest = new AuthBaiduRequest(AuthConfig.builder()
                        .clientId("")
                        .clientSecret("")
                        .redirectUri("http://localhost:8443/oauth/callback/baidu")
                        .scopes(Arrays.asList(
                                AuthBaiduScope.BASIC.getScope(),
                                AuthBaiduScope.SUPER_MSG.getScope(),
                                AuthBaiduScope.NETDISK.getScope()
                        ))
                        .build());
                break;
               }
上面需要调用端自己去根据source编码进行 new 对应的子类,这是不合理的。调用者应该只需要关心source码和对应的配置。
现在重构后,调用端只需要传入对应的source 和自定义的配置 通过统一的入口就可以拿到正确的实例。不需要调用端做各种判断,所有逻辑判断在服务端这边做好了处理。
调用端代码如下:
        AuthDefaultSource defaultSource = AuthDefaultSource.getAuthSource("wechat_mp");
        AuthRequest request = defaultSource.getAuthRequestInstance(AuthConfig.builder()
            .clientId("a")
            .clientSecret("a")
            .redirectUri("https://www.justauth.cn")
            .build());
2021-08-09 23:48:31 +08:00
kang 9fc3131640 test: 修复 twitter userInfo api 修改后测试同步问题 2021-08-07 11:28:20 +08:00
kang 3f4436bcb6 ci: add upload ossrh snapshotRepository config 2021-08-07 11:12:10 +08:00
kang 0678202baa ci: add deploy snapshot workflow 2021-08-07 11:07:52 +08:00
yadong.zhang af8fda700b 📝 更新文档 2021-07-28 13:36:36 +08:00
yadong.zhang 881a87ed95 📝 修复 twitter 平台无法获取用户邮箱的问题 2021-07-28 09:53:44 +08:00
yadong.zhang 4c8fdbae49 📝 修复“淘宝”平台授权登录后没有uid的问题、增加刷新token的功能 2021-07-06 22:19:30 +08:00
yadong.zhang e8db2dd282 📝 更新文档 2021-06-03 20:53:16 +08:00
yadong.zhang 90374762e4 增加“程序员客栈” 2021-06-03 15:18:51 +08:00
yadong.zhang e5d44e91b8 👽 优化代码 2021-05-14 17:02:12 +08:00
yadong.zhang 41559fc954 📝 更新文档 2021-05-11 12:28:38 +08:00
yadong.zhang d354278e7d 📝 更新文档 2021-05-11 12:27:15 +08:00
yadong.zhang c2d6661a76 !24 update README.md. maxkey update
Merge pull request !24 from MaxKeyTop/N/A
2021-05-11 12:22:30 +08:00
MaxKeyTop 5ee87760be update README.md. maxkey update 2021-05-10 21:03:41 +08:00
yadong.zhang 94f6540338 📝 更新文档 2021-04-19 21:00:19 +08:00
yadong.zhang 7bc7a92efb 📝 更新文档 2021-04-19 14:05:22 +08:00
yadong.zhang b5920872d5 Merge branch 'master' of github.com:justauth/JustAuth into dev 2021-04-19 14:03:25 +08:00
yadong.zhang 0bdf881849 Merge pull request #115 from leewenlong/patch-1
修正若干注释
2021-04-15 09:40:23 +08:00
yadong.zhang 46f7c72744 📝 更新文档 2021-04-12 11:41:43 +08:00
leewenlong 4a662ba5cd Update AuthCodingRequest.java
修正注释
2021-04-10 18:02:33 +08:00
leewenlong 040587d5b2 Update AuthGithubScope.java
修正注释
2021-04-10 18:01:29 +08:00
leewenlong c81411fd39 Update AuthBaiduScope.java
改正注释
2021-04-10 17:55:05 +08:00
yadong.zhang 0e1b000bd8 ⬆️ 升级 fastjsonv1.2.76 2021-04-09 08:44:53 +08:00
yadong.zhang f9b30c735b 🔖 Update version to 1.16.1 2021-04-09 08:40:10 +08:00
yadong.zhang 0f42457d91 🐛 解决企业微信授权后,回调地址中原有的参数丢失的问题 2021-04-09 08:35:26 +08:00
yadong.zhang fa75568996 抖音平台支持自定义 scope 2021-04-09 08:34:14 +08:00
yadong.zhang f44ceeeeeb 增加忽略校验 redirectUri 的配置 2021-04-09 08:33:21 +08:00
yadong.zhang ec4c009ed8 📝 更新文档 2021-04-08 09:43:34 +08:00
yadong.zhang 03fbbe0d3a 📝 更新文档 2021-04-07 20:52:04 +08:00
yadong.zhang e64e9def63 📝 更新文档 2021-04-07 20:14:01 +08:00
yadong.zhang 25f99f7560 📝 更新文档 2021-04-07 20:12:22 +08:00
yadong.zhang 0433f54564 📝 更新文档 2021-04-07 19:26:07 +08:00
yadong.zhang 423d9f45b6 📝 更新文档 2021-04-07 19:25:23 +08:00
yadong.zhang 7a9e6214de 👽 集成 Okta 2021-03-30 23:26:56 +08:00
yadong.zhang 3753e3b9bc 👽 AuthConfig 中 CodingGroupName 改为 DomainPrefix 2021-03-30 23:25:30 +08:00
yadong.zhang bc3af96328 👽 优化代码 2021-03-30 23:22:05 +08:00
yadong.zhang b700902c04 📝 更新文档 2021-03-29 11:03:37 +08:00
yadong.zhang 2d0ae7cc27 📝 文档 2021-03-29 11:00:56 +08:00
yadong.zhang 3e4faa75fe 📝 更新脚本 2021-03-29 10:45:07 +08:00
yadong.zhang 6a36ad6af2 📝 更新文档 2021-03-29 10:44:44 +08:00
yadong.zhang 13514d6a92 调整 map 声明时的初始容量 2021-03-29 10:44:28 +08:00
yadong.zhang 6a479d9c1d 🥚 集成 Slack 和 Line 2021-03-29 10:43:47 +08:00
yadong.zhang 2e64fb9693 🥚 集成 Slack 2021-03-29 10:43:15 +08:00
yadong.zhang 0e8308e6c9 🥚 集成 Line 2021-03-29 10:42:57 +08:00
yadong.zhang 3933b3b7ee 🥚 增加钉钉账号登录 2021-03-19 14:37:37 +08:00
yadong.zhang a99b818810 update CHANGELOGS.md. 2021-03-16 17:29:55 +08:00
yadong.zhang cd324cc7c1 🔖 Release version 1.16.0 2021-03-13 23:15:22 +08:00
yadong.zhang 3e890f2c2a 📌 升级 FaceBook API 版本到 v10.0 2021-03-13 23:14:26 +08:00
yadong.zhang ba56f5ab5a 🔧 facebook scope 增加默认值 2021-03-13 23:11:32 +08:00
yadong.zhang 132a7f4338 🥚 添加 amazon 平台 2021-03-13 23:10:48 +08:00
yadong.zhang c79b97a0d0 !22 jdk8 maven打包编译报错: src\main\java\me\zhyd\oauth\config\JustAuthLogConfig.java:8: 错误: 未知标记: date
Merge pull request !22 from xhal/master
2021-03-04 08:47:26 +08:00
xhal 66df3ac027 update pom.xml - 1.8 javadoc 配置 2021-03-03 17:46:16 +08:00
xhal 11df93ad44 update pom.xml. 2021-03-03 17:06:08 +08:00
yadong.zhang e3f5f49ec8 💡 更新帮助文档链接 2021-02-19 17:58:39 +08:00
yadong.zhang 23eb2cfcc8 💡 修改文档 2021-02-19 17:52:29 +08:00
yadong.zhang 30461e62f9 💡 修改文档 2021-02-19 17:51:14 +08:00
yadong.zhang dfbbd9ae0d 💡 修改文档 2021-02-19 17:46:11 +08:00
yadong.zhang 05b7746f26 💡 修改文档 2021-02-19 17:37:23 +08:00
yadong.zhang 21422b02aa Merge pull request #110 from hangsman/master
feat: 增加全局日志配置类
2021-01-15 18:33:28 +08:00
HeJin 10e445e87c feat: 增加全局日志配置类 2021-01-09 20:38:33 +08:00
yadong.zhang d6cbcecaab 💡 修改注释、文档 2021-01-04 15:19:19 +08:00
yadong.zhang 050459e616 📝 Writing docs. 2021-01-01 18:28:43 +08:00
yadong.zhang 9fd2b9b919 🥚 正式启用飞书登录 2021-01-01 17:48:49 +08:00
yadong.zhang 2ff5570399 💡 更新注释 2021-01-01 15:22:30 +08:00
yadong.zhang 1bec384525 👽 重命名企业微信扫码登录 request 类名,补充文档说明 2021-01-01 15:20:57 +08:00
yadong.zhang 824c68356d 📌 升级 FaceBook API 版本到 v9.0 2021-01-01 10:40:18 +08:00
yadong.zhang f5e3c225f6 📝 Writing docs. 2021-01-01 10:27:52 +08:00
yadong.zhang 0259669288 Merge branch 'dev' of github.com:justauth/JustAuth into dev 2021-01-01 10:10:20 +08:00
yadong.zhang edc9d1a3c3 Merge pull request #107 from 937624219/dev
添加AuthAlipayRequest网络代理构造器
2021-01-01 10:03:30 +08:00
yadong.zhang d886bc95a2 Merge pull request #105 from jianghuzai/WeChatEnterpriseWeb
支持企业微信网页授权登录
2021-01-01 09:56:40 +08:00
冬瓜 7aaf52e953 添加AuthAlipayRequest网络代理构造器 2020-12-27 11:08:57 +08:00
yadong.zhang e84bd7cdb5 🔖 merge #101 2020-12-20 17:14:06 +08:00
guanhua.li 28e19960f2 支持企业微信网页授权登录 2020-12-16 10:02:37 +08:00
yadong.zhang dce2bd1e1e Merge pull request #101 from zwzch/dev
支持喜马拉雅登录
2020-12-09 13:18:52 +08:00
zwzch bc30971482 优化代码
Signed-off-by: zwzch <zwzch4j@gmail.com>
2020-11-06 20:14:52 +08:00
zwzch 9bd6d88049 喜马拉雅登录
Signed-off-by: zwzch <zwzch4j@gmail.com>
2020-11-06 17:41:28 +08:00
yadong.zhang 442332be57 📝 Writing docs. 2020-11-02 21:30:23 +08:00
yadong.zhang d4bfa8e75f 🔖 sponsors 2020-11-02 21:22:35 +08:00
yadong.zhang fb90fbdcb8 Merge branch 'master' into dev 2020-10-25 11:04:55 +08:00
yadong.zhang 5f6cb2954f 🔖 Release version 1.15.8 2020-10-25 11:04:49 +08:00
yadong.zhang 906ae659a2 Merge pull request #96 from xkcoding/dev
⬆️ 升级 simple-http 版本 1.0.2->1.0.3,修复 jdk11 超时问题
2020-10-25 10:35:40 +08:00
Yangkai.Shen cebfd99703 ⬆️ 升级 simple-http 版本 1.0.2->1.0.3,修复 jdk11 超时问题 2020-10-25 10:02:18 +08:00
yadong.zhang 71b6080156 Merge pull request #95 from justauth/dependabot/maven/junit-junit-4.13.1
⬆️ Bump junit from 4.11 to 4.13.1
2020-10-23 11:23:09 +08:00
dependabot[bot] 492ffdbbf8 ⬆️ Bump junit from 4.11 to 4.13.1
Bumps [junit](https://github.com/junit-team/junit4) from 4.11 to 4.13.1.
- [Release notes](https://github.com/junit-team/junit4/releases)
- [Changelog](https://github.com/junit-team/junit4/blob/main/doc/ReleaseNotes4.11.md)
- [Commits](https://github.com/junit-team/junit4/compare/r4.11...r4.13.1)

Signed-off-by: dependabot[bot] <support@github.com>
2020-10-13 12:29:21 +00:00
142 changed files with 5602 additions and 871 deletions
+12
View File
@@ -0,0 +1,12 @@
# These are supported funding model platforms
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: ['https://www.justauth.cn/sponsor.html']
+2 -2
View File
@@ -9,8 +9,8 @@ assignees: ''
## Pre-submission checklist:
- [ ] I have searched the relevant information in the existing list of Issues.
- [ ] I have searched the developer documentation for that information: https://justauth.wiki
- [ ] I have read the relevant Q&A: https://justauth.wiki/#/Q&A
- [ ] I have searched the developer documentation for that information: https://www.justauth.cn
- [ ] I have read the relevant Q&A: https://www.justauth.cn
## Issue description
+65
View File
@@ -0,0 +1,65 @@
name: Deploy
on:
push:
branches: [ dev ]
paths:
- src/**
- pom.xml
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2.2.0
- name: Set up Java and Maven
uses: actions/setup-java@v2
with:
java-version: '8'
distribution: 'zulu'
- name: Cache m2 package
uses: actions/cache@v2
with:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ hashFiles('pom.xml') }}
restore-keys: |
${{ runner.os }}-maven-
- run: mvn test
deploy-snapshot:
needs: test
if: ${{ success() }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2.2.0
- name: Set up Java and Maven
uses: actions/setup-java@v2
with:
java-version: '8'
distribution: 'zulu'
server-id: ossrh
server-username: MAVEN_USERNAME
server-password: MAVEN_PASSWORD
- name: Cache m2 package
uses: actions/cache@v2
with:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ hashFiles('pom.xml') }}
restore-keys: |
${{ runner.os }}-maven-
- name: setting snapshot version
run: |
import xml.etree.ElementTree as ET
tree = ET.parse("pom.xml")
version = tree.find("{http://maven.apache.org/POM/4.0.0}version")
print(version.text + "-SNAPSHOT")
if version.text.endswith("-SNAPSHOT") == False:
tree.find("{http://maven.apache.org/POM/4.0.0}version").text = version.text + "-SNAPSHOT"
ET.register_namespace("", "http://maven.apache.org/POM/4.0.0")
tree.write("pom.xml", "utf-8", True)
shell: python
- name: deploy snapshot to ossrh repository
run: mvn -B deploy -P snapshot -DskipTests
env:
MAVEN_USERNAME: ${{ secrets.OSSRH_USERNAME }}
MAVEN_PASSWORD: ${{ secrets.OSSRH_TOKEN }}
+26
View File
@@ -0,0 +1,26 @@
name: test pull_request
on:
pull_request:
paths:
- src/**
- pom.xml
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2.2.0
- name: Set up Java and Maven
uses: actions/setup-java@v2
with:
java-version: '8'
distribution: 'zulu'
- name: Cache m2 package
uses: actions/cache@v2
with:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ hashFiles('pom.xml') }}
restore-keys: |
${{ runner.os }}-maven-
- run: mvn test
-2
View File
@@ -28,8 +28,6 @@ hs_err_pid*
bin/codecov.sh
bin/deploy.sh
bin/docsify-cli.sh
bin/push.sh
bin/push-dev.sh
target
/pom.xml.versionsBackup
+172 -6
View File
@@ -1,3 +1,169 @@
## 1.16.7
### 2024/12/14
- 新增
- 添加`微信小程序`登录能力,对接文档:[点击查看](https://justauth.cn/guide/oauth/wechat_mini_program/)。
- 添加`支付宝证书模式`登录能力(原支持的公钥登录模式依然可用),对接文档:[点击查看](https://justauth.cn/guide/oauth/alipay_cert)。
- 添加`appleid`社交登录能力,对接文档:[点击查看](https://justauth.cn/guide/oauth/appleid/)。 [Github#192](https://github.com/justauth/JustAuth/pull/192)
- 添加`QQ小程序`社交登录能力。 [Github#223](https://github.com/justauth/JustAuth/pull/223)
- 添加`figma`社交登录能力。 [Gitee#41](https://gitee.com/yadong.zhang/JustAuth/pulls/41)
- 添加新版`企业微信扫码`登录能力,对接文档:[点击查看](https://justauth.cn/guide/oauth/wechat_enterprise_qrcode_v2/)。 [Github Issue#165](https://github.com/justauth/JustAuth/issues/165)
- 添加新版`钉钉扫码`登录能力,对接文档:[点击查看](https://justauth.cn/guide/oauth/dingtalk_v2/)。 [Gitee Issue#I73FZL](https://gitee.com/yadong.zhang/JustAuth/issues/I73FZL)
- 添加新版`华为`登录能力,对接文档:[点击查看](https://justauth.cn/guide/oauth/huawei_v3/),原`AuthHuaweiRequest`会在后面版本被弃用,如有使用,请切换到`AuthHuaweiV3Request`
- 优化
- 修复文档错误。[Github #222](https://github.com/justauth/JustAuth/pull/222)
- 更新 Google 端点地址。[Github #198](https://github.com/justauth/JustAuth/pull/198)
- Amazon PKCE 中的 `code_verifier` 基于 `state` 缓存
- `AuthRequest`响应时携带泛型,避免二次解析。[Gitee#38](https://gitee.com/yadong.zhang/JustAuth/pulls/38)
- 优化业务调用方式:`getAccessToken``getUserInfo`两个方法从`AuthDefaultRequest`提升至`AuthRequest`中,部分场景下可以减少一次网络请求。[Github Issue#194](https://github.com/justauth/JustAuth/issues/194)
- ***注意:如果有基于 JustAuth 规范自定义实现的三方平台 Request([自定义第三方平台的OAuth](https://justauth.cn/features/customize-the-oauth/)),需要注意`getAccessToken``getUserInfo`接口的访问级别是否正确!!!***
- 其他
- 补充单侧,[Gitee#39](https://gitee.com/yadong.zhang/JustAuth/pulls/39)
## 1.16.6
### 2023/12/03
- 优化
- 微信公众平台支持返回快照标识(快照标识为 true 时,标识当前获取到的微信用户信息都是虚拟的)
- 企业微信网页登录--获取用户敏感信。 [Github #155](https://github.com/justauth/JustAuth/pull/155)
- 添加飞书单元测试。 [Github #159](https://github.com/justauth/JustAuth/pull/159)
- 升级fastjson版本到1.2.831.2.83版本之前存在代码执行漏洞风险 CVE-2022-25845。[Gitee PR #31](https://gitee.com/yadong.zhang/JustAuth/pulls/31)
- 新增
- 添加微软中国(世纪华联)第三方登录,新增微软方式登录的redirectUri校验。[Gitee PR #33](https://gitee.com/yadong.zhang/JustAuth/pulls/33)
- 新增爱发电平台 [Gitee #35](https://gitee.com/yadong.zhang/JustAuth/pulls/35)
- 微软平台适配 AzureAD(目前改名为 Microsoft Entra ID)登录认证
- Fixed
- 修复 twitter 平台在 Java11 环境下登录失败的问题。[#174](https://github.com/justauth/JustAuth/issues/174)
- 修复 Facebook 平台无法登录的问题(facebook 平台 API 进行了升级)
- 修复微信公众平台 scope 为 snsapi_base 登录报错的问题 [181](https://github.com/justauth/JustAuth/issues/181)
## 1.16.5
### 2021/10/18
- 合并 PR [Github #138](https://github.com/justauth/JustAuth/pull/138)
- 升级alipay-sdk-version的依赖版本
- 修改用户logo的尺寸
- 修复 alipay 登录失败的 BUG [Gitee Issue #I4E4ML](https://gitee.com/yadong.zhang/JustAuth/issues/I4E4ML)
## 1.16.4
### 2021/9/22
- 合并 PR
- [Github #134](https://github.com/justauth/JustAuth/pull/134)
- [Github #133](https://github.com/justauth/JustAuth/pull/133)
- [Github #132](https://github.com/justauth/JustAuth/pull/132)
- [Github #131](https://github.com/justauth/JustAuth/pull/131)
- 添加微软中国(世纪华联)第三方登录,新增微软方式登录的redirectUri校验。[Gitee PR #28](https://gitee.com/yadong.zhang/JustAuth/pulls/28)
- 升级第三方的依赖
- simple-http > 1.0.5
- lombok > 1.18.20
- junit > 4.13.2
- fastjson > 1.2.78
- alipay-sdk > 4.16.38.ALL
## 1.16.3
### 2021/8/15
- 发布 v1.16.3
- 新增
- 集成“企业微信的第三方应用”平台登录
- PR
- `AuthRequst` 增加 `Builder` 构建方式,使用起来更简单。 ([#27](https://gitee.com/yadong.zhang/JustAuth/pulls/27))
- 使用 Github Action 添加发布快照的 workflow。 ([#126](https://github.com/justauth/JustAuth/pull/126))
- 新增了企业微信的第三方应用登录,`AuthWeChatEnterpriseThirdQrcodeRequest`。 ([#127](https://github.com/justauth/JustAuth/pull/127))
- 添加快照版本对应更详细的文档。 ([#128](https://github.com/justauth/JustAuth/pull/128))
- 修改
- 在 Gitee PR ([#27](https://gitee.com/yadong.zhang/JustAuth/pulls/27)) 的基础上重构代码,增加 Builder 方式创建 AuthRequest
- 解决 Line 登录的错误。[#122](https://github.com/justauth/JustAuth/issues/122)
## 1.16.2
### 2021/7/28
- 发布 v1.16.2
- 新增
- 集成“程序员客栈”平台登录
- 修改
- 更新文档
- 修复“淘宝”平台授权登录后没有`uid`的问题、增加刷新token的功能
- 修复“Twitter”平台授权登录后获取不到用户邮箱的问题
## 1.16.1
### 2021/4/19
- 发布 v1.16.1
- Fix Github issue [#114](https://github.com/justauth/JustAuth/issues/114): 解决企业微信授权后,回调地址中原有的参数丢失的问题
- Fix Github issue [#82](https://github.com/justauth/JustAuth/issues/82): 抖音平台支持自定义 scope
- Fix Github issue [#92](https://github.com/justauth/JustAuth/issues/92): 增加忽略校验 redirectUri 的配置
- Merge Github PR [#115](https://github.com/justauth/JustAuth/pull/115)
- 升级 `fastjson``v1.2.76`
## 1.16.0
### 2021/4/7
- 发布 v1.16.0
- 新增
- 集成 Amazon 平台登录
- 集成 Slack 平台登录
- 集成 LINE 平台登录
- 集成 Okta 平台登录
- 集成钉钉账号登录
- 修改
- 【**重要**】 `AuthConfig`中的`codingGroupName`参数更名为`domainPrefix`,针对此类平台提供通用的配置。
- 修改 `AuthFacebookScope` 中的默认 scope,解决 justauth-demo 项目中使用 facebook 报错的问题
- 升级 facebook 的 api 到 v10.0 版本
- 优化部分代码
- 优化 Map 声明时的初始容量,避免频繁扩容
- 更新 README 文档
- PR
- 合并 [Github #110](https://github.com/justauth/JustAuth/pull/110)
- 合并 [Gitee #22](https://gitee.com/yadong.zhang/JustAuth/pulls/22)
## 1.15.9
### 2021/1/1
- 发布 v1.15.9
- 新增
- 修复并正式启用 飞书 平台的第三方登录
- AuthToken 类中新增 `refreshTokenExpireIn` 记录 refresh token 的有效期
- PR
- 合并 [Github #101](https://github.com/justauth/JustAuth/pull/101) :支持喜马拉雅登录
- 合并 [Github #105](https://github.com/justauth/JustAuth/pull/105) :支持企业微信网页授权登录
- 合并 [Github #107](https://github.com/justauth/JustAuth/pull/107) :添加AuthAlipayRequest网络代理构造器,解决 Github Issue [#102](https://github.com/justauth/JustAuth/issues/102)
- 修改
- 修改喜马拉雅配置参数,将`ClientOsType`参数提到 AuthConfig 中
- AuthChecker 中增加对喜马拉雅平台的校验
- 升级 facebook api 版本到 v9.0,解决 Gitee Issue [#I2AR5S](https://gitee.com/yadong.zhang/JustAuth/issues/I2AR5S)
- !!!**注意**!!!修改原来的企业微信 Request 类名为 `AuthWeChatEnterpriseQrcodeRequest`,升级后注意该点
注意:可能有些开发者对于 JA 集成的四个微信平台不太理解,这儿统一说明:
- 按照类名
- AuthWeChatEnterpriseQrcodeRequest:企业微信二维码登录
- AuthWeChatEnterpriseWebRequest:企业微信网页登录
- AuthWeChatOpenRequest:微信开放平台
- AuthWeChatMpRequest:微信公众平台
- 按照枚举
- WECHAT_ENTERPRISE:企业微信二维码登录
- WECHAT_ENTERPRISE_WEB:企业微信网页登录
- WECHAT_OPEN:微信开放平台
- WECHAT_MP:微信公众平台
## 1.15.8
### 2020/10/25
- Release version 1.15.8
- Merge the pr. [#95](https://github.com/justauth/JustAuth/pull/95) [#96](https://github.com/justauth/JustAuth/pull/96)
## 1.15.7
### 2020/09/11
@@ -35,7 +201,7 @@
- 新增 [微信企业版登录](oauth/wechatEnterprise.md)文档
- 新增 [Facebook 登录](oauth/facebook.md)文档
- 完善 [JustAuth 使用者](users.md)文档
- 替换“帮助文档”域名,由[https://docs.justauth.whnb.wang](https://docs.justauth.whnb.wang)迁移到[https://justauth.wiki](https://justauth.wiki)
- 替换“帮助文档”域名,由[https://docs.justauth.whnb.wang](https://docs.justauth.whnb.wang)迁移到[https://www.justauth.cn](https://www.justauth.cn)
- 新增
- 增加阿里云授权登录中刷新授权token的接口,by “QQ群用户需求”
- AuthConfig 增加忽略校验 state 的参数,详情参考:[Github#Issue#83](https://github.com/justauth/JustAuth/issues/83)
@@ -88,7 +254,7 @@ new AuthGoogleRequest(AuthConfig.builder()
- 修复
- 解决 Twitter 授权失败的BUG
- 文档
- 完善 [https://justauth.wiki](https://justauth.wiki/) 的404引导页内容
- 完善 [https://www.justauth.cn](https://www.justauth.cn/) 的404引导页内容
- 增加名词解释: `uuid`
- 补充 [Q&A](Q&A.md)
- 新增 [参考文档](references.md),包含 OAuth 授权和第三方平台的API文档等内容
@@ -168,9 +334,9 @@ System.setProperty("proxyHost", "127.0.0.1");
- 新增
- 增加微信、QQ、支付宝、微博授权登录的帮助文档
- 合并[PR#57](https://github.com/justauth/JustAuth/pull/57),增加微信公众号登录 by [@xkcoding](https://github.com/xkcoding)
- [帮助文档](https://justauth.wiki)中增加自定义的404页面
- [帮助文档](https://justauth.wiki)中增加Gittalk插件
- [帮助文档](https://justauth.wiki)中增加Java代码高亮的插件
- [帮助文档](https://www.justauth.cn)中增加自定义的404页面
- [帮助文档](https://www.justauth.cn)中增加Gittalk插件
- [帮助文档](https://www.justauth.cn)中增加Java代码高亮的插件
- 增加`AuthUserGender#getWechatRealGender`方法,兼容获取微信平台的用户性别
- 修改
- 修复抖音登录取值取错层级的问题([issue#I15SIG@Gitee](https://gitee.com/yadong.zhang/JustAuth/issues/I15SIG)
@@ -179,7 +345,7 @@ System.setProperty("proxyHost", "127.0.0.1");
- `AuthResponseStatus`枚举类中增加`ILLEGAL_STATUS`、`REQUIRED_REFRESH_TOKEN`两个枚举值
- `AuthSource`接口中增加`getName`方法,用来对外提供实际`source`的字符串值
- `AuthWeiboRequest`微博授权登录中实现`revoke`方法,支持手动回收授权
- [帮助文档](https://justauth.wiki)中修复[腾讯云登录]链接错误的问题
- [帮助文档](https://www.justauth.cn)中修复[腾讯云登录]链接错误的问题
- 升级
- 升级相关依赖:lombok@v1.18.10hutool@5.0.5fastjson@1.2.62alipay@4.8.10.ALL[PR#11@Gitee](https://gitee.com/yadong.zhang/JustAuth/pulls/11)
+89 -71
View File
@@ -1,12 +1,15 @@
<p align="center">
<a href="https://justauth.wiki"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/Justauth.png" width="400"></a>
<a href="https://www.justauth.cn"><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.7-blue" ></img>
<img src="https://img.shields.io/github/v/release/justauth/JustAuth?style=flat-square" ></img>
</a>
<a target="_blank" href="https://oss.sonatype.org/content/repositories/snapshots/me/zhyd/oauth/JustAuth/">
<img src="https://img.shields.io/nexus/s/https/oss.sonatype.org/me.zhyd.oauth/JustAuth.svg?style=flat-square" ></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,65 +17,24 @@
<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.7-orange" ></img>
</a>
<a target="_blank" href="https://justauth.wiki" title="参考文档">
<a target="_blank" href="https://www.justauth.cn" 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 href="https://codecov.io/gh/justauth/JustAuth">
<img src="https://codecov.io/gh/justauth/JustAuth/branch/master/graph/badge.svg?token=zYiAqd9aFz" />
</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>
<img src='https://gitee.com/yadong.zhang/JustAuth/badge/star.svg?theme=gvp' 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>
-------------------------------------------------------------------------------
<p align="center">
<img src='./docs/media/75a3c076.png' alt='star'></img>
</p>
-------------------------------------------------------------------------------
@@ -81,7 +43,7 @@
`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)
Docs[Reference Doc](https://www.justauth.cn)
## Features
@@ -90,34 +52,28 @@ Docs[Reference Doc](https://justauth.wiki)
## Quick start
- Add maven dependency
### Add maven dependency
- Add JustAuth dependency
These artifacts are available from Maven Central:
```xml
<dependency>
<groupId>me.zhyd.oauth</groupId>
<artifactId>JustAuth</artifactId>
<version>1.15.7-beta.2</version>
<version>{latest-version}</version>
</dependency>
```
- Using JustAuth
```java
// Create authorization request
AuthRequest authRequest = new AuthGiteeRequest(AuthConfig.builder()
.clientId("clientId")
.clientSecret("clientSecret")
.redirectUri("redirectUri")
.build());
// Generate authorization url
authRequest.authorize("state");
// After authorization to login, it will return: code(auth_code(Alipay only)),state, After version 1.8.0, you can use the AuthCallback as a parameter to the callback interface
// Note: JustAuth saves state for 3 minutes by default. If it is not used within 3 minutes, the expired state will be cleared automatically.
authRequest.login(callback);
```
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:
> **latest-version**
> - CURRENT: ![](https://img.shields.io/github/v/release/justauth/JustAuth?style=flat-square)
> - SNAPSHOT: ![](https://img.shields.io/nexus/s/https/oss.sonatype.org/me.zhyd.oauth/JustAuth.svg?style=flat-square)
- Add http dependencyOnly need one
> If there is already in the project, please ignore it. In addition, you need to pay special attention. If the low version of the dependency has been introduced in the project, please exclude the low version of the dependency first, and then introduce the high version or the latest version of the dependency
- hutool-http
```xml
@@ -148,6 +104,68 @@ Note, that since [v1.14.0](https://gitee.com/yadong.zhang/JustAuth/releases/v1.1
</dependency>
```
### Using JustAuth API
#### Simple
```java
// Create authorization request
AuthRequest authRequest = new AuthGiteeRequest(AuthConfig.builder()
.clientId("clientId")
.clientSecret("clientSecret")
.redirectUri("redirectUri")
.build());
// Generate authorization page url
authRequest.authorize("state");
// Get token and userinfo
authRequest.login(callback);
```
#### Builder 1. Use unchanging `AuthConfig`
```java
// Create authorization request
AuthRequest authRequest = AuthRequestBuilder.builder()
.source("github")
.authConfig(AuthConfig.builder()
.clientId("clientId")
.clientSecret("clientSecret")
.redirectUri("redirectUri")
.build())
.build();
```
#### Builder 2. Use dynamic `AuthConfig`
```java
// Create authorization request
AuthRequest authRequest = AuthRequestBuilder.builder()
.source("gitee")
.authConfig((source) -> {
// Use source to dynamically get AuthConfig
// Here you can flexibly take the configuration from sql or take the configuration from the configuration file
return AuthConfig.builder()
.clientId("clientId")
.clientSecret("clientSecret")
.redirectUri("redirectUri")
.build();
})
.build();
```
#### Builder 3. Support custom platform
```java
AuthRequest authRequest = AuthRequestBuilder.builder()
// Key point: configure the custom implementation of AuthSource
.extendSource(AuthExtendSource.values())
// Enum name in AuthExtendSource
.source("other")
// ... Do other things
.build();
```
## Contributions
1. Fork this project to your repository
@@ -162,11 +180,11 @@ I look forward to your joining us.
## Contributors
[contributors](https://justauth.wiki/#/contributors)
[contributors](https://www.justauth.cn/contributors.html)
## Change Logs
[CHANGELOGS](https://justauth.wiki/#/update)
[CHANGELOGS](https://www.justauth.cn/update.html)
## Recommend
+184 -99
View File
@@ -1,12 +1,15 @@
<p align="center">
<a href="https://justauth.wiki"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/Justauth.png" width="400"></a>
<a href="https://www.justauth.cn"><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.7-blue" ></img>
<img src="https://img.shields.io/github/v/release/justauth/JustAuth?style=flat-square" ></img>
</a>
<a target="_blank" href="https://oss.sonatype.org/content/repositories/snapshots/me/zhyd/oauth/JustAuth/">
<img src="https://img.shields.io/nexus/s/https/oss.sonatype.org/me.zhyd.oauth/JustAuth.svg?style=flat-square" ></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,77 +17,36 @@
<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.7-orange" ></img>
</a>
<a target="_blank" href="https://justauth.wiki" title="参考文档">
<a target="_blank" href="https://www.justauth.cn" 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 href="https://codecov.io/gh/justauth/JustAuth">
<img src="https://codecov.io/gh/justauth/JustAuth/branch/master/graph/badge.svg?token=zYiAqd9aFz" />
</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>
<img src='https://gitee.com/yadong.zhang/JustAuth/badge/star.svg?theme=gvp' 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>
-------------------------------------------------------------------------------
<p align="center">
<img src='./docs/media/75a3c076.png' alt='star'></img>
</p>
-------------------------------------------------------------------------------
QQ 群:230017570
微信群:justauth (备注`justauth`或者`ja`
帮助文档:[justauth.wiki](https://justauth.wiki)
帮助文档:[www.justauth.cn](https://www.justauth.cn)
## 什么是 JustAuth
JustAuth,如你所见,它仅仅是一个**第三方授权登录**的**工具类库**,它可以让我们脱离繁琐的第三方登录 SDK,让登录变得**So easy!**
JustAuth 集成了诸如:Github、Gitee、支付宝、新浪微博、微信、Google、Facebook、Twitter、StackOverflow等国内外数十家第三方平台。更多请参考<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>
JustAuth 集成了诸如:Github、Gitee、支付宝、新浪微博、微信、Google、Facebook、Twitter、StackOverflow等国内外数十家第三方平台。更多请参考<a href="https://www.justauth.cn" target="_blank">已集成的平台</a>
## 有哪些特点?
@@ -93,24 +55,94 @@ JustAuth 集成了诸如:Github、Gitee、支付宝、新浪微博、微信、
## 有哪些功能?
- 集成国内外数十家第三方平台,实现快速接入。<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>
- 自定义 State 缓存,支持各种分布式缓存组件。<a href="https://justauth.wiki/#/customize-the-state-cache" target="_blank">参考文档</a>
- 自定义 OAuth 平台,更容易适配自有的 OAuth 服务。<a href="https://justauth.wiki/#/customize-the-oauth" target="_blank">参考文档</a>
- 自定义 Http 实现,选择权完全交给开发者,不会单独依赖某一具体实现。<a href="https://justauth.wiki/#/customize-the-oauth" target="_blank">参考文档</a>
- 自定义 Scope,支持更完善的授权体系。<a href="https://justauth.wiki" target="_blank">参考文档</a>
- 更多...<a href="https://justauth.wiki" target="_blank">参考文档</a>
- 集成国内外数十家第三方平台,实现快速接入。<a href="https://www.justauth.cn/guide/" target="_blank">参考文档</a>
- 自定义 State 缓存,支持各种分布式缓存组件。<a href="https://www.justauth.cn/features/using-state/" target="_blank">参考文档</a>
- 自定义 OAuth 平台,更容易适配自有的 OAuth 服务。<a href="https://www.justauth.cn/features/customize-the-oauth/" target="_blank">参考文档</a>
- 自定义 Http 实现,选择权完全交给开发者,不会单独依赖某一具体实现。<a href="https://www.justauth.cn/guide/quickstart/how-to-use/#%E4%BD%BF%E7%94%A8%E6%AD%A5%E9%AA%A4" target="_blank">参考文档</a>
- 自定义 Scope,支持更完善的授权体系。<a href="https://www.justauth.cn/features/customize-scopes/#%E5%85%B3%E4%BA%8E-scope" target="_blank">参考文档</a>
- 更多...<a href="https://www.justauth.cn" target="_blank">参考文档</a>
## 快速开始
- 引入依赖
### 引入依赖
```xml
<dependency>
<groupId>me.zhyd.oauth</groupId>
<artifactId>JustAuth</artifactId>
<version>1.15.7</version>
<version>{latest-version}</version>
</dependency>
```
- 调用api
> **latest-version** 可选:
> - 稳定版:![](https://img.shields.io/github/v/release/justauth/JustAuth?style=flat-square)
> - 快照版:![](https://img.shields.io/nexus/s/https/oss.sonatype.org/me.zhyd.oauth/JustAuth.svg?style=flat-square)
> > 注意:快照版本是功能的尝鲜,并不保证稳定性。请勿在生产环境中使用。
>
> <details>
> <summary>如何引入快照版本</summary>
>
> JustAuth 的快照版本托管在 ossrh 上,所以要指定下载地址。
>
> ```xml
> <repositories>
> <repository>
> <id>ossrh-snapshot</id>
> <url>https://oss.sonatype.org/content/repositories/snapshots</url>
> <snapshots>
> <enabled>true</enabled>
> </snapshots>
> </repository>
> </repositories>
> ```
>
> 如果你想第一时间获取 JustAuth 的最新快照,可以添加下列代码,每次构建时都检查是否有最新的快照(默认每天检查)。
>
> ```diff
> <url>https://oss.sonatype.org/content/repositories/snapshots</url>
> <snapshots>
> + <updatePolicy>always</updatePolicy>
> <enabled>true</enabled>
> </snapshots>
> ```
>
> </details>
如下**任选一种** HTTP 工具 依赖,_项目内如果已有,请忽略。另外需要特别注意,如果项目中已经引入了低版本的依赖,请先排除低版本依赖后,再引入高版本或者最新版本的依赖_
- hutool-http
```xml
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-http</artifactId>
<version>5.7.7</version>
</dependency>
```
- httpclient
```xml
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.13</version>
</dependency>
```
- okhttp
```xml
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.9.1</version>
</dependency>
```
### 调用api
#### 普通方式
```java
// 创建授权request
AuthRequest authRequest = new AuthGiteeRequest(AuthConfig.builder()
@@ -125,60 +157,113 @@ authRequest.authorize("state");
authRequest.login(callback);
```
如下**任选一种** HTTP 工具 依赖,_项目内如果已有,请忽略。另外需要特别注意,如果项目中已经引入了低版本的依赖,请先排除低版本以后来,引入高版本或者最新版本的依赖_
#### Builder 方式一
- hutool-http
静态配置 `AuthConfig`
```xml
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-http</artifactId>
<version>5.2.5</version>
</dependency>
```
```java
AuthRequest authRequest = AuthRequestBuilder.builder()
.source("github")
.authConfig(AuthConfig.builder()
.clientId("clientId")
.clientSecret("clientSecret")
.redirectUri("redirectUri")
.build())
.build();
// 生成授权页面
authRequest.authorize("state");
// 授权登录后会返回code(auth_code(仅限支付宝))、state1.8.0版本后,可以用AuthCallback类作为回调接口的参数
// 注:JustAuth默认保存state的时效为3分钟,3分钟内未使用则会自动清除过期的state
authRequest.login(callback);
```
- httpclient
#### Builder 方式二
```xml
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.12</version>
</dependency>
```
动态获取并配置 `AuthConfig`
- okhttp
```java
AuthRequest authRequest = AuthRequestBuilder.builder()
.source("gitee")
.authConfig((source) -> {
// 通过 source 动态获取 AuthConfig
// 此处可以灵活的从 sql 中取配置也可以从配置文件中取配置
return AuthConfig.builder()
.clientId("clientId")
.clientSecret("clientSecret")
.redirectUri("redirectUri")
.build();
})
.build();
Assert.assertTrue(authRequest instanceof AuthGiteeRequest);
System.out.println(authRequest.authorize(AuthStateUtils.createState()));
```
```xml
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.4.1</version>
</dependency>
```
#### Builder 方式支持自定义的平台
```java
AuthRequest authRequest = AuthRequestBuilder.builder()
// 关键点:将自定义实现的 AuthSource 配置上
.extendSource(AuthExtendSource.values())
// source 对应 AuthExtendSource 中的枚举 name
.source("other")
// ... 其他内容不变,参考上面的示例
.build();
```
## 赞助和支持
感谢以下赞助商的支持:
[我要赞助](https://www.justauth.cn/sponsor.html)
## JustAuth 的用户
有很多公司、组织和个人把 JustAuth 用于学习、研究、生产环境和商业产品中,包括(但不限于):
![](docs/users/4ca0177c.png)
[![](docs/users/col.png)](https://www.mochiwang.com "给作者提供云写作的一个工具")![](docs/users/bjgyol.png)![](docs/users/foresealife.png)![](docs/users/sevnce.png)
![](docs/users/bladex.png)![](docs/users/gun.png)![](docs/users/sika.jpg)![](docs/users/maxkey.png)![](docs/users/shiroaction.png)![](docs/users/xkcoding.png)
怎么没有我?[加入]()
怎么没有我?[登记](https://gitee.com/yadong.zhang/JustAuth/issues/IZ2T7)
## 开源推荐
- `JAP` 开源的登录认证中间件: [https://gitee.com/fujieid/jap](https://gitee.com/fujieid/jap)
- `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/)
- `YurunOAuthLogin` PHP 第三方登录授权 SDK[YurunOAuthLogin](https://gitee.com/yurunsoft/YurunOAuthLogin)
- `sureness` 面向restful api的高性能认证鉴权框架:[sureness](https://github.com/usthe/sureness)
更多推荐,请参考:[JustAuth - 开源推荐](https://www.justauth.cn)
## 鸣谢
# 鸣谢
- 感谢 JetBrains 提供的免费开源 License
<img src="https://images.gitee.com/uploads/images/2020/0406/220236_f5275c90_5531506.png" alt="图片引用自lets-mica" style="float:left;">
<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>
<p>
<img src="https://images.gitee.com/uploads/images/2020/0406/220236_f5275c90_5531506.png" alt="图片引用自lets-mica" style="float:left;">
</p>
## 其他
- [CONTRIBUTORS](https://justauth.wiki/#/contributors)
- [CHANGELOGS](https://justauth.wiki/#/update)
- [PLAN](https://gitee.com/yadong.zhang/JustAuth/issues/IUGRK)
- [CONTRIBUTORS](https://www.justauth.cn/contributors.html)
- [CHANGELOGS](https://www.justauth.cn/update.html)
- [PLAN](https://gitee.com/yadong.zhang/JustAuth/issues/IUGRK)
## 贡献者列表
[![contributors](https://whnb.wang/contributors/yadong.zhang/JustAuth)](https://whnb.wang)
## Stars 趋势
### Gitee
[![Stargazers over time](https://whnb.wang/img/yadong.zhang/JustAuth?e=604800)](https://whnb.wang/yadong.zhang/JustAuth?e=604800)
### Github
[![Stargazers over time](https://starchart.cc/justauth/JustAuth.svg)](https://starchart.cc/justauth/JustAuth)
### ProductHunt
<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>
+1
View File
@@ -0,0 +1 @@
git pull origin dev && git pull github dev
+1
View File
@@ -0,0 +1 @@
git pull origin master && git pull github master
+1
View File
@@ -0,0 +1 @@
git push origin dev && git push github dev
+1
View File
@@ -0,0 +1 @@
git push origin master && git push github master
-2
View File
@@ -25,8 +25,6 @@ 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
+1 -1
View File
@@ -1 +1 @@
1.15.7
1.16.7
+1 -1
View File
@@ -16,7 +16,7 @@
</head>
<body>
<script>
window.location.href = "https://justauth.wiki";
window.location.href = "https://www.justauth.cn";
</script>
</body>
</html>
Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

+4
View File
@@ -96,6 +96,10 @@ _注:非全部平台,部分平台可能不存在图例_
![授权Stack Overflow](https://images.gitee.com/uploads/images/2019/0718/192639_cc301ba7_784199.png "授权Stack Overflow")
#### 授权afdian
![授权afdian](https://img.fastmirror.net/s/2023/03/24/641d63a8c19b5.png "授权afDian")
#### 授权Twitter
![授权Twitter]( "授权Twitter")
+15
View File
@@ -15,6 +15,21 @@ case "$1" in
'updv')
bin/updVersion.sh $2
;;
'ppd')
bin/pull-dev.sh
;;
'pp')
bin/pull.sh
;;
'pd')
bin/push-dev.sh
;;
'p')
bin/push.sh
;;
'd')
bin/deploy.sh
;;
*)
help
esac
+62 -12
View File
@@ -6,12 +6,12 @@
<groupId>me.zhyd.oauth</groupId>
<artifactId>JustAuth</artifactId>
<version>1.15.7</version>
<version>1.16.7</version>
<name>JustAuth</name>
<url>https://gitee.com/yadong.zhang/JustAuth</url>
<description>
小而全而美的第三方登录开源组件。目前已支持Github、Gitee、微博、钉钉、百度、Coding、腾讯云开发者平台、OSChina、支付宝、QQ、微信、淘宝、Google、Facebook、抖音、领英、小米、微软、今日头条、Teambition、StackOverflow、Pinterest、人人、华为、企业微信、酷家乐、Gitlab、美团、饿了么推特等第三方平台的授权登录。 Login, so easy!
小而全而美的第三方登录开源组件。目前已支持Github、Gitee、微博、钉钉、百度、Coding、腾讯云开发者平台、OSChina、支付宝、QQ、微信、淘宝、Google、Facebook、抖音、领英、小米、微软、今日头条、Teambition、StackOverflow、Pinterest、人人、华为、企业微信、酷家乐、Gitlab、美团、饿了么推特、飞书、京东、阿里云、喜马拉雅、Amazon、Slack和Line等第三方平台的授权登录。 Login, so easy!
</description>
<licenses>
@@ -52,17 +52,19 @@
<maven.compiler.target>1.8</maven.compiler.target>
<maven-source.version>2.2.1</maven-source.version>
<maven-compiler.version>3.8.1</maven-compiler.version>
<maven-javadoc.version>3.1.0</maven-javadoc.version>
<maven-javadoc.version>2.9.1</maven-javadoc.version>
<cobertura-version>2.7</cobertura-version>
<maven-surefire-version>2.20</maven-surefire-version>
<maven-gpg-version>1.6</maven-gpg-version>
<maven.test.skip>false</maven.test.skip>
<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.73</fastjson-version>
<alipay-sdk-version>4.8.10.ALL</alipay-sdk-version>
<simple-http.version>1.0.5</simple-http.version>
<lombok-version>1.18.30</lombok-version>
<junit-version>4.13.2</junit-version>
<fastjson-version>1.2.83</fastjson-version>
<alipay-sdk-version>4.39.165.ALL</alipay-sdk-version>
<jacoco-version>0.8.2</jacoco-version>
<jwt.version>0.12.3</jwt.version>
<bcpkix-jdk18on.version>1.78</bcpkix-jdk18on.version>
</properties>
<dependencies>
@@ -93,6 +95,36 @@
<artifactId>alipay-sdk-java</artifactId>
<version>${alipay-sdk-version}</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<artifactId>fastjson</artifactId>
<groupId>com.alibaba</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>${jwt.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>${jwt.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>${jwt.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk18on</artifactId>
<version>${bcpkix-jdk18on.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
@@ -135,6 +167,9 @@
</goals>
</execution>
</executions>
<configuration>
<additionalparam>${javadoc.opts}</additionalparam>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
@@ -154,6 +189,15 @@
</plugins>
</build>
<profiles>
<profile>
<id>snapshot</id>
<distributionManagement>
<snapshotRepository>
<id>ossrh</id>
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
</snapshotRepository>
</distributionManagement>
</profile>
<profile>
<id>release</id>
<build>
@@ -219,10 +263,6 @@
</plugins>
</build>
<distributionManagement>
<snapshotRepository>
<id>sonatype-nexus-snapshots</id>
<url>https://oss.sonatype.org/content/repositories/snapshots/</url>
</snapshotRepository>
<repository>
<id>sonatype-nexus-staging</id>
<url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>
@@ -260,5 +300,15 @@
</snapshotRepository>
</distributionManagement>
</profile>
<!-- 禁用 Javadoc 注释检查 -->
<profile>
<id>disable-javadoc-doclint</id>
<activation>
<jdk>[1.8,)</jdk>
</activation>
<properties>
<javadoc.opts>-Xdoclint:none</javadoc.opts>
</properties>
</profile>
</profiles>
</project>
@@ -0,0 +1,101 @@
package me.zhyd.oauth;
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.request.AuthDefaultRequest;
import me.zhyd.oauth.request.AuthRequest;
import me.zhyd.oauth.utils.StringUtils;
import java.util.Arrays;
import java.util.function.Function;
/**
* 快捷的构建 AuthRequest
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @author ngcly
* @version 1.0.0
* @since 1.16.3
*/
public class AuthRequestBuilder {
private String source;
private AuthConfig authConfig;
private AuthStateCache authStateCache;
private AuthSource[] extendSource;
private AuthRequestBuilder() {
}
public static AuthRequestBuilder builder() {
return new AuthRequestBuilder();
}
public AuthRequestBuilder source(String source) {
this.source = source;
return this;
}
public AuthRequestBuilder authConfig(AuthConfig authConfig) {
this.authConfig = authConfig;
return this;
}
public AuthRequestBuilder authConfig(Function<String, AuthConfig> authConfig) {
this.authConfig = authConfig.apply(this.source);
return this;
}
public AuthRequestBuilder authStateCache(AuthStateCache authStateCache) {
this.authStateCache = authStateCache;
return this;
}
public AuthRequestBuilder extendSource(AuthSource... extendSource) {
this.extendSource = extendSource;
return this;
}
public AuthRequest build() {
if (StringUtils.isEmpty(this.source) || null == this.authConfig) {
throw new AuthException(AuthResponseStatus.NOT_IMPLEMENTED);
}
// 合并 JustAuth 默认的 AuthDefaultSource 和 开发者自定义的 AuthSource
AuthSource[] sources = this.concat(AuthDefaultSource.values(), extendSource);
// 筛选符合条件的 AuthSource
AuthSource source = Arrays.stream(sources).distinct()
.filter(authSource -> authSource.getName().equalsIgnoreCase(this.source))
.findAny()
.orElseThrow(() -> new AuthException(AuthResponseStatus.NOT_IMPLEMENTED));
Class<? extends AuthDefaultRequest> targetClass = source.getTargetClass();
if (null == targetClass) {
throw new AuthException(AuthResponseStatus.NOT_IMPLEMENTED);
}
try {
if (this.authStateCache == null) {
return targetClass.getDeclaredConstructor(AuthConfig.class).newInstance(this.authConfig);
} else {
return targetClass.getDeclaredConstructor(AuthConfig.class, AuthStateCache.class).newInstance(this.authConfig, this.authStateCache);
}
} catch (Exception e) {
e.printStackTrace();
throw new AuthException(AuthResponseStatus.NOT_IMPLEMENTED);
}
}
private AuthSource[] concat(AuthSource[] first, AuthSource[] second) {
if (null == second || second.length == 0) {
return first;
}
AuthSource[] result = new AuthSource[first.length + second.length];
System.arraycopy(first, 0, result, 0, first.length);
System.arraycopy(second, 0, result, first.length, second.length);
return result;
}
}
@@ -2,8 +2,8 @@ package me.zhyd.oauth.config;
import com.xkcoding.http.config.HttpConfig;
import lombok.*;
import me.zhyd.oauth.enums.scope.AuthScope;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.utils.StringUtils;
import java.util.List;
@@ -38,7 +38,10 @@ public class AuthConfig {
/**
* 支付宝公钥:当选择支付宝登录时,该值可用
* 对应“RSA2(SHA256)密钥”中的“支付宝公钥”
*
* @deprecated 请使用AuthAlipayRequest的构造方法设置"alipayPublicKey"
*/
@Deprecated
private String alipayPublicKey;
/**
@@ -66,13 +69,24 @@ public class AuthConfig {
private String agentId;
/**
* 使用 Coding 登录时,需要传该值。
* <p>
* 团队域名前缀,比如以“ https://justauth.coding.net/ ”为例,{@code codingGroupName} = justauth
* 企业微信第三方授权用户类型,member|admin
*
* @since 1.15.5
* @since 1.10.0
*/
private String codingGroupName;
private String usertype;
/**
* 域名前缀。
* <p>
* 使用 Coding 登录和 Okta 登录时,需要传该值。
* <p>
* Coding 登录:团队域名前缀,比如以“ https://justauth.coding.net ”为例,{@code domainPrefix} = justauth
* <p>
* Okta 登录:Okta 账号域名前缀,比如以“ https://justauth.okta.com ”为例,{@code domainPrefix} = justauth
*
* @since 1.16.0
*/
private String domainPrefix;
/**
* 针对国外服务可以单独设置代理
@@ -110,4 +124,132 @@ public class AuthConfig {
* @since 1.15.7
*/
private List<String> scopes;
/**
* 设备ID, 设备唯一标识ID
*
* @since 1.15.8
*/
private String deviceId;
/**
* 喜马拉雅:客户端操作系统类型,1-iOS系统,2-Android系统,3-Web
*
* @since 1.15.9
*/
private Integer clientOsType;
/**
* 喜马拉雅:客户端包名,如果 {@link AuthConfig#clientOsType} 为1或2时必填。对Android客户端是包名,对IOS客户端是Bundle ID
*
* @since 1.15.9
*/
private String packId;
/**
* 是否开启 PKCE 模式,该配置仅用于支持 PKCE 模式的平台,针对无服务应用,不推荐使用隐式授权,推荐使用 PKCE 模式
*
* @since 1.15.9
*/
private boolean pkce;
/**
* Okta 授权服务器的 ID, 默认为 default。如果要使用自定义授权服务,此处传实际的授权服务器 ID(一个随机串)
* <p>
* 创建自定义授权服务器,请参考:
* <p>
* ① https://developer.okta.com/docs/concepts/auth-servers
* <p>
* ② https://developer.okta.com/docs/guides/customize-authz-server
*
* @since 1.16.0
*/
private String authServerId;
/**
* 忽略校验 {@code redirectUri} 参数,默认不开启。当 {@code ignoreCheckRedirectUri} 为 {@code true} 时,
* {@link me.zhyd.oauth.utils.AuthChecker#checkConfig(AuthConfig, AuthSource)} 将不会校验 {@code redirectUri} 的合法性。
*
* @since 1.16.1
*/
private boolean ignoreCheckRedirectUri;
/**
* 适配 builder 模式 set 值的情况
*
* @return authServerId
*/
public String getAuthServerId() {
return StringUtils.isEmpty(authServerId) ? "default" : authServerId;
}
/**
* Microsoft Entra ID(原微软 AAD)中的租户 ID
*/
private String tenantId;
/**
* 苹果开发者账号中的密钥标识符
* @see <a href="https://developer.apple.com/help/account/configure-app-capabilities/create-a-sign-in-with-apple-private-key/">create-a-sign-in-with-apple-private-key</a>
*/
private String kid;
/**
* 苹果开发者账号中的团队ID
* @see <a href="https://developer.apple.com/help/glossary/team-id/">team id</a>
*/
private String teamId;
/**
* 新版企业微信 Web 登录时的参数,
*
* 登录类型。ServiceApp:服务商登录;CorpApp:企业自建/代开发应用登录。
* @see <a href="https://developer.work.weixin.qq.com/document/path/98152">https://developer.work.weixin.qq.com/document/path/98152</a>
* @since 1.16.7
*/
private String loginType = "CorpApp";
/**
* 企业微信平台的语言编码
*
* @since 1.16.7
*/
private String lang = "zh";
/**
* 钉钉平台参数:控制输出特定类型的组织列表,org_type=management 表示只输出有管理权限的组织。
*
* scope包含corpid时该参数存在意义。
*
* @see <a href="https://open.dingtalk.com/document/orgapp/obtain-identity-credentials#title-4up-u8w-5ug">https://open.dingtalk.com/document/orgapp/obtain-identity-credentials#title-4up-u8w-5ug</a>
* @since 1.16.7
*/
private String dingTalkOrgType;
/**
* 钉钉平台参数:用于指定用户需要选择的组织。
*
* scope包含corpid时该参数存在意义。传入的corpId需要是当前用户所在的组织。
*
* @see <a href="https://open.dingtalk.com/document/orgapp/obtain-identity-credentials#title-4up-u8w-5ug">https://open.dingtalk.com/document/orgapp/obtain-identity-credentials#title-4up-u8w-5ug</a>
* @since 1.16.7
*/
private String dingTalkCorpId;
/**
* 钉钉平台参数:true表示专属帐号登录,展示组织代码输入页。
*
* @see <a href="https://open.dingtalk.com/document/orgapp/obtain-identity-credentials#title-4up-u8w-5ug">https://open.dingtalk.com/document/orgapp/obtain-identity-credentials#title-4up-u8w-5ug</a>
* @since 1.16.7
*/
private boolean dingTalkExclusiveLogin;
/**
* 钉钉平台参数:开启了专属帐号功能的组织corpId。
*
* scope包含corpid时该参数存在意义。传入的corpId需要是当前用户所在的组织。
*
* @see <a href="https://open.dingtalk.com/document/orgapp/obtain-identity-credentials#title-4up-u8w-5ug">https://open.dingtalk.com/document/orgapp/obtain-identity-credentials#title-4up-u8w-5ug</a>
* @since 1.16.7
*/
private String dingTalkExclusiveCorpId;
}
File diff suppressed because it is too large Load Diff
@@ -3,6 +3,7 @@ package me.zhyd.oauth.config;
import me.zhyd.oauth.enums.AuthResponseStatus;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.request.AuthDefaultRequest;
/**
* OAuth平台的API地址的统一接口,提供以下方法:
@@ -73,4 +74,11 @@ public interface AuthSource {
}
return this.getClass().getSimpleName();
}
/**
* 平台对应的 AuthRequest 实现类,必须继承自 {@link AuthDefaultRequest}
*
* @return class
*/
Class<? extends AuthDefaultRequest> getTargetClass();
}
@@ -0,0 +1,34 @@
package me.zhyd.oauth.config;
import me.zhyd.oauth.log.Log;
/**
* JustAuth 日志配置类
*
* @author HeJin
*/
public class JustAuthLogConfig {
/**
* 设置日志级别
*
* @param level 日志级别
*/
public static void setLevel(Log.Level level) {
Log.Config.level = level;
}
/**
* 关闭日志
*/
public static void disable() {
Log.Config.enable = false;
}
/**
* 开启日志
*/
public static void enable() {
Log.Config.enable = true;
}
}
@@ -28,9 +28,15 @@ public enum AuthResponseStatus {
ILLEGAL_CODE(5008, "Illegal code"),
ILLEGAL_STATUS(5009, "Illegal state"),
REQUIRED_REFRESH_TOKEN(5010, "The refresh token is required; it must not be null"),
ILLEGAL_TOKEN(5011, "Invalid token"),
ILLEGAL_KID(5012, "Invalid key identifier(kid)"),
ILLEGAL_TEAM_ID(5013, "Invalid team id"),
ILLEGAL_CLIENT_ID(5014, "Invalid client id"),
ILLEGAL_CLIENT_SECRET(5015, "Invalid client secret"),
ILLEGAL_WECHAT_AGENT_ID(5016, "Illegal wechat agent id"),
;
private int code;
private String msg;
private final int code;
private final String msg;
}
@@ -0,0 +1,28 @@
package me.zhyd.oauth.enums.scope;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* Amazon平台 OAuth 授权范围
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.16.0
*/
@Getter
@AllArgsConstructor
public enum AuthAmazonScope implements AuthScope {
/**
* {@code scope} 含义,以{@code description} 为准
*/
R_LITEPROFILE("profile", "The profile scope includes a user's name and email address", true),
R_EMAILADDRESS("profile:user_id", "The profile:user_id scope only includes the user_id field of the profile", true),
W_MEMBER_SOCIAL("postal_code", "This includes the user's zip/postal code number from their primary shipping address", true);
private final String scope;
private final String description;
private final boolean isDefault;
}
@@ -0,0 +1,19 @@
package me.zhyd.oauth.enums.scope;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* @see <a href="https://developer.apple.com/documentation/sign_in_with_apple/clientconfigi/3230955-scope/">scope</a>
*/
@Getter
@AllArgsConstructor
public enum AuthAppleScope implements AuthScope {
EMAIL("email", "用户邮箱", true),
NAME("name", "用户名", true),
;
private final String scope;
private final String description;
private final boolean isDefault;
}
@@ -4,7 +4,7 @@ import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 度平台 OAuth 授权范围
* 度平台 OAuth 授权范围
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
@@ -23,8 +23,8 @@ public enum AuthBaiduScope implements AuthScope {
PUBLIC("public", "可以访问公共的开放API。", false),
HAO123("hao123", "可以访问Hao123 提供的开放API接口。该权限需要申请开通,请将具体的理由和用途发邮件给tuangou@baidu.com。", false);
private String scope;
private String description;
private boolean isDefault;
private final String scope;
private final String description;
private final boolean isDefault;
}
@@ -24,8 +24,8 @@ public enum AuthCodingScope implements AuthScope {
PROJECT_DEPOT("project:depot", "完整的仓库控制权限", false),
PROJECT_WIKI("project:wiki", "授权读取与操作 wiki", false),
;
private String scope;
private String description;
private boolean isDefault;
private final String scope;
private final String description;
private final boolean isDefault;
}
@@ -0,0 +1,33 @@
package me.zhyd.oauth.enums.scope;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 钉钉平台 OAuth 授权范围
*
* https://open.dingtalk.com/document/orgapp/obtain-identity-credentials#title-4up-u8w-5ug
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.16.7
*/
@Getter
@AllArgsConstructor
public enum AuthDingTalkScope implements AuthScope {
/**
* 无需申请 默认开启
*/
openid("openid", "授权后可获得用户userid", true),
/**
* 无需申请 默认开启
*/
corpid("corpid", "授权后可获得登录过程中用户选择的组织id", false)
;
private final String scope;
private final String description;
private final boolean isDefault;
}
@@ -0,0 +1,68 @@
package me.zhyd.oauth.enums.scope;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 抖音平台 OAuth 授权范围
*
* https://open.douyin.com/platform/doc/6855240178122983437
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.16.1
*/
@Getter
@AllArgsConstructor
public enum AuthDouyinScope implements AuthScope {
/**
* 无需申请 默认开启
*/
USER_INFO("user_info", "返回抖音用户公开信息", true),
/**
* 无需申请 默认开启
*/
AWEME_SHARE("aweme.share", "抖音分享", false),
/**
* 普通权限,管理中心申请
*/
IM_SHARE("im.share", "分享给抖音好友", false),
RENEW_REFRESH_TOKEN("renew_refresh_token", "授权有效期动态续期", false),
FOLLOWING_LIST("following.list", "获取该用户的关注列表", false),
FANS_LIST("fans.list", "获取该用户的粉丝列表", false),
VIDEO_CREATE("video.create", "视频发布及管理", false),
VIDEO_DELETE("video.delete", "删除内容", false),
VIDEO_DATA("video.data", "查询授权用户的抖音视频数据", false),
VIDEO_LIST("video.list", "查询特定抖音视频的视频数据", false),
/**
* 特殊权限 默认关闭 管理中心申请
*/
SHARE_WITH_SOURCE("share_with_source", "分享携带来源标签,用户可点击标签进入转化页", false),
MOBILE("mobile", "用抖音帐号登录第三方平台,获得用户在抖音上的手机号码", false),
MOBILE_ALERT("mobile_alert", "用抖音帐号登录第三方平台,获得用户在抖音上的手机号码", false),
VIDEO_SEARCH("video.search", "关键词视频管理", false),
POI_SEARCH("poi.search", "查询POI信息", false),
LOGIN_ID("login_id", "静默授权直接获取该用户的open id", false),
/**
* 抖音数据权限, 默认关闭, 管理中心申请
*/
DATA_EXTERNAL_USER("data.external.user", "查询用户的获赞、评论、分享,主页访问等相关数据", false),
DATA_EXTERNAL_ITEM("data.external.item", "查询作品的获赞,评论,分享等相关数据", false),
FANS_DATA("fans.data", "获取用户粉丝画像数据", false),
HOTSEARCH("hotsearch", "获取抖音热门内容", false),
STAR_TOP_SCORE_DISPLAY("star_top_score_display", "星图达人与达人对应各指数评估分,以及星图6大热门维度下的达人榜单", false),
STAR_TOPS("star_tops", "星图达人与达人对应各指数评估分,以及星图6大热门维度下的达人榜单", false),
STAR_AUTHOR_SCORE_DISPLAY("star_author_score_display", "星图达人与达人对应各指数评估分,以及星图6大热门维度下的达人榜单", false),
notes("data.external.sdk_share", "获取用户通过分享SDK分享视频数据", false),
/**
* 定向开通 默认关闭 定向开通
*/
DISCOVERY_ENT("discovery.ent", "查询抖音电影榜、抖音剧集榜、抖音综艺榜数据", false),
;
private final String scope;
private final String description;
private final boolean isDefault;
}
@@ -17,6 +17,7 @@ public enum AuthFacebookScope implements AuthScope {
/**
* {@code scope} 含义,以{@code description} 为准
*/
PUBLIC_PROFILE("public_profile", "权限允许应用读取用户默认的公开资料", true),
EMAIL("email", "获取用户的邮箱", false),
USER_AGE_RANGE("user_age_range", "允许应用程序访问用户的年龄范围", false),
USER_BIRTHDAY("user_birthday", "获取用户的生日", false),
@@ -24,7 +25,7 @@ public enum AuthFacebookScope implements AuthScope {
USER_GENDER("user_gender", "获取用户的性别", false),
USER_HOMETOWN("user_hometown", "获取用户的家乡信息", false),
USER_LIKES("user_likes", "获取用户的喜欢列表", false),
USER_LINK("user_link", "获取用户的个人链接", false),
USER_LINK("user_link", "获取用户的个人链接", true),
USER_LOCATION("user_location", "获取用户的位置信息", false),
USER_PHOTOS("user_photos", "获取用户的相册信息", false),
USER_POSTS("user_posts", "获取用户发布的内容", false),
@@ -33,8 +34,8 @@ public enum AuthFacebookScope implements AuthScope {
PUBLISH_TO_GROUPS("publish_to_groups", "授权您的应用程序代表某人将内容发布到组中,前提是他们已经授予您的应用程序访问权限", false),
;
private String scope;
private String description;
private boolean isDefault;
private final String scope;
private final String description;
private final boolean isDefault;
}
@@ -0,0 +1,29 @@
package me.zhyd.oauth.enums.scope;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* Figma OAuth 授权范围
* <a href="https://www.figma.com/developers/api#authentication-scopes">...</a>
*
* @author xiangqian
* @since 1.16.6
*/
@Getter
@AllArgsConstructor
public enum AuthFigmaScope implements AuthScope {
FILE_CONTENT("files:read", "Read files, projects, users, versions, comments, components & styles, and webhooks", true),
VARIABLES("file_variables:read,file_variables:write", "Read and write to variables in Figma file. Note: this is only available to members in Enterprise organizations", false),
COMMENTS("file_comments:write", "Post and delete comments and comment reactions in files", false),
DEV_RESOURCES("file_dev_resources:read,file_dev_resources:write", "Read and write to dev resources in files", false),
LIBRARY_ANALYTICS("library_analytics:read", "Read your design system analytics", false),
WEBHOOKS("webhooks:write", "Create and manage webhooks", false);
private final String scope;
private final String description;
private final boolean isDefault;
}
@@ -29,8 +29,8 @@ public enum AuthGiteeScope implements AuthScope {
ENTERPRISES("enterprises", "查看、管理用户的企业以及成员", false),
EMAILS("emails", "查看用户的个人邮箱信息", false);
private String scope;
private String description;
private boolean isDefault;
private final String scope;
private final String description;
private final boolean isDefault;
}
@@ -4,7 +4,7 @@ import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 边度平台 OAuth 授权范围
* Github平台 OAuth 授权范围
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
@@ -47,8 +47,8 @@ public enum AuthGithubScope implements AuthScope {
READ_GPG_KEY("read:gpg_key", "List and view details for GPG keys.", false),
WORKFLOW("workflow", "Grants the ability to add and update GitHub Actions workflow files. Workflow files can be committed without this scope if the same file (with both the same path and contents) exists on another branch in the same repository.", false),
;
private String scope;
private String description;
private boolean isDefault;
private final String scope;
private final String description;
private final boolean isDefault;
}
@@ -30,8 +30,8 @@ public enum AuthGitlabScope implements AuthScope {
API("api", "Grants complete read/write access to the API, including all groups and projects, the container registry, and the package registry.", false),
;
private String scope;
private String description;
private boolean isDefault;
private final String scope;
private final String description;
private final boolean isDefault;
}
@@ -281,9 +281,9 @@ public enum AuthGoogleScope implements AuthScope {
ADEXCHANGE_BUYER("https://www.googleapis.com/auth/adexchange.buyer", "Manage your Ad Exchange buyer account configuration", false),
;
private String scope;
private String description;
private boolean isDefault;
private final String scope;
private final String description;
private final boolean isDefault;
public static List<String> getAdminDirectoryScopes() {
@@ -6,12 +6,15 @@ import lombok.Getter;
/**
* 华为平台 OAuth 授权范围
*
* 当前方式未来可能被废弃,建议使用 {@link AuthHuaweiV3Scope}
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.0
*/
@Getter
@AllArgsConstructor
@Deprecated
public enum AuthHuaweiScope implements AuthScope {
/**
@@ -39,8 +42,8 @@ public enum AuthHuaweiScope implements AuthScope {
;
private String scope;
private String description;
private boolean isDefault;
private final String scope;
private final String description;
private final boolean isDefault;
}
@@ -0,0 +1,51 @@
package me.zhyd.oauth.enums.scope;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 华为平台 V3 版本 OAuth 授权范围
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.16.7
*/
@Getter
@AllArgsConstructor
public enum AuthHuaweiV3Scope implements AuthScope {
/**
* {@code scope} 含义,以{@code description} 为准
*/
OPENID("openid", "基础scopev3必选", true),
/**
* {@code scope} 含义,以{@code description} 为准
*/
BASE_PROFILE("https://www.huawei.com/auth/account/base.profile", "获取用户的基本信息", true),
MOBILE_NUMBER("https://www.huawei.com/auth/account/mobile.number", "获取用户的手机号", false),
ACCOUNTLIST("https://www.huawei.com/auth/account/accountlist", "获取用户的账单列表", false),
/**
* 以下两个 scope 不需要经过华为评估和验证
*/
SCOPE_DRIVE_FILE("https://www.huawei.com/auth/drive.file", "只允许访问由应用程序创建或打开的文件", false),
SCOPE_DRIVE_APPDATA("https://www.huawei.com/auth/drive.appdata", "只允许访问由应用程序创建或打开的文件", false),
/**
* 以下四个 scope 使用前需要向drivekit@huawei.com提交申请
* <p>
* 参考:https://developer.huawei.com/consumer/cn/doc/development/HMSCore-Guides-V5/server-dev-0000001050039664-V5#ZH-CN_TOPIC_0000001050039664__section1618418855716
*/
SCOPE_DRIVE("https://www.huawei.com/auth/drive", "只允许访问由应用程序创建或打开的文件", false),
SCOPE_DRIVE_READONLY("https://www.huawei.com/auth/drive.readonly", "只允许访问由应用程序创建或打开的文件", false),
SCOPE_DRIVE_METADATA("https://www.huawei.com/auth/drive.metadata", "只允许访问由应用程序创建或打开的文件", false),
SCOPE_DRIVE_METADATA_READONLY("https://www.huawei.com/auth/drive.metadata.readonly", "只允许访问由应用程序创建或打开的文件", false),
;
;
private final String scope;
private final String description;
private final boolean isDefault;
}
@@ -19,8 +19,8 @@ public enum AuthJdScope implements AuthScope {
*/
SNSAPI_BASE("snsapi_base", "基础授权", true);
private String scope;
private String description;
private boolean isDefault;
private final String scope;
private final String description;
private final boolean isDefault;
}
@@ -21,8 +21,8 @@ public enum AuthKujialeScope implements AuthScope {
GET_DESIGN("get_design", "获取指定方案详情", false),
GET_BUDGET_LIST("get_budget_list", "获取清单预算概览数据", false);
private String scope;
private String description;
private boolean isDefault;
private final String scope;
private final String description;
private final boolean isDefault;
}
@@ -0,0 +1,28 @@
package me.zhyd.oauth.enums.scope;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* Line 平台 OAuth 授权范围
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @since 1.16.0
*/
@Getter
@AllArgsConstructor
public enum AuthLineScope implements AuthScope {
/**
* {@code scope} 含义,以{@code description} 为准
*/
PROFILE("profile", "Get profile details", true),
OPENID("openid", "Get id token", true),
EMAIL("email", "Get email (separate authorization required)", false);
private final String scope;
private final String description;
private final boolean isDefault;
}
@@ -36,8 +36,8 @@ public enum AuthLinkedinScope implements AuthScope {
W_ORGANIZATION_SOCIAL("w_organization_social", "Post, comment and like posts on your organization's behalf", false),
W_SHARE("w_share", "Post updates to LinkedIn as you", false);
private String scope;
private String description;
private boolean isDefault;
private final String scope;
private final String description;
private final boolean isDefault;
}
@@ -21,8 +21,8 @@ public enum AuthMiScope implements AuthScope {
OPENID("user/openIdV2", "获取用户的OpenID", true),
PHONE_EMAIL("user/phoneAndEmail", "获取用户的手机号和邮箱", true);
private String scope;
private String description;
private boolean isDefault;
private final String scope;
private final String description;
private final boolean isDefault;
}
@@ -60,8 +60,8 @@ public enum AuthMicrosoftScope implements AuthScope {
NOTES_READWRITE_ALL("Notes.ReadWrite.All", "允许应用读取、共享和修改已登录用户在组织中有权访问的 OneNote 笔记本", false),
;
private String scope;
private String description;
private boolean isDefault;
private final String scope;
private final String description;
private final boolean isDefault;
}
@@ -0,0 +1,66 @@
package me.zhyd.oauth.enums.scope;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* Okta 平台 OAuth 授权范围
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @since 1.16.0
*/
@Getter
@AllArgsConstructor
public enum AuthOktaScope implements AuthScope {
/**
* {@code scope} 含义,以{@code description} 为准
*/
OPENID("openid", "Signals that a request is an OpenID request.", true),
PROFILE("profile", "The exact data varies based on what profile information you have provided, such as: name, time zone, picture, or birthday.", true),
EMAIL("email", "This allows the app to view your email address.", true),
ADDRESS("address", "This allows the app to view your address, such as: street address, city, state, and zip code.", true),
PHONE("phone", "This allows the app to view your phone number.", true),
OFFLINE_ACCESS("offline_access", "This keeps you signed in to the app, even when you are not using it.", true),
OKTA_USERS_MANAGE("okta.users.manage", "Allows the app to create and manage users and read all profile and credential information for users", false),
OKTA_USERS_READ("okta.users.read", "Allows the app to read any user's profile and credential information", false),
OKTA_USERS_MANAGE_SELF("okta.users.manage.self", "Allows the app to manage the currently signed-in user's profile. Currently only supports user profile attribute updates.", false),
OKTA_USERS_READ_SELF("okta.users.read.self", "Allows the app to read the currently signed-in user's profile and credential information", false),
OKTA_APPS_MANAGE("okta.apps.manage", "Allows the app to create and manage Apps in your Okta organization", false),
OKTA_APPS_READ("okta.apps.read", "Allows the app to read information about Apps in your Okta organization", false),
OKTA_AUTHORIZATIONSERVERS_MANAGE("okta.authorizationServers.manage", "Allows the app to manage authorization servers", false),
OKTA_AUTHORIZATIONSERVERS_READ("okta.authorizationServers.read", "Allows the app to read authorization server information", false),
OKTA_CLIENTS_MANAGE("okta.clients.manage", "Allows the app to manage all OAuth/OIDC clients and to create new clients", false),
OKTA_CLIENTS_READ("okta.clients.read", "Allows the app to read information for all OAuth/OIDC clients", false),
OKTA_CLIENTS_REGISTER("okta.clients.register", "Allows the app to register (create) new OAuth/OIDC clients (but not read information about existing clients)", false),
OKTA_EVENTHOOKS_MANAGE("okta.eventHooks.manage", "Allows the app to create and manage Event Hooks in your Okta organization", false),
OKTA_EVENTHOOKS_READ("okta.eventHooks.read", "Allows the app to read information about Event Hooks in your Okta organization", false),
OKTA_FACTORS_MANAGE("okta.factors.manage", "Allows the app to manage all admin operations for org factors (for example, activate, deactive, read)", false),
OKTA_FACTORS_READ("okta.factors.read", "Allows the app to read org factors information", false),
OKTA_GROUPS_MANAGE("okta.groups.manage", "Allows the app to manage groups in your Okta organization", false),
OKTA_GROUPS_READ("okta.groups.read", "Allows the app to read information about groups and their members in your Okta organization", false),
OKTA_IDPS_MANAGE("okta.idps.manage", "Allows the app to create and manage Identity Providers in your Okta organization", false),
OKTA_IDPS_READ("okta.idps.read", "Allows the app to read information about Identity Providers in your Okta organization", false),
OKTA_INLINEHOOKS_MANAGE("okta.inlineHooks.manage", "Allows the app to create and manage Inline Hooks in your Okta organization.", false),
OKTA_INLINEHOOKS_READ("okta.inlineHooks.read", "Allows the app to read information about Inline Hooks in your Okta organization.", false),
OKTA_LINKEDOBJECTS_MANAGE("okta.linkedObjects.manage", "Allows the app to manage Linked Object definitions in your Okta organization.", false),
OKTA_LINKEDOBJECTS_READ("okta.linkedObjects.read", "Allows the app to read Linked Object definitions in your Okta organization.", false),
OKTA_LOGS_READ("okta.logs.read", "Allows the app to read information about System Log entries in your Okta organization", false),
OKTA_ROLES_MANAGE("okta.roles.manage", "Allows the app to create and manage Administrator Roles in your Okta organization", false),
OKTA_ROLES_READ("okta.roles.read", "Allows the app to read information about Administrator Roles in your Okta organization", false),
OKTA_SCHEMAS_MANAGE("okta.schemas.manage", "Allows the app to create and manage Schemas in your Okta organization", false),
OKTA_SCHEMAS_READ("okta.schemas.read", "Allows the app to read information about Schemas in your Okta organization", false),
OKTA_SESSIONS_MANAGE("okta.sessions.manage", "Allows the app to manage all sessions in your Okta organization", false),
OKTA_SESSIONS_READ("okta.sessions.read", "Allows the app to read all sessions in your Okta organization", false),
OKTA_TEMPLATES_MANAGE("okta.templates.manage", "Allows the app to manage all custom templates in your Okta organization", false),
OKTA_TEMPLATES_READ("okta.templates.read", "Allows the app to read all custom templates in your Okta organization", false),
OKTA_TRUSTEDORIGINS_MANAGE("okta.trustedOrigins.manage", "Allows the app to manage all Trusted Origins in your Okta organization", false),
OKTA_TRUSTEDORIGINS_READ("okta.trustedOrigins.read", "Allows the app to read all Trusted Origins in your Okta organization", false),
OKTA_POLICIES_MANAGE("okta.policies.manage", "Allows the app to manage Policies in your Okta organization", false),
OKTA_POLICIES_READ("okta.policies.read", "Allows the app to read information about Policies in your Okta organization", false),;
private final String scope;
private final String description;
private final boolean isDefault;
}
@@ -23,8 +23,8 @@ public enum AuthPinterestScope implements AuthScope {
WRITE_RELATIONSHIPS("write_relationships", "Use PATCH, POST and DELETE methods on a users follows and followers (on boards, users and interests).", false),
;
private String scope;
private String description;
private boolean isDefault;
private final String scope;
private final String description;
private final boolean isDefault;
}
@@ -0,0 +1,33 @@
package me.zhyd.oauth.enums.scope;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* Gitee 平台 OAuth 授权范围
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.0
*/
@Getter
@AllArgsConstructor
public enum AuthProginnScope implements AuthScope {
/**
* {@code scope} 含义,以{@code description} 为准
*/
BASIC("basic", "访问用户的基本信息", true),
/**
* 以上 scope 需要单独向程序员客栈平台申请,否则不可使用
*/
email("email", "获取用户的邮箱", false),
realname("realname", "获取用户的真实姓名", false),
cellphone("cellphone", "获取用户的手机号码", false),
;
private final String scope;
private final String description;
private final boolean isDefault;
}
@@ -28,8 +28,8 @@ public enum AuthQqScope implements AuthScope {
ADD_ALBUM("add_album", "在用户的空间相册里,创建一个新的个人相册", false),
LIST_PHOTO("list_photo", "获取用户QQ空间相册中的照片列表", false);
private String scope;
private String description;
private boolean isDefault;
private final String scope;
private final String description;
private final boolean isDefault;
}
@@ -48,8 +48,8 @@ public enum AuthRenrenScope implements AuthScope {
ADMIN_PAGE("admin_page", "以用户的身份,管理其可以管理的公共主页的权限。", false),
;
private String scope;
private String description;
private boolean isDefault;
private final String scope;
private final String description;
private final boolean isDefault;
}
@@ -0,0 +1,116 @@
package me.zhyd.oauth.enums.scope;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* Slack 平台 OAuth 授权范围
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @since 1.16.0
*/
@Getter
@AllArgsConstructor
public enum AuthSlackScope implements AuthScope {
/**
* {@code scope} 含义,以{@code description} 为准
*/
USERS_PROFILE_READ("users.profile:read", "View profile details about people in a workspace", true),
USERS_READ("users:read", "View people in a workspace", true),
USERS_READ_EMAIL("users:read.email", "View email addresses of people in a workspace", true),
USERS_PROFILE_WRITE("users.profile:write", "Edit a users profile information and status", false),
USERS_PROFILE_WRITE_USER("users.profile:write:user", "Change the user's profile fields", false),
USERS_WRITE("users:write", "Set presence for your slack app", false),
ADMIN("admin", "Administer a workspace", false),
ADMIN_ANALYTICS_READ("admin.analytics:read", "Access analytics data about the organization", false),
ADMIN_APPS_READ("admin.apps:read", "View apps and app requests in a workspace", false),
ADMIN_APPS_WRITE("admin.apps:write", "Manage apps in a workspace", false),
ADMIN_BARRIERS_READ("admin.barriers:read", "Read information barriers in the organization", false),
ADMIN_BARRIERS_WRITE("admin.barriers:write", "Manage information barriers in the organization", false),
ADMIN_CONVERSATIONS_READ("admin.conversations:read", "View the channels member list, topic, purpose and channel name", false),
ADMIN_CONVERSATIONS_WRITE("admin.conversations:write", "Start a new conversation, modify a conversation and modify channel details", false),
ADMIN_INVITES_READ("admin.invites:read", "Gain information about invite requests in a Grid organization.", false),
ADMIN_INVITES_WRITE("admin.invites:write", "Approve or deny invite requests in a Grid organization.", false),
ADMIN_TEAMS_READ("admin.teams:read", "Access information about a workspace", false),
ADMIN_TEAMS_WRITE("admin.teams:write", "Make changes to a workspace", false),
ADMIN_USERGROUPS_READ("admin.usergroups:read", "Access information about user groups", false),
ADMIN_USERGROUPS_WRITE("admin.usergroups:write", "Make changes to your usergroups", false),
ADMIN_USERS_READ("admin.users:read", "Access a workspaces profile information", false),
ADMIN_USERS_WRITE("admin.users:write", "Modify account information", false),
APP_MENTIONS_READ("app_mentions:read", "View messages that directly mention @your_slack_app in conversations that the app is in", false),
AUDITLOGS_READ("auditlogs:read", "View events from all workspaces, channels and users (Enterprise Grid only)", false),
BOT("bot", "Add the ability for people to direct message or mention @your_slack_app", false),
CALLS_READ("calls:read", "View information about ongoing and past calls", false),
CALLS_WRITE("calls:write", "Start and manage calls in a workspace", false),
CHANNELS_HISTORY("channels:history", "View messages and other content in public channels that your slack app has been added to", false),
CHANNELS_JOIN("channels:join", "Join public channels in a workspace", false),
CHANNELS_MANAGE("channels:manage", "Manage public channels that your slack app has been added to and create new ones", false),
CHANNELS_READ("channels:read", "View basic information about public channels in a workspace", false),
CHANNELS_WRITE("channels:write", "Manage a users public channels and create new ones on a users behalf", false),
CHAT_WRITE("chat:write", "Post messages in approved channels & conversations", false),
CHAT_WRITE_CUSTOMIZE("chat:write.customize", "Send messages as @your_slack_app with a customized username and avatar", false),
CHAT_WRITE_PUBLIC("chat:write.public", "Send messages to channels @your_slack_app isn't a member of", false),
CHAT_WRITE_BOT("chat:write:bot", "Send messages as your slack app", false),
CHAT_WRITE_USER("chat:write:user", "Send messages on a users behalf", false),
CLIENT("client", "Receive all events from a workspace in real time", false),
COMMANDS("commands", "Add shortcuts and/or slash commands that people can use", false),
CONVERSATIONS_HISTORY("conversations:history", "Deprecated: Retrieve conversation history for legacy workspace apps", false),
CONVERSATIONS_READ("conversations:read", "Deprecated: Retrieve information on conversations for legacy workspace apps", false),
CONVERSATIONS_WRITE("conversations:write", "Deprecated: Edit conversation attributes for legacy workspace apps", false),
DND_READ("dnd:read", "View Do Not Disturb settings for people in a workspace", false),
DND_WRITE("dnd:write", "Edit a users Do Not Disturb settings", false),
DND_WRITE_USER("dnd:write:user", "Change the user's Do Not Disturb settings", false),
EMOJI_READ("emoji:read", "View custom emoji in a workspace", false),
FILES_READ("files:read", "View files shared in channels and conversations that your slack app has been added to", false),
FILES_WRITE("files:write", "Upload, edit, and delete files as your slack app", false),
FILES_WRITE_USER("files:write:user", "Upload, edit, and delete files as your slack app", false),
GROUPS_HISTORY("groups:history", "View messages and other content in private channels that your slack app has been added to", false),
GROUPS_READ("groups:read", "View basic information about private channels that your slack app has been added to", false),
GROUPS_WRITE("groups:write", "Manage private channels that your slack app has been added to and create new ones", false),
IDENTIFY("identify", "View information about a users identity", false),
IDENTITY_AVATAR("identity.avatar", "View a users Slack avatar", false),
IDENTITY_AVATAR_READ_USER("identity.avatar:read:user", "View the user's profile picture", false),
IDENTITY_BASIC("identity.basic", "View information about a users identity", false),
IDENTITY_EMAIL("identity.email", "View a users email address", false),
IDENTITY_EMAIL_READ_USER("identity.email:read:user", "This scope is not yet described.", false),
IDENTITY_TEAM("identity.team", "View a users Slack workspace name", false),
IDENTITY_TEAM_READ_USER("identity.team:read:user", "View the workspace's name, domain, and icon", false),
IDENTITY_READ_USER("identity:read:user", "This scope is not yet described.", false),
IM_HISTORY("im:history", "View messages and other content in direct messages that your slack app has been added to", false),
IM_READ("im:read", "View basic information about direct messages that your slack app has been added to", false),
IM_WRITE("im:write", "Start direct messages with people", false),
INCOMING_WEBHOOK("incoming-webhook", "Create one-way webhooks to post messages to a specific channel", false),
LINKS_READ("links:read", "View URLs in messages", false),
LINKS_WRITE("links:write", "Show previews of URLs in messages", false),
MPIM_HISTORY("mpim:history", "View messages and other content in group direct messages that your slack app has been added to", false),
MPIM_READ("mpim:read", "View basic information about group direct messages that your slack app has been added to", false),
MPIM_WRITE("mpim:write", "Start group direct messages with people", false),
NONE("none", "Execute methods without needing a scope", false),
PINS_READ("pins:read", "View pinned content in channels and conversations that your slack app has been added to", false),
PINS_WRITE("pins:write", "Add and remove pinned messages and files", false),
POST("post", "Post messages to a workspace", false),
REACTIONS_READ("reactions:read", "View emoji reactions and their associated content in channels and conversations that your slack app has been added to", false),
REACTIONS_WRITE("reactions:write", "Add and edit emoji reactions", false),
READ("read", "View all content in a workspace", false),
REMINDERS_READ("reminders:read", "View reminders created by your slack app", false),
REMINDERS_READ_USER("reminders:read:user", "Access reminders created by a user or for a user", false),
REMINDERS_WRITE("reminders:write", "Add, remove, or mark reminders as complete", false),
REMINDERS_WRITE_USER("reminders:write:user", "Add, remove, or complete reminders for the user", false),
REMOTE_FILES_READ("remote_files:read", "View remote files added by the app in a workspace", false),
REMOTE_FILES_SHARE("remote_files:share", "Share remote files on a users behalf", false),
REMOTE_FILES_WRITE("remote_files:write", "Add, edit, and delete remote files on a users behalf", false),
SEARCH_READ("search:read", "Search a workspaces content", false),
STARS_READ("stars:read", "View messages and files that your slack app has starred", false),
STARS_WRITE("stars:write", "Add or remove stars", false),
TEAM_READ("team:read", "View the name, email domain, and icon for workspaces your slack app is connected to", false),
TOKENS_BASIC("tokens.basic", "Execute methods without needing a scope", false),
USERGROUPS_READ("usergroups:read", "View user groups in a workspace", false),
USERGROUPS_WRITE("usergroups:write", "Create and manage user groups", false),
WORKFLOW_STEPS_EXECUTE("workflow.steps:execute", "Add steps that people can use in Workflow Builder", false);
private final String scope;
private final String description;
private final boolean isDefault;
}
@@ -22,8 +22,8 @@ public enum AuthStackoverflowScope implements AuthScope {
WRITE_ACCESS("write_access", "perform write operations as a user", false),
PRIVATE_INFO("private_info", "access full history of a user's private actions on the site", false);
private String scope;
private String description;
private boolean isDefault;
private final String scope;
private final String description;
private final boolean isDefault;
}
@@ -0,0 +1,24 @@
package me.zhyd.oauth.enums.scope;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 企业自建应用授权范围
*
* @author liguanhua (347826496(a)qq.com)
* @since 1.15.9
*/
@Getter
@AllArgsConstructor
public enum AuthWeChatEnterpriseWebScope implements AuthScope {
/**
* {@code scope} 含义,以{@code description} 为准
*/
SNSAPI_BASE("snsapi_base", "应用授权作用域。企业自建应用固定填写:snsapi_base", true);
private final String scope;
private final String description;
private final boolean isDefault;
}
@@ -19,8 +19,8 @@ public enum AuthWechatMpScope implements AuthScope {
SNSAPI_USERINFO("snsapi_userinfo", "弹出授权页面,可通过openid拿到昵称、性别、所在地。并且, 即使在未关注的情况下,只要用户授权,也能获取其信息", true),
SNSAPI_BASE("snsapi_base", "不弹出授权页面,直接跳转,只能获取用户openid", false);
private String scope;
private String description;
private boolean isDefault;
private final String scope;
private final String description;
private final boolean isDefault;
}
@@ -27,8 +27,8 @@ public enum AuthWeiboScope implements AuthScope {
STATUSES_TO_ME_READ("statuses_to_me_read", "定向微博读取接口组,<a rel=\"nofollow\" href=\"http://open.weibo.com/wiki/API%E6%96%87%E6%A1%A3_V2#.E5.BE.AE.E5.8D.9A\">接口文档</a>", false),
FOLLOW_APP_OFFICIAL_MICROBLOG("follow_app_official_microblog", "关注应用官方微博,该参数不对应具体接口,只需在应用控制台填写官方帐号即可。填写的路径:我的应用-选择自己的应用-应用信息-基本信息-官方运营账号(默认值是应用开发者帐号)", false);
private String scope;
private String description;
private boolean isDefault;
private final String scope;
private final String description;
private final boolean isDefault;
}
+3 -3
View File
@@ -136,15 +136,15 @@ public class Log {
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @since 1.10.0
*/
static class Config {
public static class Config {
/**
* 需要打印的日志级别
*/
static Level level = Level.DEBUG;
public static Level level = Level.DEBUG;
/**
* 是否启用日志打印功能,默认启用
*/
static boolean enable = true;
public static boolean enable = true;
}
}
@@ -1,4 +1,4 @@
/**
* 针对JustAuth简单封装的日志打印工具,可用过{@link me.zhyd.oauth.log.Log.Config}开关日志和指定日志级别
* 针对JustAuth简单封装的日志打印工具,可用过{@link me.zhyd.oauth.config.JustAuthLogConfig}开关日志和指定日志级别
*/
package me.zhyd.oauth.log;
@@ -5,6 +5,7 @@ import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import me.zhyd.oauth.utils.StringUtils;
import java.io.Serializable;
@@ -57,4 +58,19 @@ public class AuthCallback implements Serializable {
*/
private String oauth_verifier;
/**
* 苹果仅在用户首次授权应用程序时返回此值。如果您的应用程序已经获得了用户的授权,那么苹果将不会再次返回此值
* @see <a href="https://developer.apple.com/documentation/sign_in_with_apple/useri">user info</a>
*/
private String user;
/**
* 苹果错误信息,仅在用户取消授权时返回此值
* @see <a href="https://developer.apple.com/documentation/sign_in_with_apple/sign_in_with_apple_js/incorporating_sign_in_with_apple_into_other_platforms">error response</a>
*/
private String error;
public String getCode() {
return StringUtils.isEmpty(code) ? auth_code : code;
}
}
@@ -19,6 +19,7 @@ public class AuthToken implements Serializable {
private String accessToken;
private int expireIn;
private String refreshToken;
private int refreshTokenExpireIn;
private String uid;
private String openId;
private String accessCode;
@@ -43,6 +44,12 @@ public class AuthToken implements Serializable {
* @since 1.10.0
*/
private String code;
/**
* 微信公众号 - 网页授权的登录时可用
*
* 微信针对网页授权登录,增加了一个快照页的逻辑,快照页获取到的微信用户的 uid oid 和头像昵称都是虚拟的信息
*/
private boolean snapshotUser;
/**
* Twitter附带属性
@@ -55,4 +62,15 @@ public class AuthToken implements Serializable {
private String screenName;
private Boolean oauthCallbackConfirmed;
/**
* Apple附带属性
*/
private String username;
/**
* 新版钉钉附带属性
*
* @since 1.16.7
*/
private String corpId;
}
@@ -72,4 +72,11 @@ public class AuthUser implements Serializable {
*/
private JSONObject rawUserInfo;
/**
* 微信公众号 - 网页授权的登录时可用
*
* 微信针对网页授权登录,增加了一个快照页的逻辑,快照页获取到的微信用户的 uid oid 和头像昵称都是虚拟的信息
*/
private boolean snapshotUser;
}
@@ -0,0 +1,104 @@
package me.zhyd.oauth.request;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import me.zhyd.oauth.cache.AuthStateCache;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.config.AuthSource;
import me.zhyd.oauth.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.GlobalAuthUtils;
import me.zhyd.oauth.utils.HttpUtils;
import me.zhyd.oauth.utils.UrlBuilder;
/**
* <p>
* 钉钉登录抽象类,负责处理使用钉钉账号登录第三方网站和扫码登录第三方网站两种钉钉的登录方式
* </p>
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @since 1.16.0
*/
public abstract class AbstractAuthDingtalkRequest extends AuthDefaultRequest {
public AbstractAuthDingtalkRequest(AuthConfig config, AuthSource source) {
super(config, source);
}
public AbstractAuthDingtalkRequest(AuthConfig config, AuthSource source, AuthStateCache authStateCache) {
super(config, source, authStateCache);
}
@Override
public AuthToken getAccessToken(AuthCallback authCallback) {
return AuthToken.builder().accessCode(authCallback.getCode()).build();
}
@Override
public AuthUser getUserInfo(AuthToken authToken) {
String code = authToken.getAccessCode();
JSONObject param = new JSONObject();
param.put("tmp_auth_code", code);
String response = new HttpUtils(config.getHttpConfig()).post(userInfoUrl(authToken), param.toJSONString()).getBody();
JSONObject object = JSON.parseObject(response);
if (object.getIntValue("errcode") != 0) {
throw new AuthException(object.getString("errmsg"));
}
object = object.getJSONObject("user_info");
AuthToken token = AuthToken.builder()
.openId(object.getString("openid"))
.unionId(object.getString("unionid"))
.build();
return AuthUser.builder()
.rawUserInfo(object)
.uuid(object.getString("unionid"))
.nickname(object.getString("nick"))
.username(object.getString("nick"))
.gender(AuthUserGender.UNKNOWN)
.source(source.toString())
.token(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("appid", config.getClientId())
.queryParam("scope", "snsapi_login")
.queryParam("redirect_uri", config.getRedirectUri())
.queryParam("state", getRealState(state))
.build();
}
/**
* 返回获取userInfo的url
*
* @param authToken 用户授权后的token
* @return 返回获取userInfo的url
*/
@Override
protected String userInfoUrl(AuthToken authToken) {
// 根据timestamp, appSecret计算签名值
String timestamp = System.currentTimeMillis() + "";
String urlEncodeSignature = GlobalAuthUtils.generateDingTalkSignature(config.getClientSecret(), timestamp);
return UrlBuilder.fromBaseUrl(source.userInfo())
.queryParam("signature", urlEncodeSignature)
.queryParam("timestamp", timestamp)
.queryParam("accessKey", config.getClientId())
.build();
}
}
@@ -0,0 +1,191 @@
package me.zhyd.oauth.request;
import com.alibaba.fastjson.JSONObject;
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.enums.AuthResponseStatus;
import me.zhyd.oauth.enums.AuthUserGender;
import me.zhyd.oauth.enums.scope.AuthMicrosoftScope;
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.AuthScopeUtils;
import me.zhyd.oauth.utils.HttpUtils;
import me.zhyd.oauth.utils.StringUtils;
import me.zhyd.oauth.utils.UrlBuilder;
import java.util.Map;
/**
* 微软登录抽象类,负责处理使用微软国际和微软中国账号登录第三方网站的登录方式
*
* @author mroldx (xzfqq5201314@gmail.com)
* @since 1.16.4
*/
public abstract class AbstractAuthMicrosoftRequest extends AuthDefaultRequest {
public AbstractAuthMicrosoftRequest(AuthConfig config, AuthSource source) {
super(config, source);
}
public AbstractAuthMicrosoftRequest(AuthConfig config, AuthSource source, AuthStateCache authStateCache) {
super(config, source, authStateCache);
}
@Override
public AuthToken getAccessToken(AuthCallback authCallback) {
return getToken(accessTokenUrl(authCallback.getCode()));
}
/**
* 获取token,适用于获取access_token和刷新token
*
* @param accessTokenUrl 实际请求token的地址
* @return token对象
*/
private AuthToken getToken(String accessTokenUrl) {
HttpHeader httpHeader = new HttpHeader();
Map<String, String> form = MapUtil.parseStringToMap(accessTokenUrl, false);
String response = new HttpUtils(config.getHttpConfig()).post(accessTokenUrl, form, httpHeader, false).getBody();
JSONObject accessTokenObject = JSONObject.parseObject(response);
this.checkResponse(accessTokenObject);
return AuthToken.builder()
.accessToken(accessTokenObject.getString("access_token"))
.expireIn(accessTokenObject.getIntValue("expires_in"))
.scope(accessTokenObject.getString("scope"))
.tokenType(accessTokenObject.getString("token_type"))
.refreshToken(accessTokenObject.getString("refresh_token"))
.build();
}
/**
* 检查响应内容是否正确
*
* @param object 请求响应内容
*/
private void checkResponse(JSONObject object) {
if (object.containsKey("error")) {
throw new AuthException(object.getString("error_description"));
}
}
@Override
public AuthUser getUserInfo(AuthToken authToken) {
String token = authToken.getAccessToken();
String tokenType = authToken.getTokenType();
String jwt = tokenType + " " + token;
HttpHeader httpHeader = new HttpHeader();
httpHeader.add("Authorization", jwt);
String userInfo = new HttpUtils(config.getHttpConfig()).get(userInfoUrl(authToken), null, httpHeader, false).getBody();
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"))
.location(object.getString("officeLocation"))
.email(object.getString("mail"))
.gender(AuthUserGender.UNKNOWN)
.token(authToken)
.source(source.toString())
.build();
}
/**
* 刷新access token (续期)
*
* @param authToken 登录成功后返回的Token信息
* @return AuthResponse
*/
@Override
public AuthResponse<AuthToken> refresh(AuthToken authToken) {
return AuthResponse.<AuthToken>builder()
.code(AuthResponseStatus.SUCCESS.getCode())
.data(getToken(refreshTokenUrl(authToken.getRefreshToken())))
.build();
}
/**
* 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state}
*
* @param state state 验证授权流程的参数,可以防止csrf
* @return 返回授权地址
* @since 1.9.3
*/
@Override
public String authorize(String state) {
// 兼容 Microsoft Entra ID 登录(原微软 AAD
// @since 1.16.6
String tenantId = StringUtils.isEmpty(config.getTenantId()) ? "common" : config.getTenantId();
return UrlBuilder.fromBaseUrl(String.format(source.authorize(), tenantId))
.queryParam("response_type", "code")
.queryParam("client_id", config.getClientId())
.queryParam("redirect_uri", config.getRedirectUri())
.queryParam("state", getRealState(state))
.queryParam("response_mode", "query")
.queryParam("scope", this.getScopes(" ", false, AuthScopeUtils.getDefaultScopes(AuthMicrosoftScope.values())))
.build();
}
/**
* 返回获取accessToken的url
*
* @param code 授权code
* @return 返回获取accessToken的url
*/
@Override
protected String accessTokenUrl(String code) {
String tenantId = StringUtils.isEmpty(config.getTenantId()) ? "common" : config.getTenantId();
return UrlBuilder.fromBaseUrl(String.format(source.accessToken(), tenantId))
.queryParam("code", code)
.queryParam("client_id", config.getClientId())
.queryParam("client_secret", config.getClientSecret())
.queryParam("grant_type", "authorization_code")
.queryParam("scope", this.getScopes(" ", false, AuthScopeUtils.getDefaultScopes(AuthMicrosoftScope.values())))
.queryParam("redirect_uri", config.getRedirectUri())
.build();
}
/**
* 返回获取userInfo的url
*
* @param authToken 用户授权后的token
* @return 返回获取userInfo的url
*/
@Override
protected String userInfoUrl(AuthToken authToken) {
return UrlBuilder.fromBaseUrl(source.userInfo()).build();
}
/**
* 返回获取accessToken的url
*
* @param refreshToken 用户授权后的token
* @return 返回获取accessToken的url
*/
@Override
protected String refreshTokenUrl(String refreshToken) {
String tenantId = StringUtils.isEmpty(config.getTenantId()) ? "common" : config.getTenantId();
return UrlBuilder.fromBaseUrl(String.format(source.refresh(), tenantId))
.queryParam("client_id", config.getClientId())
.queryParam("client_secret", config.getClientSecret())
.queryParam("refresh_token", refreshToken)
.queryParam("grant_type", "refresh_token")
.queryParam("scope", this.getScopes(" ", false, AuthScopeUtils.getDefaultScopes(AuthMicrosoftScope.values())))
.queryParam("redirect_uri", config.getRedirectUri())
.build();
}
}
@@ -1,44 +1,41 @@
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.config.AuthSource;
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.HttpUtils;
import me.zhyd.oauth.utils.StringUtils;
import me.zhyd.oauth.utils.UrlBuilder;
/**
* <p>
* 企业微信登录
* 企业微信登录父类
* </p>
*
* @author yangkai.shen (https://xkcoding.com)
* @since 1.10.0
* @author liguanhua (347826496(a)qq.com)
* @since 1.15.9
*/
public class AuthWeChatEnterpriseRequest extends AuthDefaultRequest {
public AuthWeChatEnterpriseRequest(AuthConfig config) {
super(config, AuthDefaultSource.WECHAT_ENTERPRISE);
public abstract class AbstractAuthWeChatEnterpriseRequest extends AuthDefaultRequest {
public AbstractAuthWeChatEnterpriseRequest(AuthConfig config, AuthSource source) {
super(config,source);
}
public AuthWeChatEnterpriseRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.WECHAT_ENTERPRISE, authStateCache);
public AbstractAuthWeChatEnterpriseRequest(AuthConfig config, AuthSource source, AuthStateCache authStateCache) {
super(config, source, authStateCache);
}
/**
* 微信的特殊性此时返回的信息同时包含 openid access_token
*
* @param authCallback 回调返回的参数
* @return 所有信息
*/
@Override
protected AuthToken getAccessToken(AuthCallback authCallback) {
String response = doGetAuthorizationCode(accessTokenUrl(authCallback.getCode()));
public AuthToken getAccessToken(AuthCallback authCallback) {
String response = doGetAuthorizationCode(accessTokenUrl(null));
JSONObject object = this.checkResponse(response);
@@ -50,7 +47,7 @@ public class AuthWeChatEnterpriseRequest extends AuthDefaultRequest {
}
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
public AuthUser getUserInfo(AuthToken authToken) {
String response = doGetUserInfo(authToken);
JSONObject object = this.checkResponse(response);
@@ -59,8 +56,8 @@ public class AuthWeChatEnterpriseRequest extends AuthDefaultRequest {
throw new AuthException(AuthResponseStatus.UNIDENTIFIED_PLATFORM, source);
}
String userId = object.getString("UserId");
String userDetailResponse = getUserDetail(authToken.getAccessToken(), userId);
JSONObject userDetail = this.checkResponse(userDetailResponse);
String userTicket = object.getString("user_ticket");
JSONObject userDetail = getUserDetail(authToken.getAccessToken(), userId, userTicket);
return AuthUser.builder()
.rawUserInfo(userDetail)
@@ -92,22 +89,6 @@ public class AuthWeChatEnterpriseRequest extends AuthDefaultRequest {
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
@@ -142,14 +123,31 @@ public class AuthWeChatEnterpriseRequest extends AuthDefaultRequest {
*
* @param accessToken accessToken
* @param userId 企业内用户id
* @param userTicket 成员票据用于获取用户信息或敏感信息
* @return 用户详情
*/
private String getUserDetail(String accessToken, String userId) {
String userDetailUrl = UrlBuilder.fromBaseUrl("https://qyapi.weixin.qq.com/cgi-bin/user/get")
private JSONObject getUserDetail(String accessToken, String userId, String userTicket) {
// 用户基础信息
String userInfoUrl = 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);
String userInfoResponse = new HttpUtils(config.getHttpConfig()).get(userInfoUrl).getBody();
JSONObject userInfo = checkResponse(userInfoResponse);
// 用户敏感信息
if (StringUtils.isNotEmpty(userTicket)) {
String userDetailUrl = UrlBuilder.fromBaseUrl("https://qyapi.weixin.qq.com/cgi-bin/auth/getuserdetail")
.queryParam("access_token", accessToken)
.build();
JSONObject param = new JSONObject();
param.put("user_ticket", userTicket);
String userDetailResponse = new HttpUtils(config.getHttpConfig()).post(userDetailUrl, param.toJSONString()).getBody();
JSONObject userDetail = checkResponse(userDetailResponse);
userInfo.putAll(userDetail);
}
return userInfo;
}
}
@@ -0,0 +1,73 @@
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.HttpUtils;
import me.zhyd.oauth.utils.UrlBuilder;
import java.util.HashMap;
import java.util.Map;
/**
* 爱发电
*
* @author handy
*/
public class AuthAfDianRequest extends AuthDefaultRequest {
public AuthAfDianRequest(AuthConfig config) {
super(config, AuthDefaultSource.AFDIAN);
}
public AuthAfDianRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.AFDIAN, authStateCache);
}
@Override
public AuthToken getAccessToken(AuthCallback authCallback) {
Map<String, String> params = new HashMap<>();
params.put("grant_type", "authorization_code");
params.put("client_id", config.getClientId());
params.put("client_secret", config.getClientSecret());
params.put("code", authCallback.getCode());
params.put("redirect_uri", config.getRedirectUri());
String response = new HttpUtils(config.getHttpConfig()).post(AuthDefaultSource.AFDIAN.accessToken(), params, false).getBody();
JSONObject accessTokenObject = JSONObject.parseObject(response);
String userId = accessTokenObject.getJSONObject("data").getString("user_id");
return AuthToken.builder().userId(userId).build();
}
@Override
public AuthUser getUserInfo(AuthToken authToken) {
return AuthUser.builder()
.uuid(authToken.getUserId())
.gender(AuthUserGender.UNKNOWN)
.token(authToken)
.source(source.toString())
.build();
}
/**
* 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state}
*
* @param state state 验证授权流程的参数,可以防止csrf
* @return 返回授权地址
*/
@Override
public String authorize(String state) {
return UrlBuilder.fromBaseUrl(source.authorize())
.queryParam("response_type", "code")
.queryParam("scope", "basic")
.queryParam("client_id", config.getClientId())
.queryParam("redirect_uri", config.getRedirectUri())
.queryParam("state", getRealState(state))
.build();
}
}
@@ -0,0 +1,152 @@
package me.zhyd.oauth.request;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.AlipayConfig;
import com.alipay.api.DefaultAlipayClient;
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.config.AuthConfig;
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;
import me.zhyd.oauth.utils.UrlBuilder;
import static me.zhyd.oauth.config.AuthDefaultSource.ALIPAY;
/**
* 支付宝证书模式登录
*
* @since 1.16.7
*/
public class AuthAlipayCertRequest extends AuthDefaultRequest {
private final AlipayClient alipayClient;
public AuthAlipayCertRequest(AuthConfig config, AlipayConfig alipayConfig) {
super(config, ALIPAY);
try {
this.alipayClient = new DefaultAlipayClient(alipayConfig);
} catch (AlipayApiException e) {
throw new AuthException(e);
}
}
@Override
protected void checkCode(AuthCallback authCallback) {
if (StringUtils.isEmpty(authCallback.getAuth_code())) {
throw new AuthException(AuthResponseStatus.ILLEGAL_CODE, source);
}
}
@Override
public AuthToken getAccessToken(AuthCallback authCallback) {
AlipaySystemOauthTokenRequest request = new AlipaySystemOauthTokenRequest();
request.setGrantType("authorization_code");
request.setCode(authCallback.getAuth_code());
AlipaySystemOauthTokenResponse response;
try {
response = this.alipayClient.certificateExecute(request);
} catch (Exception e) {
throw new AuthException(e);
}
if (!response.isSuccess()) {
throw new AuthException(response.getSubMsg());
}
return AuthToken.builder()
.accessToken(response.getAccessToken())
.uid(response.getUserId())
.expireIn(Integer.parseInt(response.getExpiresIn()))
.refreshToken(response.getRefreshToken())
.build();
}
/**
* 刷新access token (续期)
*
* @param authToken 登录成功后返回的Token信息
* @return AuthResponse
*/
@Override
public AuthResponse<AuthToken> refresh(AuthToken authToken) {
AlipaySystemOauthTokenRequest request = new AlipaySystemOauthTokenRequest();
request.setGrantType("refresh_token");
request.setRefreshToken(authToken.getRefreshToken());
AlipaySystemOauthTokenResponse response = null;
try {
response = this.alipayClient.certificateExecute(request);
} catch (Exception e) {
throw new AuthException(e);
}
if (!response.isSuccess()) {
throw new AuthException(response.getSubMsg());
}
return AuthResponse.<AuthToken>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
public AuthUser getUserInfo(AuthToken authToken) {
String accessToken = authToken.getAccessToken();
AlipayUserInfoShareRequest request = new AlipayUserInfoShareRequest();
AlipayUserInfoShareResponse response = null;
try {
response = this.alipayClient.certificateExecute(request, accessToken);
} catch (AlipayApiException e) {
throw new AuthException(e.getErrMsg(), e);
}
if (!response.isSuccess()) {
throw new AuthException(response.getSubMsg());
}
String province = response.getProvince(), city = response.getCity();
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.getOpenId())
.username(StringUtils.isEmpty(response.getUserName()) ? response.getNickName() : response.getUserName())
.nickname(response.getNickName())
.avatar(response.getAvatar())
.location(location)
.gender(AuthUserGender.getRealGender(response.getGender()))
.token(authToken)
.source(source.toString())
.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("app_id", config.getClientId())
.queryParam("scope", "auth_user")
.queryParam("redirect_uri", config.getRedirectUri())
.queryParam("state", getRealState(state))
.build();
}
}
@@ -18,37 +18,140 @@ import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthResponse;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.utils.AuthChecker;
import me.zhyd.oauth.utils.GlobalAuthUtils;
import me.zhyd.oauth.utils.StringUtils;
import me.zhyd.oauth.utils.UrlBuilder;
import java.net.InetSocketAddress;
/**
* 支付宝登录
* 支付宝公钥模式登录
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @since 1.0.1
*/
public class AuthAlipayRequest extends AuthDefaultRequest {
private AlipayClient alipayClient;
/**
* 支付宝公钥:当选择支付宝登录时,该值可用
* 对应“RSA2(SHA256)密钥”中的“支付宝公钥”
*/
private final String alipayPublicKey;
private final AlipayClient alipayClient;
private static final String GATEWAY = "https://openapi.alipay.com/gateway.do";
/**
* @see AuthAlipayRequest#AuthAlipayRequest(me.zhyd.oauth.config.AuthConfig, java.lang.String)
* @deprecated 请使用带有"alipayPublicKey"参数的构造方法
*/
@Deprecated
public AuthAlipayRequest(AuthConfig config) {
super(config, AuthDefaultSource.ALIPAY);
this.alipayClient = new DefaultAlipayClient(AuthDefaultSource.ALIPAY.accessToken(), config.getClientId(), config.getClientSecret(), "json", "UTF-8", config
.getAlipayPublicKey(), "RSA2");
this(config, (String) null);
}
/**
* @see AuthAlipayRequest#AuthAlipayRequest(me.zhyd.oauth.config.AuthConfig, java.lang.String, me.zhyd.oauth.cache.AuthStateCache)
* @deprecated 请使用带有"alipayPublicKey"参数的构造方法
*/
@Deprecated
public AuthAlipayRequest(AuthConfig config, AuthStateCache authStateCache) {
this(config, null, authStateCache);
}
/**
* @see AuthAlipayRequest#AuthAlipayRequest(me.zhyd.oauth.config.AuthConfig, java.lang.String, me.zhyd.oauth.cache.AuthStateCache, java.lang.String, java.lang.Integer)
* @deprecated 请使用带有"alipayPublicKey"参数的构造方法
*/
@Deprecated
public AuthAlipayRequest(AuthConfig config, AuthStateCache authStateCache, String proxyHost, Integer proxyPort) {
this(config, null, authStateCache, proxyHost, proxyPort);
}
/**
* 构造方法,需要设置"alipayPublicKey"
*
* @param config 公共的OAuth配置
* @param alipayPublicKey 支付宝公钥
* @see AuthAlipayRequest#AuthAlipayRequest(me.zhyd.oauth.config.AuthConfig)
*/
public AuthAlipayRequest(AuthConfig config, String alipayPublicKey) {
super(config, AuthDefaultSource.ALIPAY);
this.alipayPublicKey = determineAlipayPublicKey(alipayPublicKey, config);
check(config);
this.alipayClient = new DefaultAlipayClient(GATEWAY, config.getClientId(), config.getClientSecret(), "json", "UTF-8", this.alipayPublicKey, "RSA2");
}
/**
* 构造方法,需要设置"alipayPublicKey"
*
* @param config 公共的OAuth配置
* @param alipayPublicKey 支付宝公钥
* @see AuthAlipayRequest#AuthAlipayRequest(me.zhyd.oauth.config.AuthConfig, me.zhyd.oauth.cache.AuthStateCache)
*/
public AuthAlipayRequest(AuthConfig config, String alipayPublicKey, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.ALIPAY, authStateCache);
this.alipayClient = new DefaultAlipayClient(AuthDefaultSource.ALIPAY.accessToken(), config.getClientId(), config.getClientSecret(), "json", "UTF-8", config
.getAlipayPublicKey(), "RSA2");
this.alipayPublicKey = determineAlipayPublicKey(alipayPublicKey, config);
check(config);
if (config.getHttpConfig() != null && config.getHttpConfig().getProxy() != null
&& config.getHttpConfig().getProxy().address() instanceof InetSocketAddress) {
InetSocketAddress address = (InetSocketAddress) config.getHttpConfig().getProxy().address();
this.alipayClient = new DefaultAlipayClient(GATEWAY, config.getClientId(), config.getClientSecret(),
"json", "UTF-8", this.alipayPublicKey, "RSA2", address.getHostName(), address.getPort());
} else {
this.alipayClient = new DefaultAlipayClient(GATEWAY, config.getClientId(), config.getClientSecret(),
"json", "UTF-8", this.alipayPublicKey, "RSA2");
}
}
/**
* 构造方法,需要设置"alipayPublicKey"
*
* @param config 公共的OAuth配置
* @param alipayPublicKey 支付宝公钥
* @see AuthAlipayRequest#AuthAlipayRequest(me.zhyd.oauth.config.AuthConfig, me.zhyd.oauth.cache.AuthStateCache, java.lang.String, java.lang.Integer)
*/
public AuthAlipayRequest(AuthConfig config, String alipayPublicKey, AuthStateCache authStateCache, String proxyHost, Integer proxyPort) {
super(config, AuthDefaultSource.ALIPAY, authStateCache);
this.alipayPublicKey = determineAlipayPublicKey(alipayPublicKey, config);
check(config);
this.alipayClient = new DefaultAlipayClient(GATEWAY, config.getClientId(), config.getClientSecret(),
"json", "UTF-8", this.alipayPublicKey, "RSA2", proxyHost, proxyPort);
}
private String determineAlipayPublicKey(String alipayPublicKey, AuthConfig config) {
return alipayPublicKey != null ? alipayPublicKey : config.getAlipayPublicKey();
}
protected void check(AuthConfig config) {
AuthChecker.checkConfig(config, AuthDefaultSource.ALIPAY);
if (!StringUtils.isNotEmpty(alipayPublicKey)) {
throw new AuthException(AuthResponseStatus.PARAMETER_INCOMPLETE, AuthDefaultSource.ALIPAY);
}
// 支付宝在创建回调地址时,不允许使用localhost或者127.0.0.1
if (GlobalAuthUtils.isLocalHost(config.getRedirectUri())) {
// The redirect uri of alipay is forbidden to use localhost or 127.0.0.1
throw new AuthException(AuthResponseStatus.ILLEGAL_REDIRECT_URI, AuthDefaultSource.ALIPAY);
}
}
@Override
protected AuthToken getAccessToken(AuthCallback authCallback) {
protected void checkCode(AuthCallback authCallback) {
if (StringUtils.isEmpty(authCallback.getAuth_code())) {
throw new AuthException(AuthResponseStatus.ILLEGAL_CODE, source);
}
}
@Override
public AuthToken getAccessToken(AuthCallback authCallback) {
AlipaySystemOauthTokenRequest request = new AlipaySystemOauthTokenRequest();
request.setGrantType("authorization_code");
request.setCode(authCallback.getAuth_code());
AlipaySystemOauthTokenResponse response = null;
AlipaySystemOauthTokenResponse response;
try {
response = this.alipayClient.execute(request);
} catch (Exception e) {
@@ -72,7 +175,7 @@ public class AuthAlipayRequest extends AuthDefaultRequest {
* @return AuthResponse
*/
@Override
public AuthResponse refresh(AuthToken authToken) {
public AuthResponse<AuthToken> refresh(AuthToken authToken) {
AlipaySystemOauthTokenRequest request = new AlipaySystemOauthTokenRequest();
request.setGrantType("refresh_token");
request.setRefreshToken(authToken.getRefreshToken());
@@ -85,7 +188,7 @@ public class AuthAlipayRequest extends AuthDefaultRequest {
if (!response.isSuccess()) {
throw new AuthException(response.getSubMsg());
}
return AuthResponse.builder()
return AuthResponse.<AuthToken>builder()
.code(AuthResponseStatus.SUCCESS.getCode())
.data(AuthToken.builder()
.accessToken(response.getAccessToken())
@@ -97,7 +200,7 @@ public class AuthAlipayRequest extends AuthDefaultRequest {
}
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
public AuthUser getUserInfo(AuthToken authToken) {
String accessToken = authToken.getAccessToken();
AlipayUserInfoShareRequest request = new AlipayUserInfoShareRequest();
AlipayUserInfoShareResponse response = null;
@@ -8,7 +8,6 @@ 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;
/**
* 阿里云登录
@@ -27,20 +26,20 @@ public class AuthAliyunRequest extends AuthDefaultRequest {
}
@Override
protected AuthToken getAccessToken(AuthCallback authCallback) {
public 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();
.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) {
public AuthUser getUserInfo(AuthToken authToken) {
String userInfo = doGetUserInfo(authToken);
JSONObject object = JSONObject.parseObject(userInfo);
return AuthUser.builder()
@@ -0,0 +1,183 @@
package me.zhyd.oauth.request;
import com.alibaba.fastjson.JSONObject;
import com.xkcoding.http.constants.Constants;
import com.xkcoding.http.support.HttpHeader;
import com.xkcoding.http.util.UrlUtil;
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.enums.scope.AuthAmazonScope;
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.AuthScopeUtils;
import me.zhyd.oauth.utils.HttpUtils;
import me.zhyd.oauth.utils.PkceUtil;
import me.zhyd.oauth.utils.UrlBuilder;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* Amazon登录
* Login with Amazon for Websites Overview https://developer.amazon.com/zh/docs/login-with-amazon/register-web.html
* Login with Amazon SDK for JavaScript Reference Guidehttps://developer.amazon.com/zh/docs/login-with-amazon/javascript-sdk-reference.html
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @since 1.16.0
*/
public class AuthAmazonRequest extends AuthDefaultRequest {
public AuthAmazonRequest(AuthConfig config) {
super(config, AuthDefaultSource.AMAZON);
}
public AuthAmazonRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.AMAZON, authStateCache);
}
/**
* https://developer.amazon.com/zh/docs/login-with-amazon/authorization-code-grant.html#authorization-request
*
* @param state state 验证授权流程的参数,可以防止csrf
* @return String
*/
@Override
public String authorize(String state) {
String realState = getRealState(state);
UrlBuilder builder = UrlBuilder.fromBaseUrl(source.authorize())
.queryParam("client_id", config.getClientId())
.queryParam("scope", this.getScopes(" ", true, AuthScopeUtils.getDefaultScopes(AuthAmazonScope.values())))
.queryParam("redirect_uri", config.getRedirectUri())
.queryParam("response_type", "code")
.queryParam("state", realState);
if (config.isPkce()) {
String cacheKey = this.source.getName().concat(":code_verifier:").concat(realState);
String codeVerifier = PkceUtil.generateCodeVerifier();
String codeChallengeMethod = "S256";
String codeChallenge = PkceUtil.generateCodeChallenge(codeChallengeMethod, codeVerifier);
builder.queryParam("code_challenge", codeChallenge)
.queryParam("code_challenge_method", codeChallengeMethod);
// 缓存 codeVerifier 十分钟
this.authStateCache.cache(cacheKey, codeVerifier, TimeUnit.MINUTES.toMillis(10));
}
return builder.build();
}
/**
* https://developer.amazon.com/zh/docs/login-with-amazon/authorization-code-grant.html#access-token-request
*
* @return access token
*/
@Override
public AuthToken getAccessToken(AuthCallback authCallback) {
Map<String, String> form = new HashMap<>(9);
form.put("grant_type", "authorization_code");
form.put("code", authCallback.getCode());
form.put("redirect_uri", config.getRedirectUri());
form.put("client_id", config.getClientId());
form.put("client_secret", config.getClientSecret());
if (config.isPkce()) {
String cacheKey = this.source.getName().concat(":code_verifier:").concat(authCallback.getState());
String codeVerifier = this.authStateCache.get(cacheKey);
form.put("code_verifier", codeVerifier);
}
return getToken(form, this.source.accessToken());
}
@Override
public AuthResponse<AuthToken> refresh(AuthToken authToken) {
Map<String, String> form = new HashMap<>(7);
form.put("grant_type", "refresh_token");
form.put("refresh_token", authToken.getRefreshToken());
form.put("client_id", config.getClientId());
form.put("client_secret", config.getClientSecret());
return AuthResponse.<AuthToken>builder()
.code(AuthResponseStatus.SUCCESS.getCode())
.data(getToken(form, this.source.refresh()))
.build();
}
private AuthToken getToken(Map<String, String> param, String url) {
HttpHeader httpHeader = new HttpHeader();
httpHeader.add("Host", "api.amazon.com");
httpHeader.add(Constants.CONTENT_TYPE, "application/x-www-form-urlencoded;charset=UTF-8");
String response = new HttpUtils(config.getHttpConfig()).post(url, param, httpHeader, false).getBody();
JSONObject jsonObject = JSONObject.parseObject(response);
this.checkResponse(jsonObject);
return AuthToken.builder()
.accessToken(jsonObject.getString("access_token"))
.tokenType(jsonObject.getString("token_type"))
.expireIn(jsonObject.getIntValue("expires_in"))
.refreshToken(jsonObject.getString("refresh_token"))
.build();
}
/**
* 校验响应内容是否正确
*
* @param jsonObject 响应内容
*/
private void checkResponse(JSONObject jsonObject) {
if (jsonObject.containsKey("error")) {
throw new AuthException(jsonObject.getString("error_description").concat(" ") + jsonObject.getString("error_description"));
}
}
/**
* https://developer.amazon.com/zh/docs/login-with-amazon/obtain-customer-profile.html#call-profile-endpoint
*
* @param authToken token信息
* @return AuthUser
*/
@Override
public AuthUser getUserInfo(AuthToken authToken) {
String accessToken = authToken.getAccessToken();
this.checkToken(accessToken);
HttpHeader httpHeader = new HttpHeader();
httpHeader.add("Host", "api.amazon.com");
httpHeader.add("Authorization", "bearer " + accessToken);
String userInfo = new HttpUtils(config.getHttpConfig()).get(this.source.userInfo(), new HashMap<>(0), httpHeader, false).getBody();
JSONObject jsonObject = JSONObject.parseObject(userInfo);
this.checkResponse(jsonObject);
return AuthUser.builder()
.rawUserInfo(jsonObject)
.uuid(jsonObject.getString("user_id"))
.username(jsonObject.getString("name"))
.nickname(jsonObject.getString("name"))
.email(jsonObject.getString("email"))
.gender(AuthUserGender.UNKNOWN)
.source(source.toString())
.token(authToken)
.build();
}
private void checkToken(String accessToken) {
String tokenInfo = new HttpUtils(config.getHttpConfig()).get("https://api.amazon.com/auth/o2/tokeninfo?access_token=" + UrlUtil.urlEncode(accessToken)).getBody();
JSONObject jsonObject = JSONObject.parseObject(tokenInfo);
if (!config.getClientId().equals(jsonObject.getString("aud"))) {
throw new AuthException(AuthResponseStatus.ILLEGAL_TOKEN);
}
}
@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();
}
}
@@ -0,0 +1,156 @@
package me.zhyd.oauth.request;
import com.alibaba.fastjson.JSONObject;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.impl.security.AbstractJwk;
import lombok.Data;
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.scope.AuthAppleScope;
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.AuthScopeUtils;
import me.zhyd.oauth.utils.StringUtils;
import me.zhyd.oauth.utils.UrlBuilder;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import java.io.IOException;
import java.io.StringReader;
import java.security.PrivateKey;
import java.util.Base64;
import java.util.Date;
import java.util.concurrent.TimeUnit;
public class AuthAppleRequest extends AuthDefaultRequest {
private static final String AUD = "https://appleid.apple.com";
private volatile PrivateKey privateKey;
public AuthAppleRequest(AuthConfig config) {
super(config, AuthDefaultSource.APPLE);
}
public AuthAppleRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.APPLE, authStateCache);
}
@Override
public String authorize(String state) {
return UrlBuilder.fromBaseUrl(super.authorize(state))
.queryParam("response_mode", "form_post")
.queryParam("scope", this.getScopes(" ", false, AuthScopeUtils.getDefaultScopes(AuthAppleScope.values())))
.build();
}
@Override
public AuthToken getAccessToken(AuthCallback authCallback) {
if (!StringUtils.isEmpty(authCallback.getError())) {
throw new AuthException(authCallback.getError());
}
this.config.setClientSecret(this.getToken());
// if failed will throw AuthException
String response = doPostAuthorizationCode(authCallback.getCode());
JSONObject accessTokenObject = JSONObject.parseObject(response);
// https://developer.apple.com/documentation/sign_in_with_apple/tokenresponse
AuthToken.AuthTokenBuilder builder = AuthToken.builder()
.accessToken(accessTokenObject.getString("access_token"))
.expireIn(accessTokenObject.getIntValue("expires_in"))
.refreshToken(accessTokenObject.getString("refresh_token"))
.tokenType(accessTokenObject.getString("token_type"))
.idToken(accessTokenObject.getString("id_token"));
if (!StringUtils.isEmpty(authCallback.getUser())) {
try {
AppleUserInfo userInfo = JSONObject.parseObject(authCallback.getUser(), AppleUserInfo.class);
builder.username(userInfo.getName().getFirstName() + " " + userInfo.getName().getLastName());
} catch (Exception ignored) {
}
}
return builder.build();
}
@Override
public AuthUser getUserInfo(AuthToken authToken) {
Base64.Decoder urlDecoder = Base64.getUrlDecoder();
String[] idToken = authToken.getIdToken().split("\\.");
String payload = new String(urlDecoder.decode(idToken[1]));
JSONObject object = JSONObject.parseObject(payload);
// https://developer.apple.com/documentation/sign_in_with_apple/sign_in_with_apple_rest_api/authenticating_users_with_sign_in_with_apple#3383773
return AuthUser.builder()
.rawUserInfo(object)
.uuid(object.getString("sub"))
.email(object.getString("email"))
.username(authToken.getUsername())
.token(authToken)
.source(source.toString())
.build();
}
@Override
protected void checkConfig(AuthConfig config) {
super.checkConfig(config);
if (StringUtils.isEmpty(config.getClientId())) {
throw new AuthException(AuthResponseStatus.ILLEGAL_CLIENT_ID, source);
}
if (StringUtils.isEmpty(config.getClientSecret())) {
throw new AuthException(AuthResponseStatus.ILLEGAL_CLIENT_SECRET, source);
}
if (StringUtils.isEmpty(config.getKid())) {
throw new AuthException(AuthResponseStatus.ILLEGAL_KID, source);
}
if (StringUtils.isEmpty(config.getTeamId())) {
throw new AuthException(AuthResponseStatus.ILLEGAL_TEAM_ID, source);
}
}
/**
* 获取token
* @see <a href="https://developer.apple.com/documentation/accountorganizationaldatasharing/creating-a-client-secret">creating-a-client-secret</a>
* @return jwt token
*/
private String getToken() {
return Jwts.builder().header().add(AbstractJwk.KID.getId(), this.config.getKid()).and()
.issuer(this.config.getTeamId())
.subject(this.config.getClientId())
.audience().add(AUD).and()
.expiration(new Date(System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(3)))
.issuedAt(new Date())
.signWith(getPrivateKey())
.compact();
}
private PrivateKey getPrivateKey() {
if (this.privateKey == null) {
synchronized (this) {
if (this.privateKey == null) {
try (PEMParser pemParser = new PEMParser(new StringReader(this.config.getClientSecret()))) {
JcaPEMKeyConverter pemKeyConverter = new JcaPEMKeyConverter();
PrivateKeyInfo keyInfo = (PrivateKeyInfo) pemParser.readObject();
this.privateKey = pemKeyConverter.getPrivateKey(keyInfo);
} catch (IOException e) {
throw new AuthException("Failed to get apple private key", e);
}
}
}
}
return this.privateKey;
}
@Data
static class AppleUserInfo {
private AppleUsername name;
private String email;
}
@Data
static class AppleUsername {
private String firstName;
private String lastName;
}
}
@@ -34,7 +34,7 @@ public class AuthBaiduRequest extends AuthDefaultRequest {
}
@Override
protected AuthToken getAccessToken(AuthCallback authCallback) {
public AuthToken getAccessToken(AuthCallback authCallback) {
String response = doPostAuthorizationCode(authCallback.getCode());
return getAuthToken(response);
}
@@ -48,7 +48,7 @@ public class AuthBaiduRequest extends AuthDefaultRequest {
* @return AuthUser
*/
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
public AuthUser getUserInfo(AuthToken authToken) {
String userInfo = doGetUserInfo(authToken);
JSONObject object = JSONObject.parseObject(userInfo);
this.checkResponse(object);
@@ -81,15 +81,15 @@ public class AuthBaiduRequest extends AuthDefaultRequest {
}
@Override
public AuthResponse refresh(AuthToken authToken) {
public AuthResponse<AuthToken> refresh(AuthToken authToken) {
String refreshUrl = UrlBuilder.fromBaseUrl(this.source.refresh())
.queryParam("grant_type", "refresh_token")
.queryParam("refresh_token", authToken.getRefreshToken())
.queryParam("client_id", this.config.getClientId())
.queryParam("client_secret", this.config.getClientSecret())
.build();
String response = new HttpUtils(config.getHttpConfig()).get(refreshUrl);
return AuthResponse.builder()
String response = new HttpUtils(config.getHttpConfig()).get(refreshUrl).getBody();
return AuthResponse.<AuthToken>builder()
.code(AuthResponseStatus.SUCCESS.getCode())
.data(this.getAuthToken(response))
.build();
@@ -14,7 +14,7 @@ import me.zhyd.oauth.utils.AuthScopeUtils;
import me.zhyd.oauth.utils.UrlBuilder;
/**
* Cooding登录
* Coding登录
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @since 1.0.0
@@ -30,7 +30,7 @@ public class AuthCodingRequest extends AuthDefaultRequest {
}
@Override
protected AuthToken getAccessToken(AuthCallback authCallback) {
public AuthToken getAccessToken(AuthCallback authCallback) {
String response = doGetAuthorizationCode(authCallback.getCode());
JSONObject accessTokenObject = JSONObject.parseObject(response);
this.checkResponse(accessTokenObject);
@@ -42,7 +42,7 @@ public class AuthCodingRequest extends AuthDefaultRequest {
}
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
public AuthUser getUserInfo(AuthToken authToken) {
String response = doGetUserInfo(authToken);
JSONObject object = JSONObject.parseObject(response);
this.checkResponse(object);
@@ -85,7 +85,7 @@ public class AuthCodingRequest extends AuthDefaultRequest {
*/
@Override
public String authorize(String state) {
return UrlBuilder.fromBaseUrl(String.format(source.authorize(), config.getCodingGroupName()))
return UrlBuilder.fromBaseUrl(String.format(source.authorize(), config.getDomainPrefix()))
.queryParam("response_type", "code")
.queryParam("client_id", config.getClientId())
.queryParam("redirect_uri", config.getRedirectUri())
@@ -102,7 +102,7 @@ public class AuthCodingRequest extends AuthDefaultRequest {
*/
@Override
public String accessTokenUrl(String code) {
return UrlBuilder.fromBaseUrl(String.format(source.accessToken(), config.getCodingGroupName()))
return UrlBuilder.fromBaseUrl(String.format(source.accessToken(), config.getDomainPrefix()))
.queryParam("code", code)
.queryParam("client_id", config.getClientId())
.queryParam("client_secret", config.getClientSecret())
@@ -119,7 +119,7 @@ public class AuthCodingRequest extends AuthDefaultRequest {
*/
@Override
public String userInfoUrl(AuthToken authToken) {
return UrlBuilder.fromBaseUrl(String.format(source.userInfo(), config.getCodingGroupName()))
return UrlBuilder.fromBaseUrl(String.format(source.userInfo(), config.getDomainPrefix()))
.queryParam("access_token", authToken.getAccessToken())
.build();
}
@@ -28,7 +28,7 @@ public class AuthCsdnRequest extends AuthDefaultRequest {
}
@Override
protected AuthToken getAccessToken(AuthCallback authCallback) {
public AuthToken getAccessToken(AuthCallback authCallback) {
String response = doPostAuthorizationCode(authCallback.getCode());
JSONObject accessTokenObject = JSONObject.parseObject(response);
this.checkResponse(accessTokenObject);
@@ -36,7 +36,7 @@ public class AuthCsdnRequest extends AuthDefaultRequest {
}
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
public AuthUser getUserInfo(AuthToken authToken) {
String response = doGetUserInfo(authToken);
JSONObject object = JSONObject.parseObject(response);
this.checkResponse(object);
@@ -40,28 +40,9 @@ public abstract class AuthDefaultRequest implements AuthRequest {
throw new AuthException(AuthResponseStatus.PARAMETER_INCOMPLETE, source);
}
// 校验配置合法性
AuthChecker.checkConfig(config, source);
this.checkConfig(config);
}
/**
* 获取access token
*
* @param authCallback 授权成功后的回调参数
* @return token
* @see AuthDefaultRequest#authorize()
* @see AuthDefaultRequest#authorize(String)
*/
protected abstract AuthToken getAccessToken(AuthCallback authCallback);
/**
* 使用token换取用户信息
*
* @param authToken token信息
* @return 用户信息
* @see AuthDefaultRequest#getAccessToken(AuthCallback)
*/
protected abstract AuthUser getUserInfo(AuthToken authToken);
/**
* 统一的登录入口。当通过{@link AuthDefaultRequest#authorize(String)}授权成功后,会跳转到调用方的相关回调方法中
* 方法的入参可以使用{@code AuthCallback}{@code AuthCallback}类中封装好了OAuth2授权回调所需要的参数
@@ -70,29 +51,33 @@ public abstract class AuthDefaultRequest implements AuthRequest {
* @return AuthResponse
*/
@Override
public AuthResponse login(AuthCallback authCallback) {
public AuthResponse<AuthUser> login(AuthCallback authCallback) {
try {
AuthChecker.checkCode(source, authCallback);
checkCode(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();
return AuthResponse.<AuthUser>builder().code(AuthResponseStatus.SUCCESS.getCode()).data(user).build();
} catch (Exception e) {
Log.error("Failed to login with oauth authorization.", e);
return this.responseError(e);
}
}
protected void checkCode(AuthCallback authCallback) {
AuthChecker.checkCode(source, authCallback);
}
/**
* 处理{@link AuthDefaultRequest#login(AuthCallback)} 发生异常的情况,统一响应参数
*
* @param e 具体的异常
* @return AuthResponse
*/
private AuthResponse responseError(Exception e) {
AuthResponse<AuthUser> responseError(Exception e) {
int errorCode = AuthResponseStatus.FAILURE.getCode();
String errorMsg = e.getMessage();
if (e instanceof AuthException) {
@@ -102,7 +87,7 @@ public abstract class AuthDefaultRequest implements AuthRequest {
errorMsg = authException.getErrorMsg();
}
}
return AuthResponse.builder().code(errorCode).msg(errorMsg).build();
return AuthResponse.<AuthUser>builder().code(errorCode).msg(errorMsg).build();
}
/**
@@ -211,7 +196,7 @@ public abstract class AuthDefaultRequest implements AuthRequest {
* @return Response
*/
protected String doPostAuthorizationCode(String code) {
return new HttpUtils(config.getHttpConfig()).post(accessTokenUrl(code));
return new HttpUtils(config.getHttpConfig()).post(accessTokenUrl(code)).getBody();
}
/**
@@ -221,7 +206,7 @@ public abstract class AuthDefaultRequest implements AuthRequest {
* @return Response
*/
protected String doGetAuthorizationCode(String code) {
return new HttpUtils(config.getHttpConfig()).get(accessTokenUrl(code));
return new HttpUtils(config.getHttpConfig()).get(accessTokenUrl(code)).getBody();
}
/**
@@ -232,7 +217,7 @@ public abstract class AuthDefaultRequest implements AuthRequest {
*/
@Deprecated
protected String doPostUserInfo(AuthToken authToken) {
return new HttpUtils(config.getHttpConfig()).post(userInfoUrl(authToken));
return new HttpUtils(config.getHttpConfig()).post(userInfoUrl(authToken)).getBody();
}
/**
@@ -242,7 +227,7 @@ public abstract class AuthDefaultRequest implements AuthRequest {
* @return Response
*/
protected String doGetUserInfo(AuthToken authToken) {
return new HttpUtils(config.getHttpConfig()).get(userInfoUrl(authToken));
return new HttpUtils(config.getHttpConfig()).get(userInfoUrl(authToken)).getBody();
}
/**
@@ -253,7 +238,7 @@ public abstract class AuthDefaultRequest implements AuthRequest {
*/
@Deprecated
protected String doPostRevoke(AuthToken authToken) {
return new HttpUtils(config.getHttpConfig()).post(revokeUrl(authToken));
return new HttpUtils(config.getHttpConfig()).post(revokeUrl(authToken)).getBody();
}
/**
@@ -263,7 +248,7 @@ public abstract class AuthDefaultRequest implements AuthRequest {
* @return Response
*/
protected String doGetRevoke(AuthToken authToken) {
return new HttpUtils(config.getHttpConfig()).get(revokeUrl(authToken));
return new HttpUtils(config.getHttpConfig()).get(revokeUrl(authToken)).getBody();
}
/**
@@ -291,4 +276,8 @@ public abstract class AuthDefaultRequest implements AuthRequest {
return encode ? UrlUtil.urlEncode(scopeStr) : scopeStr;
}
protected void checkConfig(AuthConfig config) {
AuthChecker.checkConfig(config, source);
}
}
@@ -0,0 +1,22 @@
package me.zhyd.oauth.request;
import me.zhyd.oauth.cache.AuthStateCache;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.config.AuthDefaultSource;
/**
* 钉钉账号登录
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @since 1.0.0
*/
public class AuthDingTalkAccountRequest extends AbstractAuthDingtalkRequest {
public AuthDingTalkAccountRequest(AuthConfig config) {
super(config, AuthDefaultSource.DINGTALK_ACCOUNT);
}
public AuthDingTalkAccountRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.DINGTALK_ACCOUNT, authStateCache);
}
}
@@ -1,26 +1,16 @@
package me.zhyd.oauth.request;
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.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.GlobalAuthUtils;
import me.zhyd.oauth.utils.UrlBuilder;
/**
* 钉钉登录
* 钉钉二维码登录
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @since 1.0.0
*/
public class AuthDingTalkRequest extends AuthDefaultRequest {
public class AuthDingTalkRequest extends AbstractAuthDingtalkRequest {
public AuthDingTalkRequest(AuthConfig config) {
super(config, AuthDefaultSource.DINGTALK);
@@ -29,72 +19,4 @@ public class AuthDingTalkRequest extends AuthDefaultRequest {
public AuthDingTalkRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.DINGTALK, authStateCache);
}
@Override
protected AuthToken getAccessToken(AuthCallback authCallback) {
return AuthToken.builder().accessCode(authCallback.getCode()).build();
}
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
String code = authToken.getAccessCode();
JSONObject param = new JSONObject();
param.put("tmp_auth_code", code);
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"));
}
object = object.getJSONObject("user_info");
AuthToken token = AuthToken.builder()
.openId(object.getString("openid"))
.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.toString())
.token(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("appid", config.getClientId())
.queryParam("scope", "snsapi_login")
.queryParam("redirect_uri", config.getRedirectUri())
.queryParam("state", getRealState(state))
.build();
}
/**
* 返回获取userInfo的url
*
* @param authToken 用户授权后的token
* @return 返回获取userInfo的url
*/
@Override
protected String userInfoUrl(AuthToken authToken) {
// 根据timestamp, appSecret计算签名值
String timestamp = System.currentTimeMillis() + "";
String urlEncodeSignature = GlobalAuthUtils.generateDingTalkSignature(config.getClientSecret(), timestamp);
return UrlBuilder.fromBaseUrl(source.userInfo())
.queryParam("signature", urlEncodeSignature)
.queryParam("timestamp", timestamp)
.queryParam("accessKey", config.getClientId())
.build();
}
}
@@ -0,0 +1,108 @@
package me.zhyd.oauth.request;
import com.alibaba.fastjson.JSONObject;
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.scope.AuthDingTalkScope;
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.AuthScopeUtils;
import me.zhyd.oauth.utils.HttpUtils;
import me.zhyd.oauth.utils.UrlBuilder;
import java.util.HashMap;
import java.util.Map;
/**
* 新版钉钉二维码登录
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @since 1.16.7
*/
public class AuthDingTalkV2Request extends AuthDefaultRequest {
public AuthDingTalkV2Request(AuthConfig config) {
super(config, AuthDefaultSource.DINGTALK_V2);
}
public AuthDingTalkV2Request(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.DINGTALK_V2, authStateCache);
}
@Override
public String authorize(String state) {
return UrlBuilder.fromBaseUrl(source.authorize())
.queryParam("response_type", "code")
.queryParam("client_id", config.getClientId())
.queryParam("scope", this.getScopes(",", true, AuthScopeUtils.getDefaultScopes(AuthDingTalkScope.values())))
.queryParam("redirect_uri", config.getRedirectUri())
.queryParam("prompt", "consent")
.queryParam("org_type", config.getDingTalkOrgType())
.queryParam("corpId", config.getDingTalkCorpId())
.queryParam("exclusiveLogin", config.isDingTalkExclusiveLogin())
.queryParam("exclusiveCorpId", config.getDingTalkExclusiveCorpId())
.queryParam("state", getRealState(state))
.build();
}
@Override
public AuthToken getAccessToken(AuthCallback authCallback) {
Map<String, String> params = new HashMap<>();
params.put("grantType", "authorization_code");
params.put("clientId", config.getClientId());
params.put("clientSecret", config.getClientSecret());
params.put("code", authCallback.getCode());
String response = new HttpUtils(config.getHttpConfig()).post(this.source.accessToken(), JSONObject.toJSONString(params)).getBody();
JSONObject accessTokenObject = JSONObject.parseObject(response);
if (!accessTokenObject.containsKey("accessToken")) {
throw new AuthException(JSONObject.toJSONString(response), source);
}
return AuthToken.builder()
.accessToken(accessTokenObject.getString("accessToken"))
.refreshToken(accessTokenObject.getString("refreshToken"))
.expireIn(accessTokenObject.getIntValue("expireIn"))
.corpId(accessTokenObject.getString("corpId"))
.build();
}
@Override
public AuthUser getUserInfo(AuthToken authToken) {
HttpHeader header = new HttpHeader();
header.add("x-acs-dingtalk-access-token", authToken.getAccessToken());
String response = new HttpUtils(config.getHttpConfig()).get(this.source.userInfo(), null, header, false).getBody();
JSONObject object = JSONObject.parseObject(response);
authToken.setOpenId(object.getString("openId"));
authToken.setUnionId(object.getString("unionId"));
return AuthUser.builder()
.rawUserInfo(object)
.uuid(object.getString("unionId"))
.username(object.getString("nick"))
.nickname(object.getString("nick"))
.avatar(object.getString("avatarUrl"))
.snapshotUser(object.getBooleanValue("visitor"))
.token(authToken)
.source(source.toString())
.build();
}
/**
* 返回获取accessToken的url
*
* @param code 授权码
* @return 返回获取accessToken的url
*/
protected String accessTokenUrl(String code) {
return UrlBuilder.fromBaseUrl(source.accessToken())
.queryParam("code", code)
.queryParam("clientId", config.getClientId())
.queryParam("clientSecret", config.getClientSecret())
.queryParam("grantType", "authorization_code")
.build();
}
}
@@ -1,17 +1,19 @@
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.enums.scope.AuthDouyinScope;
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.AuthScopeUtils;
import me.zhyd.oauth.utils.HttpUtils;
import me.zhyd.oauth.utils.UrlBuilder;
@@ -32,16 +34,17 @@ public class AuthDouyinRequest extends AuthDefaultRequest {
}
@Override
protected AuthToken getAccessToken(AuthCallback authCallback) {
public AuthToken getAccessToken(AuthCallback authCallback) {
return this.getToken(accessTokenUrl(authCallback.getCode()));
}
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
public AuthUser getUserInfo(AuthToken authToken) {
String response = doGetUserInfo(authToken);
JSONObject userInfoObject = JSONObject.parseObject(response);
this.checkResponse(userInfoObject);
JSONObject object = userInfoObject.getJSONObject("data");
authToken.setUnionId(object.getString("union_id"));
return AuthUser.builder()
.rawUserInfo(object)
.uuid(object.getString("union_id"))
@@ -57,8 +60,8 @@ public class AuthDouyinRequest extends AuthDefaultRequest {
}
@Override
public AuthResponse refresh(AuthToken oldToken) {
return AuthResponse.builder()
public AuthResponse<AuthToken> refresh(AuthToken oldToken) {
return AuthResponse.<AuthToken>builder()
.code(AuthResponseStatus.SUCCESS.getCode())
.data(getToken(refreshTokenUrl(oldToken.getRefreshToken())))
.build();
@@ -85,7 +88,7 @@ public class AuthDouyinRequest extends AuthDefaultRequest {
* @return token对象
*/
private AuthToken getToken(String accessTokenUrl) {
String response = new HttpUtils(config.getHttpConfig()).post(accessTokenUrl);
String response = new HttpUtils(config.getHttpConfig()).post(accessTokenUrl).getBody();
JSONObject object = JSONObject.parseObject(response);
this.checkResponse(object);
JSONObject dataObj = object.getJSONObject("data");
@@ -94,6 +97,7 @@ public class AuthDouyinRequest extends AuthDefaultRequest {
.openId(dataObj.getString("open_id"))
.expireIn(dataObj.getIntValue("expires_in"))
.refreshToken(dataObj.getString("refresh_token"))
.refreshTokenExpireIn(dataObj.getIntValue("refresh_expires_in"))
.scope(dataObj.getString("scope"))
.build();
}
@@ -111,7 +115,7 @@ public class AuthDouyinRequest extends AuthDefaultRequest {
.queryParam("response_type", "code")
.queryParam("client_key", config.getClientId())
.queryParam("redirect_uri", config.getRedirectUri())
.queryParam("scope", "user_info")
.queryParam("scope", this.getScopes(",", true, AuthScopeUtils.getDefaultScopes(AuthDouyinScope.values())))
.queryParam("state", getRealState(state))
.build();
}
@@ -44,15 +44,15 @@ public class AuthElemeRequest extends AuthDefaultRequest {
}
@Override
protected AuthToken getAccessToken(AuthCallback authCallback) {
Map<String, String> form = new HashMap<>(4);
public AuthToken getAccessToken(AuthCallback authCallback) {
Map<String, String> form = new HashMap<>(7);
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);
String response = new HttpUtils(config.getHttpConfig()).post(source.accessToken(), form, httpHeader, false).getBody();
JSONObject object = JSONObject.parseObject(response);
this.checkResponse(object);
@@ -66,14 +66,14 @@ public class AuthElemeRequest extends AuthDefaultRequest {
}
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
Map<String, Object> parameters = new HashMap<>();
public AuthUser getUserInfo(AuthToken authToken) {
Map<String, Object> parameters = new HashMap<>(4);
// 获取商户账号信息的API接口名称
String action = "eleme.user.getUser";
// 时间戳,单位秒。API服务端允许客户端请求最大时间误差为正负5分钟。
final long timestamp = System.currentTimeMillis();
// 公共参数
Map<String, Object> metasHashMap = new HashMap<>();
Map<String, Object> metasHashMap = new HashMap<>(4);
metasHashMap.put("app_key", config.getClientId());
metasHashMap.put("timestamp", timestamp);
String signature = GlobalAuthUtils.generateElemeSignature(config.getClientId(), config.getClientSecret(), timestamp, action, authToken
@@ -91,7 +91,7 @@ public class AuthElemeRequest extends AuthDefaultRequest {
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);
String response = new HttpUtils(config.getHttpConfig()).post(source.userInfo(), JSONObject.toJSONString(paramsMap), httpHeader).getBody();
JSONObject object = JSONObject.parseObject(response);
@@ -117,19 +117,19 @@ public class AuthElemeRequest extends AuthDefaultRequest {
}
@Override
public AuthResponse refresh(AuthToken oldToken) {
Map<String, String> form = new HashMap<>(2);
public AuthResponse<AuthToken> refresh(AuthToken oldToken) {
Map<String, String> form = new HashMap<>(4);
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);
String response = new HttpUtils(config.getHttpConfig()).post(source.refresh(), form, httpHeader, false).getBody();
JSONObject object = JSONObject.parseObject(response);
this.checkResponse(object);
return AuthResponse.builder()
return AuthResponse.<AuthToken>builder()
.code(AuthResponseStatus.SUCCESS.getCode())
.data(AuthToken.builder()
.accessToken(object.getString("access_token"))
@@ -4,6 +4,7 @@ import com.alibaba.fastjson.JSONObject;
import me.zhyd.oauth.cache.AuthStateCache;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.config.AuthDefaultSource;
import me.zhyd.oauth.enums.AuthResponseStatus;
import me.zhyd.oauth.enums.AuthUserGender;
import me.zhyd.oauth.enums.scope.AuthFacebookScope;
import me.zhyd.oauth.exception.AuthException;
@@ -11,6 +12,7 @@ import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.utils.AuthScopeUtils;
import me.zhyd.oauth.utils.GlobalAuthUtils;
import me.zhyd.oauth.utils.UrlBuilder;
/**
@@ -30,7 +32,7 @@ public class AuthFacebookRequest extends AuthDefaultRequest {
}
@Override
protected AuthToken getAccessToken(AuthCallback authCallback) {
public AuthToken getAccessToken(AuthCallback authCallback) {
String response = doPostAuthorizationCode(authCallback.getCode());
JSONObject accessTokenObject = JSONObject.parseObject(response);
this.checkResponse(accessTokenObject);
@@ -42,7 +44,7 @@ public class AuthFacebookRequest extends AuthDefaultRequest {
}
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
public AuthUser getUserInfo(AuthToken authToken) {
String userInfo = doGetUserInfo(authToken);
JSONObject object = JSONObject.parseObject(userInfo);
this.checkResponse(object);
@@ -87,6 +89,16 @@ public class AuthFacebookRequest extends AuthDefaultRequest {
.build();
}
@Override
protected void checkConfig(AuthConfig config) {
super.checkConfig(config);
// facebook的回调地址必须为https的链接
if (AuthDefaultSource.FACEBOOK == source && !GlobalAuthUtils.isHttpsProtocol(config.getRedirectUri())) {
// Facebook's redirect uri must use the HTTPS protocol
throw new AuthException(AuthResponseStatus.ILLEGAL_REDIRECT_URI, source);
}
}
/**
* 检查响应内容是否正确
*
@@ -3,9 +3,11 @@ 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.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;
@@ -13,83 +15,116 @@ 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.StringUtils;
import me.zhyd.oauth.utils.UrlBuilder;
/**
* 注意:该平台暂时存在问题,请不要使用。待修复完成后会重新发版by yadong.zhang
* 飞书平台,企业自建应用授权登录,原逻辑由 beacon 集成于 1.14.0 版,但最新的飞书 api 已修改,并且飞书平台一直为 {@code Deprecated} 状态
* <p>
* 所以,最终修改该平台的实际发布版本为 1.15.9
*
* @author beacon
* @since 1.14.0
* @author yadong.zhang (yadong.zhang0415(a)gmail.com) 重构业务逻辑 20210101
* @since 1.15.9
*/
@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) {
public AuthFeishuRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.FEISHU, authStateCache);
}
/**
* 获取 app_access_token(企业自建应用)
* <p>
* Token 有效期为 2 小时,在此期间调用该接口 token 不会改变。当 token 有效期小于 30 分的时候,再次请求获取 token 的时候,
* 会生成一个新的 token,与此同时老的 token 依然有效。
*
* @return appAccessToken
*/
private String getAppAccessToken() {
String cacheKey = this.source.getName().concat(":app_access_token:").concat(config.getClientId());
String cacheAppAccessToken = this.authStateCache.get(cacheKey);
if (StringUtils.isNotEmpty(cacheAppAccessToken)) {
return cacheAppAccessToken;
}
String url = "https://open.feishu.cn/open-apis/auth/v3/app_access_token/internal/";
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"));
String response = new HttpUtils(config.getHttpConfig()).post(url, requestObject.toJSONString(), new HttpHeader()
.add("Content-Type", "application/json")).getBody();
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();
String appAccessToken = jsonObject.getString("app_access_token");
// 缓存 app access token
this.authStateCache.cache(cacheKey, appAccessToken, jsonObject.getLongValue("expire") * 1000);
return appAccessToken;
}
@Override
public AuthToken getAccessToken(AuthCallback authCallback) {
JSONObject requestObject = new JSONObject();
requestObject.put("app_access_token", this.getAppAccessToken());
requestObject.put("grant_type", "authorization_code");
requestObject.put("code", authCallback.getCode());
return getToken(requestObject, this.source.accessToken());
}
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
public 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);
.add("Authorization", "Bearer " + accessToken), false).getBody();
JSONObject object = JSON.parseObject(response);
this.checkResponse(object);
JSONObject data = object.getJSONObject("data");
return AuthUser.builder()
.rawUserInfo(object)
.avatar(object.getString("AvatarUrl"))
.username(object.getString("Mobile"))
.email(object.getString("Email"))
.nickname("Name")
.uuid(data.getString("union_id"))
.username(data.getString("name"))
.nickname(data.getString("name"))
.avatar(data.getString("avatar_url"))
.email(data.getString("email"))
.gender(AuthUserGender.UNKNOWN)
.token(authToken)
.source(source.toString())
.build();
}
@Override
public AuthResponse refresh(AuthToken authToken) {
public AuthResponse<AuthToken> refresh(AuthToken authToken) {
JSONObject requestObject = new JSONObject();
requestObject.put("app_id", config.getClientId());
requestObject.put("app_secret", config.getClientSecret());
requestObject.put("app_access_token", this.getAppAccessToken());
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()
return AuthResponse.<AuthToken>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())
.data(getToken(requestObject, this.source.refresh()))
.build();
}
private AuthToken getToken(JSONObject param, String url) {
String response = new HttpUtils(config.getHttpConfig()).post(url, param.toJSONString(), new HttpHeader()
.add("Content-Type", "application/json")).getBody();
JSONObject jsonObject = JSON.parseObject(response);
this.checkResponse(jsonObject);
JSONObject data = jsonObject.getJSONObject("data");
return AuthToken.builder()
.accessToken(data.getString("access_token"))
.refreshToken(data.getString("refresh_token"))
.expireIn(data.getIntValue("expires_in"))
.tokenType(data.getString("token_type"))
.openId(data.getString("open_id"))
.build();
}
@Override
public String authorize(String state) {
return UrlBuilder.fromBaseUrl(source.authorize())
@@ -0,0 +1,118 @@
package me.zhyd.oauth.request;
import com.alibaba.fastjson.JSONObject;
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.scope.AuthFigmaScope;
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.*;
/**
* Figma登录
* @author xiangqian
* @since 1.16.6
*/
public class AuthFigmaRequest extends AuthDefaultRequest {
public AuthFigmaRequest(AuthConfig config) {
super(config, AuthDefaultSource.FIGMA);
}
public AuthFigmaRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.FIGMA, authStateCache);
}
@Override
public String authorize(String state) {
return UrlBuilder.fromBaseUrl(super.authorize(state))
.queryParam("scope", this.getScopes(",", true, AuthScopeUtils.getDefaultScopes(AuthFigmaScope.values())))
.build();
}
@Override
public AuthToken getAccessToken(AuthCallback authCallback) {
HttpHeader header = new HttpHeader()
.add("content-type", "application/x-www-form-urlencoded")
.add("Authorization", "Basic " + Base64Utils.encode(config.getClientId().concat(":").concat(config.getClientSecret())));
String response = new HttpUtils(config.getHttpConfig()).post(super.accessTokenUrl(authCallback.getCode()), null, header, true).getBody();
JSONObject accessTokenObject = JSONObject.parseObject(response);
this.checkResponse(accessTokenObject);
return AuthToken.builder()
.accessToken(accessTokenObject.getString("access_token"))
.refreshToken(accessTokenObject.getString("refresh_token"))
.scope(accessTokenObject.getString("scope"))
.userId(accessTokenObject.getString("user_id"))
.expireIn(accessTokenObject.getIntValue("expires_in"))
.build();
}
@Override
public AuthResponse<AuthToken> refresh(AuthToken authToken) {
HttpHeader header = new HttpHeader().add("content-type", "application/x-www-form-urlencoded");
String response = new HttpUtils(config.getHttpConfig()).post(this.refreshTokenUrl(authToken.getRefreshToken()), null, header, false).getBody();
JSONObject dataObj = JSONObject.parseObject(response);
this.checkResponse(dataObj);
return AuthResponse.<AuthToken>builder()
.code(AuthResponseStatus.SUCCESS.getCode())
.data(AuthToken.builder()
.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())
.build();
}
@Override
protected String refreshTokenUrl(String refreshToken) {
return UrlBuilder.fromBaseUrl(source.refresh())
.queryParam("client_id", config.getClientId())
.queryParam("client_secret", config.getClientSecret())
.queryParam("refresh_token", refreshToken)
.build();
}
@Override
public AuthUser getUserInfo(AuthToken authToken) {
HttpHeader header = new HttpHeader().add("Authorization", "Bearer " + authToken.getAccessToken());
String response = new HttpUtils(config.getHttpConfig()).get(super.userInfoUrl(authToken), null, header, false).getBody();
JSONObject dataObj = JSONObject.parseObject(response);
this.checkResponse(dataObj);
return AuthUser.builder()
.rawUserInfo(dataObj)
.uuid(dataObj.getString("id"))
.username(dataObj.getString("handle"))
.avatar(dataObj.getString("img_url"))
.email(dataObj.getString("email"))
.token(authToken)
.source(source.toString())
.build();
}
/**
* 校验响应结果
*
* @param object 接口返回的结果
*/
private void checkResponse(JSONObject object) {
if (object.containsKey("error")) {
throw new AuthException(object.getString("error") + ":" + object.getString("message"));
}
}
}
@@ -30,7 +30,7 @@ public class AuthGiteeRequest extends AuthDefaultRequest {
}
@Override
protected AuthToken getAccessToken(AuthCallback authCallback) {
public AuthToken getAccessToken(AuthCallback authCallback) {
String response = doPostAuthorizationCode(authCallback.getCode());
JSONObject accessTokenObject = JSONObject.parseObject(response);
this.checkResponse(accessTokenObject);
@@ -44,7 +44,7 @@ public class AuthGiteeRequest extends AuthDefaultRequest {
}
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
public AuthUser getUserInfo(AuthToken authToken) {
String userInfo = doGetUserInfo(authToken);
JSONObject object = JSONObject.parseObject(userInfo);
this.checkResponse(object);
@@ -35,7 +35,7 @@ public class AuthGithubRequest extends AuthDefaultRequest {
}
@Override
protected AuthToken getAccessToken(AuthCallback authCallback) {
public AuthToken getAccessToken(AuthCallback authCallback) {
String response = doPostAuthorizationCode(authCallback.getCode());
Map<String, String> res = GlobalAuthUtils.parseStringToMap(response);
@@ -49,10 +49,10 @@ public class AuthGithubRequest extends AuthDefaultRequest {
}
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
public AuthUser getUserInfo(AuthToken authToken) {
HttpHeader header = new HttpHeader();
header.add("Authorization", "token " + authToken.getAccessToken());
String response = new HttpUtils(config.getHttpConfig()).get(UrlBuilder.fromBaseUrl(source.userInfo()).build(), null, header, false);
String response = new HttpUtils(config.getHttpConfig()).get(UrlBuilder.fromBaseUrl(source.userInfo()).build(), null, header, false).getBody();
JSONObject object = JSONObject.parseObject(response);
this.checkResponse(object.containsKey("error"), object.getString("error_description"));
@@ -30,7 +30,7 @@ public class AuthGitlabRequest extends AuthDefaultRequest {
}
@Override
protected AuthToken getAccessToken(AuthCallback authCallback) {
public AuthToken getAccessToken(AuthCallback authCallback) {
String response = doPostAuthorizationCode(authCallback.getCode());
JSONObject object = JSONObject.parseObject(response);
@@ -46,7 +46,7 @@ public class AuthGitlabRequest extends AuthDefaultRequest {
}
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
public AuthUser getUserInfo(AuthToken authToken) {
String response = doGetUserInfo(authToken);
JSONObject object = JSONObject.parseObject(response);
@@ -33,7 +33,7 @@ public class AuthGoogleRequest extends AuthDefaultRequest {
}
@Override
protected AuthToken getAccessToken(AuthCallback authCallback) {
public AuthToken getAccessToken(AuthCallback authCallback) {
String response = doPostAuthorizationCode(authCallback.getCode());
JSONObject accessTokenObject = JSONObject.parseObject(response);
this.checkResponse(accessTokenObject);
@@ -47,10 +47,10 @@ public class AuthGoogleRequest extends AuthDefaultRequest {
}
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
public AuthUser getUserInfo(AuthToken authToken) {
HttpHeader httpHeader = new HttpHeader();
httpHeader.add("Authorization", "Bearer " + authToken.getAccessToken());
String userInfo = new HttpUtils(config.getHttpConfig()).post(userInfoUrl(authToken), null, httpHeader);
String userInfo = new HttpUtils(config.getHttpConfig()).post(userInfoUrl(authToken), null, httpHeader).getBody();
JSONObject object = JSONObject.parseObject(userInfo);
this.checkResponse(object);
return AuthUser.builder()
@@ -23,10 +23,13 @@ import static me.zhyd.oauth.enums.AuthResponseStatus.SUCCESS;
/**
* 华为授权登录
*
* 当前方式未来可能被废弃,建议使用 {@link AuthHuaweiV3Request}
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0
* @since 1.10.0
*/
@Deprecated
public class AuthHuaweiRequest extends AuthDefaultRequest {
public AuthHuaweiRequest(AuthConfig config) {
@@ -46,15 +49,15 @@ public class AuthHuaweiRequest extends AuthDefaultRequest {
* @see AuthDefaultRequest#authorize(String)
*/
@Override
protected AuthToken getAccessToken(AuthCallback authCallback) {
Map<String, String> form = new HashMap<>(5);
public AuthToken getAccessToken(AuthCallback authCallback) {
Map<String, String> form = new HashMap<>(8);
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);
String response = new HttpUtils(config.getHttpConfig()).post(source.accessToken(), form, false).getBody();
return getAuthToken(response);
}
@@ -66,14 +69,16 @@ public class AuthHuaweiRequest extends AuthDefaultRequest {
* @see AuthDefaultRequest#getAccessToken(AuthCallback)
*/
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
Map<String, String> form = new HashMap<>(4);
public AuthUser getUserInfo(AuthToken authToken) {
Map<String, String> form = new HashMap<>(7);
form.put("nsp_ts", System.currentTimeMillis() + "");
form.put("access_token", authToken.getAccessToken());
form.put("nsp_fmt", "JS");
form.put("nsp_svc", "OpenUP.User.getInfo");
form.put("open_id", "OPENID");
// form.put("nsp_svc", "OpenUP.User.getInfo");
form.put("nsp_svc", "huawei.oauth2.user.getTokenInfo");
String response = new HttpUtils(config.getHttpConfig()).post(source.userInfo(), form, false);
String response = new HttpUtils(config.getHttpConfig()).post(source.userInfo(), form, false).getBody();
JSONObject object = JSONObject.parseObject(response);
this.checkResponse(object);
@@ -99,15 +104,15 @@ public class AuthHuaweiRequest extends AuthDefaultRequest {
* @return AuthResponse
*/
@Override
public AuthResponse refresh(AuthToken authToken) {
Map<String, String> form = new HashMap<>(4);
public AuthResponse<AuthToken> refresh(AuthToken authToken) {
Map<String, String> form = new HashMap<>(7);
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();
String response = new HttpUtils(config.getHttpConfig()).post(source.refresh(), form, false).getBody();
return AuthResponse.<AuthToken>builder().code(SUCCESS.getCode()).data(getAuthToken(response)).build();
}
private AuthToken getAuthToken(String response) {
@@ -0,0 +1,196 @@
package me.zhyd.oauth.request;
import com.alibaba.fastjson.JSONObject;
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.AuthUserGender;
import me.zhyd.oauth.enums.scope.AuthHuaweiV3Scope;
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.*;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import static me.zhyd.oauth.enums.AuthResponseStatus.SUCCESS;
/**
* 华为授权登录
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0
* @since 1.16.7
*/
public class AuthHuaweiV3Request extends AuthDefaultRequest {
public AuthHuaweiV3Request(AuthConfig config) {
super(config, AuthDefaultSource.HUAWEI_V3);
}
public AuthHuaweiV3Request(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.HUAWEI_V3, authStateCache);
}
/**
* 获取access token
*
* @param authCallback 授权成功后的回调参数
* @return token
* @see AuthDefaultRequest#authorize()
* @see AuthDefaultRequest#authorize(String)
*/
@Override
public AuthToken getAccessToken(AuthCallback authCallback) {
Map<String, String> form = new HashMap<>(8);
form.put("grant_type", "authorization_code");
form.put("code", authCallback.getCode());
form.put("client_id", config.getClientId());
form.put("client_secret", config.getClientSecret());
form.put("redirect_uri", config.getRedirectUri());
if (config.isPkce()) {
String cacheKey = this.source.getName().concat(":code_verifier:").concat(authCallback.getState());
String codeVerifier = this.authStateCache.get(cacheKey);
form.put("code_verifier", codeVerifier);
}
HttpHeader httpHeader = new HttpHeader();
httpHeader.add(Constants.CONTENT_TYPE, "application/x-www-form-urlencoded");
String response = new HttpUtils(config.getHttpConfig()).post(source.accessToken(), form, httpHeader, false).getBody();
return getAuthToken(response);
}
/**
* 使用token换取用户信息
*
* @param authToken token信息
* @return 用户信息
* @see AuthDefaultRequest#getAccessToken(AuthCallback)
*/
@Override
public AuthUser getUserInfo(AuthToken authToken) {
String idToken = authToken.getIdToken();
if (StringUtils.isEmpty(idToken)) {
Map<String, String> form = new HashMap<>(7);
form.put("access_token", authToken.getAccessToken());
form.put("getNickName", "1");
form.put("nsp_svc", "GOpen.User.getInfo");
HttpHeader httpHeader = new HttpHeader();
httpHeader.add(Constants.CONTENT_TYPE, "application/x-www-form-urlencoded");
String response = new HttpUtils(config.getHttpConfig()).post(source.userInfo(), form, httpHeader, false).getBody();
JSONObject object = JSONObject.parseObject(response);
this.checkResponse(object);
return AuthUser.builder()
.rawUserInfo(object)
.uuid(object.getString("unionID"))
.username(object.getString("displayName"))
.nickname(object.getString("displayName"))
.gender(AuthUserGender.UNKNOWN)
.avatar(object.getString("headPictureURL"))
.token(authToken)
.source(source.toString())
.build();
}
String payload = new String(Base64.getUrlDecoder().decode(idToken.split("\\.")[1]), StandardCharsets.UTF_8);
JSONObject object = JSONObject.parseObject(payload);
return AuthUser.builder()
.rawUserInfo(object)
.uuid(object.getString("sub"))
.username(object.getString("name"))
.nickname(object.getString("nickname"))
.gender(AuthUserGender.UNKNOWN)
.avatar(object.getString("picture"))
.token(authToken)
.source(source.toString())
.build();
}
/**
* 刷新access token (续期)
*
* @param authToken 登录成功后返回的Token信息
* @return AuthResponse
*/
@Override
public AuthResponse<AuthToken> refresh(AuthToken authToken) {
Map<String, String> form = new HashMap<>(7);
form.put("client_id", config.getClientId());
form.put("client_secret", config.getClientSecret());
form.put("refresh_token", authToken.getRefreshToken());
form.put("grant_type", "refresh_token");
HttpHeader httpHeader = new HttpHeader();
httpHeader.add(Constants.CONTENT_TYPE, "application/x-www-form-urlencoded");
String response = new HttpUtils(config.getHttpConfig()).post(source.refresh(), form, httpHeader, false).getBody();
return AuthResponse.<AuthToken>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"))
.idToken(object.getString("id_token"))
.build();
}
/**
* 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state}
*
* @param state state 验证授权流程的参数,可以防止csrf
* @return 返回授权地址
* @since 1.9.3
*/
@Override
public String authorize(String state) {
String realState = getRealState(state);
UrlBuilder builder = UrlBuilder.fromBaseUrl(super.authorize(realState))
.queryParam("access_type", "offline")
.queryParam("scope", this.getScopes(" ", true, AuthScopeUtils.getDefaultScopes(AuthHuaweiV3Scope.values())));
if (config.isPkce()) {
String cacheKey = this.source.getName().concat(":code_verifier:").concat(realState);
String codeVerifier = PkceUtil.generateCodeVerifier();
String codeChallengeMethod = "S256";
String codeChallenge = PkceUtil.generateCodeChallenge(codeChallengeMethod, codeVerifier);
builder.queryParam("code_challenge", codeChallenge)
.queryParam("code_challenge_method", codeChallengeMethod);
// 缓存 codeVerifier 十分钟
this.authStateCache.cache(cacheKey, codeVerifier, TimeUnit.MINUTES.toMillis(10));
}
return builder.build();
}
/**
* 校验响应结果
*
* @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"));
}
}
}
@@ -39,14 +39,14 @@ public class AuthJdRequest extends AuthDefaultRequest {
}
@Override
protected AuthToken getAccessToken(AuthCallback authCallback) {
public AuthToken getAccessToken(AuthCallback authCallback) {
Map<String, String> params = new HashMap<>(5);
Map<String, String> params = new HashMap<>(7);
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);
String response = new HttpUtils(config.getHttpConfig()).post(source.accessToken(), params, false).getBody();
JSONObject object = JSONObject.parseObject(response);
this.checkResponse(object);
@@ -61,7 +61,7 @@ public class AuthJdRequest extends AuthDefaultRequest {
}
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
public AuthUser getUserInfo(AuthToken authToken) {
UrlBuilder urlBuilder = UrlBuilder.fromBaseUrl(source.userInfo())
.queryParam("access_token", authToken.getAccessToken())
.queryParam("app_key", config.getClientId())
@@ -70,7 +70,7 @@ public class AuthJdRequest extends AuthDefaultRequest {
.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));
String response = new HttpUtils(config.getHttpConfig()).post(urlBuilder.build(true)).getBody();
JSONObject object = JSONObject.parseObject(response);
this.checkResponse(object);
@@ -103,18 +103,18 @@ public class AuthJdRequest extends AuthDefaultRequest {
}
@Override
public AuthResponse refresh(AuthToken oldToken) {
Map<String, String> params = new HashMap<>(5);
public AuthResponse<AuthToken> refresh(AuthToken oldToken) {
Map<String, String> params = new HashMap<>(7);
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);
String response = new HttpUtils(config.getHttpConfig()).post(source.refresh(), params, false).getBody();
JSONObject object = JSONObject.parseObject(response);
this.checkResponse(object);
return AuthResponse.builder()
return AuthResponse.<AuthToken>builder()
.code(AuthResponseStatus.SUCCESS.getCode())
.data(AuthToken.builder()
.accessToken(object.getString("access_token"))
@@ -76,7 +76,7 @@ public class AuthKujialeRequest extends AuthDefaultRequest {
String response = new HttpUtils(config.getHttpConfig()).get(UrlBuilder.fromBaseUrl(source.userInfo())
.queryParam("access_token", authToken.getAccessToken())
.queryParam("open_id", openId)
.build());
.build()).getBody();
JSONObject object = JSONObject.parseObject(response);
if (!"0".equals(object.getString("c"))) {
throw new AuthException(object.getString("m"));
@@ -103,14 +103,14 @@ public class AuthKujialeRequest extends AuthDefaultRequest {
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());
.build()).getBody();
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();
public AuthResponse<AuthToken> refresh(AuthToken authToken) {
String response = new HttpUtils(config.getHttpConfig()).post(refreshTokenUrl(authToken.getRefreshToken())).getBody();
return AuthResponse.<AuthToken>builder().code(AuthResponseStatus.SUCCESS.getCode()).data(getAuthToken(response)).build();
}
}

Some files were not shown because too many files have changed in this diff Show More