diff --git a/dinky-admin/pom.xml b/dinky-admin/pom.xml
index 0621004116..73df3f60ba 100644
--- a/dinky-admin/pom.xml
+++ b/dinky-admin/pom.xml
@@ -32,7 +32,11 @@
provided
+
+ 4.2.0
42.5.1
+ 4.0.1
+
@@ -59,6 +63,44 @@
org.xerial
sqlite-jdbc
+
+ org.pac4j
+ pac4j-springboot
+ ${pac4j.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.apache.logging.log4j
+ log4j-to-slf4j
+
+
+ org.apache.logging.log4j
+ log4j-api
+
+
+
+
+ org.pac4j
+ spring-webmvc-pac4j
+ ${spring-webmvc-pac4j.version}
+
+
+ org.springframework
+ spring-webmvc
+
+
+ org.springframework
+ spring-core
+
+
+ org.springframework
+ spring-aop
+
+
+
org.mitre.dsmiley.httpproxy
smiley-http-proxy-servlet
diff --git a/dinky-admin/src/main/java/org/dinky/configure/AppConfig.java b/dinky-admin/src/main/java/org/dinky/configure/AppConfig.java
index a35c4062b1..35cecd0ea5 100644
--- a/dinky-admin/src/main/java/org/dinky/configure/AppConfig.java
+++ b/dinky-admin/src/main/java/org/dinky/configure/AppConfig.java
@@ -25,8 +25,17 @@
import java.util.Locale;
+import org.pac4j.core.config.Config;
+import org.pac4j.core.http.adapter.JEEHttpActionAdapter;
+import org.pac4j.springframework.annotation.AnnotationConfig;
+import org.pac4j.springframework.component.ComponentConfig;
+import org.pac4j.springframework.web.SecurityInterceptor;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@@ -36,6 +45,7 @@
import cn.dev33.satoken.interceptor.SaInterceptor;
import cn.dev33.satoken.router.SaRouter;
import cn.dev33.satoken.stp.StpUtil;
+import lombok.extern.slf4j.Slf4j;
/**
* AppConfiguration
@@ -43,7 +53,15 @@
* @since 2021/11/28 19:35
*/
@Configuration
+@Slf4j
+@Import({ComponentConfig.class, AnnotationConfig.class})
+@ComponentScan(basePackages = "org.pac4j.springframework.web")
public class AppConfig implements WebMvcConfigurer {
+ @Autowired
+ private Config config;
+
+ @Value("${sso.enabled:false}")
+ private boolean ssoEnabled;
/**
* Cookie
*
@@ -86,11 +104,22 @@ public void addInterceptors(InterceptorRegistry registry) {
}))
.addPathPatterns("/api/**", "/openapi/**")
.excludePathPatterns(
- "/api/login", "/api/ldap/ldapEnableStatus", "/download/**", "/druid/**", "/api/version");
-
+ "/api/sso/ssoEnableStatus",
+ "/api/login",
+ "/api/ldap/ldapEnableStatus",
+ "/download/**",
+ "/druid/**",
+ "/api/version");
+ if (ssoEnabled) {
+ log.info("Load{}", config.getClients().getClients().get(0).getName());
+ registry.addInterceptor(buildInterceptor(
+ config.getClients().getClients().get(0).getName()))
+ .addPathPatterns("/api/sso/login")
+ .addPathPatterns("/api/sso/token");
+ }
registry.addInterceptor(new TenantInterceptor())
.addPathPatterns("/api/**")
- .excludePathPatterns("/api/login", "/api/ldap/ldapEnableStatus")
+ .excludePathPatterns("/api/sso/ssoEnableStatus", "/api/login", "/api/ldap/ldapEnableStatus")
.addPathPatterns("/api/alertGroup/**")
.addPathPatterns("/api/alertHistory/**")
.addPathPatterns("/api/alertInstance/**")
@@ -110,4 +139,8 @@ public void addInterceptors(InterceptorRegistry registry) {
.addPathPatterns("/api/git/**")
.addPathPatterns("/api/jar/*");
}
+
+ private SecurityInterceptor buildInterceptor(final String client) {
+ return new SecurityInterceptor(config, client, JEEHttpActionAdapter.INSTANCE);
+ }
}
diff --git a/dinky-admin/src/main/java/org/dinky/controller/SsoController.java b/dinky-admin/src/main/java/org/dinky/controller/SsoController.java
new file mode 100644
index 0000000000..3b63991f82
--- /dev/null
+++ b/dinky-admin/src/main/java/org/dinky/controller/SsoController.java
@@ -0,0 +1,111 @@
+/*
+ *
+ * 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 org.dinky.controller;
+
+import org.dinky.data.dto.LoginDTO;
+import org.dinky.data.dto.UserDTO;
+import org.dinky.data.enums.Status;
+import org.dinky.data.exception.AuthException;
+import org.dinky.data.result.Result;
+import org.dinky.service.UserService;
+
+import java.util.List;
+
+import javax.annotation.PostConstruct;
+
+import org.pac4j.core.config.Config;
+import org.pac4j.core.profile.CommonProfile;
+import org.pac4j.core.profile.ProfileManager;
+import org.pac4j.springframework.web.CallbackController;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.servlet.ModelAndView;
+import org.springframework.web.servlet.view.RedirectView;
+
+import cn.dev33.satoken.annotation.SaIgnore;
+import io.swagger.annotations.ApiOperation;
+import lombok.NoArgsConstructor;
+
+/**
+ * @author 杨泽翰
+ */
+@RestController
+@NoArgsConstructor
+@RequestMapping("/api/sso")
+public class SsoController {
+ @Value("${sso.redirect}")
+ private String redirect;
+
+ @Value("${sso.enabled:false}")
+ private Boolean ssoEnabled;
+
+ @Value("${pac4j.properties.principalNameAttribute:#{null}}")
+ private String principalNameAttribute;
+
+ @Autowired
+ private Config config;
+
+ @Autowired
+ CallbackController callbackController;
+
+ @Autowired
+ private ProfileManager profileManager;
+
+ @Autowired
+ private UserService userService;
+
+ @PostConstruct
+ protected void afterPropertiesSet() {
+ callbackController.setDefaultUrl(redirect);
+ callbackController.setConfig(config);
+ }
+
+ @GetMapping("/token")
+ public Result ssoToken() throws AuthException {
+ if (!ssoEnabled) {
+ return Result.failed(Status.SINGLE_LOGIN_DISABLED);
+ }
+ List all = profileManager.getAll(true);
+ String username = all.get(0).getAttribute(principalNameAttribute).toString();
+ if (username == null) {
+ throw new AuthException(Status.NOT_MATCHED_PRINCIPAL_NAME_ATTRIBUTE);
+ }
+ LoginDTO loginDTO = new LoginDTO();
+ loginDTO.setUsername(username);
+ loginDTO.setSsoLogin(true);
+ return userService.loginUser(loginDTO);
+ }
+
+ @GetMapping("/login")
+ public ModelAndView ssoLogin() {
+ RedirectView redirectView = new RedirectView(redirect);
+ return new ModelAndView(redirectView);
+ }
+
+ @GetMapping("/ssoEnableStatus")
+ @SaIgnore
+ @ApiOperation("Get SSO enable status")
+ public Result ssoStatus() {
+ return Result.succeed(ssoEnabled);
+ }
+}
diff --git a/dinky-admin/src/main/java/org/dinky/data/dto/LoginDTO.java b/dinky-admin/src/main/java/org/dinky/data/dto/LoginDTO.java
index 5794585d4a..125214b2af 100644
--- a/dinky-admin/src/main/java/org/dinky/data/dto/LoginDTO.java
+++ b/dinky-admin/src/main/java/org/dinky/data/dto/LoginDTO.java
@@ -19,6 +19,8 @@
package org.dinky.data.dto;
+import org.dinky.data.enums.UserType;
+
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
@@ -48,4 +50,18 @@ public class LoginDTO {
@ApiModelProperty(value = "ldapLogin", required = true, example = "false", dataType = "Boolean")
private boolean ldapLogin;
+
+ @ApiModelProperty(value = "ssoLogin", required = true, example = "false", dataType = "Boolean")
+ private boolean ssoLogin;
+
+ public UserType getLoginType() {
+ if (isLdapLogin()) {
+ return UserType.LDAP;
+ }
+ if (isSsoLogin()) {
+ return UserType.SSO;
+ }
+
+ return UserType.LOCAL;
+ }
}
diff --git a/dinky-admin/src/main/java/org/dinky/service/impl/UserServiceImpl.java b/dinky-admin/src/main/java/org/dinky/service/impl/UserServiceImpl.java
index e280b9f510..4487703f41 100644
--- a/dinky-admin/src/main/java/org/dinky/service/impl/UserServiceImpl.java
+++ b/dinky-admin/src/main/java/org/dinky/service/impl/UserServiceImpl.java
@@ -63,6 +63,7 @@
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;
+import org.pac4j.core.profile.ProfileManager;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -86,6 +87,7 @@
@RequiredArgsConstructor
@Slf4j
public class UserServiceImpl extends SuperServiceImpl implements UserService {
+ private final ProfileManager profileManager;
private static final String DEFAULT_PASSWORD = "123456";
@@ -177,9 +179,19 @@ public Boolean removeUser(Integer id) {
@Override
public Result loginUser(LoginDTO loginDTO) {
User user = null;
+
try {
- // Determine the login method (LDAP or local) based on the flag in loginDTO
- user = loginDTO.isLdapLogin() ? ldapLogin(loginDTO) : localLogin(loginDTO);
+
+ switch (loginDTO.getLoginType()) {
+ case LDAP:
+ user = ldapLogin(loginDTO);
+ break;
+ case SSO:
+ user = ssoLogin(loginDTO);
+ break;
+ default:
+ user = localLogin(loginDTO);
+ }
} catch (AuthException e) {
// Handle authentication exceptions and return the corresponding error status
return Result.authorizeFailed(e.getStatus());
@@ -210,6 +222,36 @@ public Result loginUser(LoginDTO loginDTO) {
return Result.succeed(userInfo, Status.LOGIN_SUCCESS);
}
+ private User ssoLogin(LoginDTO loginDTO) throws AuthException {
+ // Get user from local database by username
+ User user = getUserByUsername(loginDTO.getUsername());
+ if (Asserts.isNull(user)) {
+ // User doesn't exist,Create a user
+ // Get default tenant from system configuration
+ String defaultTeantCode =
+ SystemConfiguration.getInstances().getLdapDefaultTeant().getValue();
+ Tenant tenant = tenantService.getTenantByTenantCode(defaultTeantCode);
+ User userForm = new User();
+ userForm.setUsername(loginDTO.getUsername());
+ userForm.setNickname(loginDTO.getUsername());
+ userForm.setUserType(UserType.SSO.getCode());
+ userForm.setEnabled(true);
+ userForm.setSuperAdminFlag(false);
+ userForm.setIsDelete(false);
+ this.getBaseMapper().insert(userForm);
+ // Assign the user to the default tenant
+ List userIds = getUserIdsByTenantId(tenant.getId());
+ userIds.add(userForm.getId());
+ tenantService.assignUserToTenant(new AssignUserToTenantDTO(tenant.getId(), userIds));
+ return userForm;
+ } else {
+ if (user.getUserType() != UserType.SSO.getCode()) {
+ throw new AuthException(Status.USER_TYPE_ERROR);
+ }
+ }
+ return user;
+ }
+
private void upsertToken(UserDTO userInfo) {
Integer userId = userInfo.getUser().getId();
SysToken sysToken = new SysToken();
@@ -443,6 +485,10 @@ public void buildRowPermission() {
@Override
public void outLogin() {
+ if (profileManager != null) {
+ profileManager.logout();
+ }
+
StpUtil.logout(StpUtil.getLoginIdAsInt());
}
diff --git a/dinky-admin/src/main/resources/application-mysql.yml b/dinky-admin/src/main/resources/application-mysql.yml
index 6c71564216..d0ac8fda40 100644
--- a/dinky-admin/src/main/resources/application-mysql.yml
+++ b/dinky-admin/src/main/resources/application-mysql.yml
@@ -20,4 +20,4 @@ spring:
url: jdbc:mysql://${MYSQL_ADDR:127.0.0.1:3306}/${MYSQL_DATABASE:dinky}?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&zeroDateTimeBehavior=convertToNull&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
username: ${MYSQL_USERNAME:dinky}
password: ${MYSQL_PASSWORD:dinky}
- driver-class-name: com.mysql.cj.jdbc.Driver
+ driver-class-name: com.mysql.cj.jdbc.Driver
\ No newline at end of file
diff --git a/dinky-admin/src/main/resources/application.yml b/dinky-admin/src/main/resources/application.yml
index a8c537a333..d11263c1c8 100644
--- a/dinky-admin/src/main/resources/application.yml
+++ b/dinky-admin/src/main/resources/application.yml
@@ -165,3 +165,28 @@ knife4j:
crypto:
enabled: false
encryption-password:
+
+---
+#################################################################################################################
+################################################# SSO Config ####################################################
+#################################################################################################################
+#see https://github.com/pac4j/spring-webmvc-pac4j-boot-demo/blob/master/src/main/resources/application.properties
+sso:
+ enabled: false #enable sso default false
+ redirect: http://localhost:8000/#/user/login?from=sso #Front-end address
+
+---
+#################################################################################################################
+################################################# pac4j Config ####################################################
+#################################################################################################################
+pac4j:
+ callbackUrl: http://localhost:${server.port}/callback # The callback URL
+ # Put all parameters under `properties`
+ # Check supported sso config parameters for different authentication clients from the below link
+ # https://github.com/pac4j/pac4j/blob/master/documentation/docs/config-module.md
+ properties:
+ principalNameAttribute: login #Authenticate user principal
+ github.id:
+ github.secret:
+
+
diff --git a/dinky-common/src/main/java/org/dinky/data/enums/Status.java b/dinky-common/src/main/java/org/dinky/data/enums/Status.java
index e9d2cee6f5..d96ec6f804 100644
--- a/dinky-common/src/main/java/org/dinky/data/enums/Status.java
+++ b/dinky-common/src/main/java/org/dinky/data/enums/Status.java
@@ -255,6 +255,12 @@ public enum Status {
DS_TASK_TYPE_NOT_SUPPORT(17008, "ds.task.type.not.support"),
DS_WORK_FLOW_DEFINITION_NOT_EXIST(17009, "ds.work.flow.definition.not.exist"),
DS_PROCESS_DEFINITION_UPDATE(17010, "ds.work.flow.definition.process.update"),
+ /**
+ * SSO About *
+ */
+ USER_TYPE_ERROR(22001, "sso.user.type.error"),
+ NOT_MATCHED_PRINCIPAL_NAME_ATTRIBUTE(22002, "sso.user.type.error"),
+ SINGLE_LOGIN_DISABLED(22003, "sso.not.enabled"),
/**
* LDAP About *
@@ -461,6 +467,7 @@ public enum Status {
SYS_FLINK_SETTINGS_FLINK_HISTORY_SERVER_ARCHIVE_REFRESH_INTERVAL_NOTE(
205, "sys.flink.settings.flinkHistoryServerArchiveRefreshInterval.note"),
;
+
private final int code;
private final String key;
diff --git a/dinky-common/src/main/java/org/dinky/data/enums/UserType.java b/dinky-common/src/main/java/org/dinky/data/enums/UserType.java
index 68241c259c..50b0b7350e 100644
--- a/dinky-common/src/main/java/org/dinky/data/enums/UserType.java
+++ b/dinky-common/src/main/java/org/dinky/data/enums/UserType.java
@@ -21,7 +21,8 @@
public enum UserType {
LDAP(1, "LDAP"),
- LOCAL(0, "LOCAL");
+ LOCAL(0, "LOCAL"),
+ SSO(2, "SSO");
private final int code;
private final String type;
diff --git a/dinky-web/src/locales/en-US/pages.ts b/dinky-web/src/locales/en-US/pages.ts
index ac6bd8b55f..e9976ebfd1 100644
--- a/dinky-web/src/locales/en-US/pages.ts
+++ b/dinky-web/src/locales/en-US/pages.ts
@@ -365,6 +365,7 @@ export default {
'login.chooseTenantFailed': 'Tenant selection failed, please check. . . ',
'login.chooseTenantSuccess': '{msg}, Use [ {tenantCode} ] to enter the system, loading. . .',
'login.ldapLogin': 'LDAP Login',
+ 'login.ssoLogin': 'SSO Login',
'login.notbindtenant': 'You have not bound a tenant, please contact the administrator',
'login.password.placeholder': 'Password',
'login.password.required': 'Please input your password!',
diff --git a/dinky-web/src/locales/zh-CN/pages.ts b/dinky-web/src/locales/zh-CN/pages.ts
index 3f9080716c..2b2984830d 100644
--- a/dinky-web/src/locales/zh-CN/pages.ts
+++ b/dinky-web/src/locales/zh-CN/pages.ts
@@ -318,6 +318,7 @@ export default {
'login.chooseTenantFailed': '租户选择失败,请检查...',
'login.chooseTenantSuccess': '{msg},使用【 {tenantCode} 】进入系统,加载中...',
'login.ldapLogin': 'LDAP登录',
+ 'login.ssoLogin': 'SSO 登录',
'login.notbindtenant': '您还没有绑定租户,请联系管理员',
'login.password.placeholder': '密码',
'login.password.required': '密码是必填项!',
diff --git a/dinky-web/src/pages/AuthCenter/User/components/constants.tsx b/dinky-web/src/pages/AuthCenter/User/components/constants.tsx
index 71580b4be5..fb99ec40e4 100644
--- a/dinky-web/src/pages/AuthCenter/User/components/constants.tsx
+++ b/dinky-web/src/pages/AuthCenter/User/components/constants.tsx
@@ -20,5 +20,5 @@
export const UserType = { LOCAL: 0, LDAP: 1 };
export const USER_TYPE_ENUM = () => {
- return { 0: 'LOCAL', 1: 'LDAP' };
+ return { 0: 'LOCAL', 1: 'LDAP',2:"SSO" };
};
diff --git a/dinky-web/src/pages/Other/Login/LoginForm/index.tsx b/dinky-web/src/pages/Other/Login/LoginForm/index.tsx
index 8b05465ab6..31627bd200 100644
--- a/dinky-web/src/pages/Other/Login/LoginForm/index.tsx
+++ b/dinky-web/src/pages/Other/Login/LoginForm/index.tsx
@@ -40,8 +40,15 @@ const LoginForm: React.FC = (props) => {
const [submitting, setSubmitting] = useState(false);
const [ldapEnabled, setLdapEnabled] = useState(false);
+ const [ssoEnabled, setSsoEnabled] = useState(false);
useEffect(() => {
+ getData(API_CONSTANTS.GET_SSO_ENABLE).then(
+ (res) => {
+ setSsoEnabled(res.data);
+ },
+ (err) => console.error(err)
+ );
getData(API_CONSTANTS.GET_LDAP_ENABLE).then(
(res) => {
setLdapEnabled(res.data);
@@ -98,6 +105,11 @@ const LoginForm: React.FC = (props) => {
{l('login.ldapLogin')}
+
+
+ {l('login.ssoLogin')}
+
+
>
);
@@ -225,3 +237,4 @@ const LoginForm: React.FC = (props) => {
};
export default LoginForm;
+
diff --git a/dinky-web/src/pages/Other/Login/index.tsx b/dinky-web/src/pages/Other/Login/index.tsx
index 4a0ee89c74..0ebd4983fd 100644
--- a/dinky-web/src/pages/Other/Login/index.tsx
+++ b/dinky-web/src/pages/Other/Login/index.tsx
@@ -21,7 +21,7 @@ import Footer from '@/components/Footer';
import ChooseModal from '@/pages/Other/Login/ChooseModal';
import { gotoRedirectUrl, initSomeThing, redirectToLogin } from '@/pages/Other/Login/function';
import LangSwitch from '@/pages/Other/Login/LangSwitch';
-import { chooseTenantSubmit, login, queryDataByParams } from '@/services/BusinessCrud';
+import {chooseTenantSubmit, login, queryDataByParams, ssoToken} from '@/services/BusinessCrud';
import { API } from '@/services/data';
import { API_CONSTANTS } from '@/services/endpoints';
import { SaTokenInfo, UserBaseInfo } from '@/types/AuthCenter/data.d';
@@ -35,6 +35,8 @@ import React, { useEffect, useState } from 'react';
import HelmetTitle from './HelmetTitle';
import LoginForm from './LoginForm';
import { TOKEN_KEY } from '@/services/constants';
+import {getData} from "@/services/api";
+import {createSearchParams} from "@@/exports";
const Login: React.FC = () => {
const [submitting, setSubmitting] = useState(false);
@@ -51,6 +53,32 @@ const Login: React.FC = () => {
height: '100%'
};
});
+ useEffect(() => {
+ if (location.hash==("#/user/login?from=sso")){
+ ssoToken().then(
+ res => {
+ if (res) {
+ setLocalStorageOfToken(JSON.stringify(res));
+ } else {
+ // 如果没有获取到token信息,直接跳转到登录页
+ redirectToLogin();
+ }
+ setInitialState((s) => ({ ...s, currentUser: res.data }));
+ SuccessMessageAsync(l('login.result', '', { msg: res.msg, time: res.time }));
+ const tenantList: UserBaseInfo.Tenant[] = res.data.tenantList;
+ assertTenant(tenantList);
+ if (tenantList && tenantList.length > 1) {
+
+ handleTenantVisible(true);
+ } else {
+ singleTenant(tenantList);
+ }
+ }
+ )
+ }
+
+
+ }, []);
const fetchUserInfo = async () => {
const userInfo = await initialState?.fetchUserInfo?.();
diff --git a/dinky-web/src/services/BusinessCrud.ts b/dinky-web/src/services/BusinessCrud.ts
index d1131f64a7..757c7d5d51 100644
--- a/dinky-web/src/services/BusinessCrud.ts
+++ b/dinky-web/src/services/BusinessCrud.ts
@@ -65,6 +65,20 @@ export async function login(body: API.LoginParams, options?: { [key: string]: an
...(options ?? {})
});
}
+/**
+ * user sso login
+ */
+export async function ssoToken() {
+ return request(API_CONSTANTS.SSO_TOKEN, {
+ method: METHOD_CONSTANTS.GET,
+ headers: {
+ CONTENT_TYPE: APPLICATION_JSON
+ }
+ });
+}
+
+
+
/**
* choose tenant
diff --git a/dinky-web/src/services/endpoints.tsx b/dinky-web/src/services/endpoints.tsx
index b42731dbd5..d1110834ea 100644
--- a/dinky-web/src/services/endpoints.tsx
+++ b/dinky-web/src/services/endpoints.tsx
@@ -231,6 +231,10 @@ export enum API_CONSTANTS {
LDAP_LIST_USER = '/api/ldap/listUser',
LDAP_IMPORT_USERS = '/api/ldap/importUsers',
+ // ----------------------------------------- sso ------------------------------------
+ GET_SSO_ENABLE = '/api/sso/ssoEnableStatus',
+ SSO_TOKEN = '/api/sso/token',
+ SSO_LOGIN = '/api/sso/login',
// ------------------------------------ home ------------------------------------
GET_RESOURCE_OVERVIEW = '/api/home/getResourceOverview',
GET_JOB_STATUS_OVERVIEW = '/api/home/getJobStatusOverview',