Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature-1620][admin] Integrate Casdoor for SSO #2250

Open
wants to merge 1 commit into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions dinky-admin/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,10 @@
<artifactId>jsqlparser</artifactId>
<version>4.4</version>
</dependency>
<dependency>
<groupId>org.casbin</groupId>
<artifactId>casdoor-spring-boot-starter</artifactId>
</dependency>

<!-- test dependencies -->
<dependency>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -56,6 +59,8 @@ public class AdminController {

private final UserService userService;

private final CasdoorAuthService casdoorAuthService;

/**
* user login
*
Expand All @@ -68,6 +73,33 @@ public Result<UserDTO> 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<String> 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<UserDTO> loginWithCasdoor(@RequestParam("code") String code, @RequestParam("state") String state) {
return userService.loginCasdoorUser(code, state);
}

/**
* user logout
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
package org.dinky.data.enums;

public enum UserType {
CASDOOR(2, "CASDOOR"),
LDAP(1, "LDAP"),
LOCAL(0, "LOCAL");

Expand Down
9 changes: 9 additions & 0 deletions dinky-admin/src/main/java/org/dinky/service/UserService.java
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,15 @@ public interface UserService extends ISuperService<User> {
*/
Result<UserDTO> loginUser(LoginDTO loginDTO);

/**
* loginCasdoorUser
*
* @param code
* @param state
* @return {@link Result}{@link UserDTO} obtain the user's UserDTO
*/
Result<UserDTO> loginCasdoorUser(String code, String state);

/**
* getUserByUsername
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -95,6 +97,8 @@ public class UserServiceImpl extends SuperServiceImpl<UserMapper, User> implemen

private final MenuService menuService;

private final CasdoorAuthService casdoorAuthService;

@Override
public Result<Void> registerUser(User user) {
User userByUsername = getUserByUsername(user.getUsername());
Expand Down Expand Up @@ -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<UserDTO> loginUser(LoginDTO loginDTO) {
Expand Down Expand Up @@ -187,6 +191,77 @@ public Result<UserDTO> loginUser(LoginDTO loginDTO) {
return Result.succeed(userInfo, Status.LOGIN_SUCCESS);
}

@Override
public Result<UserDTO> 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<Integer> 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());
Expand Down
2 changes: 2 additions & 0 deletions dinky-common/src/main/java/org/dinky/data/enums/Status.java
Original file line number Diff line number Diff line change
Expand Up @@ -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默认租户不存在"),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe better to remove Casdoor ? eg : The default tenant does not exist
Or use an existing USER_NOT_BINDING_TENANT instead


// role
ROLE_ALREADY_EXISTS(10101, "Role Already Exists", "角色已存在"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,11 @@ public static Configuration.OptionBuilder key(String key) {
private final Configuration<Boolean> ldapEnable =
key("ldap.settings.enable").booleanType().defaultValue(false).note("LDAP ON-OFF");

private final Configuration<String> casdoorDefaultTeant = key("casdoor.settings.defaultTeant")
.stringType()
.defaultValue("DefaultTenant")
.note("casdoor default default teant code");

private final Configuration<Boolean> metricsSysEnable = key("metrics.settings.sys.enable")
.booleanType()
.defaultValue(false)
Expand Down
6 changes: 6 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@
<spotless.ratchetFrom>origin/dev</spotless.ratchetFrom>
<spotless.version>2.27.1</spotless.version>
<spring-boot-dependencies.version>2.7.11</spring-boot-dependencies.version>
<spring.casdoor.version>1.6.0</spring.casdoor.version>
<spring.ldap.version>3.1.0</spring.ldap.version>
<target.java.version>8</target.java.version>
<testcontainers.version>1.16.2</testcontainers.version>
Expand Down Expand Up @@ -642,6 +643,11 @@
<artifactId>spring-boot-starter-data-ldap</artifactId>
<version>${spring.ldap.version}</version>
</dependency>
<dependency>
<groupId>org.casbin</groupId>
<artifactId>casdoor-spring-boot-starter</artifactId>
<version>${spring.casdoor.version}</version>
</dependency>

</dependencies>
</dependencyManagement>
Expand Down
2 changes: 1 addition & 1 deletion script/sql/dinky-mysql.sql
Original file line number Diff line number Diff line change
Expand Up @@ -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)',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this mean that SSO login is a standalone login method and not a replacement for existing local logins?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Login type Casdoor is special, I think oidc (oauth2) is better than 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',
Expand Down