diff --git a/.gitee/ISSUE_TEMPLATE.zh-CN.md b/.gitee/ISSUE_TEMPLATE.zh-CN.md
index 6c3ee3d..a06693c 100644
--- a/.gitee/ISSUE_TEMPLATE.zh-CN.md
+++ b/.gitee/ISSUE_TEMPLATE.zh-CN.md
@@ -1,6 +1,6 @@
> 为了更高效率的处理 issue、解决问题,请按照如下格式填写 BUG 详情。
>
-> 如果提交的 issue 不属于 BUG,可以忽略该问题模板。
+> 如果提交的 issue 不属于 BUG 范围,可以忽略/重置该问题模板。
## 提交前请确认:
@@ -9,8 +9,16 @@
## 当前环境
-- 使用的版本号(比如 `1.0.1`):
-- 出问题的模块(比如 `jap-ids`):
+- 出问题的模块:
+ - [ ] jap-ids
+ - [ ] jap-mfa
+ - [ ] jap-oauth2
+ - [ ] jap-oidc
+ - [ ] jap-simple
+ - [ ] jap-social
+ - [ ] jap-sso
+ - [ ] jap-core
+- 使用的 jap 版本号:
## 问题描述
@@ -22,7 +30,7 @@
## 实际结果如何? 报错信息(请提交完整的截图或者日志)
-> 一定要提供完整详细的异常堆栈。
+> 如果涉及到 BUG,请一定要提供完整详细的异常堆栈。
diff --git a/.gitee/PULL_REQUEST_TEMPLATE.zh-CN.md b/.gitee/PULL_REQUEST_TEMPLATE.zh-CN.md
index 46b51ba..bb9f7b4 100644
--- a/.gitee/PULL_REQUEST_TEMPLATE.zh-CN.md
+++ b/.gitee/PULL_REQUEST_TEMPLATE.zh-CN.md
@@ -4,7 +4,7 @@
> - 是否新增了第三方依赖库?如果新增了第三方依赖库,其 LICENSE 是否兼容 LGPL-3.0? (LICENSE 兼容,请参考:[开源许可证兼容性指南 - 使用库的兼容性列表](https://shimo.im/docs/uL4VQaYGL2sadQOV#anchor-74ae))
> - 是否参考/借鉴/复制了其他开源项目的代码?被引用的代码所属项目的 LICENSE 是否兼容 LGPL-3.0?(LICENSE 兼容,请参考:[开源许可证兼容性指南 - 合并/修改代码的许可证兼容性列表](https://shimo.im/docs/uL4VQaYGL2sadQOV#anchor-39f8))
>
-> 如果你的代码或者代码中新引入的依赖项与 LGPL-3.0 不兼容,非常抱歉,我们可能不会合并你的代码。
+> 如果你的代码或者代码中新引入的依赖项与 LGPL-3.0 不兼容,非常抱歉,我们可能不会通过你的代码。
>
> 最后,感谢你的关注、支持。
@@ -13,6 +13,9 @@
## 【必填】是否自测完成并提供了相关单元测试代码?
+- [ ] 是,已经自测完成,并提供了单元测试代码,可以直接合并。
+- [ ] 否,仅完成了代码,并没做测试,也没有添加单元测试。
+
## 【可选】关联的 Issue(有则填)
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
index ad01cc3..c585351 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -15,8 +15,16 @@ assignees: ''
## Environment
+- JustAuthPlus(JAP) Module:
+ - [ ] jap-ids
+ - [ ] jap-mfa
+ - [ ] jap-oauth2
+ - [ ] jap-oidc
+ - [ ] jap-simple
+ - [ ] jap-social
+ - [ ] jap-sso
+ - [ ] jap-core
- JustAuthPlus(JAP) version(e.g. `1.0.1`):
-- JustAuthPlus(JAP) Module(e.g. `jap-ids`):
### Minimal test code / Steps to reproduce the issue
1.
diff --git a/.github/ISSUE_TEMPLATE/request-help.md b/.github/ISSUE_TEMPLATE/request-help.md
index 64d7ac4..52cebe8 100644
--- a/.github/ISSUE_TEMPLATE/request-help.md
+++ b/.github/ISSUE_TEMPLATE/request-help.md
@@ -12,5 +12,13 @@ assignees: ''
## Environment
-- JustAuthPlus(JAP) version(e.g. `1.0.1`):
-- JustAuthPlus(JAP) Module(e.g. `jap-ids`):
\ No newline at end of file
+- JustAuthPlus(JAP) Module:
+ - [ ] jap-ids
+ - [ ] jap-mfa
+ - [ ] jap-oauth2
+ - [ ] jap-oidc
+ - [ ] jap-simple
+ - [ ] jap-social
+ - [ ] jap-sso
+ - [ ] jap-core
+- JustAuthPlus(JAP) version(e.g. `1.0.1`):
\ No newline at end of file
diff --git a/.github/workflows/publish-snapshot.yml b/.github/workflows/publish-snapshot.yml
index 15a2bc0..e893371 100644
--- a/.github/workflows/publish-snapshot.yml
+++ b/.github/workflows/publish-snapshot.yml
@@ -7,8 +7,14 @@ on:
workflow_dispatch:
push:
branches: [ dev ]
+ paths:
+ - src/**
+ - pom.xml
pull_request:
branches: [ dev ]
+ paths:
+ - src/**
+ - pom.xml
jobs:
publish:
@@ -30,7 +36,22 @@ jobs:
gpg-passphrase: MAVEN_GPG_PASSWORD
gpg-private-key: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }}
- - name: Publish to the Maven Central Repository
+ - 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: get current project version to set env.VERSION
+ run: echo "VERSION=`mvn help:evaluate -Dexpression=project.version -q -DforceStdout`" >> $GITHUB_ENV
+
+ - name: set snapshot version
+ if: ${{ !endsWith( env.VERSION , '-SNAPSHOT') }}
+ run: mvn versions:set -DnewVersion=${{ env.VERSION }}-SNAPSHOT
+
+ - name: deploy snapshot to oss repository
run: mvn clean deploy -P release
env:
MAVEN_USERNAME: ${{ secrets.OSSRH_USERNAME }}
diff --git a/.gitignore b/.gitignore
index 5fdc48a..eb38926 100644
--- a/.gitignore
+++ b/.gitignore
@@ -51,7 +51,6 @@ build/
.vscode/
### other ###
-/jap-simple/src/main/java/com/fujieid/jap/simple/SimpleCallback.java
/docs/bin/deploy.sh
/jap-core/pom.xml.versionsBackup
/jap-oauth2/pom.xml.versionsBackup
diff --git a/CHANGELOGS.md b/CHANGELOGS.md
index b85e8fe..0eec064 100644
--- a/CHANGELOGS.md
+++ b/CHANGELOGS.md
@@ -1,4 +1,87 @@
-## v1.0.4 (2021-08-**)
+## v1.0.5 (2021-09-15)
+
+- feat: Add `jap-http-api` module. (Gitee Issue [#I43ZS7](https://gitee.com/fujieid/jap/issues/I43ZS7))
+- feat: Add `jap-ids-web` module. Package the filter of ids as a separate component.
+- feat: add HTTP servlet adapter to decouple jakarta servlets. **Note [1]**
+- feat: [jap-social] Support to bind the account of the third-party platform. (Gitee Issue [#I46J6W](https://gitee.com/fujieid/jap/issues/I46J6W))
+- change: [jap-ids] scope changed to optional.
+- change: [jap-sso] Upgrade `kisso` to 3.7.7, **Solve the vulnerability of jackson**.
+- change: [jap-mfa] Upgrade `googleauth` to 1.5.0, **Solve the vulnerability of apache httpclient**.
+- change: Upgrade `simple-http` to 1.0.5.
+- change: Upgrade `JustAuth` to 1.16.4.
+- change: Optimize code.
+
+**Note [1]:**
+
+In versions prior to version 1.0.5 of jap, rely on the `HttpServletRequest`, `Cookie`, `HttpServletResponse`, and `HttpSession` under the `javax.servlet.http` package in `jakarta-servlet`, such as:
+
+```java
+// Interface provided by jap
+public interface JapStrategy {
+ default JapResponse authenticate(AuthenticateConfig config, HttpServletRequest request, HttpServletResponse response) {
+ return null;
+ }
+}
+```
+
+```java
+// Use jap in spring framework
+XxJapStrategy.authenticate(config, request, response);
+```
+
+In order to improve the adaptability of the framework, since version 1.0.5, JAP removed the dependency of `jakarta-servlet` and adopted a new set of interfaces (reference: [jap-http](https:gitee.comfujieidjap-http) ).
+
+The developer needs to adapt the original request when calling the JAP interface.
+
+For example, if the developer uses `jakarta-servlet`, then the `HttpServletRequest` needs to be adapted:
+
+```java
+// Use 1.0.5 or higher version of jap in spring framework
+XxJapStrategy.authenticate(config, new JakartaRequestAdapter(request), new JakartaResponseAdapter(response));
+```
+
+----
+
+- feat: 增加 `jap-http-api` 模块。 (Gitee Issue [#I43ZS7](https://gitee.com/fujieid/jap/issues/I43ZS7))
+- feat: 增加 `jap-ids-web` 模块。 将 `jap-ids` 的过滤器打包为一个单独的组件。
+- feat: 添加 HTTP servlet 适配器以解耦 jakarta servlet。**注[1]**
+- feat: [jap-social] 支持绑定第三方平台账号,该版本将社会化登录和绑定账号独立开来,以使其更加使用与多场景。 (Gitee Issue [#I46J6W](https://gitee.com/fujieid/jap/issues/I46J6W))
+- change: [jap-ids] `scope` 在各个流程中都更改为可选,遵循 RFC6749 规范。
+- change: [jap-sso] 升级 `kisso` 的版本为 3.7.7, **解决 jackson 的漏洞**。
+- change: [jap-mfa] 升级 `googleauth` 的版本为 1.5.0, **解决 apache httpclient 的漏洞**。
+- change: 升级 `simple-http` 的版本为 1.0.5.
+- change: 升级 `JustAuth` 的版本为 1.16.4.
+- change: 优化代码。
+
+**注[1]:**
+
+在 1.0.5 以前版本,jap 中依赖 `jakarta-servlet` 中 `javax.servlet.http` 包下的 `HttpServletRequest`、`Cookie`、`HttpServletResponse`、`HttpSession`,比如:
+
+```java
+// jap 提供的接口
+public interface JapStrategy {
+ default JapResponse authenticate(AuthenticateConfig config, HttpServletRequest request, HttpServletResponse response) {
+ return null;
+ }
+}
+```
+
+```java
+// 在spring框架中使用 jap
+XxJapStrategy.authenticate(config, request, response);
+```
+
+为了提高框架适配性,自 1.0.5 版本开始,JAP 去掉了 `jakarta-servlet` 依赖,采用了一套全新的接口(参考:[jap-http](https://gitee.com/fujieid/jap-http)),开发者在调用 JAP 接口时需要对原 request 进行适配。
+
+比如,开发者使用了 `jakarta-servlet`,那么需要对 `HttpServletRequest` 进行适配处理:
+
+```java
+// 在spring框架中使用 1.0.5 或更高级版本的 jap
+XxJapStrategy.authenticate(config, new JakartaRequestAdapter(request), new JakartaResponseAdapter(response));
+```
+
+
+## v1.0.4 (2021-08-15)
- fix: [jap-ids] Support to generate custom token. (Gitee[#I3U1ON](https://gitee.com/fujieid/jap/issues/I3U1ON))
- fix: [jap-ids] Support custom verification of client_secret, such as: BCrypt, etc. (Gitee[#I44032](https://gitee.com/fujieid/jap/issues/I44032))
@@ -10,6 +93,7 @@
- doc: change the template of issue and PR
----
+
- fix: [jap-ids] 支持生成自定义 token(包含 access_token 和 refresh_token)。 (Gitee[#I3U1ON](https://gitee.com/fujieid/jap/issues/I3U1ON))
- fix: [jap-ids] 支持自定义验证 `client_secret`,适配多种场景,如:BCrypt 等。 (Gitee[#I44032](https://gitee.com/fujieid/jap/issues/I44032))
- feat: [jap-ids] 当启用 `IdsConfig#enableDynamicIssuer` 时,支持自定义 `context-path`
diff --git a/jap-core/src/main/java/com/fujieid/jap/core/JapUserService.java b/jap-core/src/main/java/com/fujieid/jap/core/JapUserService.java
index 318260f..8e19b36 100644
--- a/jap-core/src/main/java/com/fujieid/jap/core/JapUserService.java
+++ b/jap-core/src/main/java/com/fujieid/jap/core/JapUserService.java
@@ -86,6 +86,18 @@ public interface JapUserService {
return null;
}
+
+ /**
+ * After logging in, bind the account of the third-party platform
+ *
+ * @param japUser User information of the third-party platform after successful login
+ * @param bindUserId The user id that needs to be bound, this is a user of the business system, not a user of the third-party platform.
+ * @return When binding successfully, return {@code true}, otherwise return {@code false}
+ */
+ default boolean bindSocialUser(JapUser japUser, String bindUserId) {
+ return false;
+ }
+
/**
* Save the oauth login user information to the database and return JapUser
*
@@ -101,4 +113,15 @@ public interface JapUserService {
return null;
}
+ /**
+ * Save the http authed user information to the database and return JapUser
+ *
+ * It is suitable for the {@code jap-http-api} module
+ * @param userinfo user information
+ * @return When saving successfully, return {@code JapUser}, otherwise return {@code null}
+ */
+ default JapUser createAndGetHttpApiUser(Object userinfo){
+ return null;
+ }
+
}
diff --git a/jap-core/src/main/java/com/fujieid/jap/core/cache/package-info.java b/jap-core/src/main/java/com/fujieid/jap/core/cache/package-info.java
new file mode 100644
index 0000000..694858b
--- /dev/null
+++ b/jap-core/src/main/java/com/fujieid/jap/core/cache/package-info.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2020-2040, 北京符节科技有限公司 (support@fujieid.com & https://www.fujieid.com).
+ *
+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * The cache component of jap has a built-in local cache based on MAP.
+ *
+ * Developers can implement specific cache solutions based on the {@link com.fujieid.jap.core.cache.JapCache} interface,
+ * such as redis cache.
+ *
+ * @author yadong.zhang (yadong.zhang0415(a)gmail.com)
+ * @version 1.0.0
+ * @since 1.0.0
+ */
+package com.fujieid.jap.core.cache;
diff --git a/jap-core/src/main/java/com/fujieid/jap/core/config/package-info.java b/jap-core/src/main/java/com/fujieid/jap/core/config/package-info.java
new file mode 100644
index 0000000..c6046b8
--- /dev/null
+++ b/jap-core/src/main/java/com/fujieid/jap/core/config/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2020-2040, 北京符节科技有限公司 (support@fujieid.com & https://www.fujieid.com).
+ *
+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * JAP configuration file, including common configuration and common configuration classes.
+ *
+ * @author yadong.zhang (yadong.zhang0415(a)gmail.com)
+ * @version 1.0.0
+ * @since 1.0.0
+ */
+package com.fujieid.jap.core.config;
diff --git a/jap-core/src/main/java/com/fujieid/jap/core/context/JapAuthentication.java b/jap-core/src/main/java/com/fujieid/jap/core/context/JapAuthentication.java
index 02d2bfe..5da4729 100644
--- a/jap-core/src/main/java/com/fujieid/jap/core/context/JapAuthentication.java
+++ b/jap-core/src/main/java/com/fujieid/jap/core/context/JapAuthentication.java
@@ -25,12 +25,12 @@ import com.fujieid.jap.core.result.JapErrorCode;
import com.fujieid.jap.core.result.JapResponse;
import com.fujieid.jap.core.store.JapUserStore;
import com.fujieid.jap.core.util.JapTokenHelper;
-import com.fujieid.jap.core.util.RequestUtil;
+import com.fujieid.jap.http.JapHttpCookie;
+import com.fujieid.jap.http.JapHttpRequest;
+import com.fujieid.jap.http.JapHttpResponse;
+import com.fujieid.jap.http.RequestUtil;
import com.xkcoding.json.util.Kv;
-import javax.servlet.http.Cookie;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
import java.io.Serializable;
import java.util.Date;
import java.util.Map;
@@ -74,7 +74,7 @@ public class JapAuthentication implements Serializable {
* @param response current HTTP response
* @return JapUser
*/
- public static JapUser getUser(HttpServletRequest request, HttpServletResponse response) {
+ public static JapUser getUser(JapHttpRequest request, JapHttpResponse response) {
if (null == context) {
return null;
}
@@ -107,7 +107,7 @@ public class JapAuthentication implements Serializable {
* @param response current HTTP response
* @return JapResponse
*/
- public static JapResponse checkUser(HttpServletRequest request, HttpServletResponse response) {
+ public static JapResponse checkUser(JapHttpRequest request, JapHttpResponse response) {
JapUser japUser = getUser(request, response);
if (null == japUser) {
return JapResponse.error(JapErrorCode.NOT_LOGGED_IN);
@@ -154,7 +154,7 @@ public class JapAuthentication implements Serializable {
* @param response current HTTP response
* @return boolean
*/
- public static boolean logout(HttpServletRequest request, HttpServletResponse response) {
+ public static boolean logout(JapHttpRequest request, JapHttpResponse response) {
JapUserStore japUserStore = context.getUserStore();
if (null == japUserStore) {
return false;
@@ -162,7 +162,7 @@ public class JapAuthentication implements Serializable {
japUserStore.remove(request, response);
// Clear all cookie information
- Map cookieMap = RequestUtil.getCookieMap(request);
+ Map cookieMap = RequestUtil.getCookieMap(request);
if (CollectionUtil.isNotEmpty(cookieMap)) {
cookieMap.forEach((key, cookie) -> {
cookie.setMaxAge(0);
diff --git a/jap-core/src/main/java/com/fujieid/jap/core/context/package-info.java b/jap-core/src/main/java/com/fujieid/jap/core/context/package-info.java
new file mode 100644
index 0000000..68f808b
--- /dev/null
+++ b/jap-core/src/main/java/com/fujieid/jap/core/context/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2020-2040, 北京符节科技有限公司 (support@fujieid.com & https://www.fujieid.com).
+ *
+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * JAP context
+ *
+ * @author yadong.zhang (yadong.zhang0415(a)gmail.com)
+ * @version 1.0.0
+ * @since 1.0.0
+ */
+package com.fujieid.jap.core.context;
diff --git a/jap-core/src/main/java/com/fujieid/jap/core/package-info.java b/jap-core/src/main/java/com/fujieid/jap/core/package-info.java
index 97b8dcd..5fbce4d 100644
--- a/jap-core/src/main/java/com/fujieid/jap/core/package-info.java
+++ b/jap-core/src/main/java/com/fujieid/jap/core/package-info.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
/**
- * Jap core module, external specification interface standard
+ * Jap core module, Provide external standard interface support
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
diff --git a/jap-core/src/main/java/com/fujieid/jap/core/result/JapErrorCode.java b/jap-core/src/main/java/com/fujieid/jap/core/result/JapErrorCode.java
index c2e0358..d3ec034 100644
--- a/jap-core/src/main/java/com/fujieid/jap/core/result/JapErrorCode.java
+++ b/jap-core/src/main/java/com/fujieid/jap/core/result/JapErrorCode.java
@@ -38,6 +38,7 @@ public enum JapErrorCode {
MISS_ISSUER(1006, "OidcStrategy requires a issuer option."),
MISS_CREDENTIALS(1007, "Missing credentials"),
INVALID_GRANT_TYPE(1008, "The grant type is not supported by the authorization server, or the current client is not authorized for the grant type."),
+ ERROR_HTTP_API_CONFIG(1008,"http api config error,please check")
;
private final int errroCode;
diff --git a/jap-core/src/main/java/com/fujieid/jap/core/result/package-info.java b/jap-core/src/main/java/com/fujieid/jap/core/result/package-info.java
new file mode 100644
index 0000000..a13bab3
--- /dev/null
+++ b/jap-core/src/main/java/com/fujieid/jap/core/result/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2020-2040, 北京符节科技有限公司 (support@fujieid.com & https://www.fujieid.com).
+ *
+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * Used to standardize the return value and exception status code of the JAP interface.
+ *
+ * @author yadong.zhang (yadong.zhang0415(a)gmail.com)
+ * @version 1.0.0
+ * @since 1.0.0
+ */
+package com.fujieid.jap.core.result;
diff --git a/jap-core/src/main/java/com/fujieid/jap/core/spi/package-info.java b/jap-core/src/main/java/com/fujieid/jap/core/spi/package-info.java
new file mode 100644
index 0000000..1ffd6be
--- /dev/null
+++ b/jap-core/src/main/java/com/fujieid/jap/core/spi/package-info.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2020-2040, 北京符节科技有限公司 (support@fujieid.com & https://www.fujieid.com).
+ *
+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * Supports SPI features. When implementing an interface, developers can pass parameters through the setXx method,
+ * or through the SPI specification. for example:
+ *
+ * For the JapUserService interface provided by jap, developers can set the interface implementation class through xx.setUserService,
+ * You can also configure the implementation class of the interface in a file named after the interface name in the resources/META-INF/services folder, such as:
+ * resources/META-INF/services/com.fujieid.jap.core.JapUserService, the content of the file is xx.xxx.xx.JapUserServiceImpl
+ *
+ * @author yadong.zhang (yadong.zhang0415(a)gmail.com)
+ * @version 1.0.0
+ * @since 1.0.0
+ */
+package com.fujieid.jap.core.spi;
diff --git a/jap-core/src/main/java/com/fujieid/jap/core/store/JapUserStore.java b/jap-core/src/main/java/com/fujieid/jap/core/store/JapUserStore.java
index 552cd65..e4366db 100644
--- a/jap-core/src/main/java/com/fujieid/jap/core/store/JapUserStore.java
+++ b/jap-core/src/main/java/com/fujieid/jap/core/store/JapUserStore.java
@@ -16,9 +16,8 @@
package com.fujieid.jap.core.store;
import com.fujieid.jap.core.JapUser;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import com.fujieid.jap.http.JapHttpRequest;
+import com.fujieid.jap.http.JapHttpResponse;
/**
* Save, delete and obtain the login user information.By default, based on local caching,
@@ -38,7 +37,7 @@ public interface JapUserStore {
* @param japUser User information after successful login
* @return JapUser
*/
- JapUser save(HttpServletRequest request, HttpServletResponse response, JapUser japUser);
+ JapUser save(JapHttpRequest request, JapHttpResponse response, JapUser japUser);
/**
* Clear user information from cache
@@ -46,7 +45,7 @@ public interface JapUserStore {
* @param request current HTTP request
* @param response current HTTP response
*/
- void remove(HttpServletRequest request, HttpServletResponse response);
+ void remove(JapHttpRequest request, JapHttpResponse response);
/**
* Get the login user information from the cache, return {@code JapUser} if it exists,
@@ -56,5 +55,5 @@ public interface JapUserStore {
* @param response current HTTP response
* @return JapUser
*/
- JapUser get(HttpServletRequest request, HttpServletResponse response);
+ JapUser get(JapHttpRequest request, JapHttpResponse response);
}
diff --git a/jap-core/src/main/java/com/fujieid/jap/core/store/SessionJapUserStore.java b/jap-core/src/main/java/com/fujieid/jap/core/store/SessionJapUserStore.java
index bfa2544..c96c9f5 100644
--- a/jap-core/src/main/java/com/fujieid/jap/core/store/SessionJapUserStore.java
+++ b/jap-core/src/main/java/com/fujieid/jap/core/store/SessionJapUserStore.java
@@ -22,10 +22,9 @@ import com.fujieid.jap.core.config.JapConfig;
import com.fujieid.jap.core.context.JapAuthentication;
import com.fujieid.jap.core.util.JapTokenHelper;
import com.fujieid.jap.core.util.JapUtil;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import javax.servlet.http.HttpSession;
+import com.fujieid.jap.http.JapHttpRequest;
+import com.fujieid.jap.http.JapHttpResponse;
+import com.fujieid.jap.http.JapHttpSession;
/**
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
@@ -46,8 +45,8 @@ public class SessionJapUserStore implements JapUserStore {
* @return JapUser
*/
@Override
- public JapUser save(HttpServletRequest request, HttpServletResponse response, JapUser japUser) {
- HttpSession session = request.getSession();
+ public JapUser save(JapHttpRequest request, JapHttpResponse response, JapUser japUser) {
+ JapHttpSession session = request.getSession();
JapUser newUser = BeanUtil.copyProperties(japUser, JapUser.class);
newUser.setPassword(null);
session.setAttribute(JapConst.SESSION_USER_KEY, newUser);
@@ -68,7 +67,7 @@ public class SessionJapUserStore implements JapUserStore {
* @param response current HTTP response
*/
@Override
- public void remove(HttpServletRequest request, HttpServletResponse response) {
+ public void remove(JapHttpRequest request, JapHttpResponse response) {
JapConfig japConfig = JapAuthentication.getContext().getConfig();
if (!japConfig.isSso()) {
@@ -78,7 +77,7 @@ public class SessionJapUserStore implements JapUserStore {
}
}
- HttpSession session = request.getSession();
+ JapHttpSession session = request.getSession();
session.removeAttribute(JapConst.SESSION_USER_KEY);
session.invalidate();
}
@@ -92,8 +91,8 @@ public class SessionJapUserStore implements JapUserStore {
* @return JapUser
*/
@Override
- public JapUser get(HttpServletRequest request, HttpServletResponse response) {
- HttpSession session = request.getSession();
+ public JapUser get(JapHttpRequest request, JapHttpResponse response) {
+ JapHttpSession session = request.getSession();
return (JapUser) session.getAttribute(JapConst.SESSION_USER_KEY);
}
}
diff --git a/jap-core/src/main/java/com/fujieid/jap/core/store/SsoJapUserStore.java b/jap-core/src/main/java/com/fujieid/jap/core/store/SsoJapUserStore.java
index b4cb762..2010664 100644
--- a/jap-core/src/main/java/com/fujieid/jap/core/store/SsoJapUserStore.java
+++ b/jap-core/src/main/java/com/fujieid/jap/core/store/SsoJapUserStore.java
@@ -19,12 +19,11 @@ import cn.hutool.core.util.StrUtil;
import com.fujieid.jap.core.JapUser;
import com.fujieid.jap.core.JapUserService;
import com.fujieid.jap.core.util.JapTokenHelper;
+import com.fujieid.jap.http.JapHttpRequest;
+import com.fujieid.jap.http.JapHttpResponse;
import com.fujieid.jap.sso.JapSsoHelper;
import com.fujieid.jap.sso.config.JapSsoConfig;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
/**
* Operation on users in SSO mode (cookie)
*
@@ -57,7 +56,7 @@ public class SsoJapUserStore extends SessionJapUserStore {
* @return JapUser
*/
@Override
- public JapUser save(HttpServletRequest request, HttpServletResponse response, JapUser japUser) {
+ public JapUser save(JapHttpRequest request, JapHttpResponse response, JapUser japUser) {
String token = JapSsoHelper.login(japUser.getUserId(), japUser.getUsername(), this.japSsoConfig, request, response);
super.save(request, response, japUser);
JapTokenHelper.saveUserToken(japUser.getUserId(), token);
@@ -71,7 +70,7 @@ public class SsoJapUserStore extends SessionJapUserStore {
* @param response current HTTP response
*/
@Override
- public void remove(HttpServletRequest request, HttpServletResponse response) {
+ public void remove(JapHttpRequest request, JapHttpResponse response) {
JapUser japUser = this.get(request, response);
if (null != japUser) {
JapTokenHelper.removeUserToken(japUser.getUserId());
@@ -89,7 +88,7 @@ public class SsoJapUserStore extends SessionJapUserStore {
* @return JapUser
*/
@Override
- public JapUser get(HttpServletRequest request, HttpServletResponse response) {
+ public JapUser get(JapHttpRequest request, JapHttpResponse response) {
String userId = JapSsoHelper.checkLogin(request);
if (StrUtil.isBlank(userId)) {
// The cookie has expired. Clear session content
diff --git a/jap-core/src/main/java/com/fujieid/jap/core/strategy/AbstractJapStrategy.java b/jap-core/src/main/java/com/fujieid/jap/core/strategy/AbstractJapStrategy.java
index 93e60d9..defe2a7 100644
--- a/jap-core/src/main/java/com/fujieid/jap/core/strategy/AbstractJapStrategy.java
+++ b/jap-core/src/main/java/com/fujieid/jap/core/strategy/AbstractJapStrategy.java
@@ -32,11 +32,10 @@ import com.fujieid.jap.core.result.JapResponse;
import com.fujieid.jap.core.store.JapUserStore;
import com.fujieid.jap.core.store.SessionJapUserStore;
import com.fujieid.jap.core.store.SsoJapUserStore;
+import com.fujieid.jap.http.JapHttpRequest;
+import com.fujieid.jap.http.JapHttpResponse;
import com.fujieid.jap.sso.JapSsoHelper;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
/**
* General policy handling methods and parameters, policies of other platforms can inherit
* {@code AbstractJapStrategy}, and override the constructor
@@ -106,11 +105,11 @@ public abstract class AbstractJapStrategy implements JapStrategy {
* @param response current HTTP response
* @return boolean
*/
- protected JapUser checkSession(HttpServletRequest request, HttpServletResponse response) {
+ protected JapUser checkSession(JapHttpRequest request, JapHttpResponse response) {
return japContext.getUserStore().get(request, response);
}
- protected JapResponse loginSuccess(JapUser japUser, HttpServletRequest request, HttpServletResponse response) {
+ protected JapResponse loginSuccess(JapUser japUser, JapHttpRequest request, JapHttpResponse response) {
japContext.getUserStore().save(request, response, japUser);
return JapResponse.success(japUser);
}
diff --git a/jap-core/src/main/java/com/fujieid/jap/core/strategy/JapStrategy.java b/jap-core/src/main/java/com/fujieid/jap/core/strategy/JapStrategy.java
index f9ed83b..ca10345 100644
--- a/jap-core/src/main/java/com/fujieid/jap/core/strategy/JapStrategy.java
+++ b/jap-core/src/main/java/com/fujieid/jap/core/strategy/JapStrategy.java
@@ -18,9 +18,8 @@ package com.fujieid.jap.core.strategy;
import com.fujieid.jap.core.config.AuthenticateConfig;
import com.fujieid.jap.core.result.JapErrorCode;
import com.fujieid.jap.core.result.JapResponse;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import com.fujieid.jap.http.JapHttpRequest;
+import com.fujieid.jap.http.JapHttpResponse;
/**
* The unified implementation interface of JAP Strategy, which must be implemented for all specific business policies.
@@ -39,7 +38,7 @@ public interface JapStrategy {
* @param response The response to authenticate
* @return JapResponse
*/
- default JapResponse authenticate(AuthenticateConfig config, HttpServletRequest request, HttpServletResponse response) {
+ default JapResponse authenticate(AuthenticateConfig config, JapHttpRequest request, JapHttpResponse response) {
return JapResponse.error(JapErrorCode.ERROR.getErrroCode(), "JapStrategy#authenticate(AuthenticateConfig, HttpServletRequest, HttpServletResponse) must be overridden by subclass");
}
}
diff --git a/jap-core/src/main/java/com/fujieid/jap/core/util/JapUtil.java b/jap-core/src/main/java/com/fujieid/jap/core/util/JapUtil.java
index f564df3..4b829a1 100644
--- a/jap-core/src/main/java/com/fujieid/jap/core/util/JapUtil.java
+++ b/jap-core/src/main/java/com/fujieid/jap/core/util/JapUtil.java
@@ -16,13 +16,10 @@
package com.fujieid.jap.core.util;
import com.fujieid.jap.core.JapUser;
-import com.fujieid.jap.core.exception.JapException;
+import com.fujieid.jap.http.JapHttpRequest;
+import com.fujieid.jap.http.JapHttpResponse;
import com.fujieid.jap.sso.JapSsoUtil;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import java.io.IOException;
-
/**
* The tool class of Jap only provides static methods common to all modules
*
@@ -35,20 +32,16 @@ public class JapUtil extends com.xkcoding.json.util.ObjectUtil {
private static final String REDIRECT_ERROR = "JAP failed to redirect via HttpServletResponse.";
@Deprecated
- public static void redirect(String url, HttpServletResponse response) {
+ public static void redirect(String url, JapHttpResponse response) {
redirect(url, REDIRECT_ERROR, response);
}
@Deprecated
- public static void redirect(String url, String errorMessage, HttpServletResponse response) {
- try {
- response.sendRedirect(url);
- } catch (IOException ex) {
- throw new JapException(errorMessage, ex);
- }
+ public static void redirect(String url, String errorMessage, JapHttpResponse response) {
+ response.redirect(url);
}
- public static String createToken(JapUser japUser, HttpServletRequest request) {
+ public static String createToken(JapUser japUser, JapHttpRequest request) {
return JapSsoUtil.createToken(japUser.getUserId(), japUser.getUsername(), request);
}
}
diff --git a/jap-core/src/main/java/com/fujieid/jap/core/util/RequestUtil.java b/jap-core/src/main/java/com/fujieid/jap/core/util/RequestUtil.java
deleted file mode 100644
index 82d0af6..0000000
--- a/jap-core/src/main/java/com/fujieid/jap/core/util/RequestUtil.java
+++ /dev/null
@@ -1,246 +0,0 @@
-/*
- * Copyright (c) 2020-2040, 北京符节科技有限公司 (support@fujieid.com & https://www.fujieid.com).
- *
- * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.gnu.org/licenses/lgpl.html
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.fujieid.jap.core.util;
-
-import cn.hutool.core.map.MapUtil;
-import cn.hutool.core.util.ArrayUtil;
-import com.xkcoding.json.util.StringUtil;
-
-import javax.servlet.http.Cookie;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import java.io.UnsupportedEncodingException;
-import java.net.URLEncoder;
-import java.util.Arrays;
-import java.util.Map;
-import java.util.stream.Collectors;
-
-/**
- * http servlet request util
- *
- * @author yadong.zhang (yadong.zhang0415(a)gmail.com)
- * @version 1.0.0
- * @since 1.0.1
- */
-public class RequestUtil {
-
- /**
- * Get the url parameter value of the request through {@code request.getParameter(paramName)}
- *
- * @param paramName parameter name
- * @param request current HTTP request
- * @return string
- */
- public static String getParam(String paramName, HttpServletRequest request) {
- if (null == request) {
- return null;
- }
- return request.getParameter(paramName);
- }
-
- /**
- * Get request header
- *
- * @param headerName request header name
- * @param request current HTTP request
- * @return string
- */
- public static String getHeader(String headerName, HttpServletRequest request) {
- if (null == request) {
- return "";
- }
- return request.getHeader(headerName);
- }
-
- /**
- * Get the referer of the current HTTP request
- *
- * @param request current HTTP request
- * @return string
- */
- public static String getReferer(HttpServletRequest request) {
- return getHeader("Referer", request);
- }
-
- /**
- * Get subdomain name
- *
- * @param request current HTTP request
- * @return string
- */
- public static String getFullDomainName(HttpServletRequest request) {
- StringBuffer url = request.getRequestURL();
- return url.delete(url.length() - request.getRequestURI().length(), url.length()).toString();
- }
-
- /**
- * Get the User-Agent of the current HTTP request
- *
- * @param request current HTTP request
- * @return string
- */
- public static String getUa(HttpServletRequest request) {
- return getHeader("User-Agent", request);
- }
-
- /**
- * Get the IP of the current HTTP request
- *
- * @param request current HTTP request
- * @return string
- */
- public static String getIp(HttpServletRequest request) {
- if (null == request) {
- return "";
- }
- String[] headers = {"X-Forwarded-For", "X-Real-IP", "Proxy-Client-IP", "WL-Proxy-Client-IP", "HTTP_CLIENT_IP", "HTTP_X_FORWARDED_FOR"};
- String ip;
- for (String header : headers) {
- ip = request.getHeader(header);
- if (isValidIp(ip)) {
- return getMultistageReverseProxyIp(ip);
- }
- }
- ip = request.getRemoteAddr();
- return getMultistageReverseProxyIp(ip);
- }
-
- /**
- * Obtain the first non-unknown ip address from the multi-level reverse proxy
- *
- * @param ip IP
- * @return The first non-unknown ip address
- */
- private static String getMultistageReverseProxyIp(String ip) {
- if (ip != null && ip.indexOf(",") > 0) {
- final String[] ips = ip.trim().split(",");
- for (String subIp : ips) {
- if (isValidIp(subIp)) {
- ip = subIp;
- break;
- }
- }
- }
- return ip;
- }
-
- /**
- * Verify ip legitimacy
- *
- * @param ip ip
- * @return boolean
- */
- private static boolean isValidIp(String ip) {
- return !StringUtil.isEmpty(ip) && !"unknown".equalsIgnoreCase(ip);
- }
-
- /**
- * Get the request url
- *
- * @param encode Whether to encode url
- * @param request current HTTP request
- * @return string
- */
- public static String getRequestUrl(boolean encode, HttpServletRequest request) {
- if (null == request) {
- return "";
- }
- String currentUrl = request.getRequestURL().toString();
- String queryString = request.getQueryString();
- if (!StringUtil.isEmpty(queryString)) {
- currentUrl = currentUrl + "?" + queryString;
- }
-
- if (encode) {
- String result = "";
- try {
- result = URLEncoder.encode(currentUrl, "UTF-8");
- } catch (UnsupportedEncodingException e) {
- //ignore
- }
- return result;
- }
-
- return currentUrl;
- }
-
- /**
- * Get the value of the cookie
- *
- * @param request current HTTP request
- * @param name cookie name
- * @return String
- */
- public static String getCookieVal(HttpServletRequest request, String name) {
- Cookie cookie = getCookie(request, name);
- return cookie != null ? cookie.getValue() : null;
- }
-
- /**
- * Get cookie
- *
- * @param request current HTTP request
- * @param name cookie name
- * @return Cookie
- */
- public static Cookie getCookie(HttpServletRequest request, String name) {
- Cookie[] cookies = request.getCookies();
- if (cookies != null) {
- for (Cookie cookie : cookies) {
- if (name.equals(cookie.getName())) {
- return cookie;
- }
- }
- }
- return null;
- }
-
- /**
- * Get all the cookies, and use the cookie name as the key to form a map
- *
- * @param request current HTTP request
- * @return Map
- */
- public static Map getCookieMap(HttpServletRequest request) {
- final Cookie[] cookies = request.getCookies();
- if (ArrayUtil.isEmpty(cookies)) {
- return MapUtil.empty();
- }
-
- return Arrays.stream(cookies).collect(Collectors.toMap(Cookie::getName, v -> v, (k1, k2) -> k1));
- }
-
- /**
- * Set cookie
- *
- * @param response current HTTP response
- * @param name cookie name
- * @param value cookie value
- * @param maxAge maxAge
- * @param path path
- * @param domain domain
- */
- public static void setCookie(HttpServletResponse response, String name, String value, int maxAge, String path, String domain) {
- Cookie cookie = new Cookie(name, value);
- cookie.setPath(path);
- if (null != domain) {
- cookie.setDomain(domain);
- }
- cookie.setMaxAge(maxAge);
- cookie.setHttpOnly(false);
- response.addCookie(cookie);
- }
-}
diff --git a/jap-core/src/main/java/com/fujieid/jap/core/util/package-info.java b/jap-core/src/main/java/com/fujieid/jap/core/util/package-info.java
new file mode 100644
index 0000000..ab2b5b0
--- /dev/null
+++ b/jap-core/src/main/java/com/fujieid/jap/core/util/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2020-2040, 北京符节科技有限公司 (support@fujieid.com & https://www.fujieid.com).
+ *
+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * jap general tools
+ *
+ * @author yadong.zhang (yadong.zhang0415(a)gmail.com)
+ * @version 1.0.0
+ * @since 1.0.0
+ */
+package com.fujieid.jap.core.util;
diff --git a/jap-core/src/test/java/com/fujieid/jap/core/context/JapAuthenticationTest.java b/jap-core/src/test/java/com/fujieid/jap/core/context/JapAuthenticationTest.java
index 144e9cb..a7a925d 100644
--- a/jap-core/src/test/java/com/fujieid/jap/core/context/JapAuthenticationTest.java
+++ b/jap-core/src/test/java/com/fujieid/jap/core/context/JapAuthenticationTest.java
@@ -22,6 +22,10 @@ import com.fujieid.jap.core.cache.JapLocalCache;
import com.fujieid.jap.core.config.JapConfig;
import com.fujieid.jap.core.result.JapResponse;
import com.fujieid.jap.core.store.JapUserStore;
+import com.fujieid.jap.http.JapHttpRequest;
+import com.fujieid.jap.http.JapHttpResponse;
+import com.fujieid.jap.http.jakarta.JakartaRequestAdapter;
+import com.fujieid.jap.http.jakarta.JakartaResponseAdapter;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
@@ -39,9 +43,14 @@ public class JapAuthenticationTest {
@Mock
public HttpServletResponse httpServletResponseMock;
+ public JapHttpRequest request;
+ public JapHttpResponse response;
+
@Before
public void init() {
MockitoAnnotations.initMocks(this);
+ this.request = new JakartaRequestAdapter(httpServletRequestMock);
+ this.response = new JakartaResponseAdapter(httpServletResponseMock);
}
@Test
@@ -71,7 +80,7 @@ public class JapAuthenticationTest {
JapContext japContext = new JapContext(japUserStore, japCache, japConfig);
JapAuthentication.setContext(japContext);
- JapUser japUser = JapAuthentication.getContext().getUserStore().get(httpServletRequestMock, httpServletResponseMock);
+ JapUser japUser = JapAuthentication.getContext().getUserStore().get(request, response);
Assert.assertNull(japUser);
}
@@ -83,8 +92,8 @@ public class JapAuthenticationTest {
JapContext japContext = new JapContext(japUserStore, japCache, japConfig);
JapAuthentication.setContext(japContext);
- JapAuthentication.getContext().getUserStore().save(httpServletRequestMock, httpServletResponseMock, new JapUser());
- JapUser japUser = JapAuthentication.getContext().getUserStore().get(httpServletRequestMock, httpServletResponseMock);
+ JapAuthentication.getContext().getUserStore().save(request, response, new JapUser());
+ JapUser japUser = JapAuthentication.getContext().getUserStore().get(request, response);
Assert.assertNotNull(japUser);
}
@@ -96,7 +105,7 @@ public class JapAuthenticationTest {
JapContext japContext = new JapContext(japUserStore, japCache, japConfig);
JapAuthentication.setContext(japContext);
- JapUser japUser = JapAuthentication.getUser(httpServletRequestMock, httpServletResponseMock);
+ JapUser japUser = JapAuthentication.getUser(request, response);
Assert.assertNull(japUser);
}
@@ -108,8 +117,8 @@ public class JapAuthenticationTest {
JapContext japContext = new JapContext(japUserStore, japCache, japConfig);
JapAuthentication.setContext(japContext);
- JapResponse response = JapAuthentication.checkUser(httpServletRequestMock, httpServletResponseMock);
- Assert.assertEquals(response.getCode(), 401);
+ JapResponse japResponse = JapAuthentication.checkUser(request, response);
+ Assert.assertEquals(japResponse.getCode(), 401);
}
@Test
@@ -120,9 +129,9 @@ public class JapAuthenticationTest {
JapContext japContext = new JapContext(japUserStore, japCache, japConfig);
JapAuthentication.setContext(japContext);
- JapAuthentication.getContext().getUserStore().save(httpServletRequestMock, httpServletResponseMock, new JapUser());
- JapResponse response = JapAuthentication.checkUser(httpServletRequestMock, httpServletResponseMock);
- Assert.assertEquals(response.getCode(), 200);
+ JapAuthentication.getContext().getUserStore().save(request, response, new JapUser());
+ JapResponse japResponse = JapAuthentication.checkUser(request, response);
+ Assert.assertEquals(japResponse.getCode(), 200);
}
@Test
@@ -195,7 +204,7 @@ public class JapAuthenticationTest {
JapContext japContext = new JapContext(japUserStore, japCache, japConfig);
JapAuthentication.setContext(japContext);
- boolean result = JapAuthentication.logout(httpServletRequestMock, httpServletResponseMock);
+ boolean result = JapAuthentication.logout(request, response);
Assert.assertTrue(result);
}
@@ -207,7 +216,7 @@ public class JapAuthenticationTest {
JapContext japContext = new JapContext(japUserStore, japCache, japConfig);
JapAuthentication.setContext(japContext);
- boolean result = JapAuthentication.logout(httpServletRequestMock, httpServletResponseMock);
+ boolean result = JapAuthentication.logout(request, response);
Assert.assertFalse(result);
}
@@ -224,7 +233,7 @@ public class JapAuthenticationTest {
* @return JapUser
*/
@Override
- public JapUser save(HttpServletRequest request, HttpServletResponse response, JapUser japUser) {
+ public JapUser save(JapHttpRequest request, JapHttpResponse response, JapUser japUser) {
STORE.put(USER_KEY, japUser);
return japUser;
}
@@ -236,7 +245,7 @@ public class JapAuthenticationTest {
* @param response current HTTP response
*/
@Override
- public void remove(HttpServletRequest request, HttpServletResponse response) {
+ public void remove(JapHttpRequest request, JapHttpResponse response) {
STORE.remove(USER_KEY);
}
@@ -249,7 +258,7 @@ public class JapAuthenticationTest {
* @return JapUser
*/
@Override
- public JapUser get(HttpServletRequest request, HttpServletResponse response) {
+ public JapUser get(JapHttpRequest request, JapHttpResponse response) {
return (JapUser) STORE.get(USER_KEY);
}
}
diff --git a/jap-core/src/test/java/com/fujieid/jap/core/util/JapUtilTest.java b/jap-core/src/test/java/com/fujieid/jap/core/util/JapUtilTest.java
index e0f607b..a248f07 100644
--- a/jap-core/src/test/java/com/fujieid/jap/core/util/JapUtilTest.java
+++ b/jap-core/src/test/java/com/fujieid/jap/core/util/JapUtilTest.java
@@ -16,6 +16,8 @@
package com.fujieid.jap.core.util;
import com.fujieid.jap.core.JapUser;
+import com.fujieid.jap.http.JapHttpRequest;
+import com.fujieid.jap.http.jakarta.JakartaRequestAdapter;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
@@ -30,9 +32,12 @@ public class JapUtilTest {
@Mock
public HttpServletRequest httpServletRequestMock;
+ public JapHttpRequest request;
+
@Before
public void init() {
MockitoAnnotations.initMocks(this);
+ this.request = new JakartaRequestAdapter(httpServletRequestMock);
}
@Test
@@ -41,7 +46,7 @@ public class JapUtilTest {
when(httpServletRequestMock.getHeader("user-agent")).thenReturn("ua");
String token = JapUtil.createToken(new JapUser()
.setUserId("11111")
- .setUsername("username"), httpServletRequestMock);
+ .setUsername("username"), request);
Assert.assertNotNull(token);
}
}
diff --git a/jap-http-api/pom.xml b/jap-http-api/pom.xml
new file mode 100644
index 0000000..92aa3de
--- /dev/null
+++ b/jap-http-api/pom.xml
@@ -0,0 +1,29 @@
+
+
+
+ com.fujieid
+ jap
+ ${revision}
+
+ 4.0.0
+
+ jap-http-api
+ jap-http-api
+
+ jap-http-api
+
+
+
+
+ com.fujieid
+ jap-core
+
+
+ com.xkcoding.http
+ simple-http
+
+
+
+
diff --git a/jap-http-api/src/main/java/com/fujieid/jap/httpapi/HttpApiConfig.java b/jap-http-api/src/main/java/com/fujieid/jap/httpapi/HttpApiConfig.java
new file mode 100644
index 0000000..250a8d5
--- /dev/null
+++ b/jap-http-api/src/main/java/com/fujieid/jap/httpapi/HttpApiConfig.java
@@ -0,0 +1,228 @@
+package com.fujieid.jap.httpapi;
+
+import com.fujieid.jap.core.JapUser;
+import com.fujieid.jap.core.config.AuthenticateConfig;
+import com.fujieid.jap.httpapi.enums.AuthInfoFieldEnum;
+import com.fujieid.jap.httpapi.enums.AuthSchemaEnum;
+import com.fujieid.jap.httpapi.enums.ForBearerTokenEnum;
+import com.fujieid.jap.httpapi.enums.HttpMethodEnum;
+import com.fujieid.jap.httpapi.strategy.GetTokenFromResponseStrategy;
+import com.fujieid.jap.httpapi.strategy.RequestBodyToJapUserStrategy;
+import com.xkcoding.json.JsonUtil;
+
+import java.io.BufferedReader;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Configuration file for http api authorization module
+ *
+ * @author zhihai.yu (mvbbb(a)foxmail.com)
+ * @version 1.0.0
+ * @since 1.0.5
+ */
+public class HttpApiConfig extends AuthenticateConfig {
+
+ /**
+ * Schema for http authorization,defined by third-party system. eg:BASIC、DIGEST、BEARER.
+ */
+ private AuthSchemaEnum authSchema;
+
+ /**
+ * the http method for us to send request to third-party system, which is given by third-party system.
+ */
+ private HttpMethodEnum httpMethod;
+
+ /**
+ * the login url given by the third-party system.
+ */
+ private String loginUrl;
+
+ /**
+ * params/header/body
+ */
+ private AuthInfoFieldEnum authInfoField;
+
+ /**
+ * custom headers will be carried when request third-party system.
+ */
+ private Map customHeaders;
+
+ /**
+ * custom params will be carried when request third-party system.
+ */
+ private Map customParams;
+
+ /**
+ * custom body will be carried when request third-party system in json format.
+ */
+ private Map customBody;
+
+ /**
+ * define this field when authSchema is BEARER
+ */
+ private String bearerTokenIssueUrl;
+
+ /**
+ * if user auth info field is "body", by default, analyze user auth info by json format. ex:
+ * {
+ * "username":"admin",
+ * "password":"123456"
+ * }
+ * Developer's system should customize requestBodyToJapUserStrategy to get userinfo form request if user auth info is not this format.
+ */
+ private RequestBodyToJapUserStrategy requestBodyToJapUserStrategy = request -> {
+ BufferedReader reader = request.getReader();
+ StringBuilder body = new StringBuilder();
+ String str = null;
+ while ((str = reader.readLine()) != null) {
+ body.append(str);
+ }
+ return JsonUtil.toBean(body.toString(), JapUser.class);
+ };
+
+ /**
+ * As we know Bearer auth need a token, but how to get this token? we should do a pre-auth to get this token, then store this token
+ * in developer's system. when user request for bearer auth, http-api module will query for user's token in our system.
+ * This field is used to define how to do pre-auth, how we get the token from third-party serve.
+ * When authSchema is BEARER you must define this field.
+ */
+ private ForBearerTokenEnum forBearerTokenEnum;
+
+ /**
+ * When do pre-auth for bearer auth, we get the response from third-party server which contains the token.
+ * Use this strategy to extract the token from response body.
+ * By default, search for field like "token":"xxxxxxxx" in response.
+ */
+ private GetTokenFromResponseStrategy getTokenFromResponseStrategy = body -> {
+ try {
+ int i1 = body.indexOf("\"token\"") + 7;
+ int i2 = body.indexOf(':', i1);
+ int i3 = body.indexOf("\"", i2) + 1;
+ int i4 = body.indexOf("\"", i3 + 1);
+ return body.substring(i3, i4);
+ } catch (Exception e) {
+ return null;
+ }
+ };
+
+ public HttpApiConfig() {
+ }
+
+ public HttpApiConfig(HttpApiConfig httpApiConfig, String loginUrl) {
+ this.authSchema = httpApiConfig.authSchema;
+ this.httpMethod = httpApiConfig.httpMethod;
+ this.loginUrl = loginUrl;
+ this.authInfoField = httpApiConfig.authInfoField;
+ this.customHeaders = httpApiConfig.customHeaders;
+ this.customParams = httpApiConfig.customParams;
+ this.customBody = httpApiConfig.customBody;
+ this.bearerTokenIssueUrl = httpApiConfig.bearerTokenIssueUrl;
+ this.requestBodyToJapUserStrategy = httpApiConfig.requestBodyToJapUserStrategy;
+ this.forBearerTokenEnum = httpApiConfig.forBearerTokenEnum;
+ this.getTokenFromResponseStrategy = httpApiConfig.getTokenFromResponseStrategy;
+ }
+
+ public AuthSchemaEnum getAuthSchema() {
+ return authSchema;
+ }
+
+ public HttpApiConfig setAuthSchema(AuthSchemaEnum authSchema) {
+ this.authSchema = authSchema;
+ return this;
+ }
+
+ public HttpMethodEnum getHttpMethod() {
+ return httpMethod;
+ }
+
+ public HttpApiConfig setHttpMethod(HttpMethodEnum httpMethod) {
+ this.httpMethod = httpMethod;
+ return this;
+ }
+
+ public String getLoginUrl() {
+ return loginUrl;
+ }
+
+ public HttpApiConfig setLoginUrl(String loginUrl) {
+ this.loginUrl = loginUrl;
+ return this;
+ }
+
+ public AuthInfoFieldEnum getAuthInfoField() {
+ return authInfoField;
+ }
+
+ public HttpApiConfig setAuthInfoField(AuthInfoFieldEnum authInfoField) {
+ this.authInfoField = authInfoField;
+ return this;
+ }
+
+ public Map getCustomHeaders() {
+ return customHeaders;
+ }
+
+ public HttpApiConfig setCustomHeaders(Map customHeaders) {
+ this.customHeaders = customHeaders;
+ return this;
+ }
+
+ public RequestBodyToJapUserStrategy getRequestBodyToJapUserStrategy() {
+ return requestBodyToJapUserStrategy;
+ }
+
+ public HttpApiConfig setRequestBodyToJapUserStrategy(RequestBodyToJapUserStrategy requestBodyToJapUserStrategy) {
+ this.requestBodyToJapUserStrategy = requestBodyToJapUserStrategy;
+ return this;
+ }
+
+ public Map getCustomParams() {
+ return customParams;
+ }
+
+ public HttpApiConfig setCustomParams(Map customParams) {
+ this.customParams = customParams;
+ return this;
+ }
+
+ public Map getCustomParamsObjects() {
+ return new HashMap<>(customParams);
+ }
+
+ public Map getCustomBody() {
+ return customBody;
+ }
+
+ public HttpApiConfig setCustomBody(Map customBody) {
+ this.customBody = customBody;
+ return this;
+ }
+
+ public ForBearerTokenEnum getForBearerTokenEnum() {
+ return forBearerTokenEnum;
+ }
+
+ public HttpApiConfig setForBearerTokenEnum(ForBearerTokenEnum forBearerTokenEnum) {
+ this.forBearerTokenEnum = forBearerTokenEnum;
+ return this;
+ }
+
+ public GetTokenFromResponseStrategy getGetTokenFromResponseStrategy() {
+ return getTokenFromResponseStrategy;
+ }
+
+ public HttpApiConfig setGetTokenFromResponseStrategy(GetTokenFromResponseStrategy getTokenFromResponseStrategy) {
+ this.getTokenFromResponseStrategy = getTokenFromResponseStrategy;
+ return this;
+ }
+
+ public String getBearerTokenIssueUrl() {
+ return bearerTokenIssueUrl;
+ }
+
+ public HttpApiConfig setBearerTokenIssueUrl(String bearerTokenIssueUrl) {
+ this.bearerTokenIssueUrl = bearerTokenIssueUrl;
+ return this;
+ }
+}
diff --git a/jap-http-api/src/main/java/com/fujieid/jap/httpapi/HttpApiStrategy.java b/jap-http-api/src/main/java/com/fujieid/jap/httpapi/HttpApiStrategy.java
new file mode 100644
index 0000000..b0296b6
--- /dev/null
+++ b/jap-http-api/src/main/java/com/fujieid/jap/httpapi/HttpApiStrategy.java
@@ -0,0 +1,335 @@
+package com.fujieid.jap.httpapi;
+
+import cn.hutool.core.codec.Base64;
+import com.fujieid.jap.core.JapUser;
+import com.fujieid.jap.core.JapUserService;
+import com.fujieid.jap.core.cache.JapCache;
+import com.fujieid.jap.core.config.AuthenticateConfig;
+import com.fujieid.jap.core.config.JapConfig;
+import com.fujieid.jap.core.exception.JapException;
+import com.fujieid.jap.core.result.JapErrorCode;
+import com.fujieid.jap.core.result.JapResponse;
+import com.fujieid.jap.core.strategy.AbstractJapStrategy;
+import com.fujieid.jap.http.JapHttpRequest;
+import com.fujieid.jap.http.JapHttpResponse;
+import com.fujieid.jap.httpapi.enums.AuthSchemaEnum;
+import com.fujieid.jap.httpapi.enums.HttpMethodEnum;
+import com.fujieid.jap.httpapi.subject.DigestAuthorizationSubject;
+import com.fujieid.jap.httpapi.subject.DigestWwwAuthenticateSubject;
+import com.fujieid.jap.httpapi.subject.HttpAuthResponse;
+import com.fujieid.jap.httpapi.util.*;
+
+/**
+ * The strategy for jap-http-api module to request third party system.
+ *
+ * @author zhihai.yu (mvbbb(a)foxmail.com)
+ * @version 1.0.0
+ * @since 1.0.5
+ */
+public class HttpApiStrategy extends AbstractJapStrategy {
+
+
+ public HttpApiStrategy(JapUserService japUserService, JapConfig japConfig, JapCache japCache) {
+ super(japUserService, japConfig, japCache);
+ }
+
+ @Override
+ public JapResponse authenticate(AuthenticateConfig config, JapHttpRequest request, JapHttpResponse response) {
+ try {
+ checkAuthenticateConfig(config, HttpApiConfig.class);
+ } catch (JapException e) {
+ e.printStackTrace();
+ return JapResponse.error(e.getErrorCode(), e.getErrorMessage());
+ }
+ HttpApiConfig httpApiConfig = (HttpApiConfig) config;
+
+ return this.doAuthenticate(httpApiConfig, request, response);
+ }
+
+ private JapResponse doAuthenticate(HttpApiConfig config, JapHttpRequest request, JapHttpResponse response) {
+
+ HttpAuthResponse authResponse = null;
+ JapUser japUser = null;
+
+ try {
+ japUser = getJapUser(request, config);
+
+ if (japUser == null) {
+ return JapResponse.error(JapErrorCode.MISS_CREDENTIALS);
+ }
+
+ switch (config.getAuthSchema()) {
+ case BASIC:
+ authResponse = doBasicAuth(japUser, config, response);
+ break;
+ case DIGEST:
+ authResponse = doDigestAuth(japUser, config, response);
+ break;
+ case BEARER:
+ authResponse = doBearerAuth(japUser, config, response);
+ break;
+ default:
+ break;
+ }
+ } catch (JapException e) {
+ return JapResponse.error(e.getErrorCode(), e.getErrorMessage());
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ if (authResponse != null && authResponse.isSuccess()) {
+ if (config.getAuthSchema() == AuthSchemaEnum.BASIC || config.getAuthSchema() == AuthSchemaEnum.DIGEST) {
+ japUserService.createAndGetHttpApiUser(japUser);
+ }
+ return JapResponse.success(authResponse.getBody());
+ } else {
+ return JapResponse.error(JapErrorCode.INVALID_PASSWORD);
+ }
+ }
+
+ private HttpAuthResponse doDigestAuth(JapUser japUser, HttpApiConfig config, JapHttpResponse response) {
+
+ /*
+ * send a request to third-party server to get a random number and encryption algorithm
+ * see: https://datatracker.ietf.org/doc/html/rfc2069#section-2.1.1
+ */
+ HttpAuthResponse responseForWWWAuth = HttpAuthUtil.sendRequest(config.getLoginUrl(),
+ HttpMethodEnum.GET,
+ config.getCustomHeaders(),
+ config.getCustomParams(),
+ SimpleAuthJsonUtil.getJsonStrByParams(config.getCustomBody()));
+
+ if (responseForWWWAuth == null || responseForWWWAuth.getHeaders() == null) {
+ return null;
+ }
+ HttpAuthResponse result = null;
+ String wwwAuthenticate = null;
+
+ try {
+ wwwAuthenticate = responseForWWWAuth.getHeaders().get("WWW-Authenticate").get(0).replaceFirst("Digest ", "");
+ DigestWwwAuthenticateSubject wwwAuthenticateSubject = SubjectSerializeUtil.deserialize(wwwAuthenticate, DigestWwwAuthenticateSubject.class);
+
+ String username = japUser.getUsername();
+ String realm = wwwAuthenticateSubject.getRealm();
+ String password = japUser.getPassword();
+ String method = config.getHttpMethod().toString().toUpperCase();
+ String digestUri = URLUtil.getRelativeUri(config.getLoginUrl());
+ String nonce = wwwAuthenticateSubject.getNonce();
+ String qop = wwwAuthenticateSubject.getQop();
+ String nc = DigestUtil.getNc();
+ String cnonce = DigestUtil.getCnonce();
+ String opaque = wwwAuthenticateSubject.getOpaque();
+
+ String ha1 = DigestMD5Util.encode(username, realm, password);
+ String ha2 = DigestUtil.getHa2ByQop(qop, method, digestUri);
+ String qopResponse = DigestUtil.getResponseByQop(ha1, nonce, nc, cnonce, qop, ha2);
+
+ DigestAuthorizationSubject authorizationSubject = new DigestAuthorizationSubject()
+ .setCnonce(cnonce)
+ .setQop(qop)
+ .setNc(nc)
+ .setNonce(nonce)
+ .setRealm(realm)
+ .setUsername(username)
+ .setUri(digestUri)
+ .setResponse(qopResponse)
+ .setAlgorithm(wwwAuthenticateSubject.getAlgorithm())
+ .setOpaque(opaque);
+
+ String authStr = SubjectSerializeUtil.serialize(authorizationSubject);
+
+ // send authorization request
+ config.getCustomHeaders().put("Authorization", "Digest " + authStr);
+ result = HttpAuthUtil.sendRequest(config.getLoginUrl(),
+ config.getHttpMethod(),
+ config.getCustomHeaders(),
+ config.getCustomParams(),
+ SimpleAuthJsonUtil.getJsonStrByParams(config.getCustomBody()));
+ return result;
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ private HttpAuthResponse doBasicAuth(JapUser japUser, HttpApiConfig config, JapHttpResponse response) {
+
+ /*
+ * see: The 'Basic' HTTP Authentication Scheme:https://datatracker.ietf.org/doc/html/rfc7617
+ */
+ String basicAuth = "Basic " + Base64.encode(japUser.getUsername() + ":" + japUser.getPassword());
+ config.getCustomHeaders().put("authorization", basicAuth);
+
+ return HttpAuthUtil.sendRequest(config.getLoginUrl(),
+ config.getHttpMethod(),
+ config.getCustomHeaders(),
+ config.getCustomParams(),
+ SimpleAuthJsonUtil.getJsonStrByParams(config.getCustomBody()));
+ }
+
+ private HttpAuthResponse sendBearerTokenAuthRequest(String token, HttpApiConfig config) {
+ String bearerToken = "Bearer " + token;
+ config.getCustomHeaders().put("Authorization", bearerToken);
+ return HttpAuthUtil.sendRequest(config.getLoginUrl(),
+ config.getHttpMethod(),
+ config.getCustomHeaders(),
+ config.getCustomParams(),
+ SimpleAuthJsonUtil.getJsonStrByParams(config.getCustomBody()));
+ }
+
+ private HttpAuthResponse doBearerAuth(JapUser japUser, HttpApiConfig config, JapHttpResponse response) {
+
+ JapUser japUserInDb = japUserService.getByName(japUser.getUsername());
+ String token = null;
+ if (japUserInDb == null || japUserInDb.getToken() == null) {
+ token = doPreAuthForBearerToken(japUser, config, response);
+ } else {
+ token = japUserInDb.getToken();
+ }
+
+ HttpAuthResponse result = sendBearerTokenAuthRequest(token, config);
+
+ // old token expired, request for new token
+ if (result == null || !result.isSuccess()) {
+ String newToken = doPreAuthForBearerToken(japUser, config, response);
+ result = sendBearerTokenAuthRequest(newToken, config);
+ }
+ return result;
+ }
+
+ /**
+ * do a pre-auth in Bearer auth to get token for this user
+ *
+ * @param japUser jap user
+ * @param config Http api config
+ * @param response current http response
+ */
+ private String doPreAuthForBearerToken(JapUser japUser, HttpApiConfig config, JapHttpResponse response) {
+ HttpAuthResponse httpAuthResponse = null;
+ // clean old auth token
+ config.getCustomHeaders().remove("Authorization");
+ config.getCustomHeaders().remove("authorization");
+ switch (config.getForBearerTokenEnum()) {
+ case BY_BASIC:
+ httpAuthResponse = this.doBasicAuth(japUser, new HttpApiConfig(config, config.getBearerTokenIssueUrl()), response);
+ break;
+ case BY_DIGEST:
+ httpAuthResponse = this.doDigestAuth(japUser, new HttpApiConfig(config, config.getBearerTokenIssueUrl()), response);
+ break;
+ case BY_HEADER:
+ config.getCustomHeaders().put("username", japUser.getUsername());
+ config.getCustomHeaders().put("password", japUser.getPassword());
+ httpAuthResponse = HttpAuthUtil.sendRequest(config.getLoginUrl(),
+ config.getHttpMethod(),
+ config.getCustomHeaders(),
+ config.getCustomParams(),
+ SimpleAuthJsonUtil.getJsonStrByParams(config.getCustomBody()));
+ break;
+ case BY_PARAMS:
+ config.getCustomParams().put("username", japUser.getUsername());
+ config.getCustomParams().put("password", japUser.getPassword());
+ httpAuthResponse = HttpAuthUtil.sendRequest(config.getLoginUrl(),
+ config.getHttpMethod(),
+ config.getCustomHeaders(),
+ config.getCustomParams(),
+ SimpleAuthJsonUtil.getJsonStrByParams(config.getCustomBody()));
+ break;
+ case BY_BODY:
+ String body = SimpleAuthJsonUtil.getJsonStrByJapUserAndParams(japUser, config.getCustomBody());
+ httpAuthResponse = HttpAuthUtil.sendRequest(config.getLoginUrl(),
+ config.getHttpMethod(),
+ config.getCustomHeaders(),
+ config.getCustomParams(),
+ body);
+ break;
+ default:
+ break;
+ }
+
+ // pre-auth field
+ if (httpAuthResponse == null || !httpAuthResponse.isSuccess()) {
+ return null;
+ }
+
+ // get token from httpAuthResponse body
+ String token = config.getGetTokenFromResponseStrategy().getToken(httpAuthResponse.getBody());
+ if (token == null || token.length() == 0) {
+ return null;
+ }
+
+ // save jap user to db
+ japUser.setToken(token);
+ japUserService.createAndGetHttpApiUser(japUser);
+ return token;
+ }
+
+ /**
+ * get user information from request according to http api config.
+ *
+ * @param request current http request
+ * @param config http api config
+ * @return jap user contains username and password
+ */
+ public JapUser getJapUser(JapHttpRequest request, HttpApiConfig config) {
+ JapUser japUser = null;
+ String username = null;
+ String password = null;
+
+ switch (config.getAuthInfoField()) {
+ case PARAMS:
+ username = request.getParameter("username");
+ password = request.getParameter("password");
+ japUser = new JapUser().setUsername(username).setPassword(password);
+ break;
+
+ case HEADER:
+ username = request.getHeader("username");
+ password = request.getHeader("password");
+ japUser = new JapUser().setUsername(username).setPassword(password);
+ break;
+
+ case BODY:
+ try {
+ // get auth info body from request body.
+ japUser = config.getRequestBodyToJapUserStrategy().decode(request);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ break;
+ default:
+ break;
+ }
+ if (null == japUser || null == japUser.getUsername()) {
+ return null;
+ }
+
+ return japUser;
+ }
+
+
+ /**
+ * check http api config.
+ * NOTICE:
+ * 1. When use "GET" as request method, custom body is not supported.
+ * 2. Can not custom body and params at the same time.
+ *
+ * @param sourceConfig The parameters passed in by the caller
+ * @param targetConfigClazz The actual parameter class type
+ * @throws JapException Jap exception will be thrown when http api config configuration error
+ */
+ @Override
+ protected void checkAuthenticateConfig(AuthenticateConfig sourceConfig, Class> targetConfigClazz) throws JapException {
+ super.checkAuthenticateConfig(sourceConfig, targetConfigClazz);
+ HttpApiConfig config = (HttpApiConfig) sourceConfig;
+ if (config == null ||
+ config.getAuthSchema() == null ||
+ config.getHttpMethod() == null ||
+ config.getLoginUrl() == null ||
+ config.getAuthInfoField() == null ||
+ (config.getHttpMethod() == HttpMethodEnum.GET && (config.getCustomBody() != null && config.getCustomBody().size() != 0)) ||
+ ((config.getCustomBody() != null && config.getCustomBody().size() != 0) && (config.getCustomParams() != null && config.getCustomParams().size() != 0))
+ ) {
+ throw new JapException(JapErrorCode.ERROR_HTTP_API_CONFIG);
+ }
+ }
+}
diff --git a/jap-http-api/src/main/java/com/fujieid/jap/httpapi/enums/AuthInfoFieldEnum.java b/jap-http-api/src/main/java/com/fujieid/jap/httpapi/enums/AuthInfoFieldEnum.java
new file mode 100644
index 0000000..4b55c3e
--- /dev/null
+++ b/jap-http-api/src/main/java/com/fujieid/jap/httpapi/enums/AuthInfoFieldEnum.java
@@ -0,0 +1,12 @@
+package com.fujieid.jap.httpapi.enums;
+
+/**
+ * @author zhihai.yu (mvbbb(a)foxmail.com)
+ * @version 1.0.0
+ * @since 1.0.5
+ */
+public enum AuthInfoFieldEnum {
+ HEADER,
+ PARAMS,
+ BODY
+}
diff --git a/jap-http-api/src/main/java/com/fujieid/jap/httpapi/enums/AuthSchemaEnum.java b/jap-http-api/src/main/java/com/fujieid/jap/httpapi/enums/AuthSchemaEnum.java
new file mode 100644
index 0000000..7d63b61
--- /dev/null
+++ b/jap-http-api/src/main/java/com/fujieid/jap/httpapi/enums/AuthSchemaEnum.java
@@ -0,0 +1,12 @@
+package com.fujieid.jap.httpapi.enums;
+
+/**
+ * @author zhihai.yu (mvbbb(a)foxmail.com)
+ * @version 1.0.0
+ * @since 1.0.5
+ */
+public enum AuthSchemaEnum {
+ BASIC,
+ DIGEST,
+ BEARER
+}
diff --git a/jap-http-api/src/main/java/com/fujieid/jap/httpapi/enums/ForBearerTokenEnum.java b/jap-http-api/src/main/java/com/fujieid/jap/httpapi/enums/ForBearerTokenEnum.java
new file mode 100644
index 0000000..9c1d92e
--- /dev/null
+++ b/jap-http-api/src/main/java/com/fujieid/jap/httpapi/enums/ForBearerTokenEnum.java
@@ -0,0 +1,14 @@
+package com.fujieid.jap.httpapi.enums;
+
+/**
+ * @author zhihai.yu (mvbbb(a)foxmail.com)
+ * @version 1.0.0
+ * @since 1.0.5
+ */
+public enum ForBearerTokenEnum {
+ BY_HEADER,
+ BY_PARAMS,
+ BY_BODY,
+ BY_BASIC,
+ BY_DIGEST
+}
diff --git a/jap-http-api/src/main/java/com/fujieid/jap/httpapi/enums/HttpMethodEnum.java b/jap-http-api/src/main/java/com/fujieid/jap/httpapi/enums/HttpMethodEnum.java
new file mode 100644
index 0000000..ebbde32
--- /dev/null
+++ b/jap-http-api/src/main/java/com/fujieid/jap/httpapi/enums/HttpMethodEnum.java
@@ -0,0 +1,11 @@
+package com.fujieid.jap.httpapi.enums;
+
+/**
+ * @author zhihai.yu (mvbbb(a)foxmail.com)
+ * @version 1.0.0
+ * @since 1.0.5
+ */
+public enum HttpMethodEnum {
+ POST,
+ GET
+}
diff --git a/jap-http-api/src/main/java/com/fujieid/jap/httpapi/strategy/GetTokenFromResponseStrategy.java b/jap-http-api/src/main/java/com/fujieid/jap/httpapi/strategy/GetTokenFromResponseStrategy.java
new file mode 100644
index 0000000..1e31e6c
--- /dev/null
+++ b/jap-http-api/src/main/java/com/fujieid/jap/httpapi/strategy/GetTokenFromResponseStrategy.java
@@ -0,0 +1,20 @@
+package com.fujieid.jap.httpapi.strategy;
+
+/**
+ * Define how to get token from the third-party server's response when request to issue a bearer token
+ *
+ * @author zhihai.yu (mvbbb(a)foxmail.com)
+ * @version 1.0.0
+ * @since 1.0.5
+ */
+@FunctionalInterface
+public interface GetTokenFromResponseStrategy {
+
+ /**
+ * get token from body
+ *
+ * @param body response body
+ * @return token
+ */
+ String getToken(String body);
+}
diff --git a/jap-http-api/src/main/java/com/fujieid/jap/httpapi/strategy/RequestBodyToJapUserStrategy.java b/jap-http-api/src/main/java/com/fujieid/jap/httpapi/strategy/RequestBodyToJapUserStrategy.java
new file mode 100644
index 0000000..43feefc
--- /dev/null
+++ b/jap-http-api/src/main/java/com/fujieid/jap/httpapi/strategy/RequestBodyToJapUserStrategy.java
@@ -0,0 +1,23 @@
+package com.fujieid.jap.httpapi.strategy;
+
+import com.fujieid.jap.core.JapUser;
+import com.fujieid.jap.http.JapHttpRequest;
+
+/**
+ * Implement this functional interface if your auth info in the request body is not standard json format.
+ *
+ * @author zhihai.yu (mvbbb(a)foxmail.com)
+ * @version 1.0.0
+ * @since 1.0.5
+ */
+@FunctionalInterface
+public interface RequestBodyToJapUserStrategy {
+ /**
+ * get a JapUser from request
+ *
+ * @param request request
+ * @return JapUser
+ * @throws Exception When the data in the request cannot be decoded, an exception will be thrown
+ */
+ JapUser decode(JapHttpRequest request) throws Exception;
+}
diff --git a/jap-http-api/src/main/java/com/fujieid/jap/httpapi/subject/DigestAuthorizationSubject.java b/jap-http-api/src/main/java/com/fujieid/jap/httpapi/subject/DigestAuthorizationSubject.java
new file mode 100644
index 0000000..aa1c151
--- /dev/null
+++ b/jap-http-api/src/main/java/com/fujieid/jap/httpapi/subject/DigestAuthorizationSubject.java
@@ -0,0 +1,133 @@
+package com.fujieid.jap.httpapi.subject;
+
+/**
+ * The Authorization Request Header
+ *
+ * @author zhihai.yu (mvbbb(a)foxmail.com)
+ * @version 1.0.0
+ * @see https://en.wikipedia.org/wiki/Digest_access_authentication
+ * @see https://datatracker.ietf.org/doc/html/rfc2069#section-2.1.2
+ * @since 1.0.5
+ */
+public class DigestAuthorizationSubject {
+ private String username;
+ private String realm;
+ private String nonce;
+ private String uri;
+ private String response;
+ private String qop;
+ private String nc;
+ private String cnonce;
+ private String pop;
+ private String opaque;
+ private String digest;
+ private String algorithm;
+
+ public String getQop() {
+ return qop;
+ }
+
+ public DigestAuthorizationSubject setQop(String qop) {
+ this.qop = qop;
+ return this;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public DigestAuthorizationSubject setUsername(String username) {
+ this.username = username;
+ return this;
+ }
+
+ public String getRealm() {
+ return realm;
+ }
+
+ public DigestAuthorizationSubject setRealm(String realm) {
+ this.realm = realm;
+ return this;
+ }
+
+ public String getNonce() {
+ return nonce;
+ }
+
+ public DigestAuthorizationSubject setNonce(String nonce) {
+ this.nonce = nonce;
+ return this;
+ }
+
+ public String getUri() {
+ return uri;
+ }
+
+ public DigestAuthorizationSubject setUri(String uri) {
+ this.uri = uri;
+ return this;
+ }
+
+ public String getPop() {
+ return pop;
+ }
+
+ public DigestAuthorizationSubject setPop(String pop) {
+ this.pop = pop;
+ return this;
+ }
+
+ public String getNc() {
+ return nc;
+ }
+
+ public DigestAuthorizationSubject setNc(String nc) {
+ this.nc = nc;
+ return this;
+ }
+
+ public String getCnonce() {
+ return cnonce;
+ }
+
+ public DigestAuthorizationSubject setCnonce(String cnonce) {
+ this.cnonce = cnonce;
+ return this;
+ }
+
+ public String getResponse() {
+ return response;
+ }
+
+ public DigestAuthorizationSubject setResponse(String response) {
+ this.response = response;
+ return this;
+ }
+
+ public String getOpaque() {
+ return opaque;
+ }
+
+ public DigestAuthorizationSubject setOpaque(String opaque) {
+ this.opaque = opaque;
+ return this;
+ }
+
+ public String getDigest() {
+ return digest;
+ }
+
+ public DigestAuthorizationSubject setDigest(String digest) {
+ this.digest = digest;
+ return this;
+ }
+
+ public String getAlgorithm() {
+ return algorithm;
+ }
+
+ public DigestAuthorizationSubject setAlgorithm(String algorithm) {
+ this.algorithm = algorithm;
+ return this;
+ }
+}
diff --git a/jap-http-api/src/main/java/com/fujieid/jap/httpapi/subject/DigestWwwAuthenticateSubject.java b/jap-http-api/src/main/java/com/fujieid/jap/httpapi/subject/DigestWwwAuthenticateSubject.java
new file mode 100644
index 0000000..2661408
--- /dev/null
+++ b/jap-http-api/src/main/java/com/fujieid/jap/httpapi/subject/DigestWwwAuthenticateSubject.java
@@ -0,0 +1,85 @@
+package com.fujieid.jap.httpapi.subject;
+
+/**
+ * The WWW-Authenticate Response Header
+ *
+ * @author zhihai.yu (mvbbb(a)foxmail.com)
+ * @version 1.0.0
+ * @see https://datatracker.ietf.org/doc/html/rfc2069#section-2.1.1
+ * @since 1.0.5
+ */
+public class DigestWwwAuthenticateSubject {
+ private String realm;
+ private String domain;
+ private String nonce;
+ private String opaque;
+ private String algorithm;
+ private String stale;
+ private String qop;
+
+ public DigestWwwAuthenticateSubject() {
+ }
+
+ public String getQop() {
+ return qop;
+ }
+
+ public DigestWwwAuthenticateSubject setQop(String qop) {
+ this.qop = qop;
+ return this;
+ }
+
+ public String getRealm() {
+ return realm;
+ }
+
+ public DigestWwwAuthenticateSubject setRealm(String realm) {
+ this.realm = realm;
+ return this;
+ }
+
+ public String getDomain() {
+ return domain;
+ }
+
+ public DigestWwwAuthenticateSubject setDomain(String domain) {
+ this.domain = domain;
+ return this;
+ }
+
+ public String getNonce() {
+ return nonce;
+ }
+
+ public DigestWwwAuthenticateSubject setNonce(String nonce) {
+ this.nonce = nonce;
+ return this;
+ }
+
+ public String getOpaque() {
+ return opaque;
+ }
+
+ public DigestWwwAuthenticateSubject setOpaque(String opaque) {
+ this.opaque = opaque;
+ return this;
+ }
+
+ public String getAlgorithm() {
+ return algorithm;
+ }
+
+ public DigestWwwAuthenticateSubject setAlgorithm(String algorithm) {
+ this.algorithm = algorithm;
+ return this;
+ }
+
+ public String getStale() {
+ return stale;
+ }
+
+ public DigestWwwAuthenticateSubject setStale(String stale) {
+ this.stale = stale;
+ return this;
+ }
+}
diff --git a/jap-http-api/src/main/java/com/fujieid/jap/httpapi/subject/HttpAuthResponse.java b/jap-http-api/src/main/java/com/fujieid/jap/httpapi/subject/HttpAuthResponse.java
new file mode 100644
index 0000000..3e7391d
--- /dev/null
+++ b/jap-http-api/src/main/java/com/fujieid/jap/httpapi/subject/HttpAuthResponse.java
@@ -0,0 +1,50 @@
+package com.fujieid.jap.httpapi.subject;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * packaging auth result
+ *
+ * @author zhihai.yu (mvbbb(a)foxmail.com)
+ * @version 1.0.0
+ * @since 1.0.5
+ */
+public class HttpAuthResponse {
+ private boolean success;
+ private String body;
+ private Map> headers;
+
+ public HttpAuthResponse(boolean success, String body, Map> headers) {
+ this.success = success;
+ this.body = body;
+ this.headers = headers;
+ }
+
+ public boolean isSuccess() {
+ return success;
+ }
+
+ public HttpAuthResponse setSuccess(boolean success) {
+ this.success = success;
+ return this;
+ }
+
+ public String getBody() {
+ return body;
+ }
+
+ public HttpAuthResponse setBody(String body) {
+ this.body = body;
+ return this;
+ }
+
+ public Map> getHeaders() {
+ return headers;
+ }
+
+ public HttpAuthResponse setHeaders(Map> headers) {
+ this.headers = headers;
+ return this;
+ }
+}
diff --git a/jap-http-api/src/main/java/com/fujieid/jap/httpapi/util/DigestMD5Util.java b/jap-http-api/src/main/java/com/fujieid/jap/httpapi/util/DigestMD5Util.java
new file mode 100644
index 0000000..0dc2f9c
--- /dev/null
+++ b/jap-http-api/src/main/java/com/fujieid/jap/httpapi/util/DigestMD5Util.java
@@ -0,0 +1,62 @@
+package com.fujieid.jap.httpapi.util;
+
+import com.fujieid.jap.core.exception.JapException;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+/**
+ * Splice strings by ":", and encode using md5. eg:"MD5(MD5(username:realm:password):nonce:cnonce)"
+ *
+ * @author zhihai.yu (mvbbb(a)foxmail.com)
+ * @version 1.0.0
+ * @since 1.0.5
+ */
+public final class DigestMD5Util {
+
+ private final static String[] HEX_ARRAY = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"};
+
+ public static String encode(String... strs) {
+ StringBuilder stringBuffer = new StringBuilder();
+ boolean isFirst = true;
+ for (String str : strs) {
+ if (isFirst) {
+ stringBuffer.append(str);
+ isFirst = false;
+ } else {
+ stringBuffer.append(":").append(str);
+ }
+ }
+
+ // Create an information summary with MD5 algorithm
+ MessageDigest md = null;
+ String algorithm = "MD5";
+ try {
+ md = MessageDigest.getInstance(algorithm);
+ } catch (NoSuchAlgorithmException e) {
+ e.printStackTrace();
+ throw new JapException(algorithm + " not found");
+ }
+ byte[] bytes = md.digest(stringBuffer.toString().getBytes());
+ return byteArrayToHex(bytes);
+
+ }
+
+ private static String byteArrayToHex(byte[] b) {
+ StringBuilder sb = new StringBuilder();
+ for (byte value : b) {
+ sb.append(byteToHex(value));
+ }
+ return sb.toString();
+ }
+
+ public static String byteToHex(byte b) {
+ int n = b;
+ if (n < 0) {
+ n = n + 256;
+ }
+ int d1 = n / 16;
+ int d2 = n % 16;
+ return HEX_ARRAY[d1] + HEX_ARRAY[d2];
+ }
+}
diff --git a/jap-http-api/src/main/java/com/fujieid/jap/httpapi/util/DigestSha256Util.java b/jap-http-api/src/main/java/com/fujieid/jap/httpapi/util/DigestSha256Util.java
new file mode 100644
index 0000000..936f15b
--- /dev/null
+++ b/jap-http-api/src/main/java/com/fujieid/jap/httpapi/util/DigestSha256Util.java
@@ -0,0 +1,164 @@
+package com.fujieid.jap.httpapi.util;
+
+import java.nio.ByteBuffer;
+import java.nio.IntBuffer;
+
+/**
+ * refer: https://github.com/meyfa/java-sha256/blob/master/src/main/java/net/meyfa/sha256/Sha256.java
+ *
+ * @author zhihai.yu (mvbbb(a)foxmail.com)
+ * @version 1.0.0
+ * @since 1.0.5
+ */
+public class DigestSha256Util {
+ private static final int[] K = {
+ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
+ 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
+ 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
+ 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
+ 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
+ 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
+ 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
+ 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
+ };
+
+ private static final int[] H0 = {
+ 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19
+ };
+
+ private static final int BLOCK_BITS = 512;
+ private static final int BLOCK_BYTES = BLOCK_BITS / 8;
+
+ // working arrays
+ private static final int[] W = new int[64];
+ private static final int[] H = new int[8];
+ private static final int[] TEMP = new int[8];
+
+ /**
+ * Hashes the given message with SHA-256 and returns the hash.
+ *
+ * @param message The bytes to hash.
+ * @return The hash's bytes.
+ */
+ public static byte[] hash(byte[] message) {
+ // let H = H0
+ System.arraycopy(H0, 0, H, 0, H0.length);
+
+ // initialize all words
+ int[] words = pad(message);
+
+ // enumerate all blocks (each containing 16 words)
+ for (int i = 0, n = words.length / 16; i < n; ++i) {
+
+ // initialize W from the block's words
+ System.arraycopy(words, i * 16, W, 0, 16);
+ for (int t = 16; t < W.length; ++t) {
+ W[t] = smallSig1(W[t - 2]) + W[t - 7] + smallSig0(W[t - 15]) + W[t - 16];
+ }
+
+ // let TEMP = H
+ System.arraycopy(H, 0, TEMP, 0, H.length);
+
+ // operate on TEMP
+ for (int t = 0; t < W.length; ++t) {
+ int t1 = TEMP[7] + bigSig1(TEMP[4]) + ch(TEMP[4], TEMP[5], TEMP[6]) + K[t] + W[t];
+ int t2 = bigSig0(TEMP[0]) + maj(TEMP[0], TEMP[1], TEMP[2]);
+ System.arraycopy(TEMP, 0, TEMP, 1, TEMP.length - 1);
+ TEMP[4] += t1;
+ TEMP[0] = t1 + t2;
+ }
+
+ // add values in TEMP to values in H
+ for (int t = 0; t < H.length; ++t) {
+ H[t] += TEMP[t];
+ }
+
+ }
+
+ return toByteArray(H);
+ }
+
+ /**
+ * Internal method, no need to call. Pads the given message to have a length
+ * that is a multiple of 512 bits (64 bytes), including the addition of a
+ * 1-bit, k 0-bits, and the message length as a 64-bit integer.
+ * The result is a 32-bit integer array with big-endian byte representation.
+ *
+ * @param message The message to pad.
+ * @return A new array with the padded message bytes.
+ */
+ public static int[] pad(byte[] message) {
+ // new message length: original + 1-bit and padding + 8-byte length
+ // --> block count: whole blocks + (padding + length rounded up)
+ int finalBlockLength = message.length % BLOCK_BYTES;
+ int blockCount = message.length / BLOCK_BYTES + (finalBlockLength + 1 + 8 > BLOCK_BYTES ? 2 : 1);
+
+ final IntBuffer result = IntBuffer.allocate(blockCount * (BLOCK_BYTES / Integer.BYTES));
+
+ // copy as much of the message as possible
+ ByteBuffer buf = ByteBuffer.wrap(message);
+ for (int i = 0, n = message.length / Integer.BYTES; i < n; ++i) {
+ result.put(buf.getInt());
+ }
+ // copy the remaining bytes (less than 4) and append 1 bit (rest is zero)
+ ByteBuffer remainder = ByteBuffer.allocate(4);
+ remainder.put(buf).put((byte) 0b10000000).rewind();
+ result.put(remainder.getInt());
+
+ // ignore however many pad bytes (implicitly calculated in the beginning)
+ result.position(result.capacity() - 2);
+ // place original message length as 64-bit integer at the end
+ long msgLength = message.length * 8L;
+ result.put((int) (msgLength >>> 32));
+ result.put((int) msgLength);
+
+ return result.array();
+ }
+
+ /**
+ * Converts the given int array into a byte array via big-endian conversion
+ * (1 int becomes 4 bytes).
+ *
+ * @param ints The source array.
+ * @return The converted array.
+ */
+ private static byte[] toByteArray(int[] ints) {
+ ByteBuffer buf = ByteBuffer.allocate(ints.length * Integer.BYTES);
+ for (int i : ints) {
+ buf.putInt(i);
+ }
+ return buf.array();
+ }
+
+ private static int ch(int x, int y, int z) {
+ return (x & y) | ((~x) & z);
+ }
+
+ private static int maj(int x, int y, int z) {
+ return (x & y) | (x & z) | (y & z);
+ }
+
+ private static int bigSig0(int x) {
+ return Integer.rotateRight(x, 2)
+ ^ Integer.rotateRight(x, 13)
+ ^ Integer.rotateRight(x, 22);
+ }
+
+ private static int bigSig1(int x) {
+ return Integer.rotateRight(x, 6)
+ ^ Integer.rotateRight(x, 11)
+ ^ Integer.rotateRight(x, 25);
+ }
+
+ private static int smallSig0(int x) {
+ return Integer.rotateRight(x, 7)
+ ^ Integer.rotateRight(x, 18)
+ ^ (x >>> 3);
+ }
+
+ private static int smallSig1(int x) {
+ return Integer.rotateRight(x, 17)
+ ^ Integer.rotateRight(x, 19)
+ ^ (x >>> 10);
+ }
+}
diff --git a/jap-http-api/src/main/java/com/fujieid/jap/httpapi/util/DigestUtil.java b/jap-http-api/src/main/java/com/fujieid/jap/httpapi/util/DigestUtil.java
new file mode 100644
index 0000000..d40b0e4
--- /dev/null
+++ b/jap-http-api/src/main/java/com/fujieid/jap/httpapi/util/DigestUtil.java
@@ -0,0 +1,42 @@
+package com.fujieid.jap.httpapi.util;
+
+
+import cn.hutool.core.lang.UUID;
+
+import java.util.concurrent.atomic.LongAdder;
+
+/**
+ * @author zhihai.yu (mvbbb(a)foxmail.com)
+ * @version 1.0.0
+ * @since 1.0.5
+ */
+public final class DigestUtil {
+
+ private static final LongAdder LONG_ADDER = new LongAdder();
+
+ public static String getCnonce() {
+ return UUID.randomUUID().toString().replaceAll("-", "").substring(0, 16);
+ }
+
+ public static String getNc() {
+ LONG_ADDER.increment();
+ return LONG_ADDER.toString();
+ }
+
+ public static String getResponseByQop(String ha1, String nonce, String nc, String cnonce, String qop, String ha2) {
+ if (qop != null && (qop.contains("auth") || qop.contains("auth-init"))) {
+ return DigestMD5Util.encode(ha1, nonce, nc, cnonce, qop, ha2);
+ } else {
+ return DigestMD5Util.encode(ha1, nonce, ha2);
+ }
+ }
+
+ public static String getHa2ByQop(String qop, String method, String digestUri) {
+ if (qop == null || !qop.contains("auth-init")) {
+ return DigestMD5Util.encode(method, digestUri);
+ } else {
+ // TODO Support "auth-init" ha2 compute method
+ return null;
+ }
+ }
+}
diff --git a/jap-http-api/src/main/java/com/fujieid/jap/httpapi/util/HttpAuthUtil.java b/jap-http-api/src/main/java/com/fujieid/jap/httpapi/util/HttpAuthUtil.java
new file mode 100644
index 0000000..61e96cd
--- /dev/null
+++ b/jap-http-api/src/main/java/com/fujieid/jap/httpapi/util/HttpAuthUtil.java
@@ -0,0 +1,47 @@
+package com.fujieid.jap.httpapi.util;
+
+import com.fujieid.jap.httpapi.enums.HttpMethodEnum;
+import com.fujieid.jap.httpapi.subject.HttpAuthResponse;
+import com.xkcoding.http.HttpUtil;
+import com.xkcoding.http.support.HttpHeader;
+import com.xkcoding.http.support.SimpleHttpResponse;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author zhihai.yu (mvbbb(a)foxmail.com)
+ * @version 1.0.0
+ * @since 1.0.5
+ */
+public final class HttpAuthUtil {
+
+ public static HttpAuthResponse sendRequest(String url,
+ HttpMethodEnum method,
+ Map header,
+ Map params,
+ String body) {
+ SimpleHttpResponse response = null;
+ switch (method) {
+ case GET:
+ response = HttpUtil.get(url, params, new HttpHeader(header), false);
+ break;
+ case POST:
+ if (body == null || "".equals(body)) {
+ response = HttpUtil.post(url, params, new HttpHeader(header), false);
+ } else {
+ response = HttpUtil.post(url, body, new HttpHeader(header));
+ }
+ break;
+ default:
+ break;
+ }
+ if (response == null) {
+ return null;
+ }
+ String responseBody = response.getBody();
+ Map> headers = response.getHeaders();
+ return new HttpAuthResponse(response.isSuccess(), responseBody, headers);
+ }
+}
+
diff --git a/jap-http-api/src/main/java/com/fujieid/jap/httpapi/util/SimpleAuthJsonUtil.java b/jap-http-api/src/main/java/com/fujieid/jap/httpapi/util/SimpleAuthJsonUtil.java
new file mode 100644
index 0000000..759e552
--- /dev/null
+++ b/jap-http-api/src/main/java/com/fujieid/jap/httpapi/util/SimpleAuthJsonUtil.java
@@ -0,0 +1,42 @@
+package com.fujieid.jap.httpapi.util;
+
+import com.fujieid.jap.core.JapUser;
+
+import java.util.Map;
+
+/**
+ * Convert between json and auth info
+ *
+ * @author zhihai.yu (mvbbb(a)foxmail.com)
+ * @version 1.0.0
+ * @since 1.0.5
+ */
+public final class SimpleAuthJsonUtil {
+
+ public static String getJsonStrByParams(Map params) {
+ return getJsonStrByJapUserAndParams(null, params);
+ }
+
+ public static String getJsonStrByJapUserAndParams(JapUser japUser, Map params) {
+ if (japUser == null && params == null) {
+ return null;
+ }
+ StringBuilder str = new StringBuilder();
+ str.append("{");
+
+ if (japUser != null) {
+ str.append("\"username\":\"").append(japUser.getUsername()).append("\",\"password:\"").append(japUser.getPassword()).append("\",");
+ }
+
+ if (params != null) {
+ params.forEach((key, value) -> str.append("\"").append(key).append("\":\"").append(value).append("\","));
+ }
+
+ if (str.charAt(str.length() - 1) == ',') {
+ str.deleteCharAt(str.length() - 1);
+ }
+
+ str.append("}");
+ return str.toString();
+ }
+}
diff --git a/jap-http-api/src/main/java/com/fujieid/jap/httpapi/util/SubjectSerializeUtil.java b/jap-http-api/src/main/java/com/fujieid/jap/httpapi/util/SubjectSerializeUtil.java
new file mode 100644
index 0000000..5880f16
--- /dev/null
+++ b/jap-http-api/src/main/java/com/fujieid/jap/httpapi/util/SubjectSerializeUtil.java
@@ -0,0 +1,80 @@
+package com.fujieid.jap.httpapi.util;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.util.HashMap;
+import java.util.Objects;
+
+/**
+ * used to convert "Authorization Request Header" and "WWW-Authenticate Response Header" between string and object
+ *
+ * @author zhihai.yu (mvbbb(a)foxmail.com)
+ * @version 1.0.0
+ * @since 1.0.5
+ */
+public final class SubjectSerializeUtil {
+
+ /**
+ * Deserialize string to object
+ *
+ * @param str a String to be deserialized
+ * @param clazz The target type to deserialize
+ * @param Actual class type
+ * @return Return an object
+ * @throws NoSuchMethodException no such method
+ * @throws IllegalAccessException illegal access
+ * @throws InvocationTargetException invocation target
+ * @throws InstantiationException instantiation
+ */
+ public static T deserialize(String str, Class clazz) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
+ str = str.replace("\\", "").replace("\"", "").trim();
+ String[] split = str.split(",");
+ HashMap params = new HashMap<>(8);
+ for (String s : split) {
+ String[] param = s.split("=");
+ String paramKey = param[0].trim();
+ String paramValue = param[1].trim();
+ params.put(paramKey, paramValue);
+ }
+
+ Field[] fields = clazz.getDeclaredFields();
+
+ T t = clazz.getDeclaredConstructor().newInstance();
+
+ for (Field field : fields) {
+ field.setAccessible(true);
+ String name = field.getName();
+ field.set(t, params.get(name));
+ }
+
+ return t;
+ }
+
+ /**
+ * Serialize object to string
+ *
+ * @param t Objects that need to be serialized
+ * @return return a string after serialization
+ * @throws IllegalAccessException illegal access
+ */
+ public static String serialize(Object t) throws IllegalAccessException {
+ Class> clazz = t.getClass();
+ StringBuilder sb = new StringBuilder();
+ Field[] fields = clazz.getDeclaredFields();
+ boolean firstParam = true;
+ for (Field field : fields) {
+ field.setAccessible(true);
+ String paramName = field.getName();
+ String paramVal = ((String) field.get(t));
+ if (Objects.isNull(paramVal)) {
+ continue;
+ }
+ if (!firstParam) {
+ sb.append(",");
+ }
+ firstParam = false;
+ sb.append(paramName).append("=\"").append(paramVal).append("\"");
+ }
+ return sb.toString();
+ }
+}
diff --git a/jap-http-api/src/main/java/com/fujieid/jap/httpapi/util/URLUtil.java b/jap-http-api/src/main/java/com/fujieid/jap/httpapi/util/URLUtil.java
new file mode 100644
index 0000000..a287acc
--- /dev/null
+++ b/jap-http-api/src/main/java/com/fujieid/jap/httpapi/util/URLUtil.java
@@ -0,0 +1,41 @@
+package com.fujieid.jap.httpapi.util;
+
+/**
+ * URLUtil
+ *
+ * @author zhihai.yu (mvbbb(a)foxmail.com)
+ * @version 1.0.0
+ * @since 1.0.5
+ */
+public class URLUtil {
+
+ private static final String HTTPS_PREFIX = "https://";
+ private static final String HTTP_PREFIX = "http://";
+
+ /**
+ * Get relative uri from uri.
+ * for example:
+ *
+ *
+ * https://www.justauth.plus return to www.justauth.plus
+ *
+ *
+ * @param uri URI with protocol. For example: https://www.justauth.plus
+ * @return Return only the domain name part in the uri
+ */
+ public static String getRelativeUri(String uri) {
+ if (uri == null) {
+ return null;
+ }
+ String relativeUri = null;
+ if (uri.startsWith(HTTPS_PREFIX)) {
+ relativeUri = uri.replaceFirst(HTTPS_PREFIX, "");
+ } else if (uri.startsWith(HTTP_PREFIX)) {
+ relativeUri = uri.replaceFirst(HTTP_PREFIX, "");
+ } else {
+ relativeUri = uri;
+ }
+ relativeUri = relativeUri.substring(relativeUri.indexOf('/'));
+ return relativeUri;
+ }
+}
diff --git a/jap-ids-web/README.md b/jap-ids-web/README.md
new file mode 100644
index 0000000..8b9a3cf
--- /dev/null
+++ b/jap-ids-web/README.md
@@ -0,0 +1,67 @@
+> 本项目主要为了配合 `jap-ids` 使用,内置了 `IdsAccessTokenFilter`(Access Token 验权过滤器) 和 `IdsUserStatusFilter`(用户登录状态过滤器)
+> 如果不需要该项目中的过滤器,开发者可以自己实现。
+
+完整示例代码参考:[https://gitee.com/fujieid/jap-ids-demo](https://gitee.com/fujieid/jap-ids-demo)
+
+
+## 配置过滤器
+
+`jap-ids` 默认提供了两类过滤器:
+- Access Token 验权过滤器
+- 用户登录状态过滤器
+
+以本项目为例,配置以下两个过滤器:
+
+### Access Token 验权过滤器
+```java
+@Bean
+public FilterRegistrationBean registeraccessTokenFilter() {
+ FilterRegistrationBean registration = new FilterRegistrationBean<>();
+ registration.setFilter(new IdsAccessTokenFilter());
+ registration.addUrlPatterns("/*");
+ registration.addInitParameter("ignoreUrl",
+ "/," +
+ "/oauth/login," +
+ "/oauth/error," +
+ "/oauth/confirm," +
+ "/oauth/authorize," +
+ "/oauth/token," +
+ "/oauth/check_session," +
+ "/oauth/registration," +
+ "/.well-known/jwks.json," +
+ "/.well-known/openid-configuration"
+ );
+ registration.setName("IdsAccessTokenFilter");
+ registration.setOrder(1);
+ return registration;
+}
+```
+
+### 用户登录状态过滤器
+```java
+@Bean
+public FilterRegistrationBean registerUserStatusFilter() {
+ FilterRegistrationBean registration = new FilterRegistrationBean<>();
+ registration.setFilter(new IdsUserStatusFilter());
+ registration.addUrlPatterns("/*");
+ registration.addInitParameter("ignoreUrl",
+ "/," +
+ "/oauth/login," +
+ "/oauth/error," +
+ "/oauth/confirm," +
+ "/oauth/authorize," +
+ "/oauth/token," +
+ "/oauth/check_session," +
+ "/oauth/registration," +
+ "/.well-known/jwks.json," +
+ "/.well-known/openid-configuration"
+ );
+ registration.setName("IdsUserStatusFilter");
+ registration.setOrder(1);
+ return registration;
+}
+```
+
+## 总结
+
+基于以上步骤, 就可快速搭建起来一套本地化的 OAuth2.0 服务。更多功能,请参考 [帮助文档](https://justauth.plus/)
diff --git a/jap-ids-web/pom.xml b/jap-ids-web/pom.xml
new file mode 100644
index 0000000..00c6016
--- /dev/null
+++ b/jap-ids-web/pom.xml
@@ -0,0 +1,22 @@
+
+
+
+ com.fujieid
+ jap
+ ${revision}
+
+ 4.0.0
+
+ jap-ids-web
+ jap-ids-web
+ jap-ids-web
+
+
+
+ com.fujieid
+ jap-ids
+ provided
+
+
+
diff --git a/jap-ids/src/main/java/com/fujieid/jap/ids/filter/AbstractIdsFilter.java b/jap-ids-web/src/main/java/com/fujieid/jap/web/filter/AbstractIdsFilter.java
similarity index 90%
rename from jap-ids/src/main/java/com/fujieid/jap/ids/filter/AbstractIdsFilter.java
rename to jap-ids-web/src/main/java/com/fujieid/jap/web/filter/AbstractIdsFilter.java
index 88a47d1..446db06 100644
--- a/jap-ids/src/main/java/com/fujieid/jap/ids/filter/AbstractIdsFilter.java
+++ b/jap-ids-web/src/main/java/com/fujieid/jap/web/filter/AbstractIdsFilter.java
@@ -13,17 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.fujieid.jap.ids.filter;
+package com.fujieid.jap.web.filter;
-import cn.hutool.log.Log;
-import cn.hutool.log.LogFactory;
+import com.fujieid.jap.http.JapHttpRequest;
+import com.fujieid.jap.http.JapHttpResponse;
import com.fujieid.jap.ids.JapIds;
import com.fujieid.jap.ids.config.IdsConfig;
import com.fujieid.jap.ids.pipeline.IdsPipeline;
import com.xkcoding.json.util.StringUtil;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.Arrays;
@@ -37,7 +35,6 @@ import java.util.List;
* @since 1.0.0
*/
public class AbstractIdsFilter {
- protected static final Log log = LogFactory.get();
protected final List ignoreUrls = new ArrayList<>();
/**
@@ -111,8 +108,8 @@ public class AbstractIdsFilter {
if (null == idsFilterErrorPipeline) {
idsFilterErrorPipeline = new IdsPipeline