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',