diff --git a/README.md b/README.md
index e93b2368..afaeb232 100644
--- a/README.md
+++ b/README.md
@@ -120,7 +120,8 @@ For full documentation & more details, visit: [Docs](https://www.hippo4j.cn)
- [[ JavaGuide ]](https://github.com/Snailclimb/JavaGuide):一份涵盖大部分 Java 程序员所需要掌握的核心知识。
- [[ toBeBetterJavaer ]](https://github.com/itwanger/toBeBetterJavaer):一份通俗易懂、风趣幽默的 Java 学习指南。
- [[ Jpom ]](https://gitee.com/dromara/Jpom):简而轻的低侵入式在线构建、自动部署、日常运维、项目监控软件。
-- [[ CongoMall ]](https://gitee.com/opengoofy/congomall):作者的另一个开源项目刚果商城,包含了商城业务和基础架构两大模块。
+- [[ 12306 ]](https://gitee.com/nageoffer/12306):完成高仿 12306 用户+抢票+订单+支付服务,帮助学生主打就业的项目。
+- [[ CongoMall ]](https://gitee.com/nageoffer/congomall):企业级商城,基于 DDD 领域驱动模型开发,包含商城业务和基础架构。
### 贡献者
diff --git a/docs/docusaurus.config.js b/docs/docusaurus.config.js
index 2c6faa93..26890a2a 100644
--- a/docs/docusaurus.config.js
+++ b/docs/docusaurus.config.js
@@ -6,215 +6,211 @@ const darkCodeTheme = require('prism-react-renderer/themes/dracula');
/** @type {import('@docusaurus/types').Config} */
const config = {
- title: 'Hippo4j',
- tagline: '动态可观测线程池框架,为业务系统提高线上运行保障能力',
- url: 'https://hippo4j.cn',
- baseUrl: '/',
- onBrokenLinks: 'throw',
- onBrokenMarkdownLinks: 'warn',
- favicon: 'img/hippo4j_favicon.ico',
- // GitHub pages deployment config.
- // If you aren't using GitHub pages, you don't need these.
- organizationName: 'hippo4j', // Usually your GitHub org/user name.
- projectName: 'hippo4j.github.io', // Usually your repo name.
- deploymentBranch: 'main',
+ title: 'Hippo4j',
+ tagline: '动态可观测线程池框架,为业务系统提高线上运行保障能力',
+ url: 'https://hippo4j.cn',
+ baseUrl: '/',
+ onBrokenLinks: 'throw',
+ onBrokenMarkdownLinks: 'warn',
+ favicon: 'img/hippo4j_favicon.ico',
+ // GitHub pages deployment config.
+ // If you aren't using GitHub pages, you don't need these.
+ organizationName: 'hippo4j', // Usually your GitHub org/user name.
+ projectName: 'hippo4j.github.io', // Usually your repo name.
+ deploymentBranch: 'main',
- // Even if you don't use internalization, you can use this field to set useful
- // metadata like html lang. For example, if your site is Chinese, you may want
- // to replace "en" with "zh-Hans".
- i18n: {
- defaultLocale: 'en',
- locales: ['en', 'zh'],
- localeConfigs: {
- en: {
- label: 'English',
- direction: 'ltr',
- },
- zh: {
- label: '简体中文',
- direction: 'ltr',
- },
+ // Even if you don't use internalization, you can use this field to set useful
+ // metadata like html lang. For example, if your site is Chinese, you may want
+ // to replace "en" with "zh-Hans".
+ i18n: {
+ defaultLocale: 'en',
+ locales: ['en', 'zh'],
+ localeConfigs: {
+ en: {
+ label: 'English',
+ direction: 'ltr',
+ },
+ zh: {
+ label: '简体中文',
+ direction: 'ltr',
+ },
+ },
},
- },
- presets: [
- [
- 'classic',
- /** @type {import('@docusaurus/preset-classic').Options} */
- ({
- docs: {
- sidebarPath: require.resolve('./sidebars.js'),
- // Please change this to your repo.
- // Remove this to remove the "edit this page" links.
- sidebarCollapsed: false,
- /*editUrl: 'https://github.com/longtai-cn',*/
- },
- blog: {
- showReadingTime: true,
- // Please change this to your repo.
- // Remove this to remove the "edit this page" links.
- editUrl:
- 'https://github.com/facebook/docusaurus/tree/main/packages/create-docusaurus/templates/shared/',
- },
- theme: {
- customCss: require.resolve('./src/css/custom.css'),
- },
- }),
+ presets: [
+ [
+ 'classic',
+ /** @type {import('@docusaurus/preset-classic').Options} */
+ ({
+ docs: {
+ sidebarPath: require.resolve('./sidebars.js'),
+ // Please change this to your repo.
+ // Remove this to remove the "edit this page" links.
+ sidebarCollapsed: false,
+ /*editUrl: 'https://github.com/longtai-cn',*/
+ },
+ blog: {
+ showReadingTime: true,
+ // Please change this to your repo.
+ // Remove this to remove the "edit this page" links.
+ editUrl:
+ 'https://github.com/facebook/docusaurus/tree/main/packages/create-docusaurus/templates/shared/',
+ },
+ theme: {
+ customCss: require.resolve('./src/css/custom.css'),
+ },
+ }),
+ ],
],
- ],
- plugins: [
- [
- '@docusaurus/plugin-content-docs',
- {
- id: 'community',
- path: 'community',
- routeBasePath: 'community',
- sidebarPath: require.resolve('./sidebarsCommunity.js'),
- },
+ plugins: [
+ [
+ '@docusaurus/plugin-content-docs',
+ {
+ id: 'community',
+ path: 'community',
+ routeBasePath: 'community',
+ sidebarPath: require.resolve('./sidebarsCommunity.js'),
+ },
+ ],
],
- ],
- themeConfig:
+ themeConfig:
/** @type {import('@docusaurus/preset-classic').ThemeConfig} */
- ({
- autoCollapseCategories: true,
- announcementBar: {
- id: 'announcementBar-1', // Increment on change
- // content: `⭐️ If you like hippo4j, give it a star on Gitee, thanks.`,
- // content: `⭐️ 如果您喜欢 hippo4j,请在 Gitee 和 GitHub 上给它一个 star,谢谢!`,
- content: `⭐️ 开源不易,如果 Hippo4j 对您有帮助,请在 GitHub 上给它一个 Star 🌟`,
- // content: `👉 《小马哥的代码实战课》官方知识星球来啦!!!`,
- },
- navbar: {
- title: '',
- logo: {
- alt: 'Hippo4j 动态可观测线程池框架',
- src: 'img/hippo4j.png',
- },
- items: [
- {
- type: 'docSidebar',
- docId: 'intro',
- position: 'left',
- sidebarId: 'user_docs',
- label: '文档',
- },
- {
- to: '/community/contributor-guide',
- label: '社区',
- position: 'left',
- activeBaseRegex: `/community/`,
- },
- /*{ to: "/team", label: "团队", position: "left" },*/
- { to: '/users', label: '采用公司', position: 'left' },
- { to: '/group', label: '加群沟通', position: 'left' },
- /*{to: '/blog', label: '博客', position: 'left'},*/
- {
- href: 'http://console.hippo4j.cn/index.html',
- label: '控制台样例',
- position: 'left',
- },
- {
- href: 'https://gitee.com/opengoofy/congomall',
- label: '👉 刚果商城',
- position: 'left',
- },
- {
- type: 'docsVersionDropdown',
- position: 'right',
- dropdownActiveClassDisabled: true,
- },
-
- { type: 'localeDropdown', position: 'right' },
- /*{
- href: 'https://gitee.com/mabaiwancn/hippo4j',
- label: 'Gitee',
+ ({
+ autoCollapseCategories: true,
+ announcementBar: {
+ id: 'announcementBar-1', // Increment on change
+ // content: `⭐️ If you like hippo4j, give it a star on Gitee, thanks.`,
+ // content: `⭐️ 如果您喜欢 hippo4j,请在 Gitee 和 GitHub 上给它一个 star,谢谢!`,
+ content: `⭐️ 开源不易,如果 Hippo4j 对您有帮助,请在 GitHub 上给它一个 Star 🌟`,
+ // content: `👉 《小马哥的代码实战课》官方知识星球来啦!!!`,
+ },
+ navbar: {
+ title: '',
+ logo: {
+ alt: 'Hippo4j 动态可观测线程池框架',
+ src: 'img/hippo4j.png',
+ },
+ items: [
+ {
+ type: 'docSidebar',
+ docId: 'intro',
+ position: 'left',
+ sidebarId: 'user_docs',
+ label: '文档',
+ },
+ {
+ to: '/community/contributor-guide',
+ label: '社区',
+ position: 'left',
+ activeBaseRegex: `/community/`,
+ },
+ /*{ to: "/team", label: "团队", position: "left" },*/
+ {to: '/users', label: '采用公司', position: 'left'},
+ {to: '/group', label: '加群沟通', position: 'left'},
+ /*{to: '/blog', label: '博客', position: 'left'},*/
+ {
+ href: 'http://console.hippo4j.cn/index.html',
+ label: '控制台样例',
+ position: 'left',
+ },
+ {
+ href: 'https://magestack.cn',
+ label: '🚀 拿个offer',
+ position: 'left',
+ },
+ {
+ type: 'docsVersionDropdown',
position: 'right',
- },*/
- {
- href: 'https://github.com/opengoofy/hippo4j',
- className: 'header-github-link',
- 'aria-label': 'GitHub repository',
- position: 'right',
- },
+ dropdownActiveClassDisabled: true,
+ },
- /*{
+ {type: 'localeDropdown', position: 'right'},
+ /*{
+ href: 'https://gitee.com/mabaiwancn/hippo4j',
+ label: 'Gitee',
+ position: 'right',
+ },*/
+ {
href: 'https://github.com/opengoofy/hippo4j',
- label: 'GitHub',
+ className: 'header-github-link',
+ 'aria-label': 'GitHub repository',
position: 'right',
- },*/
- ],
- },
- footer: {
- style: 'dark',
- links: [
- {
- title: 'Docs',
- items: [
- {
- label: 'Intro',
- to: '/docs/user_docs/intro',
- },
- {
- label: 'Config Mode',
- to: '/docs/user_docs/getting_started/config/hippo4j-config-start',
- },
- {
- label: 'Server Mode',
- to: '/docs/user_docs/getting_started/server/hippo4j-server-start',
- },
- ],
- },
- {
- title: 'Community',
- items: [
- {
- label: 'Group',
- href: 'https://hippo4j.cn/group',
- },
- {
- label: 'WeChat',
- href: 'https://mp.weixin.qq.com/s/diVHYvwiuYH9aWpZDPc27g',
- },
- ],
- },
- {
- title: 'More',
- items: [
- {
- label: 'Gitee',
- href: 'https://gitee.com/opengoofy/hippo4j',
- },
- {
- label: 'GitHub',
- href: 'https://github.com/opengoofy/hippo4j',
- },
- ],
- },
- {
- title: 'Links',
- items: [
- {
- label: '书源',
- href: 'https://bookyuan.cn/',
- },
- {
- label: '推广合作',
- href: 'https://hippo4j.cn/docs/user_docs/other/operation',
- },
- ],
- },
- ],
- copyright: `Copyright © 2021-2022 马丁版权所有 京ICP备2021038095号
+ },
+
+ /*{
+ href: 'https://github.com/opengoofy/hippo4j',
+ label: 'GitHub',
+ position: 'right',
+ },*/
+ ],
+ },
+ footer: {
+ style: 'dark',
+ links: [
+ {
+ title: 'Docs',
+ items: [
+ {
+ label: 'Intro',
+ to: '/docs/user_docs/intro',
+ },
+ {
+ label: 'Config Mode',
+ to: '/docs/user_docs/getting_started/config/hippo4j-config-start',
+ },
+ {
+ label: 'Server Mode',
+ to: '/docs/user_docs/getting_started/server/hippo4j-server-start',
+ },
+ ],
+ },
+ {
+ title: 'Community',
+ items: [
+ {
+ label: 'Group',
+ href: 'https://hippo4j.cn/group',
+ },
+ {
+ label: 'WeChat',
+ href: 'https://mp.weixin.qq.com/s/diVHYvwiuYH9aWpZDPc27g',
+ },
+ ],
+ },
+ {
+ title: 'More',
+ items: [
+ {
+ label: 'Gitee',
+ href: 'https://gitee.com/opengoofy/hippo4j',
+ },
+ {
+ label: 'GitHub',
+ href: 'https://github.com/opengoofy/hippo4j',
+ },
+ ],
+ },
+ {
+ title: 'Links',
+ items: [
+ {
+ label: '推广合作',
+ href: 'https://hippo4j.cn/docs/user_docs/other/operation',
+ },
+ ],
+ },
+ ],
+ copyright: `Copyright © 2021-2023 马丁版权所有 京ICP备2021038095号
`,
- },
- prism: {
- theme: lightCodeTheme,
- darkTheme: darkCodeTheme,
- additionalLanguages: ['java'],
- },
- }),
+ },
+ prism: {
+ theme: lightCodeTheme,
+ darkTheme: darkCodeTheme,
+ additionalLanguages: ['java'],
+ },
+ }),
};
module.exports = config;
diff --git a/docs/i18n/zh/docusaurus-plugin-content-pages/users.md b/docs/i18n/zh/docusaurus-plugin-content-pages/users.md
index f06de39a..61618305 100644
--- a/docs/i18n/zh/docusaurus-plugin-content-pages/users.md
+++ b/docs/i18n/zh/docusaurus-plugin-content-pages/users.md
@@ -5,7 +5,7 @@ title: 采用公司
## 谁在使用 Hippo4j
-共计 39+ 家公司生产接入 Hippo4j,按照公司登记时间排序。
+共计 42+ 家公司生产接入 Hippo4j,按照公司登记时间排序。
- [身边云](https://serviceshare.com)
- [思派健康科技](https://www.medbanks.cn)
@@ -46,6 +46,9 @@ title: 采用公司
- [广联达科技股份有限公司](https://www.glodon.com)
- [天健联创控股集团有限公司](https://www.tjlc.com.cn)
- [知乎](https://www.zhihu.com/)
+- [广东谷通科技有限公司](https://www.duofriend.com)
+- [成都全域智旅科技有限公司](http://qyzl.com)
+- [深圳市华云中盛科技股份有限公司](http://www.hua-cloud.cn)
## 登记
diff --git a/docs/src/pages/users.md b/docs/src/pages/users.md
index f06de39a..61618305 100644
--- a/docs/src/pages/users.md
+++ b/docs/src/pages/users.md
@@ -5,7 +5,7 @@ title: 采用公司
## 谁在使用 Hippo4j
-共计 39+ 家公司生产接入 Hippo4j,按照公司登记时间排序。
+共计 42+ 家公司生产接入 Hippo4j,按照公司登记时间排序。
- [身边云](https://serviceshare.com)
- [思派健康科技](https://www.medbanks.cn)
@@ -46,6 +46,9 @@ title: 采用公司
- [广联达科技股份有限公司](https://www.glodon.com)
- [天健联创控股集团有限公司](https://www.tjlc.com.cn)
- [知乎](https://www.zhihu.com/)
+- [广东谷通科技有限公司](https://www.duofriend.com)
+- [成都全域智旅科技有限公司](http://qyzl.com)
+- [深圳市华云中盛科技股份有限公司](http://www.hua-cloud.cn)
## 登记
diff --git a/infra/common/src/test/java/cn/hippo4j/common/executor/support/ThreadPoolExecutorUtilTest.java b/infra/common/src/test/java/cn/hippo4j/common/executor/support/ThreadPoolExecutorUtilTest.java
new file mode 100644
index 00000000..88f523bb
--- /dev/null
+++ b/infra/common/src/test/java/cn/hippo4j/common/executor/support/ThreadPoolExecutorUtilTest.java
@@ -0,0 +1,94 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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 cn.hippo4j.common.executor.support;
+
+import cn.hippo4j.common.toolkit.ThreadPoolExecutorUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Thread pool executor util test
+ */
+@Slf4j
+public class ThreadPoolExecutorUtilTest {
+
+ private ThreadPoolExecutor executor;
+ private int corePoolSize;
+ private int maxPoolSize;
+
+ @Before
+ public void testSafeSetPoolSize() {
+ corePoolSize = 2;
+ maxPoolSize = 4;
+ executor = new ThreadPoolExecutor(
+ corePoolSize,
+ maxPoolSize,
+ 1L,
+ TimeUnit.SECONDS,
+ new ArrayBlockingQueue<>(10));
+ }
+
+ @Test
+ public void testEquals() {
+ // Test when the new core pool size equals the original maximum pool size.
+ int newCorePoolSize1 = maxPoolSize;
+ int newMaxPoolSize1 = 6;
+ ThreadPoolExecutorUtil.safeSetPoolSize(executor, newCorePoolSize1, newMaxPoolSize1);
+ Assert.assertEquals(newCorePoolSize1, executor.getCorePoolSize());
+ Assert.assertEquals(newMaxPoolSize1, executor.getMaximumPoolSize());
+ }
+
+ @Test
+ public void testGreater() {
+ // Test when the new core pool size is greater than the original maximum pool size.
+ int newCorePoolSize2 = 8;
+ int newMaxPoolSize2 = 10;
+ ThreadPoolExecutorUtil.safeSetPoolSize(executor, newCorePoolSize2, newMaxPoolSize2);
+ Assert.assertEquals(newCorePoolSize2, executor.getCorePoolSize());
+ Assert.assertEquals(newMaxPoolSize2, executor.getMaximumPoolSize());
+ }
+
+ @Test
+ public void testLess() {
+ // Test when the new core pool size is less than the original maximum pool size.
+ int newCorePoolSize3 = 3;
+ int newMaxPoolSize3 = 5;
+ ThreadPoolExecutorUtil.safeSetPoolSize(executor, newCorePoolSize3, newMaxPoolSize3);
+ Assert.assertEquals(newCorePoolSize3, executor.getCorePoolSize());
+ Assert.assertEquals(newMaxPoolSize3, executor.getMaximumPoolSize());
+ }
+
+ @Test
+ public void testException() {
+ // Test when the new core pool size is greater than the new maximum pool size, which should throw an IllegalArgumentException.
+ int newCorePoolSize4 = 6;
+ int newMaxPoolSize4 = 4;
+ try {
+ ThreadPoolExecutorUtil.safeSetPoolSize(executor, newCorePoolSize4, newMaxPoolSize4);
+ } catch (IllegalArgumentException e) {
+ // Expected to throw an exception.
+ Assert.assertEquals("newCorePoolSize must be smaller than newMaximumPoolSize", e.getMessage());
+ log.error("newCorePoolSize must be smaller than newMaximumPoolSize;{}", e.getMessage());
+ }
+ }
+}
diff --git a/starters/threadpool/monitor/hippo4j-spring-boot-starter-monitor-micrometer/src/main/java/cn/hippo4j/springboot/starter/monitor/micrometer/MicrometerMonitorAutoConfiguration.java b/starters/threadpool/monitor/hippo4j-spring-boot-starter-monitor-micrometer/src/main/java/cn/hippo4j/springboot/starter/monitor/micrometer/MicrometerMonitorAutoConfiguration.java
index 1eb7b70b..73834bb8 100644
--- a/starters/threadpool/monitor/hippo4j-spring-boot-starter-monitor-micrometer/src/main/java/cn/hippo4j/springboot/starter/monitor/micrometer/MicrometerMonitorAutoConfiguration.java
+++ b/starters/threadpool/monitor/hippo4j-spring-boot-starter-monitor-micrometer/src/main/java/cn/hippo4j/springboot/starter/monitor/micrometer/MicrometerMonitorAutoConfiguration.java
@@ -27,7 +27,6 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
-import org.springframework.boot.autoconfigure.AutoConfigureBefore;
/**
* Micrometer monitor auto configuration.
diff --git a/starters/threadpool/server/src/main/java/cn/hippo4j/springboot/starter/monitor/ReportingEventExecutor.java b/starters/threadpool/server/src/main/java/cn/hippo4j/springboot/starter/monitor/ReportingEventExecutor.java
index 0038c62d..884aeaa7 100644
--- a/starters/threadpool/server/src/main/java/cn/hippo4j/springboot/starter/monitor/ReportingEventExecutor.java
+++ b/starters/threadpool/server/src/main/java/cn/hippo4j/springboot/starter/monitor/ReportingEventExecutor.java
@@ -45,6 +45,7 @@ import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
@@ -136,7 +137,7 @@ public class ReportingEventExecutor implements Runnable, CommandLineRunner, Disp
properties.getCollectInterval(),
TimeUnit.MILLISECONDS);
Integer bufferSize = properties.getTaskBufferSize();
- messageCollectVessel = new ArrayBlockingQueue(bufferSize);
+ messageCollectVessel = new LinkedBlockingQueue(bufferSize);
// Get all data collection components, currently only historical operation data collection.
collectors = ApplicationContextHolder.getBeansOfType(Collector.class);
// Start reporting monitoring data thread.
diff --git a/starters/threadpool/server/src/main/java/cn/hippo4j/springboot/starter/support/AdaptedThreadPoolDestroyPostProcessor.java b/starters/threadpool/server/src/main/java/cn/hippo4j/springboot/starter/support/AdaptedThreadPoolDestroyPostProcessor.java
index 0490b32b..9b60a4f6 100644
--- a/starters/threadpool/server/src/main/java/cn/hippo4j/springboot/starter/support/AdaptedThreadPoolDestroyPostProcessor.java
+++ b/starters/threadpool/server/src/main/java/cn/hippo4j/springboot/starter/support/AdaptedThreadPoolDestroyPostProcessor.java
@@ -78,6 +78,20 @@ public class AdaptedThreadPoolDestroyPostProcessor implements DestructionAwareBe
.ifPresent(executorHolder -> destroyAdaptedThreadPoolExecutor(beanName, executorHolder));
}
+ @Override
+ public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
+ // forked default implementation from spring-beans-5.1.14.RELEASE.jar
+ // org.springframework.beans.factory.config.BeanPostProcessor#postProcessBeforeInitialization
+ return bean;
+ }
+
+ @Override
+ public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
+ // forked default implementation from spring-beans-5.1.14.RELEASE.jar
+ // org.springframework.beans.factory.config.BeanPostProcessor#postProcessAfterInitialization
+ return bean;
+ }
+
private void destroyAdaptedThreadPoolExecutor(String beanName, ThreadPoolExecutorHolder executorHolder) {
try {
if (log.isDebugEnabled()) {
diff --git a/threadpool/console/src/views/hippo4j/threadpool/index.vue b/threadpool/console/src/views/hippo4j/threadpool/index.vue
index 87e4aed3..dfa0e21b 100755
--- a/threadpool/console/src/views/hippo4j/threadpool/index.vue
+++ b/threadpool/console/src/views/hippo4j/threadpool/index.vue
@@ -744,6 +744,7 @@ export default {
} else if (value === 5) {
this.temp.capacity = 2147483647;
}
+ this.$forceUpdate();
},
tenantSelectList() {
diff --git a/threadpool/console/src/views/login/index.vue b/threadpool/console/src/views/login/index.vue
index 66ce2387..c422fae3 100755
--- a/threadpool/console/src/views/login/index.vue
+++ b/threadpool/console/src/views/login/index.vue
@@ -128,6 +128,7 @@ export default {
if (hostname === 'console.hippo4j.cn') {
this.loginForm.username = 'hippo4j';
this.loginForm.password = 'hippo4j';
+ this.rememberMe = 1;
}
console.log(hostname);
},
diff --git a/threadpool/monitor/micrometer/src/main/java/cn/hippo4j/monitor/micrometer/AdapterThreadPoolMicrometerMonitorHandler.java b/threadpool/monitor/micrometer/src/main/java/cn/hippo4j/monitor/micrometer/AdapterThreadPoolMicrometerMonitorHandler.java
index ea993663..fe28dc21 100644
--- a/threadpool/monitor/micrometer/src/main/java/cn/hippo4j/monitor/micrometer/AdapterThreadPoolMicrometerMonitorHandler.java
+++ b/threadpool/monitor/micrometer/src/main/java/cn/hippo4j/monitor/micrometer/AdapterThreadPoolMicrometerMonitorHandler.java
@@ -58,7 +58,9 @@ public class AdapterThreadPoolMicrometerMonitorHandler extends AbstractAdapterTh
Tag.of(APPLICATION_NAME_TAG, applicationName));
Metrics.gauge(metricName("core.size"), tags, threadPoolAdapterState, ThreadPoolAdapterState::getCoreSize);
Metrics.gauge(metricName("maximum.size"), tags, threadPoolAdapterState, ThreadPoolAdapterState::getMaximumSize);
- Metrics.gauge(metricName("queue.capacity"), tags, threadPoolAdapterState, ThreadPoolAdapterState::getBlockingQueueCapacity);
+ if (threadPoolAdapterState.getBlockingQueueCapacity() != null) {
+ Metrics.gauge(metricName("queue.capacity"), tags, threadPoolAdapterState, ThreadPoolAdapterState::getBlockingQueueCapacity);
+ }
}
private String metricName(String name) {
diff --git a/threadpool/server/auth/pom.xml b/threadpool/server/auth/pom.xml
index ce8d8175..aff6a5d5 100644
--- a/threadpool/server/auth/pom.xml
+++ b/threadpool/server/auth/pom.xml
@@ -62,5 +62,9 @@
hippo4j-threadpool-server-common
${project.version}
+
+ org.springframework.boot
+ spring-boot-starter-data-ldap
+
diff --git a/threadpool/server/auth/src/main/java/cn/hippo4j/auth/config/GlobalSecurityConfig.java b/threadpool/server/auth/src/main/java/cn/hippo4j/auth/config/GlobalSecurityConfig.java
index b239b42c..83b3d1ca 100644
--- a/threadpool/server/auth/src/main/java/cn/hippo4j/auth/config/GlobalSecurityConfig.java
+++ b/threadpool/server/auth/src/main/java/cn/hippo4j/auth/config/GlobalSecurityConfig.java
@@ -20,6 +20,7 @@ package cn.hippo4j.auth.config;
import cn.hippo4j.auth.constant.Constants;
import cn.hippo4j.auth.filter.JWTAuthenticationFilter;
import cn.hippo4j.auth.filter.JWTAuthorizationFilter;
+import cn.hippo4j.auth.filter.LdapAuthenticationFilter;
import cn.hippo4j.auth.security.JwtTokenManager;
import cn.hippo4j.auth.service.impl.UserDetailsServiceImpl;
import org.springframework.beans.factory.annotation.Value;
@@ -28,6 +29,7 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.BeanIds;
+import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
@@ -54,9 +56,12 @@ public class GlobalSecurityConfig extends WebSecurityConfigurerAdapter {
@Value("${hippo4j.core.auth.enabled:true}")
private Boolean enableAuthentication;
- @Resource
+ @Resource(name = "userDetailsServiceImpl")
private UserDetailsService userDetailsService;
+ @Resource(name = "ldapUserDetailsServiceImpl")
+ private UserDetailsService ldapUserDetailsService;
+
@Resource
private JwtTokenManager tokenManager;
@@ -93,7 +98,9 @@ public class GlobalSecurityConfig extends WebSecurityConfigurerAdapter {
.antMatchers("/static/**", "/index.html", "/favicon.ico", "/avatar.jpg").permitAll()
.antMatchers("/doc.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs").anonymous()
.and()
- .addFilter(new JWTAuthenticationFilter(authenticationManager()))
+ // .addFilter(new JWTAuthenticationFilter(authenticationManager())).authenticationProvider(authenticationProvider())
+ .addFilter(JWTAuthenticationFilter()).authenticationProvider(ldapAuthenticationProvider())
+ .addFilter(LdapAuthenticationFilter()).authenticationProvider(ldapAuthenticationProvider())
.addFilter(new JWTAuthorizationFilter(tokenManager, authenticationManager()))
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
disableAuthenticationIfNeeded(http);
@@ -106,6 +113,20 @@ public class GlobalSecurityConfig extends WebSecurityConfigurerAdapter {
web.ignoring().antMatchers(ignores);
}
+ private LdapAuthenticationFilter LdapAuthenticationFilter() throws Exception {
+ LdapAuthenticationFilter filter = new LdapAuthenticationFilter(authenticationManager());
+ filter.setLdapUserDetailsService(ldapUserDetailsService);
+ filter.setAuthenticationManager(authenticationManagerBean());
+ return filter;
+ }
+
+ private JWTAuthenticationFilter JWTAuthenticationFilter() throws Exception {
+ JWTAuthenticationFilter filter = new JWTAuthenticationFilter(authenticationManager());
+ filter.setLdapUserDetailsService(userDetailsService);
+ filter.setAuthenticationManager(authenticationManagerBean());
+ return filter;
+ }
+
/**
* Injection DaoAuthenticationProvider
* Modify hideUserNotFoundExceptions initial value to false
@@ -120,6 +141,20 @@ public class GlobalSecurityConfig extends WebSecurityConfigurerAdapter {
return provider;
}
+ @Bean
+ public DaoAuthenticationProvider ldapAuthenticationProvider() {
+ DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
+ authProvider.setUserDetailsService(ldapUserDetailsService);
+ authProvider.setPasswordEncoder(bCryptPasswordEncoder());
+ return authProvider;
+ }
+
+ @Override
+ protected void configure(AuthenticationManagerBuilder auth) throws Exception {
+ auth.authenticationProvider(authenticationProvider())
+ .authenticationProvider(ldapAuthenticationProvider());
+ }
+
private void disableAuthenticationIfNeeded(HttpSecurity http) throws Exception {
if (Boolean.FALSE.equals(enableAuthentication)) {
http.authorizeRequests().antMatchers("/hippo4j/v1/cs/**").permitAll();
diff --git a/threadpool/server/auth/src/main/java/cn/hippo4j/auth/config/LdapConfiguration.java b/threadpool/server/auth/src/main/java/cn/hippo4j/auth/config/LdapConfiguration.java
new file mode 100644
index 00000000..1932878d
--- /dev/null
+++ b/threadpool/server/auth/src/main/java/cn/hippo4j/auth/config/LdapConfiguration.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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 cn.hippo4j.auth.config;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.ldap.core.LdapTemplate;
+import org.springframework.ldap.core.support.LdapContextSource;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Ldap config.
+ */
+@Configuration
+public class LdapConfiguration {
+
+ private LdapTemplate ldapTemplate;
+
+ @Value("${spring.ldap.urls:}")
+ private String url;
+
+ @Value("${spring.ldap.base:}")
+ private String base;
+
+ @Value("${spring.ldap.embedded.credential.username:}")
+ private String username;
+
+ @Value("${spring.ldap.embedded.credential.password:}")
+ private String password;
+
+ @Bean
+ public LdapContextSource contextSource() {
+ LdapContextSource contextSource = new LdapContextSource();
+ Map config = new HashMap<>(10);
+ contextSource.setUrl(url);
+ contextSource.setBase(base);
+ contextSource.setUserDn(username);
+ contextSource.setPassword(password);
+ // fix garbled characters
+ config.put("java.naming.ldap.attributes.binary", "objectGUID");
+
+ contextSource.setPooled(true);
+ contextSource.setBaseEnvironmentProperties(config);
+ return contextSource;
+ }
+
+ @Bean
+ public LdapTemplate ldapTemplate() {
+ if (null == ldapTemplate) {
+ ldapTemplate = new LdapTemplate(contextSource());
+ }
+ return ldapTemplate;
+ }
+}
diff --git a/threadpool/server/auth/src/main/java/cn/hippo4j/auth/filter/JWTAuthenticationFilter.java b/threadpool/server/auth/src/main/java/cn/hippo4j/auth/filter/JWTAuthenticationFilter.java
index 408aa3d4..d9f270b5 100644
--- a/threadpool/server/auth/src/main/java/cn/hippo4j/auth/filter/JWTAuthenticationFilter.java
+++ b/threadpool/server/auth/src/main/java/cn/hippo4j/auth/filter/JWTAuthenticationFilter.java
@@ -25,23 +25,25 @@ import cn.hippo4j.auth.toolkit.ReturnT;
import cn.hippo4j.common.toolkit.JSONUtil;
import cn.hippo4j.server.common.base.Results;
import com.fasterxml.jackson.databind.ObjectMapper;
+import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.codec.DecodingException;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
-import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
+import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
import javax.servlet.FilterChain;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.security.GeneralSecurityException;
-import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
@@ -60,11 +62,18 @@ public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilte
private final ThreadLocal rememberMe = new ThreadLocal();
+ private UserDetailsService userDetailsService;
+
public JWTAuthenticationFilter(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
super.setFilterProcessesUrl(BASE_PATH + "/auth/login");
}
+ public void setLdapUserDetailsService(UserDetailsService userDetailsServiceImpl) {
+ this.userDetailsService = userDetailsServiceImpl;
+ }
+
+ @SneakyThrows
@Override
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response) throws AuthenticationException {
@@ -78,8 +87,9 @@ public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilte
request.setAttribute("loginUser", loginUser);
rememberMe.set(loginUser.getRememberMe());
- authenticate = authenticationManager.authenticate(
- new UsernamePasswordAuthenticationToken(loginUser.getUsername(), loginUser.getPassword(), new ArrayList()));
+ UserDetails userDetails = userDetailsService.loadUserByUsername(loginUser.getUsername());
+ authenticate = new PreAuthenticatedAuthenticationToken(userDetails, null, userDetails.getAuthorities());
+
} catch (GeneralSecurityException e) {
log.warn("Password decode exception: {}", e.getMessage());
throw new DecodingException(e.getMessage());
diff --git a/threadpool/server/auth/src/main/java/cn/hippo4j/auth/filter/LdapAuthenticationFilter.java b/threadpool/server/auth/src/main/java/cn/hippo4j/auth/filter/LdapAuthenticationFilter.java
new file mode 100644
index 00000000..9ce982a3
--- /dev/null
+++ b/threadpool/server/auth/src/main/java/cn/hippo4j/auth/filter/LdapAuthenticationFilter.java
@@ -0,0 +1,155 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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 cn.hippo4j.auth.filter;
+
+import cn.hippo4j.auth.model.biz.user.JwtUser;
+import cn.hippo4j.auth.model.biz.user.LoginUser;
+import cn.hippo4j.auth.toolkit.AESUtil;
+import cn.hippo4j.auth.toolkit.JwtTokenUtil;
+import cn.hippo4j.auth.toolkit.ReturnT;
+import cn.hippo4j.common.toolkit.JSONUtil;
+import cn.hippo4j.server.common.base.Results;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.authentication.AuthenticationServiceException;
+import org.springframework.security.authentication.BadCredentialsException;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
+import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
+
+import javax.servlet.FilterChain;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import static cn.hippo4j.auth.constant.Constants.SPLIT_COMMA;
+import static cn.hippo4j.common.constant.Constants.BASE_PATH;
+import static cn.hippo4j.common.constant.Constants.MAP_INITIAL_CAPACITY;
+
+/**
+ * Ldap Filter
+ */
+@Slf4j
+public class LdapAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
+
+ private final ThreadLocal rememberMe = new ThreadLocal<>();
+
+ private UserDetailsService ldapUserDetailsService;
+
+ public void setLdapUserDetailsService(UserDetailsService ldapUserDetailsServiceImpl) {
+ this.ldapUserDetailsService = ldapUserDetailsServiceImpl;
+ }
+
+ public LdapAuthenticationFilter(AuthenticationManager authenticationManager) {
+ super.setFilterProcessesUrl(BASE_PATH + "/auth/ldap/login");
+ }
+
+ /**
+ * Whether it's just the post way
+ */
+ private boolean postOnly = true;
+
+
+ /**
+ * filter obtains the username and password of LDAP and assembles it on the token.
+ * Then give the token for authorization
+ */
+ @Override
+ public Authentication attemptAuthentication(HttpServletRequest request
+ , HttpServletResponse response) throws AuthenticationException {
+ if (postOnly && !"POST".equals(request.getMethod())) {
+ throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
+ } else {
+ // Get logged in information from the input stream.
+ Authentication authenticate = null;
+ try {
+ LoginUser loginUser = new ObjectMapper().readValue(request.getInputStream(), LoginUser.class);
+ String key = new StringBuffer(loginUser.getTag()).reverse().toString();
+ String password = AESUtil.decrypt(loginUser.getPassword(), key);
+ loginUser.setPassword(password);
+ request.setAttribute("loginUser", loginUser);
+ rememberMe.set(loginUser.getRememberMe());
+ // ldap validated
+ UserDetails userDetails = ldapUserDetailsService.loadUserByUsername(loginUser.getUsername());
+ authenticate = new PreAuthenticatedAuthenticationToken(userDetails, null, userDetails.getAuthorities());
+ } catch (UsernameNotFoundException e) {
+ log.debug("User {} not found", e.getMessage());
+ throw e;
+ } catch (BadCredentialsException e) {
+ log.debug("Bad credentials exception: {}", e.getMessage());
+ throw e;
+ } catch (Exception e) {
+ log.debug("Attempt authentication error", e);
+ }
+ return authenticate;
+ }
+ }
+
+ @Override
+ protected void successfulAuthentication(HttpServletRequest request,
+ HttpServletResponse response,
+ FilterChain chain,
+ Authentication authResult) throws IOException {
+ try {
+ JwtUser jwtUser = (JwtUser) authResult.getPrincipal();
+ boolean isRemember = rememberMe.get() == 1;
+ String role = "";
+ Collection extends GrantedAuthority> authorities = jwtUser.getAuthorities();
+ for (GrantedAuthority authority : authorities) {
+ role = authority.getAuthority();
+ }
+ String token = JwtTokenUtil.createToken(jwtUser.getId(), jwtUser.getUsername(), role, isRemember);
+ response.setHeader("token", JwtTokenUtil.TOKEN_PREFIX + token);
+ response.setCharacterEncoding("UTF-8");
+ Map maps = new HashMap<>(MAP_INITIAL_CAPACITY);
+ maps.put("data", JwtTokenUtil.TOKEN_PREFIX + token);
+ maps.put("roles", role.split(SPLIT_COMMA));
+ response.getWriter().write(JSONUtil.toJSONString(Results.success(maps)));
+ } finally {
+ rememberMe.remove();
+ }
+ }
+
+ @Override
+ protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException {
+ response.setCharacterEncoding("UTF-8");
+ response.getWriter().write(JSONUtil.toJSONString(new ReturnT<>(ReturnT.JWT_FAIL_CODE, getMessage(failed))));
+ }
+
+ /**
+ * Return different echo information to the front end according to different exception types
+ */
+ private String getMessage(AuthenticationException failed) {
+ String message = "Server Error";
+ if (failed instanceof UsernameNotFoundException) {
+ message = "用户不存在";
+ } else if (failed instanceof BadCredentialsException) {
+ message = "密码错误";
+ }
+ return message;
+ }
+}
diff --git a/threadpool/server/auth/src/main/java/cn/hippo4j/auth/model/LdapUserInfo.java b/threadpool/server/auth/src/main/java/cn/hippo4j/auth/model/LdapUserInfo.java
new file mode 100644
index 00000000..9a09c0b8
--- /dev/null
+++ b/threadpool/server/auth/src/main/java/cn/hippo4j/auth/model/LdapUserInfo.java
@@ -0,0 +1,39 @@
+package cn.hippo4j.auth.model;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import lombok.Data;
+import org.springframework.ldap.odm.annotations.Attribute;
+import org.springframework.ldap.odm.annotations.DnAttribute;
+import org.springframework.ldap.odm.annotations.Entry;
+import org.springframework.ldap.odm.annotations.Id;
+
+import javax.naming.Name;
+
+@Data
+@Entry(objectClasses = {"inetOrgPerson", "top"})
+public class LdapUserInfo {
+
+ @JsonIgnore
+ @Id
+ private Name dn;
+
+ @Attribute(name = "cn")
+ @DnAttribute(value = "cn")
+ private String userName;
+
+ @Attribute(name = "sn")
+ private String lastName;
+
+ @Attribute(name = "description")
+ private String description;
+
+ @Attribute(name = "telephoneNumber")
+ private String telephoneNumber;
+
+ @Attribute(name = "userPassword")
+ private String password;
+
+ @Attribute(name = "ou")
+ private String organizational;
+
+}
diff --git a/threadpool/server/auth/src/main/java/cn/hippo4j/auth/service/LdapService.java b/threadpool/server/auth/src/main/java/cn/hippo4j/auth/service/LdapService.java
new file mode 100644
index 00000000..823286d3
--- /dev/null
+++ b/threadpool/server/auth/src/main/java/cn/hippo4j/auth/service/LdapService.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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 cn.hippo4j.auth.service;
+
+/**
+ * Ldap service.
+ */
+public interface LdapService {
+ /**
+ * Login ldap
+ */
+ void login(String username, String password);
+}
diff --git a/threadpool/server/auth/src/main/java/cn/hippo4j/auth/service/impl/LdapServiceImpl.java b/threadpool/server/auth/src/main/java/cn/hippo4j/auth/service/impl/LdapServiceImpl.java
new file mode 100644
index 00000000..bb43be02
--- /dev/null
+++ b/threadpool/server/auth/src/main/java/cn/hippo4j/auth/service/impl/LdapServiceImpl.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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 cn.hippo4j.auth.service.impl;
+
+import cn.hippo4j.auth.service.LdapService;
+import cn.hippo4j.server.common.base.exception.ServiceException;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.dao.EmptyResultDataAccessException;
+import org.springframework.ldap.AuthenticationException;
+import org.springframework.ldap.UncategorizedLdapException;
+import org.springframework.ldap.core.LdapTemplate;
+import org.springframework.ldap.query.LdapQueryBuilder;
+import org.springframework.security.authentication.BadCredentialsException;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+import org.springframework.stereotype.Service;
+
+import static org.springframework.ldap.query.LdapQueryBuilder.query;
+
+@Service
+@Slf4j
+public class LdapServiceImpl implements LdapService {
+
+ private final LdapTemplate ldapTemplate;
+
+ @Value("${spring.ldap.object-class:}")
+ private String objectClassName;
+
+ @Value("${spring.ldap.account-attribute:}")
+ private String accountAttribute;
+
+ public LdapServiceImpl(LdapTemplate ldapTemplate) {
+ this.ldapTemplate = ldapTemplate;
+ }
+
+ @Override
+ public void login(String username, String password) {
+ try {
+ ldapTemplate.authenticate(LdapQueryBuilder.query()
+ .where(accountAttribute).is(username)
+ .and(query().where("objectClass").is(objectClassName))
+ , password);
+ log.debug("{} ldap Login successful", username);
+ } catch (EmptyResultDataAccessException e) {
+ throw new UsernameNotFoundException("ldap Can't find the user information ");
+ } catch (AuthenticationException e) {
+ log.debug("The user name or account error");
+ throw new BadCredentialsException("The username or password error");
+ } catch (UncategorizedLdapException e) {
+ log.debug("Please check whether the user name password input");
+ throw new BadCredentialsException("Please check whether the username password input");
+ } catch (Exception e) {
+ throw new ServiceException("Abnormal server");
+ }
+ }
+
+}
diff --git a/threadpool/server/auth/src/main/java/cn/hippo4j/auth/service/impl/LdapUserDetailsServiceImpl.java b/threadpool/server/auth/src/main/java/cn/hippo4j/auth/service/impl/LdapUserDetailsServiceImpl.java
new file mode 100644
index 00000000..ee5411f1
--- /dev/null
+++ b/threadpool/server/auth/src/main/java/cn/hippo4j/auth/service/impl/LdapUserDetailsServiceImpl.java
@@ -0,0 +1,115 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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 cn.hippo4j.auth.service.impl;
+
+import cn.hippo4j.auth.mapper.UserMapper;
+import cn.hippo4j.auth.model.UserInfo;
+import cn.hippo4j.auth.model.biz.user.JwtUser;
+import cn.hippo4j.auth.model.biz.user.LoginUser;
+import cn.hippo4j.auth.service.LdapService;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.stereotype.Service;
+import org.springframework.web.context.request.RequestAttributes;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import java.util.Collections;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * User details service impl.
+ */
+@Slf4j
+@Service
+public class LdapUserDetailsServiceImpl implements UserDetailsService {
+
+ @Value("${hippo4j.core.auth.enabled:true}")
+ private Boolean enableAuthentication;
+
+ @Resource
+ private UserMapper userMapper;
+
+ @Resource
+ private LdapService ldapService;
+
+ @Override
+ public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
+ JwtUser anonymous = dealWithAnonymous();
+ if (!Objects.isNull(anonymous)) {
+ return anonymous;
+ }
+ RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
+ HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(requestAttributes)).getRequest();
+ LoginUser loginUser = (LoginUser) request.getAttribute("loginUser");
+ // ldap authentication
+ ldapService.login(userName, loginUser.getPassword());
+ // By querying the data inventory this user does not exist
+ UserInfo userInfo = userMapper.selectOne(Wrappers.lambdaQuery(UserInfo.class)
+ .eq(UserInfo::getUserName, userName)
+ );
+ // the database does not, create a ROLE_USER permission to the default user, password is empty
+ if (Objects.isNull(userInfo)) {
+ userInfo = new UserInfo();
+ userInfo.setPassword("");
+ userInfo.setUserName(loginUser.getUsername());
+ userInfo.setRole("ROLE_USER");
+ userMapper.insert(userInfo);
+ }
+ // structure jwtUser
+ JwtUser jwtUser = new JwtUser();
+ jwtUser.setId(userInfo.getId());
+ jwtUser.setUsername(userName);
+ jwtUser.setPassword("");
+ Set authorities = Collections.singleton(new SimpleGrantedAuthority(userInfo.getRole() + ""));
+ jwtUser.setAuthorities(authorities);
+ return jwtUser;
+ }
+
+ private JwtUser dealWithAnonymous() {
+ RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
+ if (requestAttributes == null) {
+ return null;
+ }
+ HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
+ LoginUser loginUser = (LoginUser) request.getAttribute("loginUser");
+ if (Objects.isNull(loginUser)) {
+ return null;
+ }
+ if (Boolean.FALSE.equals(enableAuthentication)) {
+ JwtUser jwtUser = new JwtUser();
+ BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
+ jwtUser.setId(1L);
+ jwtUser.setUsername("anonymous");
+ jwtUser.setPassword(bCryptPasswordEncoder.encode(loginUser.getPassword()));
+ Set authorities = Collections.singleton(new SimpleGrantedAuthority("ROLE_ADMIN"));
+ jwtUser.setAuthorities(authorities);
+ return jwtUser;
+ }
+ return null;
+ }
+}
diff --git a/threadpool/server/auth/src/main/java/cn/hippo4j/auth/service/impl/UserDetailsServiceImpl.java b/threadpool/server/auth/src/main/java/cn/hippo4j/auth/service/impl/UserDetailsServiceImpl.java
index dc62a170..960c25ab 100644
--- a/threadpool/server/auth/src/main/java/cn/hippo4j/auth/service/impl/UserDetailsServiceImpl.java
+++ b/threadpool/server/auth/src/main/java/cn/hippo4j/auth/service/impl/UserDetailsServiceImpl.java
@@ -24,11 +24,13 @@ import cn.hippo4j.auth.model.biz.user.LoginUser;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
+import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.stereotype.Service;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
@@ -43,6 +45,7 @@ import java.util.Set;
* User details service impl.
*/
@Slf4j
+@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Value("${hippo4j.core.auth.enabled:true}")
@@ -57,10 +60,20 @@ public class UserDetailsServiceImpl implements UserDetailsService {
if (!Objects.isNull(anonymous)) {
return anonymous;
}
- UserInfo userInfo = userMapper.selectOne(Wrappers.lambdaQuery(UserInfo.class).eq(UserInfo::getUserName, userName));
+ HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
+ LoginUser loginUser = (LoginUser) request.getAttribute("loginUser");
+ String loginPassword = loginUser.getPassword();
+ UserInfo userInfo = userMapper.selectOne(Wrappers.lambdaQuery(UserInfo.class)
+ .eq(UserInfo::getUserName, userName)
+ );
if (Objects.isNull(userInfo)) {
throw new UsernameNotFoundException(userName);
}
+ // Validation password
+ BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
+ if (!bCryptPasswordEncoder.matches(loginPassword, userInfo.getPassword())) {
+ throw new BadCredentialsException(userName + "密码错误,请重新输入");
+ }
JwtUser jwtUser = new JwtUser();
jwtUser.setId(userInfo.getId());
jwtUser.setUsername(userName);
diff --git a/threadpool/server/auth/src/test/java/cn/hippo4j/auth/toolkit/BCryptPasswordEncoderTest.java b/threadpool/server/auth/src/test/java/cn/hippo4j/auth/toolkit/BCryptPasswordEncoderTest.java
new file mode 100644
index 00000000..f1d8a24b
--- /dev/null
+++ b/threadpool/server/auth/src/test/java/cn/hippo4j/auth/toolkit/BCryptPasswordEncoderTest.java
@@ -0,0 +1,20 @@
+package cn.hippo4j.auth.toolkit;
+
+import cn.hippo4j.common.toolkit.Assert;
+import org.junit.Test;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+
+public class BCryptPasswordEncoderTest {
+
+ @Test
+ public void bCryptPasswordEncoderTest() {
+
+ String password = "12345abc";
+
+ BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
+ String encode = bCryptPasswordEncoder.encode(password);
+ boolean matches = bCryptPasswordEncoder.matches(password, encode);
+ Assert.isTrue(matches);
+ }
+
+}
diff --git a/threadpool/server/bootstrap/src/main/resources/ldap-back.properties b/threadpool/server/bootstrap/src/main/resources/ldap-back.properties
new file mode 100644
index 00000000..5b950ede
--- /dev/null
+++ b/threadpool/server/bootstrap/src/main/resources/ldap-back.properties
@@ -0,0 +1,14 @@
+#*************** Ldap Sample Configurations ***************#
+### This configuration file does not take effect
+### Change the LDAP server information to yourself
+### Configure the following configuration file into application.properties
+
+# Ldap Config
+spring.ldap.urls=ldap://127.0.0.1:389
+spring.ldap.base=dc=xxx,dc=com
+spring.ldap.embedded.credential.username=cn=xxxx,dc=xxx,dc=com
+spring.ldap.embedded.credential.password=password
+# Ldap Entry object-class
+spring.ldap.object-class=person
+# Ldap account-attribute CommonName ( cn / uid / username / ... )
+spring.ldap.account-attribute=cn
diff --git a/threadpool/server/common/src/test/java/cn/hippo4j/server/common/base/ServiceExceptionTest.java b/threadpool/server/common/src/test/java/cn/hippo4j/server/common/base/ServiceExceptionTest.java
new file mode 100644
index 00000000..833afe1b
--- /dev/null
+++ b/threadpool/server/common/src/test/java/cn/hippo4j/server/common/base/ServiceExceptionTest.java
@@ -0,0 +1,100 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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 cn.hippo4j.server.common.base;
+
+import cn.hippo4j.common.toolkit.Assert;
+import cn.hippo4j.server.common.base.exception.ErrorCode;
+import cn.hippo4j.server.common.base.exception.ErrorCodeEnum;
+import cn.hippo4j.server.common.base.exception.ServiceException;
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import org.checkerframework.checker.units.qual.A;
+import org.junit.jupiter.api.Test;
+import java.util.Objects;
+
+/**
+ * Service exception test
+ */
+public class ServiceExceptionTest {
+
+ @Test
+ public void ServiceExceptionTest1() {
+ ServiceException serviceException = new ServiceException();
+ Assert.isTrue(Objects.equals(serviceException.getErrorCode().getCode(), "3"));
+ Assert.isTrue(Objects.equals(serviceException.getMessage(), "SERVICE_ERROR"));
+ }
+
+ @Test
+ public void ServiceExceptionTest2() {
+ ErrorCode errorCode = ErrorCodeEnum.LOGIN_TIMEOUT;
+ ServiceException serviceException = new ServiceException(errorCode);
+ Assert.isTrue(Objects.equals(serviceException.getErrorCode().getCode(), ErrorCodeEnum.LOGIN_TIMEOUT.getCode()));
+ Assert.isTrue(Objects.equals(serviceException.getMessage(), ErrorCodeEnum.LOGIN_TIMEOUT.getMessage()));
+ }
+
+ @Test
+ public void ServiceExceptionTest3() {
+ String message = ErrorCodeEnum.SERVICE_ERROR.getMessage();
+ ServiceException serviceException = new ServiceException(message);
+ Assert.isTrue(Objects.equals(serviceException.getMessage(), message));
+ }
+
+ @Test
+ public void ServiceExceptionTest4() {
+ Throwable cause = new Throwable();
+ ServiceException serviceException = new ServiceException(cause);
+ Assert.isTrue(Objects.equals(serviceException.getCause().getMessage(), cause.getMessage()));
+ }
+
+ @Test
+ public void ServiceExceptionTest5() {
+ String message = ErrorCodeEnum.SERVICE_ERROR.getMessage();
+ Throwable cause = new Throwable();
+ ServiceException serviceException = new ServiceException(message, cause);
+ Assert.isTrue(Objects.equals(serviceException.getCause().getMessage(), cause.getMessage()));
+ Assert.isTrue(Objects.equals(serviceException.getMessage(), message));
+ }
+
+ @Test
+ public void ServiceExceptionTest6() {
+ String message = ErrorCodeEnum.SERVICE_ERROR.getMessage();
+ Throwable cause = new Throwable();
+ ServiceException serviceException = new ServiceException(cause, message);
+ Assert.isTrue(Objects.equals(serviceException.getCause().getMessage(), cause.getMessage()));
+ Assert.isTrue(Objects.equals(serviceException.getMessage(), message));
+ }
+
+ @Test
+ public void ServiceExceptionTest7() {
+ Throwable cause = new Throwable();
+ ErrorCode errorCode = ErrorCodeEnum.LOGIN_TIMEOUT;
+ ServiceException serviceException = new ServiceException(cause, errorCode);
+ Assert.isTrue(Objects.equals(serviceException.getCause().getMessage(), cause.getMessage()));
+ Assert.isTrue(Objects.equals(serviceException.getErrorCode().getCode(), ErrorCodeEnum.LOGIN_TIMEOUT.getCode()));
+ }
+
+ @Test
+ public void ServiceExceptionTest8() {
+ Throwable cause = new Throwable();
+ ErrorCode errorCode = ErrorCodeEnum.LOGIN_TIMEOUT;
+ String message = ErrorCodeEnum.SERVICE_ERROR.getMessage();
+ ServiceException serviceException = new ServiceException(message, cause, errorCode);
+ Assert.isTrue(Objects.equals(serviceException.getCause().getMessage(), cause.getMessage()));
+ Assert.isTrue(Objects.equals(serviceException.getErrorCode().getCode(), ErrorCodeEnum.LOGIN_TIMEOUT.getCode()));
+ Assert.isTrue(Objects.equals(serviceException.getMessage(), message));
+ }
+}
diff --git a/threadpool/server/discovery/src/main/java/cn/hippo4j/discovery/core/Lease.java b/threadpool/server/discovery/src/main/java/cn/hippo4j/discovery/core/Lease.java
index 0d796f9e..f1b37ceb 100644
--- a/threadpool/server/discovery/src/main/java/cn/hippo4j/discovery/core/Lease.java
+++ b/threadpool/server/discovery/src/main/java/cn/hippo4j/discovery/core/Lease.java
@@ -47,8 +47,8 @@ public class Lease {
public Lease(T r) {
holder = r;
registrationTimestamp = System.currentTimeMillis();
- lastUpdateTimestamp = registrationTimestamp;
duration = DEFAULT_DURATION_IN_SECS;
+ lastUpdateTimestamp = registrationTimestamp + duration;
}
public void renew() {