From 07f62513d1750386c6fa21056c4770d47037790f Mon Sep 17 00:00:00 2001
From: gaopeng <450498893@qq.com>
Date: Mon, 21 Aug 2023 09:40:45 +0800
Subject: [PATCH] [Feature-1620][feat] Integrate Casdoor for SSO
add casdoor login support SSO
This closes #1620
---
dinky-admin/pom.xml | 4 +
.../java/org/dinky/configure/AppConfig.java | 1 +
.../org/dinky/controller/AdminController.java | 32 ++++++++
.../java/org/dinky/data/enums/UserType.java | 1 +
.../java/org/dinky/service/UserService.java | 9 +++
.../dinky/service/impl/UserServiceImpl.java | 77 ++++++++++++++++++-
.../java/org/dinky/data/enums/Status.java | 2 +
.../dinky/data/model/SystemConfiguration.java | 5 ++
pom.xml | 6 ++
script/sql/dinky-mysql.sql | 2 +-
10 files changed, 137 insertions(+), 2 deletions(-)
diff --git a/dinky-admin/pom.xml b/dinky-admin/pom.xml
index c01a803486..fe06ed3847 100644
--- a/dinky-admin/pom.xml
+++ b/dinky-admin/pom.xml
@@ -243,6 +243,10 @@
jsqlparser
4.4
+
+ org.casbin
+ casdoor-spring-boot-starter
+
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 f483c64e7c..d64b948737 100644
--- a/dinky-admin/src/main/java/org/dinky/configure/AppConfig.java
+++ b/dinky-admin/src/main/java/org/dinky/configure/AppConfig.java
@@ -107,6 +107,7 @@ public void addInterceptors(InterceptorRegistry registry) {
.addPathPatterns("/api/**")
.excludePathPatterns(
"/api/login", "/api/ldap/ldapEnableStatus",
+ "/api/getCasdoorUrl", "/api/loginWithCasdoor",
"/druid/**", "/openapi/**");
registry.addInterceptor(new TenantInterceptor())
diff --git a/dinky-admin/src/main/java/org/dinky/controller/AdminController.java b/dinky-admin/src/main/java/org/dinky/controller/AdminController.java
index 99db7e463a..451332bfcc 100644
--- a/dinky-admin/src/main/java/org/dinky/controller/AdminController.java
+++ b/dinky-admin/src/main/java/org/dinky/controller/AdminController.java
@@ -27,6 +27,9 @@
import org.dinky.service.UserService;
import org.dinky.utils.I18nMsgUtils;
+import javax.servlet.http.HttpServletRequest;
+
+import org.casbin.casdoor.service.CasdoorAuthService;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
@@ -56,6 +59,8 @@ public class AdminController {
private final UserService userService;
+ private final CasdoorAuthService casdoorAuthService;
+
/**
* user login
*
@@ -68,6 +73,33 @@ public Result login(@RequestBody LoginDTO loginDTO) {
return userService.loginUser(loginDTO);
}
+ /**
+ * get casdoor login url
+ *
+ * @param request
+ * @return redirect address
+ */
+ @PostMapping("/getCasdoorUrl")
+ @ApiOperation("Get Casdoor Url")
+ public Result getCasdoorUrl(HttpServletRequest request) {
+ String origin = request.getParameter("origin");
+ String url = casdoorAuthService.getSigninUrl(origin);
+ return Result.succeed(url, "Get Url successful");
+ }
+
+ /**
+ * user login with casdoor
+ *
+ * @param code
+ * @param state
+ * @return {@link Result}{@link UserDTO} obtain the user's UserDTO
+ */
+ @PostMapping("/loginWithCasdoor")
+ @ApiOperation("loginWithCasdoor")
+ public Result loginWithCasdoor(@RequestParam("code") String code, @RequestParam("state") String state) {
+ return userService.loginCasdoorUser(code, state);
+ }
+
/**
* user logout
*
diff --git a/dinky-admin/src/main/java/org/dinky/data/enums/UserType.java b/dinky-admin/src/main/java/org/dinky/data/enums/UserType.java
index 68241c259c..fe1c6eb875 100644
--- a/dinky-admin/src/main/java/org/dinky/data/enums/UserType.java
+++ b/dinky-admin/src/main/java/org/dinky/data/enums/UserType.java
@@ -20,6 +20,7 @@
package org.dinky.data.enums;
public enum UserType {
+ CASDOOR(2, "CASDOOR"),
LDAP(1, "LDAP"),
LOCAL(0, "LOCAL");
diff --git a/dinky-admin/src/main/java/org/dinky/service/UserService.java b/dinky-admin/src/main/java/org/dinky/service/UserService.java
index a47fcf973c..b3c76a432d 100644
--- a/dinky-admin/src/main/java/org/dinky/service/UserService.java
+++ b/dinky-admin/src/main/java/org/dinky/service/UserService.java
@@ -79,6 +79,15 @@ public interface UserService extends ISuperService {
*/
Result loginUser(LoginDTO loginDTO);
+ /**
+ * loginCasdoorUser
+ *
+ * @param code
+ * @param state
+ * @return {@link Result}{@link UserDTO} obtain the user's UserDTO
+ */
+ Result loginCasdoorUser(String code, String state);
+
/**
* getUserByUsername
*
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 b44c910d81..af6a47a1f2 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
@@ -56,6 +56,8 @@
import java.util.List;
import java.util.stream.Collectors;
+import org.casbin.casdoor.entity.CasdoorUser;
+import org.casbin.casdoor.service.CasdoorAuthService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -95,6 +97,8 @@ public class UserServiceImpl extends SuperServiceImpl implemen
private final MenuService menuService;
+ private final CasdoorAuthService casdoorAuthService;
+
@Override
public Result registerUser(User user) {
User userByUsername = getUserByUsername(user.getUsername());
@@ -152,7 +156,7 @@ public Boolean removeUser(Integer id) {
*
* @param loginDTO a user based on the provided login credentials.
* @return a Result object containing the user information if the login is successful, or an
- * appropriate error status if the login fails.
+ * appropriate error status if the login fails.
*/
@Override
public Result loginUser(LoginDTO loginDTO) {
@@ -187,6 +191,77 @@ public Result loginUser(LoginDTO loginDTO) {
return Result.succeed(userInfo, Status.LOGIN_SUCCESS);
}
+ @Override
+ public Result loginCasdoorUser(String code, String state) {
+ User user = null;
+ try {
+ user = casdoorLogin(code, state);
+ } catch (AuthException e) {
+ return Result.failed(e.getStatus() + e.getMessage());
+ }
+
+ // Check if the user is enabled
+ if (!user.getEnabled()) {
+ loginLogService.saveLoginLog(user, Status.USER_DISABLED_BY_ADMIN);
+ return Result.failed(Status.USER_DISABLED_BY_ADMIN);
+ }
+
+ UserDTO userInfo = refreshUserInfo(user);
+ if (Asserts.isNullCollection(userInfo.getTenantList())) {
+ loginLogService.saveLoginLog(user, Status.USER_NOT_BINDING_TENANT);
+ return Result.failed(Status.USER_NOT_BINDING_TENANT);
+ }
+
+ // Perform login using StpUtil (Assuming it handles the session management)
+ StpUtil.login(user.getId());
+
+ // save login log record
+ loginLogService.saveLoginLog(user, Status.LOGIN_SUCCESS);
+
+ // Return the user information along with a success status
+ return Result.succeed(userInfo, Status.LOGIN_SUCCESS);
+ }
+
+ private User casdoorLogin(String code, String state) throws AuthException {
+ User userFromCasdoor = null;
+ String token = casdoorAuthService.getOAuthToken(code, state);
+ CasdoorUser casdoorUser = casdoorAuthService.parseJwtToken(token);
+ if (casdoorUser.getName() != null) {
+ userFromCasdoor = new User();
+ userFromCasdoor.setUsername(casdoorUser.getName());
+ userFromCasdoor.setNickname(casdoorUser.getDisplayName());
+ } else {
+ throw new AuthException(Status.STATE_CODE_ERROR);
+ }
+ // Get user from local database
+ User userFromLocal = getUserByUsername(casdoorUser.getName());
+
+ if (Asserts.isNull(userFromLocal)) {
+ String defaultTeantCode =
+ SystemConfiguration.getInstances().getCasdoorDefaultTeant().getValue();
+ Tenant tenant = tenantService.getTenantByTenantCode(defaultTeantCode);
+ if (Asserts.isNull(tenant)) {
+ loginLogService.saveLoginLog(userFromLocal, Status.CASDOOR_DEFAULT_TENANT_NOFOUND);
+ throw new AuthException(Status.CASDOOR_DEFAULT_TENANT_NOFOUND);
+ }
+
+ // Update Casdoor user properties and save
+ userFromCasdoor.setUserType(UserType.CASDOOR.getCode());
+ userFromCasdoor.setEnabled(true);
+ userFromCasdoor.setSuperAdminFlag(false);
+ userFromCasdoor.setIsDelete(false);
+ save(userFromCasdoor);
+
+ // Assign the user to the default tenant
+ List userIds = getUserIdsByTenantId(tenant.getId());
+ User user = getUserByUsername(casdoorUser.getName());
+ userIds.add(user.getId());
+ tenantService.assignUserToTenant(new AssignUserToTenantParams(tenant.getId(), userIds));
+ return user;
+ }
+ return userFromLocal;
+ }
+
private User localLogin(LoginDTO loginDTO) throws AuthException {
// Get user from local database by username
User user = getUserByUsername(loginDTO.getUsername());
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 90751cd33e..2c1e375b84 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
@@ -109,6 +109,8 @@ public enum Status {
KICK_OUT(10024, "token has been kicked offline", "token 已被踢下线"),
TOKEN_FREEZED(10025, "token has been frozen", "token 已被冻结"),
NO_PREFIX(10026, "The token was not submitted according to the specified prefix", "未按照指定前缀提交 token"),
+ STATE_CODE_ERROR(10027, "state inconsistency or state and code not pair", "状态码前后不一致或状态码和code不匹配"),
+ CASDOOR_DEFAULT_TENANT_NOFOUND(10028, "The Casdoor default tenant does not exist", "casdoor默认租户不存在"),
// role
ROLE_ALREADY_EXISTS(10101, "Role Already Exists", "角色已存在"),
diff --git a/dinky-common/src/main/java/org/dinky/data/model/SystemConfiguration.java b/dinky-common/src/main/java/org/dinky/data/model/SystemConfiguration.java
index c3679d0c9e..ac1a41bec5 100644
--- a/dinky-common/src/main/java/org/dinky/data/model/SystemConfiguration.java
+++ b/dinky-common/src/main/java/org/dinky/data/model/SystemConfiguration.java
@@ -158,6 +158,11 @@ public static Configuration.OptionBuilder key(String key) {
private final Configuration ldapEnable =
key("ldap.settings.enable").booleanType().defaultValue(false).note("LDAP ON-OFF");
+ private final Configuration casdoorDefaultTeant = key("casdoor.settings.defaultTeant")
+ .stringType()
+ .defaultValue("DefaultTenant")
+ .note("casdoor default default teant code");
+
private final Configuration metricsSysEnable = key("metrics.settings.sys.enable")
.booleanType()
.defaultValue(false)
diff --git a/pom.xml b/pom.xml
index 6cf1dcd40f..70b387dd84 100644
--- a/pom.xml
+++ b/pom.xml
@@ -111,6 +111,7 @@
origin/dev
2.27.1
2.7.11
+ 1.6.0
3.1.0
8
1.16.2
@@ -642,6 +643,11 @@
spring-boot-starter-data-ldap
${spring.ldap.version}
+
+ org.casbin
+ casdoor-spring-boot-starter
+ ${spring.casdoor.version}
+
diff --git a/script/sql/dinky-mysql.sql b/script/sql/dinky-mysql.sql
index edf6592c6b..b677000ab3 100644
--- a/script/sql/dinky-mysql.sql
+++ b/script/sql/dinky-mysql.sql
@@ -1105,7 +1105,7 @@ CREATE TABLE `dinky_sys_login_log` (
`id` int NOT NULL AUTO_INCREMENT COMMENT 'key',
`user_id` int NOT NULL COMMENT 'user id',
`username` varchar(60) COLLATE utf8mb4_general_ci NOT NULL COMMENT 'username',
- `login_type` int NOT NULL COMMENT 'login type(0:LOCAL,1:LDAP)',
+ `login_type` int NOT NULL COMMENT 'login type(0:LOCAL,1:ldap,2:Casdoor)',
`ip` varchar(40) COLLATE utf8mb4_general_ci NOT NULL COMMENT 'ip addr',
`status` int NOT NULL COMMENT 'login status',
`msg` text COLLATE utf8mb4_general_ci NOT NULL COMMENT 'status msg',