Compare commits
147 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3b8337ff21 | |||
| 694bbf1b01 | |||
| bbf63c6056 | |||
| 44d77f3d6a | |||
| 2b6e419943 | |||
| 56bfe2803a | |||
| 89d107f6da | |||
| cdcf9a5e8d | |||
| 354f00660b | |||
| 4b07aecfcf | |||
| 9862f487b4 | |||
| 9153e97398 | |||
| 64e1098519 | |||
| e9135436f9 | |||
| 20731ea77e | |||
| 587d2f4072 | |||
| 602a48b3ce | |||
| 91167bcbf7 | |||
| 8f376129d4 | |||
| 6859b8a949 | |||
| b01704ff78 | |||
| 0731ebd130 | |||
| 5eb8160a42 | |||
| 415c703539 | |||
| 5d521e3ea4 | |||
| 0eafb96a43 | |||
| 6d8b3b5f1d | |||
| 15b1a974f3 | |||
| 74b5b4b3bc | |||
| 6cc414997d | |||
| 7c6f236733 | |||
| bf5a6d1b17 | |||
| 9db9fbdcc8 | |||
| 0acd67e704 | |||
| 992f4a67ac | |||
| 2ee2483aee | |||
| a1c1ab93bd | |||
| eaafd49890 | |||
| 74be7d4c8b | |||
| d62bb6eecf | |||
| b2b007aa4d | |||
| 431adb447f | |||
| 78a318e799 | |||
| 988d3cd293 | |||
| 72e4a1d846 | |||
| 65bb994012 | |||
| 6b758e0e1e | |||
| 02bd057963 | |||
| 4d2a110fea | |||
| 2ef8298278 | |||
| 80ce57bce4 | |||
| 2fed3e5ba2 | |||
| 1d38ce2835 | |||
| 14dd2b6179 | |||
| d6ba3bba10 | |||
| fe328c7375 | |||
| 102521a979 | |||
| 924f0f552b | |||
| 7167a74af2 | |||
| c0ac81df08 | |||
| 56cdd20600 | |||
| 87d8c1d164 | |||
| d1f820514f | |||
| 6d192e94ac | |||
| 02bec576de | |||
| 0d599d75f0 | |||
| 5171fb8148 | |||
| 0025748ba7 | |||
| 87cf45b675 | |||
| bd1192f17f | |||
| a7cb4aefc0 | |||
| 7ffda8737c | |||
| 90a9d56fd0 | |||
| c53c763540 | |||
| dc18304eee | |||
| a558379680 | |||
| 142846b2fa | |||
| 80b59fc13c | |||
| 765127e847 | |||
| 11707e6903 | |||
| 3564c846f4 | |||
| 0bc4c806bf | |||
| 35c754f327 | |||
| 45ebe26f1b | |||
| 30f4a29f48 | |||
| ca8e194c4e | |||
| 81ea4b1620 | |||
| cc74d4a5c8 | |||
| e80304601f | |||
| afddf269e1 | |||
| bac494a7fc | |||
| 7a9d602499 | |||
| f3a8cf675c | |||
| 571466f079 | |||
| e1b912bdf6 | |||
| 02674049d0 | |||
| f1714726f5 | |||
| a3108caccf | |||
| e134dd06b0 | |||
| 172ba7be55 | |||
| f9e3053ecc | |||
| 99c9ba2854 | |||
| 544ffd5372 | |||
| 522ebbfa7f | |||
| 497498dbf4 | |||
| 62cd7af619 | |||
| 3a3f69692c | |||
| c87015e34f | |||
| 96773dd92d | |||
| 644ef02264 | |||
| 4c205a9957 | |||
| ea70c5fc81 | |||
| 9ced525309 | |||
| 2de0ad5013 | |||
| 23b7bcf43d | |||
| 976b7d8b2b | |||
| 80132b69e7 | |||
| 95158654a9 | |||
| f2e609192a | |||
| 8c8f7a27a2 | |||
| 8283124c57 | |||
| 39b113b194 | |||
| e45ef2ec31 | |||
| d114368a0d | |||
| 0047cde50e | |||
| f2c1c0f8a6 | |||
| 21e23aadb9 | |||
| db3f7da181 | |||
| 004e5a180f | |||
| 50c31e5dea | |||
| 7d6049da67 | |||
| 9a24553acd | |||
| d75d91db0d | |||
| e1a4688ac0 | |||
| b1d3790ae1 | |||
| e5548b0173 | |||
| ebf39627dd | |||
| 74ee17b242 | |||
| b77de0bd0c | |||
| e55033f4f5 | |||
| 37b7784f89 | |||
| 7cdc719166 | |||
| 5073f82897 | |||
| 9971793f0c | |||
| 9fc3131640 | |||
| 3f4436bcb6 | |||
| 0678202baa |
@@ -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']
|
||||
@@ -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
|
||||
- [ ] 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
|
||||
|
||||
|
||||
@@ -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 }}
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -1,3 +1,87 @@
|
||||
## 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.83,1.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
|
||||
@@ -117,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)
|
||||
@@ -170,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文档等内容
|
||||
@@ -250,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))
|
||||
@@ -261,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.10,hutool@5.0.5,fastjson@1.2.62,alipay@4.8.10.ALL([PR#11@Gitee](https://gitee.com/yadong.zhang/JustAuth/pulls/11))
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<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>
|
||||
@@ -8,13 +8,16 @@
|
||||
<a target="_blank" href="https://search.maven.org/search?q=JustAuth">
|
||||
<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>
|
||||
</a>
|
||||
<a target="_blank" href="https://www.oracle.com/technetwork/java/javase/downloads/index.html">
|
||||
<img src="https://img.shields.io/badge/JDK-1.8+-green.svg" ></img>
|
||||
</a>
|
||||
<a target="_blank" href="https://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/justauth/JustAuth">
|
||||
@@ -40,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
|
||||
|
||||
@@ -49,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.16.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: 
|
||||
> - SNAPSHOT: 
|
||||
|
||||
|
||||
- Add http dependency(Only 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
|
||||
@@ -107,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
|
||||
@@ -121,11 +180,11 @@ I look forward to your joining us.
|
||||
|
||||
## Contributors
|
||||
|
||||
[contributors](https://justauth.wiki/contributors.html)
|
||||
[contributors](https://www.justauth.cn/contributors.html)
|
||||
|
||||
## Change Logs
|
||||
|
||||
[CHANGELOGS](https://justauth.wiki/update.html)
|
||||
[CHANGELOGS](https://www.justauth.cn/update.html)
|
||||
|
||||
## Recommend
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<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>
|
||||
@@ -8,13 +8,16 @@
|
||||
<a target="_blank" href="https://search.maven.org/search?q=JustAuth">
|
||||
<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>
|
||||
</a>
|
||||
<a target="_blank" href="https://www.oracle.com/technetwork/java/javase/downloads/index.html">
|
||||
<img src="https://img.shields.io/badge/JDK-1.8+-green.svg" ></img>
|
||||
</a>
|
||||
<a target="_blank" href="https://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/justauth/JustAuth">
|
||||
@@ -37,13 +40,13 @@
|
||||
|
||||
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" target="_blank">已集成的平台</a>
|
||||
JustAuth 集成了诸如:Github、Gitee、支付宝、新浪微博、微信、Google、Facebook、Twitter、StackOverflow等国内外数十家第三方平台。更多请参考<a href="https://www.justauth.cn" target="_blank">已集成的平台</a>
|
||||
|
||||
## 有哪些特点?
|
||||
|
||||
@@ -52,24 +55,94 @@ JustAuth 集成了诸如:Github、Gitee、支付宝、新浪微博、微信、
|
||||
|
||||
## 有哪些功能?
|
||||
|
||||
- 集成国内外数十家第三方平台,实现快速接入。<a href="https://justauth.wiki/quickstart/how-to-use.html" target="_blank">参考文档</a>
|
||||
- 自定义 State 缓存,支持各种分布式缓存组件。<a href="https://justauth.wiki/features/customize-the-state-cache.html" target="_blank">参考文档</a>
|
||||
- 自定义 OAuth 平台,更容易适配自有的 OAuth 服务。<a href="https://justauth.wiki/features/customize-the-oauth.html" target="_blank">参考文档</a>
|
||||
- 自定义 Http 实现,选择权完全交给开发者,不会单独依赖某一具体实现。<a href="https://justauth.wiki/quickstart/how-to-use.html#%E4%BD%BF%E7%94%A8%E6%96%B9%E5%BC%8F" target="_blank">参考文档</a>
|
||||
- 自定义 Scope,支持更完善的授权体系。<a href="https://justauth.wiki/features/customize-scopes.html" 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.16.2</version>
|
||||
<version>{latest-version}</version>
|
||||
</dependency>
|
||||
```
|
||||
- 调用api
|
||||
|
||||
> **latest-version** 可选:
|
||||
> - 稳定版:
|
||||
> - 快照版:
|
||||
> > 注意:快照版本是功能的尝鲜,并不保证稳定性。请勿在生产环境中使用。
|
||||
>
|
||||
> <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()
|
||||
@@ -84,47 +157,72 @@ 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(仅限支付宝))、state,1.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://justauth.wiki/sponsor.html)
|
||||
[我要赞助](https://www.justauth.cn/sponsor.html)
|
||||
|
||||
## JustAuth 的用户
|
||||
有很多公司、组织和个人把 JustAuth 用于学习、研究、生产环境和商业产品中,包括(但不限于):
|
||||

|
||||
|
||||
[](https://www.mochiwang.com "给作者提供云写作的一个工具")
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
怎么没有我?[登记](https://gitee.com/yadong.zhang/JustAuth/issues/IZ2T7)
|
||||
@@ -136,7 +234,7 @@ authRequest.login(callback);
|
||||
- `mica` SpringBoot 微服务高效开发工具集: [https://github.com/lets-mica/mica](https://github.com/lets-mica/mica)
|
||||
- `sureness` 面向restful api的高性能认证鉴权框架:[sureness](https://github.com/usthe/sureness)
|
||||
|
||||
更多推荐,请参考:[JustAuth - 开源推荐](https://justauth.wiki)
|
||||
更多推荐,请参考:[JustAuth - 开源推荐](https://www.justauth.cn)
|
||||
|
||||
## 鸣谢
|
||||
|
||||
@@ -148,8 +246,8 @@ authRequest.login(callback);
|
||||
|
||||
## 其他
|
||||
|
||||
- [CONTRIBUTORS](https://justauth.wiki/contributors.html)
|
||||
- [CHANGELOGS](https://justauth.wiki/update.html)
|
||||
- [CONTRIBUTORS](https://www.justauth.cn/contributors.html)
|
||||
- [CHANGELOGS](https://www.justauth.cn/update.html)
|
||||
- [PLAN](https://gitee.com/yadong.zhang/JustAuth/issues/IUGRK)
|
||||
|
||||
## 贡献者列表
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
git pull origin dev && git pull github dev
|
||||
@@ -0,0 +1 @@
|
||||
git pull origin master && git pull github master
|
||||
@@ -0,0 +1 @@
|
||||
git push origin dev && git push github dev
|
||||
@@ -0,0 +1 @@
|
||||
git push origin master && git push github master
|
||||
@@ -1 +1 @@
|
||||
1.16.2
|
||||
1.16.7
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
window.location.href = "https://justauth.wiki";
|
||||
window.location.href = "https://www.justauth.cn";
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
Before Width: | Height: | Size: 100 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 7.4 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 9.7 KiB |
|
After Width: | Height: | Size: 6.0 KiB |
|
After Width: | Height: | Size: 6.1 KiB |
|
After Width: | Height: | Size: 7.9 KiB |
|
After Width: | Height: | Size: 7.3 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
@@ -96,6 +96,10 @@ _注:非全部平台,部分平台可能不存在图例_
|
||||
|
||||

|
||||
|
||||
#### 授权afdian
|
||||
|
||||

|
||||
|
||||
#### 授权Twitter
|
||||
|
||||

|
||||
|
||||
@@ -15,6 +15,12 @@ case "$1" in
|
||||
'updv')
|
||||
bin/updVersion.sh $2
|
||||
;;
|
||||
'ppd')
|
||||
bin/pull-dev.sh
|
||||
;;
|
||||
'pp')
|
||||
bin/pull.sh
|
||||
;;
|
||||
'pd')
|
||||
bin/push-dev.sh
|
||||
;;
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
<groupId>me.zhyd.oauth</groupId>
|
||||
<artifactId>JustAuth</artifactId>
|
||||
<version>1.16.2</version>
|
||||
<version>1.16.7</version>
|
||||
|
||||
<name>JustAuth</name>
|
||||
<url>https://gitee.com/yadong.zhang/JustAuth</url>
|
||||
@@ -57,12 +57,14 @@
|
||||
<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.3</simple-http.version>
|
||||
<lombok-version>1.18.10</lombok-version>
|
||||
<junit-version>4.13.1</junit-version>
|
||||
<fastjson-version>1.2.76</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.84</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>
|
||||
|
||||
@@ -157,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>
|
||||
@@ -222,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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -38,7 +38,10 @@ public class AuthConfig {
|
||||
/**
|
||||
* 支付宝公钥:当选择支付宝登录时,该值可用
|
||||
* 对应“RSA2(SHA256)密钥”中的“支付宝公钥”
|
||||
*
|
||||
* @deprecated 请使用AuthAlipayRequest的构造方法设置"alipayPublicKey"
|
||||
*/
|
||||
@Deprecated
|
||||
private String alipayPublicKey;
|
||||
|
||||
/**
|
||||
@@ -65,6 +68,13 @@ public class AuthConfig {
|
||||
*/
|
||||
private String agentId;
|
||||
|
||||
/**
|
||||
* 企业微信第三方授权用户类型,member|admin
|
||||
*
|
||||
* @since 1.10.0
|
||||
*/
|
||||
private String usertype;
|
||||
|
||||
/**
|
||||
* 域名前缀。
|
||||
* <p>
|
||||
@@ -171,4 +181,75 @@ public class AuthConfig {
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package me.zhyd.oauth.config;
|
||||
|
||||
import me.zhyd.oauth.enums.AuthResponseStatus;
|
||||
import me.zhyd.oauth.exception.AuthException;
|
||||
import me.zhyd.oauth.request.*;
|
||||
|
||||
/**
|
||||
* JustAuth内置的各api需要的url, 用枚举类分平台类型管理
|
||||
@@ -28,6 +29,11 @@ public enum AuthDefaultSource implements AuthSource {
|
||||
public String userInfo() {
|
||||
return "https://api.github.com/user";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends AuthDefaultRequest> getTargetClass() {
|
||||
return AuthGithubRequest.class;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 新浪微博
|
||||
@@ -52,6 +58,11 @@ public enum AuthDefaultSource implements AuthSource {
|
||||
public String revoke() {
|
||||
return "https://api.weibo.com/oauth2/revokeoauth2";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends AuthDefaultRequest> getTargetClass() {
|
||||
return AuthWeiboRequest.class;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* gitee
|
||||
@@ -71,6 +82,11 @@ public enum AuthDefaultSource implements AuthSource {
|
||||
public String userInfo() {
|
||||
return "https://gitee.com/api/v5/user";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends AuthDefaultRequest> getTargetClass() {
|
||||
return AuthGiteeRequest.class;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 钉钉扫码登录
|
||||
@@ -90,6 +106,35 @@ public enum AuthDefaultSource implements AuthSource {
|
||||
public String userInfo() {
|
||||
return "https://oapi.dingtalk.com/sns/getuserinfo_bycode";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends AuthDefaultRequest> getTargetClass() {
|
||||
return AuthDingTalkRequest.class;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 新版钉钉扫码登录
|
||||
*/
|
||||
DINGTALK_V2 {
|
||||
@Override
|
||||
public String authorize() {
|
||||
return "https://login.dingtalk.com/oauth2/challenge.htm";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String accessToken() {
|
||||
return "https://api.dingtalk.com/v1.0/oauth2/userAccessToken";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String userInfo() {
|
||||
return "https://api.dingtalk.com/v1.0/contact/users/me";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends AuthDefaultRequest> getTargetClass() {
|
||||
return AuthDingTalkV2Request.class;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 钉钉账号登录
|
||||
@@ -109,6 +154,11 @@ public enum AuthDefaultSource implements AuthSource {
|
||||
public String userInfo() {
|
||||
return DINGTALK.userInfo();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends AuthDefaultRequest> getTargetClass() {
|
||||
return AuthDingTalkAccountRequest.class;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 百度
|
||||
@@ -138,6 +188,11 @@ public enum AuthDefaultSource implements AuthSource {
|
||||
public String refresh() {
|
||||
return "https://openapi.baidu.com/oauth/2.0/token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends AuthDefaultRequest> getTargetClass() {
|
||||
return AuthBaiduRequest.class;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* csdn
|
||||
@@ -157,6 +212,11 @@ public enum AuthDefaultSource implements AuthSource {
|
||||
public String userInfo() {
|
||||
return "https://api.csdn.net/user/getinfo";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends AuthDefaultRequest> getTargetClass() {
|
||||
return AuthCsdnRequest.class;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Coding,
|
||||
@@ -179,6 +239,11 @@ public enum AuthDefaultSource implements AuthSource {
|
||||
public String userInfo() {
|
||||
return "https://%s.coding.net/api/account/current_user";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends AuthDefaultRequest> getTargetClass() {
|
||||
return AuthCodingRequest.class;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* oschina 开源中国
|
||||
@@ -198,6 +263,11 @@ public enum AuthDefaultSource implements AuthSource {
|
||||
public String userInfo() {
|
||||
return "https://www.oschina.net/action/openapi/user";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends AuthDefaultRequest> getTargetClass() {
|
||||
return AuthOschinaRequest.class;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 支付宝
|
||||
@@ -217,6 +287,11 @@ public enum AuthDefaultSource implements AuthSource {
|
||||
public String userInfo() {
|
||||
return "https://openapi.alipay.com/gateway.do";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends AuthDefaultRequest> getTargetClass() {
|
||||
return AuthAlipayRequest.class;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* QQ
|
||||
@@ -241,6 +316,11 @@ public enum AuthDefaultSource implements AuthSource {
|
||||
public String refresh() {
|
||||
return "https://graph.qq.com/oauth2.0/token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends AuthDefaultRequest> getTargetClass() {
|
||||
return AuthQqRequest.class;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 微信开放平台
|
||||
@@ -265,6 +345,11 @@ public enum AuthDefaultSource implements AuthSource {
|
||||
public String refresh() {
|
||||
return "https://api.weixin.qq.com/sns/oauth2/refresh_token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends AuthDefaultRequest> getTargetClass() {
|
||||
return AuthWeChatOpenRequest.class;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 微信公众平台
|
||||
@@ -289,6 +374,11 @@ public enum AuthDefaultSource implements AuthSource {
|
||||
public String refresh() {
|
||||
return "https://api.weixin.qq.com/sns/oauth2/refresh_token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends AuthDefaultRequest> getTargetClass() {
|
||||
return AuthWeChatMpRequest.class;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 淘宝
|
||||
@@ -308,9 +398,15 @@ public enum AuthDefaultSource implements AuthSource {
|
||||
public String userInfo() {
|
||||
throw new AuthException(AuthResponseStatus.UNSUPPORTED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends AuthDefaultRequest> getTargetClass() {
|
||||
return AuthTaobaoRequest.class;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Google
|
||||
* 端点地址:https://accounts.google.com/.well-known/openid-configuration
|
||||
*/
|
||||
GOOGLE {
|
||||
@Override
|
||||
@@ -320,12 +416,17 @@ public enum AuthDefaultSource implements AuthSource {
|
||||
|
||||
@Override
|
||||
public String accessToken() {
|
||||
return "https://www.googleapis.com/oauth2/v4/token";
|
||||
return "https://oauth2.googleapis.com/token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String userInfo() {
|
||||
return "https://www.googleapis.com/oauth2/v3/userinfo";
|
||||
return "https://openidconnect.googleapis.com/v1/userinfo";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends AuthDefaultRequest> getTargetClass() {
|
||||
return AuthGoogleRequest.class;
|
||||
}
|
||||
},
|
||||
/**
|
||||
@@ -334,17 +435,22 @@ public enum AuthDefaultSource implements AuthSource {
|
||||
FACEBOOK {
|
||||
@Override
|
||||
public String authorize() {
|
||||
return "https://www.facebook.com/v10.0/dialog/oauth";
|
||||
return "https://www.facebook.com/v18.0/dialog/oauth";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String accessToken() {
|
||||
return "https://graph.facebook.com/v10.0/oauth/access_token";
|
||||
return "https://graph.facebook.com/v18.0/oauth/access_token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String userInfo() {
|
||||
return "https://graph.facebook.com/v10.0/me";
|
||||
return "https://graph.facebook.com/v18.0/me";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends AuthDefaultRequest> getTargetClass() {
|
||||
return AuthFacebookRequest.class;
|
||||
}
|
||||
},
|
||||
/**
|
||||
@@ -370,6 +476,11 @@ public enum AuthDefaultSource implements AuthSource {
|
||||
public String refresh() {
|
||||
return "https://open.douyin.com/oauth/refresh_token/";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends AuthDefaultRequest> getTargetClass() {
|
||||
return AuthDouyinRequest.class;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 领英
|
||||
@@ -394,6 +505,11 @@ public enum AuthDefaultSource implements AuthSource {
|
||||
public String refresh() {
|
||||
return "https://www.linkedin.com/oauth/v2/accessToken";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends AuthDefaultRequest> getTargetClass() {
|
||||
return AuthLinkedinRequest.class;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 微软
|
||||
@@ -401,12 +517,12 @@ public enum AuthDefaultSource implements AuthSource {
|
||||
MICROSOFT {
|
||||
@Override
|
||||
public String authorize() {
|
||||
return "https://login.microsoftonline.com/common/oauth2/v2.0/authorize";
|
||||
return "https://login.microsoftonline.com/%s/oauth2/v2.0/authorize";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String accessToken() {
|
||||
return "https://login.microsoftonline.com/common/oauth2/v2.0/token";
|
||||
return "https://login.microsoftonline.com/%s/oauth2/v2.0/token";
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -416,8 +532,40 @@ public enum AuthDefaultSource implements AuthSource {
|
||||
|
||||
@Override
|
||||
public String refresh() {
|
||||
return "https://login.microsoftonline.com/common/oauth2/v2.0/token";
|
||||
return "https://login.microsoftonline.com/%s/oauth2/v2.0/token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends AuthDefaultRequest> getTargetClass() {
|
||||
return AuthMicrosoftRequest.class;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 微软中国(世纪互联)
|
||||
*/
|
||||
MICROSOFT_CN {
|
||||
@Override
|
||||
public String authorize() {
|
||||
return "https://login.partner.microsoftonline.cn/%s/oauth2/v2.0/authorize";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String accessToken() {
|
||||
return "https://login.partner.microsoftonline.cn/%s/oauth2/v2.0/token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String userInfo() {
|
||||
return "https://microsoftgraph.chinacloudapi.cn/v1.0/me";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String refresh() {
|
||||
return "https://login.partner.microsoftonline.cn/%s/oauth2/v2.0/token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends AuthDefaultRequest> getTargetClass() { return AuthMicrosoftCnRequest.class; }
|
||||
},
|
||||
/**
|
||||
* 小米
|
||||
@@ -442,6 +590,11 @@ public enum AuthDefaultSource implements AuthSource {
|
||||
public String refresh() {
|
||||
return "https://account.xiaomi.com/oauth2/token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends AuthDefaultRequest> getTargetClass() {
|
||||
return AuthMiRequest.class;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 今日头条
|
||||
@@ -461,6 +614,11 @@ public enum AuthDefaultSource implements AuthSource {
|
||||
public String userInfo() {
|
||||
return "https://open.snssdk.com/data/user_profile";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends AuthDefaultRequest> getTargetClass() {
|
||||
return AuthToutiaoRequest.class;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Teambition
|
||||
@@ -485,6 +643,11 @@ public enum AuthDefaultSource implements AuthSource {
|
||||
public String userInfo() {
|
||||
return "https://api.teambition.com/users/me";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends AuthDefaultRequest> getTargetClass() {
|
||||
return AuthTeambitionRequest.class;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -510,6 +673,11 @@ public enum AuthDefaultSource implements AuthSource {
|
||||
public String userInfo() {
|
||||
return "https://api.renren.com/v2/user/get";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends AuthDefaultRequest> getTargetClass() {
|
||||
return AuthRenrenRequest.class;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -530,6 +698,11 @@ public enum AuthDefaultSource implements AuthSource {
|
||||
public String userInfo() {
|
||||
return "https://api.pinterest.com/v1/me";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends AuthDefaultRequest> getTargetClass() {
|
||||
return AuthPinterestRequest.class;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -550,13 +723,21 @@ public enum AuthDefaultSource implements AuthSource {
|
||||
public String userInfo() {
|
||||
return "https://api.stackexchange.com/2.2/me";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends AuthDefaultRequest> getTargetClass() {
|
||||
return AuthStackOverflowRequest.class;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 华为
|
||||
*
|
||||
* 当前方式未来可能被废弃,建议使用 {@link this#HUAWEI_V3}
|
||||
*
|
||||
* @since 1.10.0
|
||||
*/
|
||||
@Deprecated
|
||||
HUAWEI {
|
||||
@Override
|
||||
public String authorize() {
|
||||
@@ -577,6 +758,43 @@ public enum AuthDefaultSource implements AuthSource {
|
||||
public String refresh() {
|
||||
return "https://oauth-login.cloud.huawei.com/oauth2/v2/token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends AuthDefaultRequest> getTargetClass() {
|
||||
return AuthHuaweiRequest.class;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 华为最新版本的 API
|
||||
*
|
||||
* @since 1.16.7
|
||||
*/
|
||||
HUAWEI_V3 {
|
||||
@Override
|
||||
public String authorize() {
|
||||
return "https://oauth-login.cloud.huawei.com/oauth2/v3/authorize";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String accessToken() {
|
||||
return "https://oauth-login.cloud.huawei.com/oauth2/v3/token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String userInfo() {
|
||||
return "https://account.cloud.huawei.com/rest.php";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String refresh() {
|
||||
return "https://oauth-login.cloud.huawei.com/oauth2/v3/token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends AuthDefaultRequest> getTargetClass() {
|
||||
return AuthHuaweiV3Request.class;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -599,8 +817,77 @@ public enum AuthDefaultSource implements AuthSource {
|
||||
public String userInfo() {
|
||||
return "https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo";
|
||||
}
|
||||
},
|
||||
|
||||
@Override
|
||||
public Class<? extends AuthDefaultRequest> getTargetClass() {
|
||||
return AuthWeChatEnterpriseQrcodeRequest.class;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 新版企业微信 Web 登录(扫码),参考 <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
|
||||
*/
|
||||
WECHAT_ENTERPRISE_V2 {
|
||||
@Override
|
||||
public String authorize() {
|
||||
return "https://login.work.weixin.qq.com/wwlogin/sso/login";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String accessToken() {
|
||||
return "https://qyapi.weixin.qq.com/cgi-bin/gettoken";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String userInfo() {
|
||||
return "https://qyapi.weixin.qq.com/cgi-bin/auth/getuserinfo";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends AuthDefaultRequest> getTargetClass() {
|
||||
return AuthWeChatEnterpriseQrcodeV2Request.class;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 企业微信二维码第三方登录
|
||||
*/
|
||||
WECHAT_ENTERPRISE_QRCODE_THIRD {
|
||||
/**
|
||||
* 授权的api
|
||||
*
|
||||
* @return url
|
||||
*/
|
||||
@Override
|
||||
public String authorize() {
|
||||
return "https://open.work.weixin.qq.com/wwopen/sso/3rd_qrConnect";
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取accessToken的api
|
||||
*
|
||||
* @return url
|
||||
*/
|
||||
@Override
|
||||
public String accessToken() {
|
||||
return "https://qyapi.weixin.qq.com/cgi-bin/service/get_provider_token";
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户信息的api
|
||||
*
|
||||
* @return url
|
||||
*/
|
||||
@Override
|
||||
public String userInfo() {
|
||||
return "https://qyapi.weixin.qq.com/cgi-bin/service/get_login_info";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends AuthDefaultRequest> getTargetClass() {
|
||||
return AuthWeChatEnterpriseThirdQrcodeRequest.class;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 企业微信网页登录
|
||||
*/
|
||||
@@ -619,6 +906,11 @@ public enum AuthDefaultSource implements AuthSource {
|
||||
public String userInfo() {
|
||||
return "https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends AuthDefaultRequest> getTargetClass() {
|
||||
return AuthWeChatEnterpriseWebRequest.class;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -646,6 +938,11 @@ public enum AuthDefaultSource implements AuthSource {
|
||||
public String refresh() {
|
||||
return "https://oauth.kujiale.com/oauth2/auth/token/refresh";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends AuthDefaultRequest> getTargetClass() {
|
||||
return AuthKujialeRequest.class;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -668,6 +965,11 @@ public enum AuthDefaultSource implements AuthSource {
|
||||
public String userInfo() {
|
||||
return "https://gitlab.com/api/v4/user";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends AuthDefaultRequest> getTargetClass() {
|
||||
return AuthGitlabRequest.class;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -695,6 +997,11 @@ public enum AuthDefaultSource implements AuthSource {
|
||||
public String refresh() {
|
||||
return "https://openapi.waimai.meituan.com/oauth/refresh_token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends AuthDefaultRequest> getTargetClass() {
|
||||
return AuthMeituanRequest.class;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -724,6 +1031,11 @@ public enum AuthDefaultSource implements AuthSource {
|
||||
public String refresh() {
|
||||
return "https://open-api.shop.ele.me/token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends AuthDefaultRequest> getTargetClass() {
|
||||
return AuthElemeRequest.class;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -746,6 +1058,11 @@ public enum AuthDefaultSource implements AuthSource {
|
||||
public String userInfo() {
|
||||
return "https://api.twitter.com/1.1/account/verify_credentials.json";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends AuthDefaultRequest> getTargetClass() {
|
||||
return AuthTwitterRequest.class;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -775,6 +1092,11 @@ public enum AuthDefaultSource implements AuthSource {
|
||||
public String refresh() {
|
||||
return "https://open.feishu.cn/open-apis/authen/v1/refresh_access_token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends AuthDefaultRequest> getTargetClass() {
|
||||
return AuthFeishuRequest.class;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 京东
|
||||
@@ -801,6 +1123,11 @@ public enum AuthDefaultSource implements AuthSource {
|
||||
public String refresh() {
|
||||
return "https://open-oauth.jd.com/oauth2/refresh_token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends AuthDefaultRequest> getTargetClass() {
|
||||
return AuthJdRequest.class;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -826,6 +1153,11 @@ public enum AuthDefaultSource implements AuthSource {
|
||||
public String refresh() {
|
||||
return "https://oauth.aliyun.com/v1/token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends AuthDefaultRequest> getTargetClass() {
|
||||
return AuthAliyunRequest.class;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -851,6 +1183,11 @@ public enum AuthDefaultSource implements AuthSource {
|
||||
public String refresh() {
|
||||
return "https://oauth.aliyun.com/v1/token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends AuthDefaultRequest> getTargetClass() {
|
||||
return AuthXmlyRequest.class;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -878,6 +1215,11 @@ public enum AuthDefaultSource implements AuthSource {
|
||||
public String refresh() {
|
||||
return "https://api.amazon.com/auth/o2/token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends AuthDefaultRequest> getTargetClass() {
|
||||
return AuthAmazonRequest.class;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Slack
|
||||
@@ -911,6 +1253,11 @@ public enum AuthDefaultSource implements AuthSource {
|
||||
public String revoke() {
|
||||
return "https://slack.com/api/auth.revoke";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends AuthDefaultRequest> getTargetClass() {
|
||||
return AuthSlackRequest.class;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* line
|
||||
@@ -942,6 +1289,11 @@ public enum AuthDefaultSource implements AuthSource {
|
||||
public String revoke() {
|
||||
return "https://api.line.me/oauth2/v2.1/revoke";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends AuthDefaultRequest> getTargetClass() {
|
||||
return AuthLineRequest.class;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Okta,
|
||||
@@ -975,6 +1327,11 @@ public enum AuthDefaultSource implements AuthSource {
|
||||
public String revoke() {
|
||||
return "https://%s.okta.com/oauth2/%s/v1/revoke";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends AuthDefaultRequest> getTargetClass() {
|
||||
return AuthOktaRequest.class;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 程序员客栈
|
||||
@@ -996,5 +1353,146 @@ public enum AuthDefaultSource implements AuthSource {
|
||||
public String userInfo() {
|
||||
return "https://www.proginn.com/openapi/user/basic_info";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends AuthDefaultRequest> getTargetClass() {
|
||||
return AuthProginnRequest.class;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 爱发电 <a href="https://afdian.net/">爱发电</a>
|
||||
*/
|
||||
AFDIAN {
|
||||
@Override
|
||||
public String authorize() {
|
||||
return "https://afdian.net/oauth2/authorize";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String accessToken() {
|
||||
return "https://afdian.net/api/oauth2/access_token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String userInfo() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends AuthDefaultRequest> getTargetClass() {
|
||||
return AuthProginnRequest.class;
|
||||
}
|
||||
},
|
||||
|
||||
APPLE {
|
||||
@Override
|
||||
public String authorize() {
|
||||
return "https://appleid.apple.com/auth/authorize";
|
||||
}
|
||||
|
||||
/**
|
||||
* @see <a href="https://developer.apple.com/documentation/sign_in_with_apple/generate_and_validate_tokens">generate_and_validate_tokens</a>
|
||||
*/
|
||||
@Override
|
||||
public String accessToken() {
|
||||
return "https://appleid.apple.com/auth/token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String userInfo() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends AuthDefaultRequest> getTargetClass() {
|
||||
return AuthAppleRequest.class;
|
||||
}
|
||||
},
|
||||
|
||||
FIGMA{
|
||||
@Override
|
||||
public String authorize() {
|
||||
return "https://www.figma.com/oauth";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String accessToken() {
|
||||
return "https://www.figma.com/api/oauth/token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String userInfo() {
|
||||
return "https://api.figma.com/v1/me";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String refresh() {
|
||||
return "https://www.figma.com/api/oauth/refresh";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends AuthDefaultRequest> getTargetClass() {
|
||||
return AuthFigmaRequest.class;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 微信小程序授权登录
|
||||
* @since yudaocode
|
||||
*/
|
||||
WECHAT_MINI_PROGRAM {
|
||||
|
||||
@Override
|
||||
public String authorize() {
|
||||
// 参见 https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/login.html 文档
|
||||
throw new UnsupportedOperationException("不支持获取授权 url,请使用小程序内置函数 wx.login() 登录获取 code");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String accessToken() {
|
||||
// 参见 https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/login/auth.code2Session.html 文档
|
||||
// 获取 openid, unionId , session_key 等字段
|
||||
return "https://api.weixin.qq.com/sns/jscode2session";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String userInfo() {
|
||||
// 参见 https://developers.weixin.qq.com/miniprogram/dev/api/open-api/user-info/wx.getUserProfile.html 文档
|
||||
throw new UnsupportedOperationException("不支持获取用户信息 url,请使用小程序内置函数 wx.getUserProfile() 获取用户信息");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends AuthDefaultRequest> getTargetClass() {
|
||||
return AuthWechatMiniProgramRequest.class;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* QQ小程序授权登录
|
||||
*/
|
||||
QQ_MINI_PROGRAM {
|
||||
@Override
|
||||
public String authorize() {
|
||||
// 参见 https://q.qq.com/wiki/develop/miniprogram/frame/open_ability/open_userinfo.html 文档
|
||||
throw new UnsupportedOperationException("不支持获取授权 url,请使用小程序内置函数 qq.login() 登录获取 code");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String accessToken() {
|
||||
// 参见 https://q.qq.com/wiki/develop/miniprogram/server/open_port/port_login.html 文档
|
||||
// 获取 openid, unionId , session_key 等字段
|
||||
return "https://api.q.qq.com/sns/jscode2session";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String userInfo() {
|
||||
// 参见 https://q.qq.com/wiki/develop/miniprogram/API/open_port/port_userinfo.html 文档
|
||||
throw new UnsupportedOperationException("不支持获取用户信息 url,请使用小程序内置函数 qq.getUserInfo() 获取用户信息");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends AuthDefaultRequest> getTargetClass() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -29,6 +29,11 @@ public enum AuthResponseStatus {
|
||||
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 final int code;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -17,18 +17,19 @@ public enum AuthFacebookScope implements AuthScope {
|
||||
/**
|
||||
* {@code scope} 含义,以{@code description} 为准
|
||||
*/
|
||||
EMAIL("email", "获取用户的邮箱", true),
|
||||
USER_AGE_RANGE("user_age_range", "允许应用程序访问用户的年龄范围", true),
|
||||
USER_BIRTHDAY("user_birthday", "获取用户的生日", true),
|
||||
USER_FRIENDS("user_friends", "获取用户的好友列表", true),
|
||||
USER_GENDER("user_gender", "获取用户的性别", true),
|
||||
USER_HOMETOWN("user_hometown", "获取用户的家乡信息", true),
|
||||
USER_LIKES("user_likes", "获取用户的喜欢列表", true),
|
||||
PUBLIC_PROFILE("public_profile", "权限允许应用读取用户默认的公开资料", true),
|
||||
EMAIL("email", "获取用户的邮箱", false),
|
||||
USER_AGE_RANGE("user_age_range", "允许应用程序访问用户的年龄范围", false),
|
||||
USER_BIRTHDAY("user_birthday", "获取用户的生日", false),
|
||||
USER_FRIENDS("user_friends", "获取用户的好友列表", false),
|
||||
USER_GENDER("user_gender", "获取用户的性别", false),
|
||||
USER_HOMETOWN("user_hometown", "获取用户的家乡信息", false),
|
||||
USER_LIKES("user_likes", "获取用户的喜欢列表", false),
|
||||
USER_LINK("user_link", "获取用户的个人链接", true),
|
||||
USER_LOCATION("user_location", "获取用户的位置信息", true),
|
||||
USER_PHOTOS("user_photos", "获取用户的相册信息", true),
|
||||
USER_POSTS("user_posts", "获取用户发布的内容", true),
|
||||
USER_VIDEOS("user_videos", "获取用户上传的视频信息", true),
|
||||
USER_LOCATION("user_location", "获取用户的位置信息", false),
|
||||
USER_PHOTOS("user_photos", "获取用户的相册信息", false),
|
||||
USER_POSTS("user_posts", "获取用户发布的内容", false),
|
||||
USER_VIDEOS("user_videos", "获取用户上传的视频信息", false),
|
||||
GROUPS_ACCESS_MEMBER_INFO("groups_access_member_info", "获取公开的群组成员信息", false),
|
||||
PUBLISH_TO_GROUPS("publish_to_groups", "授权您的应用程序代表某人将内容发布到组中,前提是他们已经授予您的应用程序访问权限", false),
|
||||
;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
/**
|
||||
|
||||
@@ -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", "基础scope,v3必选", 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;
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,6 +44,12 @@ public class AuthToken implements Serializable {
|
||||
* @since 1.10.0
|
||||
*/
|
||||
private String code;
|
||||
/**
|
||||
* 微信公众号 - 网页授权的登录时可用
|
||||
*
|
||||
* 微信针对网页授权登录,增加了一个快照页的逻辑,快照页获取到的微信用户的 uid oid 和头像昵称都是虚拟的信息
|
||||
*/
|
||||
private boolean snapshotUser;
|
||||
|
||||
/**
|
||||
* Twitter附带属性
|
||||
@@ -56,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;
|
||||
|
||||
}
|
||||
|
||||
@@ -34,16 +34,16 @@ public abstract class AbstractAuthDingtalkRequest extends AuthDefaultRequest {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AuthToken getAccessToken(AuthCallback authCallback) {
|
||||
public AuthToken getAccessToken(AuthCallback authCallback) {
|
||||
return AuthToken.builder().accessCode(authCallback.getCode()).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AuthUser getUserInfo(AuthToken authToken) {
|
||||
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());
|
||||
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"));
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,6 @@ 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.config.AuthSource;
|
||||
import me.zhyd.oauth.enums.AuthResponseStatus;
|
||||
import me.zhyd.oauth.enums.AuthUserGender;
|
||||
@@ -12,6 +11,7 @@ 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;
|
||||
|
||||
/**
|
||||
@@ -34,7 +34,7 @@ public abstract class AbstractAuthWeChatEnterpriseRequest extends AuthDefaultReq
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AuthToken getAccessToken(AuthCallback authCallback) {
|
||||
public AuthToken getAccessToken(AuthCallback authCallback) {
|
||||
String response = doGetAuthorizationCode(accessTokenUrl(null));
|
||||
|
||||
JSONObject object = this.checkResponse(response);
|
||||
@@ -47,7 +47,7 @@ public abstract class AbstractAuthWeChatEnterpriseRequest extends AuthDefaultReq
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AuthUser getUserInfo(AuthToken authToken) {
|
||||
public AuthUser getUserInfo(AuthToken authToken) {
|
||||
String response = doGetUserInfo(authToken);
|
||||
JSONObject object = this.checkResponse(response);
|
||||
|
||||
@@ -56,8 +56,8 @@ public abstract class AbstractAuthWeChatEnterpriseRequest extends AuthDefaultReq
|
||||
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)
|
||||
@@ -123,14 +123,31 @@ public abstract class AbstractAuthWeChatEnterpriseRequest extends AuthDefaultReq
|
||||
*
|
||||
* @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,43 +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) {
|
||||
super(config, AuthDefaultSource.ALIPAY, authStateCache);
|
||||
this.alipayClient = new DefaultAlipayClient(AuthDefaultSource.ALIPAY.accessToken(), config.getClientId(), config.getClientSecret(), "json", "UTF-8", config
|
||||
.getAlipayPublicKey(), "RSA2");
|
||||
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", proxyHost, proxyPort);
|
||||
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) {
|
||||
@@ -78,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());
|
||||
@@ -91,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())
|
||||
@@ -103,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;
|
||||
|
||||
@@ -26,7 +26,7 @@ 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()
|
||||
@@ -39,7 +39,7 @@ public class AuthAliyunRequest extends AuthDefaultRequest {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AuthUser getUserInfo(AuthToken authToken) {
|
||||
public AuthUser getUserInfo(AuthToken authToken) {
|
||||
String userInfo = doGetUserInfo(authToken);
|
||||
JSONObject object = JSONObject.parseObject(userInfo);
|
||||
return AuthUser.builder()
|
||||
|
||||
@@ -50,15 +50,16 @@ public class AuthAmazonRequest extends AuthDefaultRequest {
|
||||
*/
|
||||
@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", getRealState(state));
|
||||
.queryParam("state", realState);
|
||||
|
||||
if (config.isPkce()) {
|
||||
String cacheKey = this.source.getName().concat(":code_verifier:").concat(config.getClientId());
|
||||
String cacheKey = this.source.getName().concat(":code_verifier:").concat(realState);
|
||||
String codeVerifier = PkceUtil.generateCodeVerifier();
|
||||
String codeChallengeMethod = "S256";
|
||||
String codeChallenge = PkceUtil.generateCodeChallenge(codeChallengeMethod, codeVerifier);
|
||||
@@ -77,7 +78,7 @@ public class AuthAmazonRequest extends AuthDefaultRequest {
|
||||
* @return access token
|
||||
*/
|
||||
@Override
|
||||
protected AuthToken getAccessToken(AuthCallback authCallback) {
|
||||
public AuthToken getAccessToken(AuthCallback authCallback) {
|
||||
Map<String, String> form = new HashMap<>(9);
|
||||
form.put("grant_type", "authorization_code");
|
||||
form.put("code", authCallback.getCode());
|
||||
@@ -86,7 +87,7 @@ public class AuthAmazonRequest extends AuthDefaultRequest {
|
||||
form.put("client_secret", config.getClientSecret());
|
||||
|
||||
if (config.isPkce()) {
|
||||
String cacheKey = this.source.getName().concat(":code_verifier:").concat(config.getClientId());
|
||||
String cacheKey = this.source.getName().concat(":code_verifier:").concat(authCallback.getState());
|
||||
String codeVerifier = this.authStateCache.get(cacheKey);
|
||||
form.put("code_verifier", codeVerifier);
|
||||
}
|
||||
@@ -94,13 +95,13 @@ public class AuthAmazonRequest extends AuthDefaultRequest {
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthResponse refresh(AuthToken authToken) {
|
||||
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.builder()
|
||||
return AuthResponse.<AuthToken>builder()
|
||||
.code(AuthResponseStatus.SUCCESS.getCode())
|
||||
.data(getToken(form, this.source.refresh()))
|
||||
.build();
|
||||
@@ -111,7 +112,7 @@ public class AuthAmazonRequest extends AuthDefaultRequest {
|
||||
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);
|
||||
String response = new HttpUtils(config.getHttpConfig()).post(url, param, httpHeader, false).getBody();
|
||||
JSONObject jsonObject = JSONObject.parseObject(response);
|
||||
this.checkResponse(jsonObject);
|
||||
return AuthToken.builder()
|
||||
@@ -140,14 +141,14 @@ public class AuthAmazonRequest extends AuthDefaultRequest {
|
||||
* @return AuthUser
|
||||
*/
|
||||
@Override
|
||||
protected AuthUser getUserInfo(AuthToken authToken) {
|
||||
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);
|
||||
String userInfo = new HttpUtils(config.getHttpConfig()).get(this.source.userInfo(), new HashMap<>(0), httpHeader, false).getBody();
|
||||
JSONObject jsonObject = JSONObject.parseObject(userInfo);
|
||||
this.checkResponse(jsonObject);
|
||||
|
||||
@@ -164,7 +165,7 @@ public class AuthAmazonRequest extends AuthDefaultRequest {
|
||||
}
|
||||
|
||||
private void checkToken(String accessToken) {
|
||||
String tokenInfo = new HttpUtils(config.getHttpConfig()).get("https://api.amazon.com/auth/o2/tokeninfo?access_token=" + UrlUtil.urlEncode(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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,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();
|
||||
}
|
||||
}
|
||||
@@ -34,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"))
|
||||
@@ -59,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();
|
||||
@@ -87,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");
|
||||
@@ -96,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();
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ public class AuthElemeRequest extends AuthDefaultRequest {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AuthToken getAccessToken(AuthCallback authCallback) {
|
||||
public AuthToken getAccessToken(AuthCallback authCallback) {
|
||||
Map<String, String> form = new HashMap<>(7);
|
||||
form.put("client_id", config.getClientId());
|
||||
form.put("redirect_uri", config.getRedirectUri());
|
||||
@@ -52,7 +52,7 @@ public class AuthElemeRequest extends AuthDefaultRequest {
|
||||
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,7 +66,7 @@ public class AuthElemeRequest extends AuthDefaultRequest {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AuthUser getUserInfo(AuthToken authToken) {
|
||||
public AuthUser getUserInfo(AuthToken authToken) {
|
||||
Map<String, Object> parameters = new HashMap<>(4);
|
||||
// 获取商户账号信息的API接口名称
|
||||
String action = "eleme.user.getUser";
|
||||
@@ -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) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查响应内容是否正确
|
||||
*
|
||||
|
||||
@@ -56,7 +56,7 @@ public class AuthFeishuRequest extends AuthDefaultRequest {
|
||||
requestObject.put("app_id", config.getClientId());
|
||||
requestObject.put("app_secret", config.getClientSecret());
|
||||
String response = new HttpUtils(config.getHttpConfig()).post(url, requestObject.toJSONString(), new HttpHeader()
|
||||
.add("Content-Type", "application/json"));
|
||||
.add("Content-Type", "application/json")).getBody();
|
||||
JSONObject jsonObject = JSON.parseObject(response);
|
||||
this.checkResponse(jsonObject);
|
||||
String appAccessToken = jsonObject.getString("app_access_token");
|
||||
@@ -66,7 +66,7 @@ public class AuthFeishuRequest extends AuthDefaultRequest {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AuthToken getAccessToken(AuthCallback authCallback) {
|
||||
public AuthToken getAccessToken(AuthCallback authCallback) {
|
||||
JSONObject requestObject = new JSONObject();
|
||||
requestObject.put("app_access_token", this.getAppAccessToken());
|
||||
requestObject.put("grant_type", "authorization_code");
|
||||
@@ -76,11 +76,11 @@ public class AuthFeishuRequest extends AuthDefaultRequest {
|
||||
}
|
||||
|
||||
@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");
|
||||
@@ -98,12 +98,12 @@ public class AuthFeishuRequest extends AuthDefaultRequest {
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthResponse refresh(AuthToken authToken) {
|
||||
public AuthResponse<AuthToken> refresh(AuthToken authToken) {
|
||||
JSONObject requestObject = new JSONObject();
|
||||
requestObject.put("app_access_token", this.getAppAccessToken());
|
||||
requestObject.put("grant_type", "refresh_token");
|
||||
requestObject.put("refresh_token", authToken.getRefreshToken());
|
||||
return AuthResponse.builder()
|
||||
return AuthResponse.<AuthToken>builder()
|
||||
.code(AuthResponseStatus.SUCCESS.getCode())
|
||||
.data(getToken(requestObject, this.source.refresh()))
|
||||
.build();
|
||||
@@ -112,7 +112,7 @@ public class AuthFeishuRequest extends AuthDefaultRequest {
|
||||
|
||||
private AuthToken getToken(JSONObject param, String url) {
|
||||
String response = new HttpUtils(config.getHttpConfig()).post(url, param.toJSONString(), new HttpHeader()
|
||||
.add("Content-Type", "application/json"));
|
||||
.add("Content-Type", "application/json")).getBody();
|
||||
JSONObject jsonObject = JSON.parseObject(response);
|
||||
this.checkResponse(jsonObject);
|
||||
JSONObject data = jsonObject.getJSONObject("data");
|
||||
|
||||
@@ -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,7 +49,7 @@ public class AuthHuaweiRequest extends AuthDefaultRequest {
|
||||
* @see AuthDefaultRequest#authorize(String)
|
||||
*/
|
||||
@Override
|
||||
protected AuthToken getAccessToken(AuthCallback authCallback) {
|
||||
public AuthToken getAccessToken(AuthCallback authCallback) {
|
||||
Map<String, String> form = new HashMap<>(8);
|
||||
form.put("grant_type", "authorization_code");
|
||||
form.put("code", authCallback.getAuthorization_code());
|
||||
@@ -54,7 +57,7 @@ public class AuthHuaweiRequest extends AuthDefaultRequest {
|
||||
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) {
|
||||
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) {
|
||||
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<>(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) {
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,15 +36,30 @@ public class AuthLineRequest extends AuthDefaultRequest {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AuthToken getAccessToken(AuthCallback authCallback) {
|
||||
return this.getToken(accessTokenUrl(authCallback.getCode()));
|
||||
public AuthToken getAccessToken(AuthCallback authCallback) {
|
||||
Map<String, String> params = new HashMap<>();
|
||||
params.put("grant_type", "authorization_code");
|
||||
params.put("code", authCallback.getCode());
|
||||
params.put("redirect_uri", config.getRedirectUri());
|
||||
params.put("client_id", config.getClientId());
|
||||
params.put("client_secret", config.getClientSecret());
|
||||
String response = new HttpUtils(config.getHttpConfig()).post(source.accessToken(), params, false).getBody();
|
||||
JSONObject accessTokenObject = JSONObject.parseObject(response);
|
||||
return AuthToken.builder()
|
||||
.accessToken(accessTokenObject.getString("access_token"))
|
||||
.refreshToken(accessTokenObject.getString("refresh_token"))
|
||||
.expireIn(accessTokenObject.getIntValue("expires_in"))
|
||||
.idToken(accessTokenObject.getString("id_token"))
|
||||
.scope(accessTokenObject.getString("scope"))
|
||||
.tokenType(accessTokenObject.getString("token_type"))
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AuthUser getUserInfo(AuthToken authToken) {
|
||||
public AuthUser getUserInfo(AuthToken authToken) {
|
||||
String userInfo = new HttpUtils(config.getHttpConfig()).get(source.userInfo(), null, new HttpHeader()
|
||||
.add("Content-Type", "application/x-www-form-urlencoded")
|
||||
.add("Authorization", "Bearer ".concat(authToken.getAccessToken())), false);
|
||||
.add("Authorization", "Bearer ".concat(authToken.getAccessToken())), false).getBody();
|
||||
JSONObject object = JSONObject.parseObject(userInfo);
|
||||
return AuthUser.builder()
|
||||
.rawUserInfo(object)
|
||||
@@ -65,7 +80,7 @@ public class AuthLineRequest extends AuthDefaultRequest {
|
||||
params.put("access_token", authToken.getAccessToken());
|
||||
params.put("client_id", config.getClientId());
|
||||
params.put("client_secret", config.getClientSecret());
|
||||
String userInfo = new HttpUtils(config.getHttpConfig()).post(source.revoke(), params, false);
|
||||
String userInfo = new HttpUtils(config.getHttpConfig()).post(source.revoke(), params, false).getBody();
|
||||
JSONObject object = JSONObject.parseObject(userInfo);
|
||||
// 返回1表示取消授权成功,否则失败
|
||||
AuthResponseStatus status = object.getBooleanValue("revoked") ? AuthResponseStatus.SUCCESS : AuthResponseStatus.FAILURE;
|
||||
@@ -73,29 +88,24 @@ public class AuthLineRequest extends AuthDefaultRequest {
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthResponse refresh(AuthToken oldToken) {
|
||||
return AuthResponse.builder()
|
||||
.code(AuthResponseStatus.SUCCESS.getCode())
|
||||
.data(this.getToken(refreshTokenUrl(oldToken.getRefreshToken())))
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取token,适用于获取access_token和刷新token
|
||||
*
|
||||
* @param accessTokenUrl 实际请求token的地址
|
||||
* @return token对象
|
||||
*/
|
||||
private AuthToken getToken(String accessTokenUrl) {
|
||||
String response = new HttpUtils(config.getHttpConfig()).post(accessTokenUrl);
|
||||
public AuthResponse<AuthToken> refresh(AuthToken oldToken) {
|
||||
Map<String, String> params = new HashMap<>();
|
||||
params.put("grant_type", "refresh_token");
|
||||
params.put("refresh_token", oldToken.getRefreshToken());
|
||||
params.put("client_id", config.getClientId());
|
||||
params.put("client_secret", config.getClientSecret());
|
||||
String response = new HttpUtils(config.getHttpConfig()).post(source.accessToken(), params, false).getBody();
|
||||
JSONObject accessTokenObject = JSONObject.parseObject(response);
|
||||
return AuthToken.builder()
|
||||
return AuthResponse.<AuthToken>builder()
|
||||
.code(AuthResponseStatus.SUCCESS.getCode())
|
||||
.data(AuthToken.builder()
|
||||
.accessToken(accessTokenObject.getString("access_token"))
|
||||
.refreshToken(accessTokenObject.getString("refresh_token"))
|
||||
.expireIn(accessTokenObject.getIntValue("expires_in"))
|
||||
.idToken(accessTokenObject.getString("id_token"))
|
||||
.scope(accessTokenObject.getString("scope"))
|
||||
.tokenType(accessTokenObject.getString("token_type"))
|
||||
.build())
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
@@ -36,19 +36,19 @@ public class AuthLinkedinRequest 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 accessToken = authToken.getAccessToken();
|
||||
HttpHeader httpHeader = new HttpHeader();
|
||||
httpHeader.add("Host", "api.linkedin.com");
|
||||
httpHeader.add("Connection", "Keep-Alive");
|
||||
httpHeader.add("Authorization", "Bearer " + accessToken);
|
||||
|
||||
String response = new HttpUtils(config.getHttpConfig()).get(userInfoUrl(authToken), null, httpHeader, false);
|
||||
String response = new HttpUtils(config.getHttpConfig()).get(userInfoUrl(authToken), null, httpHeader, false).getBody();
|
||||
JSONObject userInfoObject = JSONObject.parseObject(response);
|
||||
|
||||
this.checkResponse(userInfoObject);
|
||||
@@ -138,7 +138,9 @@ public class AuthLinkedinRequest extends AuthDefaultRequest {
|
||||
httpHeader.add("Connection", "Keep-Alive");
|
||||
httpHeader.add("Authorization", "Bearer " + accessToken);
|
||||
|
||||
String emailResponse = new HttpUtils(config.getHttpConfig()).get("https://api.linkedin.com/v2/emailAddress?q=members&projection=(elements*(handle~))", null, httpHeader, false);
|
||||
String emailResponse = new HttpUtils(config.getHttpConfig())
|
||||
.get("https://api.linkedin.com/v2/emailAddress?q=members&projection=(elements*(handle~))", null, httpHeader, false)
|
||||
.getBody();
|
||||
JSONObject emailObj = JSONObject.parseObject(emailResponse);
|
||||
|
||||
this.checkResponse(emailObj);
|
||||
@@ -178,7 +180,7 @@ public class AuthLinkedinRequest extends AuthDefaultRequest {
|
||||
httpHeader.add("Host", "www.linkedin.com");
|
||||
httpHeader.add(Constants.CONTENT_TYPE, "application/x-www-form-urlencoded");
|
||||
|
||||
String response = new HttpUtils(config.getHttpConfig()).post(accessTokenUrl, null, httpHeader);
|
||||
String response = new HttpUtils(config.getHttpConfig()).post(accessTokenUrl, null, httpHeader).getBody();
|
||||
JSONObject accessTokenObject = JSONObject.parseObject(response);
|
||||
|
||||
this.checkResponse(accessTokenObject);
|
||||
|
||||
@@ -34,14 +34,14 @@ public class AuthMeituanRequest extends AuthDefaultRequest {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AuthToken getAccessToken(AuthCallback authCallback) {
|
||||
public AuthToken getAccessToken(AuthCallback authCallback) {
|
||||
Map<String, String> form = new HashMap<>(7);
|
||||
form.put("app_id", config.getClientId());
|
||||
form.put("secret", config.getClientSecret());
|
||||
form.put("code", authCallback.getCode());
|
||||
form.put("grant_type", "authorization_code");
|
||||
|
||||
String response = new HttpUtils(config.getHttpConfig()).post(source.accessToken(), form, false);
|
||||
String response = new HttpUtils(config.getHttpConfig()).post(source.accessToken(), form, false).getBody();
|
||||
JSONObject object = JSONObject.parseObject(response);
|
||||
|
||||
this.checkResponse(object);
|
||||
@@ -54,13 +54,13 @@ public class AuthMeituanRequest extends AuthDefaultRequest {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AuthUser getUserInfo(AuthToken authToken) {
|
||||
public AuthUser getUserInfo(AuthToken authToken) {
|
||||
Map<String, String> form = new HashMap<>(5);
|
||||
form.put("app_id", config.getClientId());
|
||||
form.put("secret", config.getClientSecret());
|
||||
form.put("access_token", authToken.getAccessToken());
|
||||
|
||||
String response = new HttpUtils(config.getHttpConfig()).post(source.userInfo(), form, false);
|
||||
String response = new HttpUtils(config.getHttpConfig()).post(source.userInfo(), form, false).getBody();
|
||||
JSONObject object = JSONObject.parseObject(response);
|
||||
|
||||
this.checkResponse(object);
|
||||
@@ -78,19 +78,19 @@ public class AuthMeituanRequest extends AuthDefaultRequest {
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthResponse refresh(AuthToken oldToken) {
|
||||
public AuthResponse<AuthToken> refresh(AuthToken oldToken) {
|
||||
Map<String, String> form = new HashMap<>(7);
|
||||
form.put("app_id", config.getClientId());
|
||||
form.put("secret", config.getClientSecret());
|
||||
form.put("refresh_token", oldToken.getRefreshToken());
|
||||
form.put("grant_type", "refresh_token");
|
||||
|
||||
String response = new HttpUtils(config.getHttpConfig()).post(source.refresh(), form, false);
|
||||
String response = new HttpUtils(config.getHttpConfig()).post(source.refresh(), form, 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"))
|
||||
|
||||
@@ -38,12 +38,12 @@ public class AuthMiRequest extends AuthDefaultRequest {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AuthToken getAccessToken(AuthCallback authCallback) {
|
||||
public AuthToken getAccessToken(AuthCallback authCallback) {
|
||||
return getToken(accessTokenUrl(authCallback.getCode()));
|
||||
}
|
||||
|
||||
private AuthToken getToken(String accessTokenUrl) {
|
||||
String response = new HttpUtils(config.getHttpConfig()).get(accessTokenUrl);
|
||||
String response = new HttpUtils(config.getHttpConfig()).get(accessTokenUrl).getBody();
|
||||
String jsonStr = response.replace(PREFIX, Constants.EMPTY);
|
||||
JSONObject accessTokenObject = JSONObject.parseObject(jsonStr);
|
||||
|
||||
@@ -64,7 +64,7 @@ public class AuthMiRequest extends AuthDefaultRequest {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AuthUser getUserInfo(AuthToken authToken) {
|
||||
public AuthUser getUserInfo(AuthToken authToken) {
|
||||
// 获取用户信息
|
||||
String userResponse = doGetUserInfo(authToken);
|
||||
|
||||
@@ -91,7 +91,7 @@ public class AuthMiRequest extends AuthDefaultRequest {
|
||||
String emailPhoneUrl = MessageFormat.format("{0}?clientId={1}&token={2}", "https://open.account.xiaomi.com/user/phoneAndEmail", config
|
||||
.getClientId(), authToken.getAccessToken());
|
||||
|
||||
String emailResponse = new HttpUtils(config.getHttpConfig()).get(emailPhoneUrl);
|
||||
String emailResponse = new HttpUtils(config.getHttpConfig()).get(emailPhoneUrl).getBody();
|
||||
JSONObject userEmailPhone = JSONObject.parseObject(emailResponse);
|
||||
if (!"error".equalsIgnoreCase(userEmailPhone.getString("result"))) {
|
||||
JSONObject emailPhone = userEmailPhone.getJSONObject("data");
|
||||
@@ -110,8 +110,8 @@ public class AuthMiRequest extends AuthDefaultRequest {
|
||||
* @return AuthResponse
|
||||
*/
|
||||
@Override
|
||||
public AuthResponse refresh(AuthToken authToken) {
|
||||
return AuthResponse.builder()
|
||||
public AuthResponse<AuthToken> refresh(AuthToken authToken) {
|
||||
return AuthResponse.<AuthToken>builder()
|
||||
.code(AuthResponseStatus.SUCCESS.getCode())
|
||||
.data(getToken(refreshTokenUrl(authToken.getRefreshToken())))
|
||||
.build();
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package me.zhyd.oauth.request;
|
||||
|
||||
import me.zhyd.oauth.cache.AuthStateCache;
|
||||
import me.zhyd.oauth.config.AuthConfig;
|
||||
import me.zhyd.oauth.config.AuthDefaultSource;
|
||||
import me.zhyd.oauth.enums.AuthResponseStatus;
|
||||
import me.zhyd.oauth.exception.AuthException;
|
||||
import me.zhyd.oauth.utils.GlobalAuthUtils;
|
||||
|
||||
/**
|
||||
* 微软中国登录(世纪华联)
|
||||
*
|
||||
* @author mroldx (xzfqq5201314@gmail.com)
|
||||
* @since 1.16.4
|
||||
*/
|
||||
public class AuthMicrosoftCnRequest extends AbstractAuthMicrosoftRequest {
|
||||
|
||||
public AuthMicrosoftCnRequest(AuthConfig config) {
|
||||
super(config, AuthDefaultSource.MICROSOFT_CN);
|
||||
}
|
||||
|
||||
public AuthMicrosoftCnRequest(AuthConfig config, AuthStateCache authStateCache) {
|
||||
super(config, AuthDefaultSource.MICROSOFT_CN, authStateCache);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void checkConfig(AuthConfig config) {
|
||||
super.checkConfig(config);
|
||||
// 微软中国的回调地址必须为https的链接或者localhost,不允许使用http
|
||||
if (AuthDefaultSource.MICROSOFT_CN == source && !GlobalAuthUtils.isHttpsProtocolOrLocalHost(config.getRedirectUri())) {
|
||||
// Microsoft's redirect uri must use the HTTPS or localhost
|
||||
throw new AuthException(AuthResponseStatus.ILLEGAL_REDIRECT_URI, source);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,32 +1,21 @@
|
||||
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.AuthDefaultSource;
|
||||
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.UrlBuilder;
|
||||
|
||||
import java.util.Map;
|
||||
import me.zhyd.oauth.utils.GlobalAuthUtils;
|
||||
|
||||
/**
|
||||
* 微软登录
|
||||
* update 2021-08-24 mroldx (xzfqq5201314@gmail.com)
|
||||
*
|
||||
* @author yangkai.shen (https://xkcoding.com)
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public class AuthMicrosoftRequest extends AuthDefaultRequest {
|
||||
public class AuthMicrosoftRequest extends AbstractAuthMicrosoftRequest {
|
||||
|
||||
public AuthMicrosoftRequest(AuthConfig config) {
|
||||
super(config, AuthDefaultSource.MICROSOFT);
|
||||
}
|
||||
@@ -36,144 +25,13 @@ public class AuthMicrosoftRequest extends AuthDefaultRequest {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected 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);
|
||||
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"));
|
||||
protected void checkConfig(AuthConfig config) {
|
||||
super.checkConfig(config);
|
||||
// 微软的回调地址必须为https的链接或者localhost,不允许使用http
|
||||
if (AuthDefaultSource.MICROSOFT == source && !GlobalAuthUtils.isHttpsProtocolOrLocalHost(config.getRedirectUri())) {
|
||||
// Microsoft's redirect uri must use the HTTPS or localhost
|
||||
throw new AuthException(AuthResponseStatus.ILLEGAL_REDIRECT_URI, source);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected 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);
|
||||
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 refresh(AuthToken authToken) {
|
||||
return AuthResponse.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) {
|
||||
return UrlBuilder.fromBaseUrl(super.authorize(state))
|
||||
.queryParam("response_mode", "query")
|
||||
.queryParam("scope", this.getScopes(" ", true, AuthScopeUtils.getDefaultScopes(AuthMicrosoftScope.values())))
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回获取accessToken的url
|
||||
*
|
||||
* @param code 授权code
|
||||
* @return 返回获取accessToken的url
|
||||
*/
|
||||
@Override
|
||||
protected String accessTokenUrl(String code) {
|
||||
return UrlBuilder.fromBaseUrl(source.accessToken())
|
||||
.queryParam("code", code)
|
||||
.queryParam("client_id", config.getClientId())
|
||||
.queryParam("client_secret", config.getClientSecret())
|
||||
.queryParam("grant_type", "authorization_code")
|
||||
.queryParam("scope", this.getScopes(" ", true, 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) {
|
||||
return UrlBuilder.fromBaseUrl(source.refresh())
|
||||
.queryParam("client_id", config.getClientId())
|
||||
.queryParam("client_secret", config.getClientSecret())
|
||||
.queryParam("refresh_token", refreshToken)
|
||||
.queryParam("grant_type", "refresh_token")
|
||||
.queryParam("scope", this.getScopes(" ", true, AuthScopeUtils.getDefaultScopes(AuthMicrosoftScope.values())))
|
||||
.queryParam("redirect_uri", config.getRedirectUri())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ public class AuthOktaRequest extends AuthDefaultRequest {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AuthToken getAccessToken(AuthCallback authCallback) {
|
||||
public AuthToken getAccessToken(AuthCallback authCallback) {
|
||||
String tokenUrl = accessTokenUrl(authCallback.getCode());
|
||||
return getAuthToken(tokenUrl);
|
||||
}
|
||||
@@ -50,7 +50,7 @@ public class AuthOktaRequest extends AuthDefaultRequest {
|
||||
.add("accept", "application/json")
|
||||
.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(tokenUrl, null, header, false);
|
||||
String response = new HttpUtils(config.getHttpConfig()).post(tokenUrl, null, header, false).getBody();
|
||||
JSONObject accessTokenObject = JSONObject.parseObject(response);
|
||||
this.checkResponse(accessTokenObject);
|
||||
return AuthToken.builder()
|
||||
@@ -64,25 +64,25 @@ public class AuthOktaRequest extends AuthDefaultRequest {
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthResponse refresh(AuthToken authToken) {
|
||||
public AuthResponse<AuthToken> refresh(AuthToken authToken) {
|
||||
if (null == authToken.getRefreshToken()) {
|
||||
return AuthResponse.builder()
|
||||
return AuthResponse.<AuthToken>builder()
|
||||
.code(AuthResponseStatus.ILLEGAL_TOKEN.getCode())
|
||||
.msg(AuthResponseStatus.ILLEGAL_TOKEN.getMsg())
|
||||
.build();
|
||||
}
|
||||
String refreshUrl = refreshTokenUrl(authToken.getRefreshToken());
|
||||
return AuthResponse.builder()
|
||||
return AuthResponse.<AuthToken>builder()
|
||||
.code(AuthResponseStatus.SUCCESS.getCode())
|
||||
.data(this.getAuthToken(refreshUrl))
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AuthUser getUserInfo(AuthToken authToken) {
|
||||
public AuthUser getUserInfo(AuthToken authToken) {
|
||||
HttpHeader header = new HttpHeader()
|
||||
.add("Authorization", "Bearer " + authToken.getAccessToken());
|
||||
String response = new HttpUtils(config.getHttpConfig()).post(userInfoUrl(authToken), null, header, false);
|
||||
String response = new HttpUtils(config.getHttpConfig()).post(userInfoUrl(authToken), null, header, false).getBody();
|
||||
JSONObject object = JSONObject.parseObject(response);
|
||||
this.checkResponse(object);
|
||||
JSONObject address = object.getJSONObject("address");
|
||||
|
||||
@@ -28,7 +28,7 @@ public class AuthOschinaRequest 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);
|
||||
@@ -41,7 +41,7 @@ public class AuthOschinaRequest 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);
|
||||
|
||||
@@ -36,7 +36,7 @@ public class AuthPinterestRequest 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 AuthPinterestRequest extends AuthDefaultRequest {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AuthUser getUserInfo(AuthToken authToken) {
|
||||
public AuthUser getUserInfo(AuthToken authToken) {
|
||||
String userinfoUrl = userInfoUrl(authToken);
|
||||
// TODO: 是否需要 .setFollowRedirects(true)
|
||||
String response = new HttpUtils(config.getHttpConfig()).get(userinfoUrl);
|
||||
String response = new HttpUtils(config.getHttpConfig()).get(userinfoUrl).getBody();
|
||||
JSONObject object = JSONObject.parseObject(response);
|
||||
this.checkResponse(object);
|
||||
JSONObject userObj = object.getJSONObject("data");
|
||||
|
||||
@@ -34,14 +34,14 @@ public class AuthProginnRequest extends AuthDefaultRequest {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AuthToken getAccessToken(AuthCallback authCallback) {
|
||||
public AuthToken getAccessToken(AuthCallback authCallback) {
|
||||
Map<String, String> params = new HashMap<>();
|
||||
params.put("code", authCallback.getCode());
|
||||
params.put("client_id", config.getClientId());
|
||||
params.put("client_secret", config.getClientSecret());
|
||||
params.put("grant_type", "authorization_code");
|
||||
params.put("redirect_uri", config.getRedirectUri());
|
||||
String response = new HttpUtils(config.getHttpConfig()).post(AuthDefaultSource.PROGINN.accessToken(), params, false);
|
||||
String response = new HttpUtils(config.getHttpConfig()).post(AuthDefaultSource.PROGINN.accessToken(), params, false).getBody();
|
||||
JSONObject accessTokenObject = JSONObject.parseObject(response);
|
||||
this.checkResponse(accessTokenObject);
|
||||
return AuthToken.builder()
|
||||
@@ -54,7 +54,7 @@ public class AuthProginnRequest 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);
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
package me.zhyd.oauth.request;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.alibaba.fastjson.annotation.JSONField;
|
||||
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.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.UrlBuilder;
|
||||
|
||||
/**
|
||||
* QQ小程序登陆 Request 请求
|
||||
* <p>
|
||||
* 参照微信小程序实现
|
||||
*
|
||||
* @author hurentian
|
||||
* @since 2024-10-08
|
||||
*/
|
||||
public class AuthQQMiniProgramRequest extends AuthDefaultRequest {
|
||||
public AuthQQMiniProgramRequest(AuthConfig config) {
|
||||
super(config, AuthDefaultSource.QQ_MINI_PROGRAM);
|
||||
}
|
||||
|
||||
public AuthQQMiniProgramRequest(AuthConfig config, AuthStateCache authStateCache) {
|
||||
super(config, AuthDefaultSource.QQ_MINI_PROGRAM, authStateCache);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthToken getAccessToken(AuthCallback authCallback) {
|
||||
// 参见 https://q.qq.com/wiki/develop/miniprogram/server/open_port/port_login.html#code2session 文档
|
||||
// 使用 code 获取对应的 openId、unionId 等字段
|
||||
String response = new HttpUtils(config.getHttpConfig()).get(accessTokenUrl(authCallback.getCode())).getBody();
|
||||
JSCode2SessionResponse accessTokenObject = JSONObject.parseObject(response, JSCode2SessionResponse.class);
|
||||
assert accessTokenObject != null;
|
||||
checkResponse(accessTokenObject);
|
||||
// 拼装结果
|
||||
return AuthToken.builder()
|
||||
.openId(accessTokenObject.getOpenid())
|
||||
.unionId(accessTokenObject.getUnionId())
|
||||
.accessToken(accessTokenObject.getSessionKey())
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthUser getUserInfo(AuthToken authToken) {
|
||||
// 参见 https://q.qq.com/wiki/develop/game/API/open-port/user-info.html#qq-getuserinfo 文档
|
||||
// 如果需要用户信息,需要在小程序调用函数后传给后端
|
||||
return AuthUser.builder()
|
||||
.username("")
|
||||
.nickname("")
|
||||
.avatar("")
|
||||
.uuid(authToken.getOpenId())
|
||||
.token(authToken)
|
||||
.source(source.toString())
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查响应内容是否正确
|
||||
*
|
||||
* @param response 请求响应内容
|
||||
*/
|
||||
private void checkResponse(JSCode2SessionResponse response) {
|
||||
if (response.getErrorCode() != 0) {
|
||||
throw new AuthException(response.getErrorCode(), response.getErrorMsg());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String accessTokenUrl(String code) {
|
||||
return UrlBuilder.fromBaseUrl(source.accessToken())
|
||||
.queryParam("appid", config.getClientId())
|
||||
.queryParam("secret", config.getClientSecret())
|
||||
.queryParam("js_code", code)
|
||||
.queryParam("grant_type", "authorization_code")
|
||||
.build();
|
||||
}
|
||||
|
||||
@Data
|
||||
@SuppressWarnings("SpellCheckingInspection")
|
||||
private static class JSCode2SessionResponse {
|
||||
|
||||
@JSONField(name = "errcode")
|
||||
private int errorCode;
|
||||
@JSONField(name = "errmsg")
|
||||
private String errorMsg;
|
||||
@JSONField(name = "session_key")
|
||||
private String sessionKey;
|
||||
private String openid;
|
||||
@JSONField(name = "unionid")
|
||||
private String unionId;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -33,19 +33,19 @@ public class AuthQqRequest extends AuthDefaultRequest {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AuthToken getAccessToken(AuthCallback authCallback) {
|
||||
public AuthToken getAccessToken(AuthCallback authCallback) {
|
||||
String response = doGetAuthorizationCode(authCallback.getCode());
|
||||
return getAuthToken(response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthResponse refresh(AuthToken authToken) {
|
||||
String response = new HttpUtils(config.getHttpConfig()).get(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()).get(refreshTokenUrl(authToken.getRefreshToken())).getBody();
|
||||
return AuthResponse.<AuthToken>builder().code(AuthResponseStatus.SUCCESS.getCode()).data(getAuthToken(response)).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AuthUser getUserInfo(AuthToken authToken) {
|
||||
public AuthUser getUserInfo(AuthToken authToken) {
|
||||
String openId = this.getOpenId(authToken);
|
||||
String response = doGetUserInfo(authToken);
|
||||
JSONObject object = JSONObject.parseObject(response);
|
||||
@@ -82,7 +82,7 @@ public class AuthQqRequest extends AuthDefaultRequest {
|
||||
String response = new HttpUtils(config.getHttpConfig()).get(UrlBuilder.fromBaseUrl("https://graph.qq.com/oauth2.0/me")
|
||||
.queryParam("access_token", authToken.getAccessToken())
|
||||
.queryParam("unionid", config.isUnionId() ? 1 : 0)
|
||||
.build());
|
||||
.build()).getBody();
|
||||
String removePrefix = response.replace("callback(", "");
|
||||
String removeSuffix = removePrefix.replace(");", "");
|
||||
String openId = removeSuffix.trim();
|
||||
|
||||
@@ -38,12 +38,12 @@ public class AuthRenrenRequest 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 userObj = JSONObject.parseObject(response).getJSONObject("response");
|
||||
|
||||
@@ -60,15 +60,15 @@ public class AuthRenrenRequest extends AuthDefaultRequest {
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthResponse refresh(AuthToken authToken) {
|
||||
return AuthResponse.builder()
|
||||
public AuthResponse<AuthToken> refresh(AuthToken authToken) {
|
||||
return AuthResponse.<AuthToken>builder()
|
||||
.code(SUCCESS.getCode())
|
||||
.data(getToken(this.refreshTokenUrl(authToken.getRefreshToken())))
|
||||
.build();
|
||||
}
|
||||
|
||||
private AuthToken getToken(String url) {
|
||||
String response = new HttpUtils(config.getHttpConfig()).post(url);
|
||||
String response = new HttpUtils(config.getHttpConfig()).post(url).getBody();
|
||||
JSONObject jsonObject = JSONObject.parseObject(response);
|
||||
if (jsonObject.containsKey("error")) {
|
||||
throw new AuthException("Failed to get token from Renren: " + jsonObject);
|
||||
|
||||
@@ -5,6 +5,7 @@ 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;
|
||||
|
||||
/**
|
||||
* JustAuth {@code Request}公共接口,所有平台的{@code Request}都需要实现该接口
|
||||
@@ -43,13 +44,32 @@ public interface AuthRequest {
|
||||
throw new AuthException(AuthResponseStatus.NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取access token
|
||||
*
|
||||
* @param authCallback 授权成功后的回调参数
|
||||
* @return token
|
||||
* @see AuthDefaultRequest#authorize()
|
||||
* @see AuthDefaultRequest#authorize(String)
|
||||
*/
|
||||
AuthToken getAccessToken(AuthCallback authCallback);
|
||||
|
||||
/**
|
||||
* 使用token换取用户信息
|
||||
*
|
||||
* @param authToken token信息
|
||||
* @return 用户信息
|
||||
* @see AuthDefaultRequest#getAccessToken(AuthCallback)
|
||||
*/
|
||||
AuthUser getUserInfo(AuthToken authToken);
|
||||
|
||||
/**
|
||||
* 第三方登录
|
||||
*
|
||||
* @param authCallback 用于接收回调参数的实体
|
||||
* @return 返回登录成功后的用户信息
|
||||
*/
|
||||
default AuthResponse login(AuthCallback authCallback) {
|
||||
default AuthResponse<AuthUser> login(AuthCallback authCallback) {
|
||||
throw new AuthException(AuthResponseStatus.NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
@@ -69,7 +89,7 @@ public interface AuthRequest {
|
||||
* @param authToken 登录成功后返回的Token信息
|
||||
* @return AuthResponse
|
||||
*/
|
||||
default AuthResponse refresh(AuthToken authToken) {
|
||||
default AuthResponse<AuthToken> refresh(AuthToken authToken) {
|
||||
throw new AuthException(AuthResponseStatus.NOT_IMPLEMENTED);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,10 +35,11 @@ public class AuthSlackRequest extends AuthDefaultRequest {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AuthToken getAccessToken(AuthCallback authCallback) {
|
||||
public AuthToken getAccessToken(AuthCallback authCallback) {
|
||||
HttpHeader header = new HttpHeader()
|
||||
.add("Content-Type", "application/x-www-form-urlencoded");
|
||||
String response = new HttpUtils(config.getHttpConfig()).get(accessTokenUrl(authCallback.getCode()), null, header, false);
|
||||
String response = new HttpUtils(config.getHttpConfig())
|
||||
.get(accessTokenUrl(authCallback.getCode()), null, header, false).getBody();
|
||||
JSONObject accessTokenObject = JSONObject.parseObject(response);
|
||||
this.checkResponse(accessTokenObject);
|
||||
return AuthToken.builder()
|
||||
@@ -50,11 +51,12 @@ public class AuthSlackRequest extends AuthDefaultRequest {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AuthUser getUserInfo(AuthToken authToken) {
|
||||
public AuthUser getUserInfo(AuthToken authToken) {
|
||||
HttpHeader header = new HttpHeader()
|
||||
.add("Content-Type", "application/x-www-form-urlencoded")
|
||||
.add("Authorization", "Bearer ".concat(authToken.getAccessToken()));
|
||||
String userInfo = new HttpUtils(config.getHttpConfig()).get(userInfoUrl(authToken), null, header, false);
|
||||
String userInfo = new HttpUtils(config.getHttpConfig())
|
||||
.get(userInfoUrl(authToken), null, header, false).getBody();
|
||||
JSONObject object = JSONObject.parseObject(userInfo);
|
||||
this.checkResponse(object);
|
||||
JSONObject user = object.getJSONObject("user");
|
||||
@@ -77,7 +79,8 @@ public class AuthSlackRequest extends AuthDefaultRequest {
|
||||
HttpHeader header = new HttpHeader()
|
||||
.add("Content-Type", "application/x-www-form-urlencoded")
|
||||
.add("Authorization", "Bearer ".concat(authToken.getAccessToken()));
|
||||
String userInfo = new HttpUtils(config.getHttpConfig()).get(source.revoke(), null, header, false);
|
||||
String userInfo = new HttpUtils(config.getHttpConfig())
|
||||
.get(source.revoke(), null, header, false).getBody();
|
||||
JSONObject object = JSONObject.parseObject(userInfo);
|
||||
this.checkResponse(object);
|
||||
// 返回1表示取消授权成功,否则失败
|
||||
|
||||
@@ -37,12 +37,12 @@ public class AuthStackOverflowRequest extends AuthDefaultRequest {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AuthToken getAccessToken(AuthCallback authCallback) {
|
||||
public AuthToken getAccessToken(AuthCallback authCallback) {
|
||||
String accessTokenUrl = accessTokenUrl(authCallback.getCode());
|
||||
Map<String, String> form = MapUtil.parseStringToMap(accessTokenUrl, false);
|
||||
HttpHeader httpHeader = new HttpHeader();
|
||||
httpHeader.add(Constants.CONTENT_TYPE, "application/x-www-form-urlencoded");
|
||||
String response = new HttpUtils(config.getHttpConfig()).post(accessTokenUrl, form, httpHeader, false);
|
||||
String response = new HttpUtils(config.getHttpConfig()).post(accessTokenUrl, form, httpHeader, false).getBody();
|
||||
|
||||
JSONObject accessTokenObject = JSONObject.parseObject(response);
|
||||
this.checkResponse(accessTokenObject);
|
||||
@@ -54,13 +54,13 @@ public class AuthStackOverflowRequest extends AuthDefaultRequest {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AuthUser getUserInfo(AuthToken authToken) {
|
||||
public AuthUser getUserInfo(AuthToken authToken) {
|
||||
String userInfoUrl = UrlBuilder.fromBaseUrl(this.source.userInfo())
|
||||
.queryParam("access_token", authToken.getAccessToken())
|
||||
.queryParam("site", "stackoverflow")
|
||||
.queryParam("key", this.config.getStackOverflowKey())
|
||||
.build();
|
||||
String response = new HttpUtils(config.getHttpConfig()).get(userInfoUrl);
|
||||
String response = new HttpUtils(config.getHttpConfig()).get(userInfoUrl).getBody();
|
||||
JSONObject object = JSONObject.parseObject(response);
|
||||
this.checkResponse(object);
|
||||
JSONObject userObj = object.getJSONArray("items").getJSONObject(0);
|
||||
|
||||
@@ -33,7 +33,7 @@ public class AuthTaobaoRequest extends AuthDefaultRequest {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AuthToken getAccessToken(AuthCallback authCallback) {
|
||||
public AuthToken getAccessToken(AuthCallback authCallback) {
|
||||
return AuthToken.builder().accessCode(authCallback.getCode()).build();
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ public class AuthTaobaoRequest extends AuthDefaultRequest {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AuthUser getUserInfo(AuthToken authToken) {
|
||||
public AuthUser getUserInfo(AuthToken authToken) {
|
||||
String response = doPostAuthorizationCode(authToken.getAccessCode());
|
||||
JSONObject accessTokenObject = JSONObject.parseObject(response);
|
||||
if (accessTokenObject.containsKey("error")) {
|
||||
@@ -79,11 +79,11 @@ public class AuthTaobaoRequest extends AuthDefaultRequest {
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthResponse refresh(AuthToken oldToken) {
|
||||
public AuthResponse<AuthToken> refresh(AuthToken oldToken) {
|
||||
String tokenUrl = refreshTokenUrl(oldToken.getRefreshToken());
|
||||
String response = new HttpUtils(config.getHttpConfig()).post(tokenUrl);
|
||||
String response = new HttpUtils(config.getHttpConfig()).post(tokenUrl).getBody();
|
||||
JSONObject accessTokenObject = JSONObject.parseObject(response);
|
||||
return AuthResponse.builder()
|
||||
return AuthResponse.<AuthToken>builder()
|
||||
.code(AuthResponseStatus.SUCCESS.getCode())
|
||||
.data(this.getAuthToken(accessTokenObject))
|
||||
.build();
|
||||
|
||||
@@ -38,14 +38,14 @@ public class AuthTeambitionRequest extends AuthDefaultRequest {
|
||||
* @return 所有信息
|
||||
*/
|
||||
@Override
|
||||
protected AuthToken getAccessToken(AuthCallback authCallback) {
|
||||
public AuthToken getAccessToken(AuthCallback authCallback) {
|
||||
Map<String, String> form = new HashMap<>(7);
|
||||
form.put("client_id", config.getClientId());
|
||||
form.put("client_secret", config.getClientSecret());
|
||||
form.put("code", authCallback.getCode());
|
||||
form.put("grant_type", "code");
|
||||
|
||||
String response = new HttpUtils(config.getHttpConfig()).post(source.accessToken(), form, false);
|
||||
String response = new HttpUtils(config.getHttpConfig()).post(source.accessToken(), form, false).getBody();
|
||||
JSONObject accessTokenObject = JSONObject.parseObject(response);
|
||||
|
||||
this.checkResponse(accessTokenObject);
|
||||
@@ -57,13 +57,14 @@ public class AuthTeambitionRequest extends AuthDefaultRequest {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AuthUser getUserInfo(AuthToken authToken) {
|
||||
public AuthUser getUserInfo(AuthToken authToken) {
|
||||
String accessToken = authToken.getAccessToken();
|
||||
|
||||
HttpHeader httpHeader = new HttpHeader();
|
||||
httpHeader.add("Authorization", "OAuth2 " + accessToken);
|
||||
|
||||
String response = new HttpUtils(config.getHttpConfig()).get(source.userInfo(), null, httpHeader, false);
|
||||
String response = new HttpUtils(config.getHttpConfig())
|
||||
.get(source.userInfo(), null, httpHeader, false).getBody();
|
||||
JSONObject object = JSONObject.parseObject(response);
|
||||
|
||||
this.checkResponse(object);
|
||||
@@ -86,19 +87,19 @@ public class AuthTeambitionRequest extends AuthDefaultRequest {
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthResponse refresh(AuthToken oldToken) {
|
||||
public AuthResponse<AuthToken> refresh(AuthToken oldToken) {
|
||||
String uid = oldToken.getUid();
|
||||
String refreshToken = oldToken.getRefreshToken();
|
||||
|
||||
Map<String, String> form = new HashMap<>(4);
|
||||
form.put("_userId", uid);
|
||||
form.put("refresh_token", refreshToken);
|
||||
String response = new HttpUtils(config.getHttpConfig()).post(source.refresh(), form, false);
|
||||
String response = new HttpUtils(config.getHttpConfig()).post(source.refresh(), form, false).getBody();
|
||||
JSONObject refreshTokenObject = JSONObject.parseObject(response);
|
||||
|
||||
this.checkResponse(refreshTokenObject);
|
||||
|
||||
return AuthResponse.builder()
|
||||
return AuthResponse.<AuthToken>builder()
|
||||
.code(AuthResponseStatus.SUCCESS.getCode())
|
||||
.data(AuthToken.builder()
|
||||
.accessToken(refreshTokenObject.getString("access_token"))
|
||||
|
||||
@@ -29,7 +29,7 @@ public class AuthToutiaoRequest extends AuthDefaultRequest {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AuthToken getAccessToken(AuthCallback authCallback) {
|
||||
public AuthToken getAccessToken(AuthCallback authCallback) {
|
||||
String response = doGetAuthorizationCode(authCallback.getCode());
|
||||
JSONObject accessTokenObject = JSONObject.parseObject(response);
|
||||
|
||||
@@ -43,7 +43,7 @@ public class AuthToutiaoRequest extends AuthDefaultRequest {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AuthUser getUserInfo(AuthToken authToken) {
|
||||
public AuthUser getUserInfo(AuthToken authToken) {
|
||||
String userResponse = doGetUserInfo(authToken);
|
||||
|
||||
JSONObject userProfile = JSONObject.parseObject(userResponse);
|
||||
|
||||
@@ -69,10 +69,8 @@ public class AuthTwitterRequest extends AuthDefaultRequest {
|
||||
|
||||
HttpHeader httpHeader = new HttpHeader();
|
||||
httpHeader.add("Authorization", header);
|
||||
httpHeader.add("User-Agent", "themattharris' HTTP Client");
|
||||
httpHeader.add("Host", "api.twitter.com");
|
||||
httpHeader.add("Accept", "*/*");
|
||||
String requestToken = new HttpUtils(config.getHttpConfig()).post(baseUrl, null, httpHeader);
|
||||
httpHeader.add("User-Agent", "'JustAuth' HTTP Client Simple-Http");
|
||||
String requestToken = new HttpUtils(config.getHttpConfig()).post(baseUrl, null, httpHeader).getBody();
|
||||
|
||||
Map<String, String> res = MapUtil.parseStringToMap(requestToken, false);
|
||||
|
||||
@@ -90,7 +88,7 @@ public class AuthTwitterRequest extends AuthDefaultRequest {
|
||||
* @return access token
|
||||
*/
|
||||
@Override
|
||||
protected AuthToken getAccessToken(AuthCallback authCallback) {
|
||||
public AuthToken getAccessToken(AuthCallback authCallback) {
|
||||
Map<String, String> oauthParams = buildOauthParams();
|
||||
oauthParams.put("oauth_token", authCallback.getOauth_token());
|
||||
oauthParams.put("oauth_verifier", authCallback.getOauth_verifier());
|
||||
@@ -104,7 +102,7 @@ public class AuthTwitterRequest extends AuthDefaultRequest {
|
||||
|
||||
Map<String, String> form = new HashMap<>(3);
|
||||
form.put("oauth_verifier", authCallback.getOauth_verifier());
|
||||
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();
|
||||
|
||||
Map<String, String> requestToken = MapUtil.parseStringToMap(response, false);
|
||||
|
||||
@@ -117,7 +115,7 @@ public class AuthTwitterRequest extends AuthDefaultRequest {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AuthUser getUserInfo(AuthToken authToken) {
|
||||
public AuthUser getUserInfo(AuthToken authToken) {
|
||||
Map<String, String> queryParams = new HashMap<>(5);
|
||||
queryParams.put("include_entities", Boolean.toString(true));
|
||||
queryParams.put("include_email", Boolean.toString(true));
|
||||
@@ -132,7 +130,8 @@ public class AuthTwitterRequest extends AuthDefaultRequest {
|
||||
|
||||
HttpHeader httpHeader = new HttpHeader();
|
||||
httpHeader.add("Authorization", header);
|
||||
String response = new HttpUtils(config.getHttpConfig()).get(userInfoUrl(authToken), null, httpHeader, false);
|
||||
String response = new HttpUtils(config.getHttpConfig())
|
||||
.get(userInfoUrl(authToken), null, httpHeader, false).getBody();
|
||||
JSONObject userInfo = JSONObject.parseObject(response);
|
||||
|
||||
return AuthUser.builder()
|
||||
|
||||
@@ -31,6 +31,7 @@ public class AuthWeChatEnterpriseQrcodeRequest extends AbstractAuthWeChatEnterpr
|
||||
.queryParam("agentid", config.getAgentId())
|
||||
.queryParam("redirect_uri", config.getRedirectUri())
|
||||
.queryParam("state", getRealState(state))
|
||||
.queryParam("lang", config.getLang())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
package me.zhyd.oauth.request;
|
||||
|
||||
import me.zhyd.oauth.cache.AuthStateCache;
|
||||
import me.zhyd.oauth.config.AuthConfig;
|
||||
import me.zhyd.oauth.config.AuthDefaultSource;
|
||||
import me.zhyd.oauth.enums.AuthResponseStatus;
|
||||
import me.zhyd.oauth.exception.AuthException;
|
||||
import me.zhyd.oauth.utils.GlobalAuthUtils;
|
||||
import me.zhyd.oauth.utils.StringUtils;
|
||||
import me.zhyd.oauth.utils.UrlBuilder;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 新版企业微信 Web 登录,参考 <a href="https://developer.work.weixin.qq.com/document/path/98152">https://developer.work.weixin.qq.com/document/path/98152</a>
|
||||
* </p>
|
||||
*
|
||||
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
|
||||
* @since 1.16.7
|
||||
*/
|
||||
public class AuthWeChatEnterpriseQrcodeV2Request extends AbstractAuthWeChatEnterpriseRequest {
|
||||
public AuthWeChatEnterpriseQrcodeV2Request(AuthConfig config) {
|
||||
super(config, AuthDefaultSource.WECHAT_ENTERPRISE_V2);
|
||||
}
|
||||
|
||||
public AuthWeChatEnterpriseQrcodeV2Request(AuthConfig config, AuthStateCache authStateCache) {
|
||||
super(config, AuthDefaultSource.WECHAT_ENTERPRISE_V2, authStateCache);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String authorize(String state) {
|
||||
return UrlBuilder.fromBaseUrl(source.authorize())
|
||||
.queryParam("login_type", config.getLoginType())
|
||||
// 登录类型为企业自建应用/服务商代开发应用时填企业 CorpID,第三方登录时填登录授权 SuiteID
|
||||
.queryParam("appid", config.getClientId())
|
||||
// 企业自建应用/服务商代开发应用 AgentID,当login_type=CorpApp时填写
|
||||
.queryParam("agentid", config.getAgentId())
|
||||
.queryParam("redirect_uri", GlobalAuthUtils.urlEncode(config.getRedirectUri()))
|
||||
.queryParam("state", getRealState(state))
|
||||
.queryParam("lang", config.getLang())
|
||||
.build()
|
||||
.concat("#wechat_redirect");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void checkConfig(AuthConfig config) {
|
||||
super.checkConfig(config);
|
||||
if ("CorpApp".equals(config.getLoginType()) && StringUtils.isEmpty(config.getAgentId())) {
|
||||
throw new AuthException(AuthResponseStatus.ILLEGAL_WECHAT_AGENT_ID, source);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
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.AuthResponseStatus;
|
||||
import me.zhyd.oauth.exception.AuthException;
|
||||
import me.zhyd.oauth.log.Log;
|
||||
import me.zhyd.oauth.model.AuthCallback;
|
||||
import me.zhyd.oauth.model.AuthResponse;
|
||||
import me.zhyd.oauth.model.AuthToken;
|
||||
import me.zhyd.oauth.model.AuthUser;
|
||||
import me.zhyd.oauth.utils.*;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 企业微信第三方二维码登录
|
||||
* </p>
|
||||
*
|
||||
* @author zhengjx
|
||||
* @since 1.16.3
|
||||
*/
|
||||
public class AuthWeChatEnterpriseThirdQrcodeRequest extends AbstractAuthWeChatEnterpriseRequest {
|
||||
public AuthWeChatEnterpriseThirdQrcodeRequest(AuthConfig config) {
|
||||
super(config, AuthDefaultSource.WECHAT_ENTERPRISE_QRCODE_THIRD);
|
||||
}
|
||||
|
||||
public AuthWeChatEnterpriseThirdQrcodeRequest(AuthConfig config, AuthStateCache authStateCache) {
|
||||
super(config, AuthDefaultSource.WECHAT_ENTERPRISE_QRCODE_THIRD, authStateCache);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String authorize(String state) {
|
||||
return UrlBuilder.fromBaseUrl(source.authorize())
|
||||
.queryParam("appid", config.getClientId())
|
||||
.queryParam("redirect_uri", config.getRedirectUri())
|
||||
.queryParam("state", getRealState(state))
|
||||
.queryParam("usertype", config.getUsertype())
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthResponse<AuthUser> login(AuthCallback authCallback) {
|
||||
try {
|
||||
if (!config.isIgnoreCheckState()) {
|
||||
AuthChecker.checkState(authCallback.getState(), source, authStateCache);
|
||||
}
|
||||
AuthToken authToken = this.getAccessToken(authCallback);
|
||||
AuthUser user = this.getUserInfo(authToken);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthToken getAccessToken(AuthCallback authCallback) {
|
||||
try {
|
||||
String response = doGetAuthorizationCode(accessTokenUrl());
|
||||
JSONObject object = this.checkResponse(response);
|
||||
AuthToken authToken = AuthToken.builder()
|
||||
.accessToken(object.getString("provider_access_token"))
|
||||
.expireIn(object.getIntValue("expires_in"))
|
||||
.code(authCallback.getCode())
|
||||
.build();
|
||||
return authToken;
|
||||
} catch (Exception e) {
|
||||
throw new AuthException("企业微信获取token失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String doGetAuthorizationCode(String code) {
|
||||
JSONObject data = new JSONObject();
|
||||
data.put("corpid", config.getClientId());
|
||||
data.put("provider_secret", config.getClientSecret());
|
||||
return new HttpUtils(config.getHttpConfig()).post(accessTokenUrl(code), data.toJSONString()).getBody();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取token的URL
|
||||
*
|
||||
* @return accessTokenUrl
|
||||
*/
|
||||
protected String accessTokenUrl() {
|
||||
return UrlBuilder.fromBaseUrl(source.accessToken())
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthUser getUserInfo(AuthToken authToken) {
|
||||
JSONObject response = this.checkResponse(doGetUserInfo(authToken));
|
||||
return AuthUser.builder()
|
||||
.rawUserInfo(response)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String doGetUserInfo(AuthToken authToken) {
|
||||
JSONObject data = new JSONObject();
|
||||
data.put("auth_code", authToken.getCode());
|
||||
return new HttpUtils(config.getHttpConfig())
|
||||
.post(userInfoUrl(authToken), data.toJSONString()).getBody();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String userInfoUrl(AuthToken authToken) {
|
||||
return UrlBuilder.fromBaseUrl(source.userInfo())
|
||||
.queryParam("access_token", authToken.getAccessToken()).
|
||||
build();
|
||||
}
|
||||
|
||||
private JSONObject checkResponse(String response) {
|
||||
JSONObject object = JSONObject.parseObject(response);
|
||||
if (object.containsKey("errcode") && object.getIntValue("errcode") != 0) {
|
||||
throw new AuthException(object.getString("errmsg"), source);
|
||||
}
|
||||
return object;
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import me.zhyd.oauth.config.AuthConfig;
|
||||
import me.zhyd.oauth.config.AuthDefaultSource;
|
||||
import me.zhyd.oauth.enums.scope.AuthWeChatEnterpriseWebScope;
|
||||
import me.zhyd.oauth.utils.AuthScopeUtils;
|
||||
import me.zhyd.oauth.utils.GlobalAuthUtils;
|
||||
import me.zhyd.oauth.utils.UrlBuilder;
|
||||
|
||||
/**
|
||||
@@ -28,7 +29,8 @@ public class AuthWeChatEnterpriseWebRequest extends AbstractAuthWeChatEnterprise
|
||||
public String authorize(String state) {
|
||||
return UrlBuilder.fromBaseUrl(source.authorize())
|
||||
.queryParam("appid", config.getClientId())
|
||||
.queryParam("redirect_uri", config.getRedirectUri())
|
||||
.queryParam("agentid", config.getAgentId())
|
||||
.queryParam("redirect_uri", GlobalAuthUtils.urlEncode(config.getRedirectUri()))
|
||||
.queryParam("response_type", "code")
|
||||
.queryParam("scope", this.getScopes(",", false, AuthScopeUtils.getDefaultScopes(AuthWeChatEnterpriseWebScope.values())))
|
||||
.queryParam("state", getRealState(state).concat("#wechat_redirect"))
|
||||
|
||||
@@ -12,10 +12,7 @@ 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.GlobalAuthUtils;
|
||||
import me.zhyd.oauth.utils.HttpUtils;
|
||||
import me.zhyd.oauth.utils.UrlBuilder;
|
||||
import me.zhyd.oauth.utils.*;
|
||||
|
||||
/**
|
||||
* 微信公众平台登录
|
||||
@@ -39,25 +36,34 @@ public class AuthWeChatMpRequest extends AuthDefaultRequest {
|
||||
* @return 所有信息
|
||||
*/
|
||||
@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 openId = authToken.getOpenId();
|
||||
|
||||
String scope = authToken.getScope();
|
||||
if (!StringUtils.isEmpty(scope) && !scope.contains("snsapi_userinfo")) {
|
||||
return AuthUser.builder()
|
||||
.rawUserInfo(JSONObject.parseObject(JSONObject.toJSONString(authToken)))
|
||||
.uuid(openId)
|
||||
.snapshotUser(authToken.isSnapshotUser())
|
||||
.token(authToken)
|
||||
.source(source.toString())
|
||||
.build();
|
||||
}
|
||||
|
||||
String response = doGetUserInfo(authToken);
|
||||
JSONObject object = JSONObject.parseObject(response);
|
||||
|
||||
this.checkResponse(object);
|
||||
|
||||
String location = String.format("%s-%s-%s", object.getString("country"), object.getString("province"), object.getString("city"));
|
||||
|
||||
if (object.containsKey("unionid")) {
|
||||
authToken.setUnionId(object.getString("unionid"));
|
||||
}
|
||||
|
||||
return AuthUser.builder()
|
||||
.rawUserInfo(object)
|
||||
.username(object.getString("nickname"))
|
||||
@@ -65,6 +71,7 @@ public class AuthWeChatMpRequest extends AuthDefaultRequest {
|
||||
.avatar(object.getString("headimgurl"))
|
||||
.location(location)
|
||||
.uuid(openId)
|
||||
.snapshotUser(authToken.isSnapshotUser())
|
||||
.gender(AuthUserGender.getWechatRealGender(object.getString("sex")))
|
||||
.token(authToken)
|
||||
.source(source.toString())
|
||||
@@ -72,8 +79,8 @@ public class AuthWeChatMpRequest 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(this.getToken(refreshTokenUrl(oldToken.getRefreshToken())))
|
||||
.build();
|
||||
@@ -97,7 +104,7 @@ public class AuthWeChatMpRequest extends AuthDefaultRequest {
|
||||
* @return token对象
|
||||
*/
|
||||
private AuthToken getToken(String accessTokenUrl) {
|
||||
String response = new HttpUtils(config.getHttpConfig()).get(accessTokenUrl);
|
||||
String response = new HttpUtils(config.getHttpConfig()).get(accessTokenUrl).getBody();
|
||||
JSONObject accessTokenObject = JSONObject.parseObject(response);
|
||||
|
||||
this.checkResponse(accessTokenObject);
|
||||
@@ -108,6 +115,7 @@ public class AuthWeChatMpRequest extends AuthDefaultRequest {
|
||||
.expireIn(accessTokenObject.getIntValue("expires_in"))
|
||||
.openId(accessTokenObject.getString("openid"))
|
||||
.scope(accessTokenObject.getString("scope"))
|
||||
.snapshotUser(accessTokenObject.getIntValue("is_snapshotuser") == 1)
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
@@ -36,12 +36,12 @@ public class AuthWeChatOpenRequest extends AuthDefaultRequest {
|
||||
* @return 所有信息
|
||||
*/
|
||||
@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 openId = authToken.getOpenId();
|
||||
|
||||
String response = doGetUserInfo(authToken);
|
||||
@@ -69,8 +69,8 @@ public class AuthWeChatOpenRequest 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(this.getToken(refreshTokenUrl(oldToken.getRefreshToken())))
|
||||
.build();
|
||||
@@ -94,7 +94,7 @@ public class AuthWeChatOpenRequest extends AuthDefaultRequest {
|
||||
* @return token对象
|
||||
*/
|
||||
private AuthToken getToken(String accessTokenUrl) {
|
||||
String response = new HttpUtils(config.getHttpConfig()).get(accessTokenUrl);
|
||||
String response = new HttpUtils(config.getHttpConfig()).get(accessTokenUrl).getBody();
|
||||
JSONObject accessTokenObject = JSONObject.parseObject(response);
|
||||
|
||||
this.checkResponse(accessTokenObject);
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
package me.zhyd.oauth.request;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.alibaba.fastjson.annotation.JSONField;
|
||||
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.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.UrlBuilder;
|
||||
|
||||
/**
|
||||
* 微信小程序授权登录
|
||||
*
|
||||
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
|
||||
* @author yudaocode
|
||||
* @version 1.0.0
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class AuthWechatMiniProgramRequest extends AuthDefaultRequest {
|
||||
public AuthWechatMiniProgramRequest(AuthConfig config) {
|
||||
super(config, AuthDefaultSource.WECHAT_MINI_PROGRAM);
|
||||
}
|
||||
|
||||
public AuthWechatMiniProgramRequest(AuthConfig config, AuthStateCache authStateCache) {
|
||||
super(config, AuthDefaultSource.WECHAT_MINI_PROGRAM, authStateCache);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthToken getAccessToken(AuthCallback authCallback) {
|
||||
// 参见 https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/login/auth.code2Session.html 文档
|
||||
// 使用 code 获取对应的 openId、unionId 等字段
|
||||
String response = new HttpUtils(config.getHttpConfig()).get(accessTokenUrl(authCallback.getCode())).getBody();
|
||||
JSCode2SessionResponse accessTokenObject = JSONObject.parseObject(response, JSCode2SessionResponse.class);
|
||||
assert accessTokenObject != null;
|
||||
checkResponse(accessTokenObject);
|
||||
// 拼装结果
|
||||
return AuthToken.builder()
|
||||
.openId(accessTokenObject.getOpenid())
|
||||
.unionId(accessTokenObject.getUnionId())
|
||||
.accessToken(accessTokenObject.getSessionKey())
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthUser getUserInfo(AuthToken authToken) {
|
||||
// 参见 https://developers.weixin.qq.com/miniprogram/dev/api/open-api/user-info/wx.getUserProfile.html 文档
|
||||
// 如果需要用户信息,需要在小程序调用函数后传给后端
|
||||
return AuthUser.builder()
|
||||
.username("")
|
||||
.nickname("")
|
||||
.avatar("")
|
||||
.uuid(authToken.getOpenId())
|
||||
.token(authToken)
|
||||
.source(source.toString())
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查响应内容是否正确
|
||||
*
|
||||
* @param response 请求响应内容
|
||||
*/
|
||||
private void checkResponse(JSCode2SessionResponse response) {
|
||||
if (response.getErrorCode() != 0) {
|
||||
throw new AuthException(response.getErrorCode(), response.getErrorMsg());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String accessTokenUrl(String code) {
|
||||
return UrlBuilder.fromBaseUrl(source.accessToken())
|
||||
.queryParam("appid", config.getClientId())
|
||||
.queryParam("secret", config.getClientSecret())
|
||||
.queryParam("js_code", code)
|
||||
.queryParam("grant_type", "authorization_code")
|
||||
.build();
|
||||
}
|
||||
|
||||
@Data
|
||||
@SuppressWarnings("SpellCheckingInspection")
|
||||
private static class JSCode2SessionResponse {
|
||||
|
||||
@JSONField(name = "errcode")
|
||||
private int errorCode;
|
||||
@JSONField(name = "errmsg")
|
||||
private String errorMsg;
|
||||
@JSONField(name = "session_key")
|
||||
private String sessionKey;
|
||||
private String openid;
|
||||
@JSONField(name = "unionid")
|
||||
private String unionId;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -33,7 +33,7 @@ public class AuthWeiboRequest extends AuthDefaultRequest {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AuthToken getAccessToken(AuthCallback authCallback) {
|
||||
public AuthToken getAccessToken(AuthCallback authCallback) {
|
||||
String response = doPostAuthorizationCode(authCallback.getCode());
|
||||
JSONObject accessTokenObject = JSONObject.parseObject(response);
|
||||
if (accessTokenObject.containsKey("error")) {
|
||||
@@ -48,7 +48,7 @@ public class AuthWeiboRequest extends AuthDefaultRequest {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AuthUser getUserInfo(AuthToken authToken) {
|
||||
public AuthUser getUserInfo(AuthToken authToken) {
|
||||
String accessToken = authToken.getAccessToken();
|
||||
String uid = authToken.getUid();
|
||||
String oauthParam = String.format("uid=%s&access_token=%s", uid, accessToken);
|
||||
@@ -56,7 +56,8 @@ public class AuthWeiboRequest extends AuthDefaultRequest {
|
||||
HttpHeader httpHeader = new HttpHeader();
|
||||
httpHeader.add("Authorization", "OAuth2 " + oauthParam);
|
||||
httpHeader.add("API-RemoteIP", IpUtils.getLocalIp());
|
||||
String userInfo = new HttpUtils(config.getHttpConfig()).get(userInfoUrl(authToken), null, httpHeader, false);
|
||||
String userInfo = new HttpUtils(config.getHttpConfig())
|
||||
.get(userInfoUrl(authToken), null, httpHeader, false).getBody();
|
||||
JSONObject object = JSONObject.parseObject(userInfo);
|
||||
if (object.containsKey("error")) {
|
||||
throw new AuthException(object.getString("error"));
|
||||
|
||||
@@ -42,7 +42,7 @@ public class AuthXmlyRequest extends AuthDefaultRequest {
|
||||
* @see AuthDefaultRequest#authorize(String)
|
||||
*/
|
||||
@Override
|
||||
protected AuthToken getAccessToken(AuthCallback authCallback) {
|
||||
public AuthToken getAccessToken(AuthCallback authCallback) {
|
||||
Map<String, String> map = new HashMap<>(9);
|
||||
map.put("code", authCallback.getCode());
|
||||
map.put("client_id", config.getClientId());
|
||||
@@ -50,7 +50,7 @@ public class AuthXmlyRequest extends AuthDefaultRequest {
|
||||
map.put("device_id", config.getDeviceId());
|
||||
map.put("grant_type", "authorization_code");
|
||||
map.put("redirect_uri", config.getRedirectUri());
|
||||
String response = HttpUtil.post(source.accessToken(), map, true);
|
||||
String response = HttpUtil.post(source.accessToken(), map, true).getBody();
|
||||
JSONObject accessTokenObject = JSONObject.parseObject(response);
|
||||
this.checkResponse(accessTokenObject);
|
||||
|
||||
@@ -97,7 +97,7 @@ public class AuthXmlyRequest extends AuthDefaultRequest {
|
||||
map.put("pack_id", config.getPackId());
|
||||
map.put("access_token", authToken.getAccessToken());
|
||||
map.put("sig", GlobalAuthUtils.generateXmlySignature(map, config.getClientSecret()));
|
||||
String rawUserInfo = HttpUtil.get(source.userInfo(), map, false);
|
||||
String rawUserInfo = HttpUtil.get(source.userInfo(), map, false).getBody();
|
||||
JSONObject object = JSONObject.parseObject(rawUserInfo);
|
||||
checkResponse(object);
|
||||
return AuthUser.builder()
|
||||
|
||||
@@ -27,9 +27,6 @@ public class AuthChecker {
|
||||
public static boolean isSupportedAuth(AuthConfig config, AuthSource source) {
|
||||
boolean isSupported = StringUtils.isNotEmpty(config.getClientId())
|
||||
&& StringUtils.isNotEmpty(config.getClientSecret());
|
||||
if (isSupported && AuthDefaultSource.ALIPAY == source) {
|
||||
isSupported = StringUtils.isNotEmpty(config.getAlipayPublicKey());
|
||||
}
|
||||
if (isSupported && AuthDefaultSource.STACK_OVERFLOW == source) {
|
||||
isSupported = StringUtils.isNotEmpty(config.getStackOverflowKey());
|
||||
}
|
||||
@@ -66,16 +63,6 @@ public class AuthChecker {
|
||||
if (!GlobalAuthUtils.isHttpProtocol(redirectUri) && !GlobalAuthUtils.isHttpsProtocol(redirectUri)) {
|
||||
throw new AuthException(AuthResponseStatus.ILLEGAL_REDIRECT_URI, source);
|
||||
}
|
||||
// facebook的回调地址必须为https的链接
|
||||
if (AuthDefaultSource.FACEBOOK == source && !GlobalAuthUtils.isHttpsProtocol(redirectUri)) {
|
||||
// Facebook's redirect uri must use the HTTPS protocol
|
||||
throw new AuthException(AuthResponseStatus.ILLEGAL_REDIRECT_URI, source);
|
||||
}
|
||||
// 支付宝在创建回调地址时,不允许使用localhost或者127.0.0.1
|
||||
if (AuthDefaultSource.ALIPAY == source && GlobalAuthUtils.isLocalHost(redirectUri)) {
|
||||
// The redirect uri of alipay is forbidden to use localhost or 127.0.0.1
|
||||
throw new AuthException(AuthResponseStatus.ILLEGAL_REDIRECT_URI, source);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -93,9 +80,7 @@ public class AuthChecker {
|
||||
return;
|
||||
}
|
||||
String code = callback.getCode();
|
||||
if (source == AuthDefaultSource.ALIPAY) {
|
||||
code = callback.getAuth_code();
|
||||
} else if (source == AuthDefaultSource.HUAWEI) {
|
||||
if (StringUtils.isEmpty(code) && source == AuthDefaultSource.HUAWEI) {
|
||||
code = callback.getAuthorization_code();
|
||||
}
|
||||
if (StringUtils.isEmpty(code)) {
|
||||
|
||||
@@ -175,6 +175,19 @@ public class GlobalAuthUtils {
|
||||
return StringUtils.isEmpty(url) || url.contains("127.0.0.1") || url.contains("localhost");
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为https协议或本地主机(域名)
|
||||
*
|
||||
* @param url 待验证的url
|
||||
* @return true: https协议或本地主机 false: 非https协议或本机主机
|
||||
*/
|
||||
public static boolean isHttpsProtocolOrLocalHost(String url) {
|
||||
if (StringUtils.isEmpty(url)) {
|
||||
return false;
|
||||
}
|
||||
return isHttpsProtocol(url) || isLocalHost(url);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generate nonce with given length
|
||||
|
||||