mirror of
https://github.com/PlayEdu/PlayEdu
synced 2025-06-07 09:44:03 +08:00
parent
069c3e4cc9
commit
d5e410cb1f
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@ -4,6 +4,8 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- dev
|
||||
- 'feat/**'
|
||||
|
||||
env:
|
||||
IMAGE_FQDN: registry.cn-hangzhou.aliyuncs.com/playedu/api
|
||||
|
@ -4,7 +4,7 @@ WORKDIR /app
|
||||
|
||||
COPY . /app
|
||||
|
||||
RUN /app/docker-build.sh
|
||||
RUN /app/mvnw -Dmaven.test.skip=true clean package
|
||||
|
||||
FROM eclipse-temurin:17
|
||||
|
||||
@ -14,7 +14,7 @@ WORKDIR /app
|
||||
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
|
||||
|
||||
# 将指定目录下的jar包复制到docker容器的/目录下
|
||||
COPY --from=builder /app/target/playedu-api-*.jar /app/app.jar
|
||||
COPY --from=builder /app/playedu-api/target/playedu-api.jar /app/app.jar
|
||||
|
||||
# 声明服务运行在8080端口
|
||||
EXPOSE 9898
|
||||
|
@ -1,7 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
echo '开始打包...'
|
||||
|
||||
export MAVEN_OPTS=-Dmaven.test.skip=true
|
||||
|
||||
/app/mvnw clean package
|
143
playedu-api/src/main/java/xyz/playedu/api/bus/LoginBus.java
Normal file
143
playedu-api/src/main/java/xyz/playedu/api/bus/LoginBus.java
Normal file
@ -0,0 +1,143 @@
|
||||
/*
|
||||
* Copyright (C) 2023 杭州白书科技有限公司
|
||||
*
|
||||
* Licensed 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 xyz.playedu.api.bus;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import xyz.playedu.api.event.UserLoginEvent;
|
||||
import xyz.playedu.common.domain.LdapUser;
|
||||
import xyz.playedu.common.domain.User;
|
||||
import xyz.playedu.common.exception.ServiceException;
|
||||
import xyz.playedu.common.service.*;
|
||||
import xyz.playedu.common.util.HelperUtil;
|
||||
import xyz.playedu.common.util.IpUtil;
|
||||
import xyz.playedu.common.util.RequestUtil;
|
||||
import xyz.playedu.common.util.StringUtil;
|
||||
import xyz.playedu.common.util.ldap.LdapTransformUser;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
@Component
|
||||
@Slf4j
|
||||
public class LoginBus {
|
||||
|
||||
@Autowired private FrontendAuthService authService;
|
||||
|
||||
@Autowired private DepartmentService departmentService;
|
||||
|
||||
@Autowired private LdapUserService ldapUserService;
|
||||
|
||||
@Autowired private UserService userService;
|
||||
|
||||
@Autowired private AppConfigService appConfigService;
|
||||
|
||||
@Autowired private ApplicationContext ctx;
|
||||
|
||||
public HashMap<String, Object> tokenByUser(User user) {
|
||||
String token = authService.loginUsingId(user.getId(), RequestUtil.url());
|
||||
|
||||
HashMap<String, Object> data = new HashMap<>();
|
||||
data.put("token", token);
|
||||
|
||||
ctx.publishEvent(
|
||||
new UserLoginEvent(
|
||||
this,
|
||||
user.getId(),
|
||||
user.getEmail(),
|
||||
token,
|
||||
IpUtil.getIpAddress(),
|
||||
RequestUtil.ua()));
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public HashMap<String, Object> tokenByLdapTransformUser(LdapTransformUser ldapTransformUser)
|
||||
throws ServiceException {
|
||||
// LDAP用户的名字
|
||||
String ldapUserName = ldapTransformUser.getCn();
|
||||
|
||||
// 将LDAP用户所属的部门同步到本地
|
||||
Integer depId = departmentService.createWithChainList(ldapTransformUser.getOu());
|
||||
Integer[] depIds = depId == 0 ? null : new Integer[] {depId};
|
||||
|
||||
// LDAP用户在本地的缓存记录
|
||||
LdapUser ldapUser = ldapUserService.findByUUID(ldapTransformUser.getId());
|
||||
User user;
|
||||
|
||||
// 计算将LDAP用户关联到本地users表的email字段值
|
||||
String localUserEmail = ldapTransformUser.getUid();
|
||||
if (StringUtil.isNotEmpty(ldapTransformUser.getEmail())) {
|
||||
localUserEmail = ldapTransformUser.getEmail();
|
||||
}
|
||||
|
||||
if (ldapUser == null) {
|
||||
// 检测localUserEmail是否存在
|
||||
if (userService.find(localUserEmail) != null) {
|
||||
throw new ServiceException(String.format("已有其它账号在使用:%s", localUserEmail));
|
||||
}
|
||||
// LDAP用户数据缓存到本地
|
||||
ldapUser = ldapUserService.store(ldapTransformUser);
|
||||
// 创建本地user
|
||||
user =
|
||||
userService.createWithDepIds(
|
||||
localUserEmail,
|
||||
ldapUserName,
|
||||
appConfigService.defaultAvatar(),
|
||||
HelperUtil.randomString(20),
|
||||
"",
|
||||
depIds);
|
||||
// 将LDAP缓存数据与本地user关联
|
||||
ldapUserService.updateUserId(ldapUser.getId(), user.getId());
|
||||
} else {
|
||||
user = userService.find(ldapUser.getUserId());
|
||||
// 账号修改[账号有可能是email也有可能是uid]
|
||||
if (!localUserEmail.equals(user.getEmail())) {
|
||||
// 检测localUserEmail是否存在
|
||||
if (userService.find(localUserEmail) != null) {
|
||||
throw new ServiceException(String.format("已有其它账号在使用:%s", localUserEmail));
|
||||
}
|
||||
userService.updateEmail(user.getId(), localUserEmail);
|
||||
}
|
||||
// ldap-email的变化
|
||||
if (!ldapUser.getEmail().equals(ldapTransformUser.getEmail())) {
|
||||
ldapUserService.updateEmail(ldapUser.getId(), ldapTransformUser.getEmail());
|
||||
}
|
||||
// ldap-uid的变化
|
||||
if (!ldapUser.getUid().equals(ldapTransformUser.getUid())) {
|
||||
ldapUserService.updateUid(ldapUser.getId(), ldapTransformUser.getUid());
|
||||
}
|
||||
// 名字同步修改
|
||||
if (!ldapUserName.equals(ldapUser.getCn())) {
|
||||
userService.updateName(user.getId(), ldapUserName);
|
||||
ldapUserService.updateCN(ldapUser.getId(), ldapUserName);
|
||||
}
|
||||
// 部门修改同步
|
||||
String newOU = String.join(",", ldapTransformUser.getOu());
|
||||
if (!newOU.equals(ldapUser.getOu())) {
|
||||
userService.updateDepId(user.getId(), depIds);
|
||||
ldapUserService.updateOU(ldapUser.getId(), newOU);
|
||||
}
|
||||
}
|
||||
|
||||
return tokenByUser(user);
|
||||
}
|
||||
}
|
50
playedu-api/src/main/java/xyz/playedu/api/cache/LoginLimitCache.java
vendored
Normal file
50
playedu-api/src/main/java/xyz/playedu/api/cache/LoginLimitCache.java
vendored
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (C) 2023 杭州白书科技有限公司
|
||||
*
|
||||
* Licensed 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 xyz.playedu.api.cache;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import xyz.playedu.common.config.PlayEduConfig;
|
||||
import xyz.playedu.common.exception.ServiceException;
|
||||
import xyz.playedu.common.service.RateLimiterService;
|
||||
import xyz.playedu.common.util.RedisUtil;
|
||||
|
||||
@Component
|
||||
public class LoginLimitCache {
|
||||
|
||||
@Autowired private RateLimiterService rateLimiterService;
|
||||
|
||||
@Autowired private PlayEduConfig playEduConfig;
|
||||
|
||||
public void check(String email) throws ServiceException {
|
||||
String limitKey = cacheKey(email);
|
||||
Long reqCount = rateLimiterService.current(limitKey, 600L);
|
||||
if (reqCount >= 10 && !playEduConfig.getTesting()) {
|
||||
Long exp = RedisUtil.ttlWithoutPrefix(limitKey);
|
||||
String msg = String.format("您的账号已被锁定,请%s后重试", exp > 60 ? exp / 60 + "分钟" : exp + "秒");
|
||||
throw new ServiceException(msg);
|
||||
}
|
||||
}
|
||||
|
||||
public void destroy(String email) {
|
||||
RedisUtil.del(cacheKey(email));
|
||||
}
|
||||
|
||||
private String cacheKey(String email) {
|
||||
return "login-limit:" + email;
|
||||
}
|
||||
}
|
42
playedu-api/src/main/java/xyz/playedu/api/cache/LoginLockCache.java
vendored
Normal file
42
playedu-api/src/main/java/xyz/playedu/api/cache/LoginLockCache.java
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (C) 2023 杭州白书科技有限公司
|
||||
*
|
||||
* Licensed 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 xyz.playedu.api.cache;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import xyz.playedu.common.util.RedisDistributedLock;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@Component
|
||||
public class LoginLockCache {
|
||||
|
||||
@Autowired private RedisDistributedLock redisDistributedLock;
|
||||
|
||||
public boolean apply(String username) {
|
||||
String key = cacheKey(username);
|
||||
return redisDistributedLock.tryLock(key, 10L, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
public void release(String username) {
|
||||
redisDistributedLock.releaseLock(cacheKey(username));
|
||||
}
|
||||
|
||||
private String cacheKey(String username) {
|
||||
return "login-lock:" + username;
|
||||
}
|
||||
}
|
@ -35,6 +35,7 @@ import xyz.playedu.common.context.BCtx;
|
||||
import xyz.playedu.common.domain.Department;
|
||||
import xyz.playedu.common.domain.User;
|
||||
import xyz.playedu.common.exception.NotFoundException;
|
||||
import xyz.playedu.common.service.AppConfigService;
|
||||
import xyz.playedu.common.service.DepartmentService;
|
||||
import xyz.playedu.common.service.UserService;
|
||||
import xyz.playedu.common.types.JsonResponse;
|
||||
@ -66,6 +67,8 @@ public class DepartmentController {
|
||||
|
||||
@Autowired private ApplicationContext ctx;
|
||||
|
||||
@Autowired private AppConfigService appConfigService;
|
||||
|
||||
@GetMapping("/index")
|
||||
@Log(title = "部门-列表", businessType = BusinessTypeConstant.GET)
|
||||
public JsonResponse index() {
|
||||
@ -98,6 +101,9 @@ public class DepartmentController {
|
||||
@Log(title = "部门-新建", businessType = BusinessTypeConstant.INSERT)
|
||||
public JsonResponse store(@RequestBody @Validated DepartmentRequest req)
|
||||
throws NotFoundException {
|
||||
if (appConfigService.enabledLdapLogin()) {
|
||||
return JsonResponse.error("已启用LDAP服务,禁止添加部门");
|
||||
}
|
||||
departmentService.create(req.getName(), req.getParentId(), req.getSort());
|
||||
return JsonResponse.success();
|
||||
}
|
||||
@ -115,6 +121,9 @@ public class DepartmentController {
|
||||
@Log(title = "部门-编辑", businessType = BusinessTypeConstant.UPDATE)
|
||||
public JsonResponse update(@PathVariable Integer id, @RequestBody DepartmentRequest req)
|
||||
throws NotFoundException {
|
||||
if (appConfigService.enabledLdapLogin()) {
|
||||
return JsonResponse.error("已启用LDAP服务,禁止添加部门");
|
||||
}
|
||||
Department department = departmentService.findOrFail(id);
|
||||
departmentService.update(department, req.getName(), req.getParentId(), req.getSort());
|
||||
return JsonResponse.success();
|
||||
@ -124,6 +133,9 @@ public class DepartmentController {
|
||||
@GetMapping("/{id}/destroy")
|
||||
@Log(title = "部门-批量删除", businessType = BusinessTypeConstant.DELETE)
|
||||
public JsonResponse preDestroy(@PathVariable Integer id) {
|
||||
if (appConfigService.enabledLdapLogin()) {
|
||||
return JsonResponse.error("已启用LDAP服务,禁止添加部门");
|
||||
}
|
||||
List<Integer> courseIds = courseDepartmentService.getCourseIdsByDepId(id);
|
||||
List<Integer> userIds = departmentService.getUserIdsByDepId(id);
|
||||
|
||||
@ -165,6 +177,9 @@ public class DepartmentController {
|
||||
@DeleteMapping("/{id}")
|
||||
@Log(title = "部门-删除", businessType = BusinessTypeConstant.DELETE)
|
||||
public JsonResponse destroy(@PathVariable Integer id) throws NotFoundException {
|
||||
if (appConfigService.enabledLdapLogin()) {
|
||||
return JsonResponse.error("已启用LDAP服务,禁止添加部门");
|
||||
}
|
||||
Department department = departmentService.findOrFail(id);
|
||||
departmentService.destroy(department.getId());
|
||||
ctx.publishEvent(new DepartmentDestroyEvent(this, BCtx.getId(), department.getId()));
|
||||
@ -184,6 +199,9 @@ public class DepartmentController {
|
||||
@Log(title = "部门-更新父级", businessType = BusinessTypeConstant.UPDATE)
|
||||
public JsonResponse updateParent(@RequestBody @Validated DepartmentParentRequest req)
|
||||
throws NotFoundException {
|
||||
if (appConfigService.enabledLdapLogin()) {
|
||||
return JsonResponse.error("已启用LDAP服务,禁止添加部门");
|
||||
}
|
||||
departmentService.changeParent(req.getId(), req.getParentId(), req.getIds());
|
||||
return JsonResponse.success();
|
||||
}
|
||||
|
@ -28,7 +28,6 @@ import xyz.playedu.api.request.frontend.CourseHourRecordRequest;
|
||||
import xyz.playedu.common.context.FCtx;
|
||||
import xyz.playedu.common.types.JsonResponse;
|
||||
import xyz.playedu.common.util.RedisDistributedLock;
|
||||
import xyz.playedu.course.bus.UserBus;
|
||||
import xyz.playedu.course.caches.CourseCache;
|
||||
import xyz.playedu.course.caches.UserCanSeeCourseCache;
|
||||
import xyz.playedu.course.caches.UserLastLearnTimeCache;
|
||||
@ -61,8 +60,6 @@ public class HourController {
|
||||
|
||||
@Autowired private UserCourseHourRecordService userCourseHourRecordService;
|
||||
|
||||
@Autowired private UserBus userBus;
|
||||
|
||||
// ------- CACHE ----------
|
||||
@Autowired private UserCanSeeCourseCache userCanSeeCourseCache;
|
||||
@Autowired private CourseCache courseCache;
|
||||
|
@ -15,6 +15,9 @@
|
||||
*/
|
||||
package xyz.playedu.api.controller.frontend;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
@ -23,26 +26,29 @@ import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import xyz.playedu.api.event.UserLoginEvent;
|
||||
import xyz.playedu.api.bus.LoginBus;
|
||||
import xyz.playedu.api.cache.LoginLimitCache;
|
||||
import xyz.playedu.api.cache.LoginLockCache;
|
||||
import xyz.playedu.api.event.UserLogoutEvent;
|
||||
import xyz.playedu.api.request.frontend.LoginLdapRequest;
|
||||
import xyz.playedu.api.request.frontend.LoginPasswordRequest;
|
||||
import xyz.playedu.common.config.PlayEduConfig;
|
||||
import xyz.playedu.common.constant.ConfigConstant;
|
||||
import xyz.playedu.common.context.FCtx;
|
||||
import xyz.playedu.common.domain.User;
|
||||
import xyz.playedu.common.exception.LimitException;
|
||||
import xyz.playedu.common.service.FrontendAuthService;
|
||||
import xyz.playedu.common.service.RateLimiterService;
|
||||
import xyz.playedu.common.service.UserService;
|
||||
import xyz.playedu.common.exception.ServiceException;
|
||||
import xyz.playedu.common.service.*;
|
||||
import xyz.playedu.common.types.JsonResponse;
|
||||
import xyz.playedu.common.util.HelperUtil;
|
||||
import xyz.playedu.common.util.IpUtil;
|
||||
import xyz.playedu.common.util.RedisUtil;
|
||||
import xyz.playedu.common.util.RequestUtil;
|
||||
import xyz.playedu.common.util.*;
|
||||
import xyz.playedu.common.util.ldap.LdapTransformUser;
|
||||
import xyz.playedu.common.util.ldap.LdapUtil;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/auth/login")
|
||||
@Slf4j
|
||||
public class LoginController {
|
||||
|
||||
@Autowired private UserService userService;
|
||||
@ -51,13 +57,27 @@ public class LoginController {
|
||||
|
||||
@Autowired private ApplicationContext ctx;
|
||||
|
||||
@Autowired private RateLimiterService rateLimiterService;
|
||||
@Autowired private AppConfigService appConfigService;
|
||||
|
||||
@Autowired private PlayEduConfig playEduConfig;
|
||||
@Autowired private LdapUserService ldapUserService;
|
||||
|
||||
@Autowired private DepartmentService departmentService;
|
||||
|
||||
@Autowired private LoginBus loginBus;
|
||||
|
||||
@Autowired private LoginLimitCache loginLimitCache;
|
||||
|
||||
@Autowired private LoginLockCache loginLockCache;
|
||||
|
||||
@PostMapping("/password")
|
||||
public JsonResponse password(@RequestBody @Validated LoginPasswordRequest req)
|
||||
@SneakyThrows
|
||||
public JsonResponse password(
|
||||
@RequestBody @Validated LoginPasswordRequest req, LoginBus loginBus)
|
||||
throws LimitException {
|
||||
if (appConfigService.enabledLdapLogin()) {
|
||||
return JsonResponse.error("请使用LDAP登录");
|
||||
}
|
||||
|
||||
String email = req.getEmail();
|
||||
|
||||
User user = userService.find(email);
|
||||
@ -65,39 +85,74 @@ public class LoginController {
|
||||
return JsonResponse.error("邮箱或密码错误");
|
||||
}
|
||||
|
||||
String limitKey = "login-limit:" + req.getEmail();
|
||||
Long reqCount = rateLimiterService.current(limitKey, 600L);
|
||||
if (reqCount >= 10 && !playEduConfig.getTesting()) {
|
||||
Long exp = RedisUtil.ttlWithoutPrefix(limitKey);
|
||||
return JsonResponse.error(
|
||||
String.format("您的账号已被锁定,请%s后重试", exp > 60 ? exp / 60 + "分钟" : exp + "秒"));
|
||||
}
|
||||
loginLimitCache.check(email);
|
||||
|
||||
if (!HelperUtil.MD5(req.getPassword() + user.getSalt()).equals(user.getPassword())) {
|
||||
return JsonResponse.error("邮箱或密码错误");
|
||||
}
|
||||
|
||||
RedisUtil.del(limitKey);
|
||||
|
||||
if (user.getIsLock() == 1) {
|
||||
return JsonResponse.error("当前学员已锁定无法登录");
|
||||
}
|
||||
|
||||
String token = authService.loginUsingId(user.getId(), RequestUtil.url());
|
||||
loginLimitCache.destroy(email);
|
||||
|
||||
HashMap<String, Object> data = new HashMap<>();
|
||||
data.put("token", token);
|
||||
return JsonResponse.data(loginBus.tokenByUser(user));
|
||||
}
|
||||
|
||||
ctx.publishEvent(
|
||||
new UserLoginEvent(
|
||||
this,
|
||||
user.getId(),
|
||||
user.getEmail(),
|
||||
token,
|
||||
IpUtil.getIpAddress(),
|
||||
RequestUtil.ua()));
|
||||
@PostMapping("/ldap")
|
||||
@SneakyThrows
|
||||
public JsonResponse ldap(@RequestBody @Validated LoginLdapRequest req) {
|
||||
String username = req.getUsername();
|
||||
|
||||
return JsonResponse.data(data);
|
||||
// 系统配置
|
||||
Map<String, String> config = appConfigService.keyValues();
|
||||
String url = config.get(ConfigConstant.LDAP_URL);
|
||||
String adminUser = config.get(ConfigConstant.LDAP_ADMIN_USER);
|
||||
String adminPass = config.get(ConfigConstant.LDAP_ADMIN_PASS);
|
||||
String baseDN = config.get(ConfigConstant.LDAP_BASE_DN);
|
||||
if (url.isEmpty() || adminUser.isEmpty() || adminPass.isEmpty() || baseDN.isEmpty()) {
|
||||
return JsonResponse.error("LDAP服务未配置");
|
||||
}
|
||||
|
||||
String mail = null;
|
||||
String uid = null;
|
||||
if (StringUtil.contains(username, "@")) {
|
||||
mail = username;
|
||||
} else {
|
||||
uid = username;
|
||||
}
|
||||
|
||||
// 限流控制
|
||||
loginLimitCache.check(username);
|
||||
|
||||
// 锁控制-防止并发登录重复写入数据
|
||||
if (!loginLockCache.apply(username)) {
|
||||
return JsonResponse.error("请稍候再试");
|
||||
}
|
||||
|
||||
try {
|
||||
LdapTransformUser ldapTransformUser =
|
||||
LdapUtil.loginByMailOrUid(
|
||||
url, adminUser, adminPass, baseDN, mail, uid, req.getPassword());
|
||||
if (ldapTransformUser == null) {
|
||||
return JsonResponse.error("登录失败.请检查账号和密码");
|
||||
}
|
||||
|
||||
HashMap<String, Object> data = loginBus.tokenByLdapTransformUser(ldapTransformUser);
|
||||
|
||||
// 删除限流控制
|
||||
loginLimitCache.destroy(username);
|
||||
|
||||
return JsonResponse.data(data);
|
||||
} catch (ServiceException e) {
|
||||
return JsonResponse.error(e.getMessage());
|
||||
} catch (Exception e) {
|
||||
log.error("LDAP登录失败", e);
|
||||
return JsonResponse.error("系统错误");
|
||||
} finally {
|
||||
loginLockCache.release(username);
|
||||
}
|
||||
}
|
||||
|
||||
@PostMapping("/logout")
|
||||
|
@ -53,6 +53,8 @@ public class SystemController {
|
||||
data.put("player-bullet-secret-opacity", configs.get("player.bullet_secret_opacity"));
|
||||
data.put("player-disabled-drag", configs.get("player.disabled_drag"));
|
||||
|
||||
data.put("ldap-enabled", configs.get(ConfigConstant.LDAP_ENABLED));
|
||||
|
||||
return JsonResponse.data(data);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (C) 2023 杭州白书科技有限公司
|
||||
*
|
||||
* Licensed 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 xyz.playedu.api.request.frontend;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class LoginLdapRequest {
|
||||
|
||||
@NotBlank(message = "请输入账户名")
|
||||
private String username;
|
||||
|
||||
@NotBlank(message = "请输入密码")
|
||||
private String password;
|
||||
}
|
@ -35,4 +35,11 @@ public class ConfigConstant {
|
||||
public static final String MINIO_BUCKET = "minio.bucket";
|
||||
public static final String MINIO_ENDPOINT = "minio.endpoint";
|
||||
public static final String MINIO_DOMAIN = "minio.domain";
|
||||
|
||||
public static final String LDAP_ENABLED = "ldap.enabled";
|
||||
public static final String LDAP_URL = "ldap.url";
|
||||
public static final String LDAP_ADMIN_USER = "ldap.admin_user";
|
||||
public static final String LDAP_ADMIN_PASS = "ldap.admin_pass";
|
||||
public static final String LDAP_BASE_DN = "ldap.base_dn";
|
||||
public static final String LDAP_USER_DN_PREFIX = "ldap.user_dn_prefix";
|
||||
}
|
||||
|
@ -31,6 +31,7 @@ public class FrontendConstant {
|
||||
add("/api/v1/system/config");
|
||||
add("/api/v1/system/image-captcha");
|
||||
add("/api/v1/auth/login/password");
|
||||
add("/api/v1/auth/login/ldap");
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -0,0 +1,244 @@
|
||||
/*
|
||||
* Copyright (C) 2023 杭州白书科技有限公司
|
||||
*
|
||||
* Licensed 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 xyz.playedu.common.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* @TableName ldap_user
|
||||
*/
|
||||
@TableName(value = "ldap_user")
|
||||
public class LdapUser implements Serializable {
|
||||
/** */
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Integer id;
|
||||
|
||||
/** 唯一特征值 */
|
||||
private String uuid;
|
||||
|
||||
/** 用户ID */
|
||||
private Integer userId;
|
||||
|
||||
/** cn */
|
||||
private String cn;
|
||||
|
||||
/** dn */
|
||||
private String dn;
|
||||
|
||||
/** ou */
|
||||
private String ou;
|
||||
|
||||
/** uid */
|
||||
private String uid;
|
||||
|
||||
/** 邮箱 */
|
||||
private String email;
|
||||
|
||||
/** */
|
||||
private Date createdAt;
|
||||
|
||||
/** */
|
||||
private Date updatedAt;
|
||||
|
||||
@TableField(exist = false)
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** */
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/** */
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
/** 唯一特征值 */
|
||||
public String getUuid() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
/** 唯一特征值 */
|
||||
public void setUuid(String uuid) {
|
||||
this.uuid = uuid;
|
||||
}
|
||||
|
||||
/** 用户ID */
|
||||
public Integer getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
/** 用户ID */
|
||||
public void setUserId(Integer userId) {
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
/** cn */
|
||||
public String getCn() {
|
||||
return cn;
|
||||
}
|
||||
|
||||
/** cn */
|
||||
public void setCn(String cn) {
|
||||
this.cn = cn;
|
||||
}
|
||||
|
||||
/** dn */
|
||||
public String getDn() {
|
||||
return dn;
|
||||
}
|
||||
|
||||
/** dn */
|
||||
public void setDn(String dn) {
|
||||
this.dn = dn;
|
||||
}
|
||||
|
||||
/** ou */
|
||||
public String getOu() {
|
||||
return ou;
|
||||
}
|
||||
|
||||
/** ou */
|
||||
public void setOu(String ou) {
|
||||
this.ou = ou;
|
||||
}
|
||||
|
||||
/** uid */
|
||||
public String getUid() {
|
||||
return uid;
|
||||
}
|
||||
|
||||
/** uid */
|
||||
public void setUid(String uid) {
|
||||
this.uid = uid;
|
||||
}
|
||||
|
||||
/** 邮箱 */
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
/** 邮箱 */
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
/** */
|
||||
public Date getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
/** */
|
||||
public void setCreatedAt(Date createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
|
||||
/** */
|
||||
public Date getUpdatedAt() {
|
||||
return updatedAt;
|
||||
}
|
||||
|
||||
/** */
|
||||
public void setUpdatedAt(Date updatedAt) {
|
||||
this.updatedAt = updatedAt;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object that) {
|
||||
if (this == that) {
|
||||
return true;
|
||||
}
|
||||
if (that == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != that.getClass()) {
|
||||
return false;
|
||||
}
|
||||
LdapUser other = (LdapUser) that;
|
||||
return (this.getId() == null ? other.getId() == null : this.getId().equals(other.getId()))
|
||||
&& (this.getUuid() == null
|
||||
? other.getUuid() == null
|
||||
: this.getUuid().equals(other.getUuid()))
|
||||
&& (this.getUserId() == null
|
||||
? other.getUserId() == null
|
||||
: this.getUserId().equals(other.getUserId()))
|
||||
&& (this.getCn() == null
|
||||
? other.getCn() == null
|
||||
: this.getCn().equals(other.getCn()))
|
||||
&& (this.getDn() == null
|
||||
? other.getDn() == null
|
||||
: this.getDn().equals(other.getDn()))
|
||||
&& (this.getOu() == null
|
||||
? other.getOu() == null
|
||||
: this.getOu().equals(other.getOu()))
|
||||
&& (this.getUid() == null
|
||||
? other.getUid() == null
|
||||
: this.getUid().equals(other.getUid()))
|
||||
&& (this.getEmail() == null
|
||||
? other.getEmail() == null
|
||||
: this.getEmail().equals(other.getEmail()))
|
||||
&& (this.getCreatedAt() == null
|
||||
? other.getCreatedAt() == null
|
||||
: this.getCreatedAt().equals(other.getCreatedAt()))
|
||||
&& (this.getUpdatedAt() == null
|
||||
? other.getUpdatedAt() == null
|
||||
: this.getUpdatedAt().equals(other.getUpdatedAt()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((getId() == null) ? 0 : getId().hashCode());
|
||||
result = prime * result + ((getUuid() == null) ? 0 : getUuid().hashCode());
|
||||
result = prime * result + ((getUserId() == null) ? 0 : getUserId().hashCode());
|
||||
result = prime * result + ((getCn() == null) ? 0 : getCn().hashCode());
|
||||
result = prime * result + ((getDn() == null) ? 0 : getDn().hashCode());
|
||||
result = prime * result + ((getOu() == null) ? 0 : getOu().hashCode());
|
||||
result = prime * result + ((getUid() == null) ? 0 : getUid().hashCode());
|
||||
result = prime * result + ((getEmail() == null) ? 0 : getEmail().hashCode());
|
||||
result = prime * result + ((getCreatedAt() == null) ? 0 : getCreatedAt().hashCode());
|
||||
result = prime * result + ((getUpdatedAt() == null) ? 0 : getUpdatedAt().hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(getClass().getSimpleName());
|
||||
sb.append(" [");
|
||||
sb.append("Hash = ").append(hashCode());
|
||||
sb.append(", id=").append(id);
|
||||
sb.append(", uuid=").append(uuid);
|
||||
sb.append(", userId=").append(userId);
|
||||
sb.append(", cn=").append(cn);
|
||||
sb.append(", dn=").append(dn);
|
||||
sb.append(", ou=").append(ou);
|
||||
sb.append(", uid=").append(uid);
|
||||
sb.append(", email=").append(email);
|
||||
sb.append(", createdAt=").append(createdAt);
|
||||
sb.append(", updatedAt=").append(updatedAt);
|
||||
sb.append(", serialVersionUID=").append(serialVersionUID);
|
||||
sb.append("]");
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
@ -15,7 +15,7 @@
|
||||
*/
|
||||
package xyz.playedu.common.exception;
|
||||
|
||||
public class ServiceException extends Exception {
|
||||
public class ServiceException extends RuntimeException {
|
||||
public ServiceException() {}
|
||||
|
||||
public ServiceException(String message) {
|
||||
|
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2023 杭州白书科技有限公司
|
||||
*
|
||||
* Licensed 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 xyz.playedu.common.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
|
||||
import xyz.playedu.common.domain.LdapUser;
|
||||
|
||||
/**
|
||||
* @author tengyongzhi
|
||||
* @description 针对表【ldap_user】的数据库操作Mapper
|
||||
* @createDate 2023-08-31 14:33:19 @Entity xyz.playedu.common.domain.LdapUser
|
||||
*/
|
||||
public interface LdapUserMapper extends BaseMapper<LdapUser> {}
|
@ -35,4 +35,8 @@ public interface AppConfigService extends IService<AppConfig> {
|
||||
Map<String, String> keyValues();
|
||||
|
||||
MinioConfig getMinioConfig();
|
||||
|
||||
boolean enabledLdapLogin();
|
||||
|
||||
String defaultAvatar();
|
||||
}
|
||||
|
@ -45,7 +45,7 @@ public interface DepartmentService extends IService<Department> {
|
||||
|
||||
String childrenParentChain(Department department);
|
||||
|
||||
void create(String name, Integer parentId, Integer sort) throws NotFoundException;
|
||||
Integer create(String name, Integer parentId, Integer sort) throws NotFoundException;
|
||||
|
||||
void remoteRelateUsersByDepId(Integer depId);
|
||||
|
||||
@ -64,4 +64,6 @@ public interface DepartmentService extends IService<Department> {
|
||||
Map<Integer, Integer> getDepartmentsUserCount();
|
||||
|
||||
List<Department> chunk(List<Integer> ids);
|
||||
|
||||
Integer createWithChainList(List<String> ou);
|
||||
}
|
||||
|
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (C) 2023 杭州白书科技有限公司
|
||||
*
|
||||
* Licensed 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 xyz.playedu.common.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
|
||||
import xyz.playedu.common.domain.LdapUser;
|
||||
import xyz.playedu.common.util.ldap.LdapTransformUser;
|
||||
|
||||
/**
|
||||
* @author tengyongzhi
|
||||
* @description 针对表【ldap_user】的数据库操作Service
|
||||
* @createDate 2023-08-31 11:59:27
|
||||
*/
|
||||
public interface LdapUserService extends IService<LdapUser> {
|
||||
LdapUser findByUUID(String id);
|
||||
|
||||
LdapUser store(LdapTransformUser ldapTransformUser);
|
||||
|
||||
void updateUserId(Integer id, Integer userId);
|
||||
|
||||
void updateCN(Integer id, String cn);
|
||||
|
||||
void updateOU(Integer id, String newOU);
|
||||
|
||||
void updateEmail(Integer id, String email);
|
||||
|
||||
void updateUid(Integer id, String uid);
|
||||
}
|
@ -80,4 +80,10 @@ public interface UserService extends IService<User> {
|
||||
Map<Integer, List<Integer>> getDepIdsGroup(List<Integer> userIds);
|
||||
|
||||
void changeAvatar(Integer userId, String avatar);
|
||||
|
||||
void updateName(Integer id, String cn);
|
||||
|
||||
void updateDepId(Integer id, Integer[] depIds);
|
||||
|
||||
void updateEmail(Integer id, String email);
|
||||
}
|
||||
|
@ -81,7 +81,7 @@ public class AppConfigServiceImpl extends ServiceImpl<AppConfigMapper, AppConfig
|
||||
});
|
||||
});
|
||||
|
||||
if (list.size() > 0) {
|
||||
if (!list.isEmpty()) {
|
||||
updateBatchById(list);
|
||||
}
|
||||
}
|
||||
@ -103,4 +103,18 @@ public class AppConfigServiceImpl extends ServiceImpl<AppConfigMapper, AppConfig
|
||||
minioConfig.setDomain(config.get(ConfigConstant.MINIO_DOMAIN));
|
||||
return minioConfig;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean enabledLdapLogin() {
|
||||
AppConfig appConfig =
|
||||
getOne(query().getWrapper().eq("key_name", ConfigConstant.LDAP_ENABLED));
|
||||
return "1".equals(appConfig.getKeyValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String defaultAvatar() {
|
||||
AppConfig appConfig =
|
||||
getOne(query().getWrapper().eq("key_name", ConfigConstant.MEMBER_DEFAULT_AVATAR));
|
||||
return appConfig.getKeyValue();
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ package xyz.playedu.common.service.impl;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@ -108,7 +109,7 @@ public class DepartmentServiceImpl extends ServiceImpl<DepartmentMapper, Departm
|
||||
private void updateParentChain(String newChildrenPC, String oldChildrenPC) {
|
||||
List<Department> children =
|
||||
list(query().getWrapper().like("parent_chain", oldChildrenPC + "%"));
|
||||
if (children.size() == 0) {
|
||||
if (children.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -125,7 +126,7 @@ public class DepartmentServiceImpl extends ServiceImpl<DepartmentMapper, Departm
|
||||
.getParentChain()
|
||||
.replaceFirst(
|
||||
oldChildrenPC + ",",
|
||||
newChildrenPC.length() == 0
|
||||
newChildrenPC.isEmpty()
|
||||
? newChildrenPC
|
||||
: newChildrenPC + ',');
|
||||
}
|
||||
@ -133,7 +134,7 @@ public class DepartmentServiceImpl extends ServiceImpl<DepartmentMapper, Departm
|
||||
|
||||
// parentId计算
|
||||
int parentId = 0;
|
||||
if (pc != null && pc.length() > 0) {
|
||||
if (pc != null && !pc.isEmpty()) {
|
||||
String[] parentIds = pc.split(",");
|
||||
parentId = Integer.parseInt(parentIds[parentIds.length - 1]);
|
||||
}
|
||||
@ -153,7 +154,7 @@ public class DepartmentServiceImpl extends ServiceImpl<DepartmentMapper, Departm
|
||||
throw new NotFoundException("父级部门不存在");
|
||||
}
|
||||
String pc = parentDepartment.getParentChain();
|
||||
parentChain = pc == null || pc.length() == 0 ? parentId + "" : pc + "," + parentId;
|
||||
parentChain = pc == null || pc.isEmpty() ? parentId + "" : pc + "," + parentId;
|
||||
}
|
||||
return parentChain;
|
||||
}
|
||||
@ -161,14 +162,14 @@ public class DepartmentServiceImpl extends ServiceImpl<DepartmentMapper, Departm
|
||||
@Override
|
||||
public String childrenParentChain(Department department) {
|
||||
String prefix = department.getId() + "";
|
||||
if (department.getParentChain() != null && department.getParentChain().length() > 0) {
|
||||
if (department.getParentChain() != null && !department.getParentChain().isEmpty()) {
|
||||
prefix = department.getParentChain() + "," + prefix;
|
||||
}
|
||||
return prefix;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void create(String name, Integer parentId, Integer sort) throws NotFoundException {
|
||||
public Integer create(String name, Integer parentId, Integer sort) throws NotFoundException {
|
||||
String parentChain = "";
|
||||
if (parentId != 0) {
|
||||
parentChain = compParentChain(parentId);
|
||||
@ -183,6 +184,8 @@ public class DepartmentServiceImpl extends ServiceImpl<DepartmentMapper, Departm
|
||||
department.setUpdatedAt(new Date());
|
||||
|
||||
save(department);
|
||||
|
||||
return department.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -258,9 +261,31 @@ public class DepartmentServiceImpl extends ServiceImpl<DepartmentMapper, Departm
|
||||
|
||||
@Override
|
||||
public List<Department> chunk(List<Integer> ids) {
|
||||
if (ids == null || ids.size() == 0) {
|
||||
if (ids == null || ids.isEmpty()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
return list(query().getWrapper().in("id", ids));
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public Integer createWithChainList(List<String> ou) {
|
||||
if (ou == null || ou.isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
Department department = null;
|
||||
for (int i = 0; i < ou.size(); i++) {
|
||||
String name = ou.get(i);
|
||||
Integer parentId = department == null ? 0 : department.getId();
|
||||
department = getOne(query().getWrapper().eq("name", name).eq("parent_id", parentId));
|
||||
if (department == null) {
|
||||
Integer depId = create(name, parentId, i);
|
||||
// refresh
|
||||
department = new Department();
|
||||
department.setId(depId);
|
||||
}
|
||||
}
|
||||
return department.getId();
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright (C) 2023 杭州白书科技有限公司
|
||||
*
|
||||
* Licensed 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 xyz.playedu.common.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import xyz.playedu.common.domain.LdapUser;
|
||||
import xyz.playedu.common.mapper.LdapUserMapper;
|
||||
import xyz.playedu.common.service.LdapUserService;
|
||||
import xyz.playedu.common.util.StringUtil;
|
||||
import xyz.playedu.common.util.ldap.LdapTransformUser;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* @author tengyongzhi
|
||||
* @description 针对表【ldap_user】的数据库操作Service实现
|
||||
* @createDate 2023-08-31 11:59:27
|
||||
*/
|
||||
@Service
|
||||
public class LdapUserServiceImpl extends ServiceImpl<LdapUserMapper, LdapUser>
|
||||
implements LdapUserService {
|
||||
|
||||
@Override
|
||||
public LdapUser findByUUID(String id) {
|
||||
return getOne(query().getWrapper().eq("uuid", id));
|
||||
}
|
||||
|
||||
@Override
|
||||
public LdapUser store(LdapTransformUser ldapTransformUser) {
|
||||
LdapUser user = new LdapUser();
|
||||
user.setUuid(ldapTransformUser.getId());
|
||||
user.setCn(ldapTransformUser.getCn());
|
||||
user.setDn(ldapTransformUser.getDn());
|
||||
user.setUid(ldapTransformUser.getUid());
|
||||
// ou
|
||||
user.setOu(String.join(",", ldapTransformUser.getOu()));
|
||||
// 邮箱可能不存在
|
||||
if (StringUtil.isNotEmpty(ldapTransformUser.getEmail())) {
|
||||
user.setEmail(ldapTransformUser.getEmail());
|
||||
}
|
||||
user.setCreatedAt(new Date());
|
||||
user.setUpdatedAt(new Date());
|
||||
|
||||
save(user);
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateUserId(Integer id, Integer userId) {
|
||||
LdapUser user = new LdapUser();
|
||||
user.setId(id);
|
||||
user.setUserId(userId);
|
||||
updateById(user);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateCN(Integer id, String cn) {
|
||||
LdapUser user = new LdapUser();
|
||||
user.setId(id);
|
||||
user.setCn(cn == null ? "" : cn);
|
||||
updateById(user);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateOU(Integer id, String newOU) {
|
||||
LdapUser user = new LdapUser();
|
||||
user.setId(id);
|
||||
user.setOu(newOU == null ? "" : newOU);
|
||||
updateById(user);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateEmail(Integer id, String email) {
|
||||
LdapUser user = new LdapUser();
|
||||
user.setId(id);
|
||||
user.setEmail(email == null ? "" : email);
|
||||
updateById(user);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateUid(Integer id, String uid) {
|
||||
LdapUser user = new LdapUser();
|
||||
user.setId(id);
|
||||
user.setUid(uid);
|
||||
updateById(user);
|
||||
}
|
||||
}
|
@ -117,7 +117,7 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
|
||||
user.setCreatedAt(new Date());
|
||||
user.setUpdatedAt(new Date());
|
||||
|
||||
if (idCard != null && idCard.length() > 0) {
|
||||
if (idCard != null && !idCard.isEmpty()) {
|
||||
user.setVerifyAt(new Date());
|
||||
user.setIsVerify(1);
|
||||
}
|
||||
@ -144,14 +144,14 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
|
||||
newUser.setAvatar(avatar);
|
||||
newUser.setIdCard(idCard);
|
||||
|
||||
if (password != null && password.length() > 0) {
|
||||
if (password != null && !password.isEmpty()) {
|
||||
newUser.setPassword(HelperUtil.MD5(password + user.getSalt()));
|
||||
}
|
||||
|
||||
if (newUser.getName() != null
|
||||
&& newUser.getName().length() > 0
|
||||
&& !newUser.getName().isEmpty()
|
||||
&& newUser.getIdCard() != null
|
||||
&& newUser.getIdCard().length() > 0) {
|
||||
&& !newUser.getIdCard().isEmpty()) {
|
||||
if (user.getVerifyAt() == null) {
|
||||
newUser.setIsVerify(1);
|
||||
newUser.setVerifyAt(new Date());
|
||||
@ -204,7 +204,7 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
|
||||
|
||||
@Override
|
||||
public List<User> chunks(List<Integer> ids, List<String> fields) {
|
||||
if (ids == null || ids.size() == 0) {
|
||||
if (ids == null || ids.isEmpty()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
return list(query().getWrapper().in("id", ids).select(fields));
|
||||
@ -212,7 +212,7 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
|
||||
|
||||
@Override
|
||||
public List<User> chunks(List<Integer> ids) {
|
||||
if (ids == null || ids.size() == 0) {
|
||||
if (ids == null || ids.isEmpty()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
return list(query().getWrapper().in("id", ids));
|
||||
@ -250,7 +250,7 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
|
||||
|
||||
@Override
|
||||
public Map<Integer, List<Integer>> getDepIdsGroup(List<Integer> userIds) {
|
||||
if (userIds == null || userIds.size() == 0) {
|
||||
if (userIds == null || userIds.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
Map<Integer, List<UserDepartment>> data =
|
||||
@ -273,4 +273,25 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
|
||||
user.setAvatar(avatar);
|
||||
updateById(user);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateName(Integer id, String cn) {
|
||||
User user = new User();
|
||||
user.setId(id);
|
||||
user.setName(cn);
|
||||
updateById(user);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateDepId(Integer id, Integer[] depIds) {
|
||||
userDepartmentService.resetStoreDepIds(id, depIds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateEmail(Integer id, String email) {
|
||||
User user = new User();
|
||||
user.setId(id);
|
||||
user.setEmail(email);
|
||||
updateById(user);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (C) 2023 杭州白书科技有限公司
|
||||
*
|
||||
* Licensed 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 xyz.playedu.common.util.ldap;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class LdapTransformUser {
|
||||
private String id;
|
||||
|
||||
private String dn;
|
||||
|
||||
private String cn;
|
||||
|
||||
private List<String> ou;
|
||||
|
||||
private String email;
|
||||
|
||||
private String uid;
|
||||
}
|
@ -0,0 +1,229 @@
|
||||
/*
|
||||
* Copyright (C) 2023 杭州白书科技有限公司
|
||||
*
|
||||
* Licensed 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 xyz.playedu.common.util.ldap;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import xyz.playedu.common.exception.ServiceException;
|
||||
import xyz.playedu.common.util.StringUtil;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import javax.naming.Context;
|
||||
import javax.naming.NamingEnumeration;
|
||||
import javax.naming.NamingException;
|
||||
import javax.naming.directory.Attributes;
|
||||
import javax.naming.directory.SearchControls;
|
||||
import javax.naming.directory.SearchResult;
|
||||
import javax.naming.ldap.InitialLdapContext;
|
||||
import javax.naming.ldap.LdapContext;
|
||||
|
||||
@Slf4j
|
||||
public class LdapUtil {
|
||||
|
||||
private static final String USER_OBJECT_CLASS =
|
||||
"(|(objectClass=person)(objectClass=posixAccount)(objectClass=inetOrgPerson)(objectClass=organizationalPerson))";
|
||||
|
||||
private static final String[] USER_RETURN_ATTRS =
|
||||
new String[] {
|
||||
"uid", // 用户的唯一识别符号,全局唯一,可以看做用户表的手机号,此字段可用于配合密码直接登录
|
||||
"cn", // CommonName -> 可以认作为人的名字,比如:张三。在LDAP中此字段是可以重复的,但是同一ou下不可重复
|
||||
"mail", // 邮箱,此值不一定存在,全局唯一,可配合密码直接登录
|
||||
"email", // 邮箱,同上
|
||||
"entryUUID",
|
||||
};
|
||||
private static final String[] OU_RETURN_ATTRS = new String[] {"ou"};
|
||||
|
||||
public static LdapContext initContext(String url, String adminUser, String adminPass)
|
||||
throws NamingException {
|
||||
Hashtable<String, String> context = new Hashtable<>();
|
||||
context.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
|
||||
context.put(Context.SECURITY_AUTHENTICATION, "simple");
|
||||
// 服务地址
|
||||
context.put(Context.PROVIDER_URL, url);
|
||||
// 管理员账户和密码
|
||||
context.put(Context.SECURITY_PRINCIPAL, adminUser);
|
||||
context.put(Context.SECURITY_CREDENTIALS, adminPass);
|
||||
return new InitialLdapContext(context, null);
|
||||
}
|
||||
|
||||
public static List<HashMap<String, String>> users(LdapContext ldapContext, String baseDN)
|
||||
throws NamingException {
|
||||
SearchControls controls = new SearchControls();
|
||||
controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
|
||||
controls.setReturningAttributes(USER_RETURN_ATTRS);
|
||||
controls.setReturningObjFlag(true);
|
||||
|
||||
NamingEnumeration<SearchResult> result = null;
|
||||
try {
|
||||
result = ldapContext.search(baseDN, USER_OBJECT_CLASS, controls);
|
||||
} catch (NamingException e) {
|
||||
log.error("LDAP用户查询失败", e);
|
||||
} finally {
|
||||
closeContext(ldapContext);
|
||||
}
|
||||
|
||||
if (result == null || !result.hasMoreElements()) {
|
||||
log.info("LDAP服务中没有用户");
|
||||
return null;
|
||||
}
|
||||
|
||||
List<HashMap<String, String>> users = new ArrayList<>();
|
||||
while (result.hasMoreElements()) {
|
||||
SearchResult item = result.nextElement();
|
||||
if (item == null) {
|
||||
continue;
|
||||
}
|
||||
Attributes attributes = item.getAttributes();
|
||||
log.info("name={},attributes={}", item.getName(), attributes);
|
||||
}
|
||||
|
||||
return users;
|
||||
}
|
||||
|
||||
public static List<String> departments(LdapContext ldapContext, String baseDN)
|
||||
throws NamingException {
|
||||
SearchControls controls = new SearchControls();
|
||||
controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
|
||||
controls.setReturningAttributes(OU_RETURN_ATTRS);
|
||||
controls.setReturningObjFlag(true);
|
||||
|
||||
String filter = "(objectClass=organizationalUnit)";
|
||||
NamingEnumeration<SearchResult> result = null;
|
||||
try {
|
||||
result = ldapContext.search(baseDN, filter, controls);
|
||||
} catch (NamingException e) {
|
||||
log.error("LDAP部门查询失败", e);
|
||||
} finally {
|
||||
closeContext(ldapContext);
|
||||
}
|
||||
|
||||
if (result == null || !result.hasMoreElements()) {
|
||||
log.info("LDAP部门为空");
|
||||
return null;
|
||||
}
|
||||
|
||||
List<String> units = new ArrayList<>();
|
||||
while (result.hasMoreElements()) {
|
||||
SearchResult item = result.nextElement();
|
||||
if (item == null) {
|
||||
continue;
|
||||
}
|
||||
units.add(item.getName());
|
||||
}
|
||||
|
||||
return units;
|
||||
}
|
||||
|
||||
public static LdapTransformUser loginByMailOrUid(
|
||||
String url,
|
||||
String adminUser,
|
||||
String adminPass,
|
||||
String baseDN,
|
||||
String mail,
|
||||
String uid,
|
||||
String password)
|
||||
throws ServiceException, NamingException {
|
||||
if (StringUtil.isEmpty(mail) && StringUtil.isEmpty(uid)) {
|
||||
throw new ServiceException("mail和Uid不能同时为空");
|
||||
}
|
||||
|
||||
SearchControls controls = new SearchControls();
|
||||
controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
|
||||
controls.setReturningAttributes(USER_RETURN_ATTRS);
|
||||
controls.setReturningObjFlag(true);
|
||||
controls.setCountLimit(1);
|
||||
|
||||
String userFilter = "";
|
||||
if (StringUtil.isNotEmpty(mail)) {
|
||||
userFilter = String.format("(|(mail=%s)(email=%s))", mail, mail);
|
||||
} else if (StringUtil.isNotEmpty(uid)) {
|
||||
userFilter = String.format("(uid=%s)", uid);
|
||||
}
|
||||
|
||||
String filter = String.format("(&%s%s)", userFilter, USER_OBJECT_CLASS);
|
||||
|
||||
LdapContext ldapContext = initContext(url, adminUser, adminPass);
|
||||
NamingEnumeration<SearchResult> result = null;
|
||||
try {
|
||||
result = ldapContext.search(baseDN, filter, controls);
|
||||
} catch (NamingException e) {
|
||||
log.error("通过mail或uid登录失败", e);
|
||||
} finally {
|
||||
closeContext(ldapContext);
|
||||
}
|
||||
|
||||
if (result == null || !result.hasMoreElements()) {
|
||||
log.info("用户不存在");
|
||||
return null;
|
||||
}
|
||||
|
||||
// 根据mail或uid查询出来的用户
|
||||
SearchResult item = result.nextElement();
|
||||
Attributes attributes = item.getAttributes();
|
||||
|
||||
String email =
|
||||
attributes.get("mail") == null ? null : (String) attributes.get("mail").get();
|
||||
if (email == null) {
|
||||
email = attributes.get("email") == null ? null : (String) attributes.get("email").get();
|
||||
}
|
||||
|
||||
LdapTransformUser ldapUser = new LdapTransformUser();
|
||||
ldapUser.setDn(item.getName());
|
||||
ldapUser.setId((String) attributes.get("entryUUID").get());
|
||||
ldapUser.setCn((String) attributes.get("cn").get());
|
||||
ldapUser.setUid((String) attributes.get("uid").get());
|
||||
ldapUser.setEmail(email);
|
||||
|
||||
// 使用用户dn+提交的密码去登录ldap系统
|
||||
// 登录成功则意味着密码正确
|
||||
// 登录失败则意味着密码错误
|
||||
try {
|
||||
ldapContext = initContext(url, ldapUser.getDn() + "," + baseDN, password);
|
||||
log.info("LDAP登录成功");
|
||||
} catch (Exception e) {
|
||||
// 无法登录->密码错误
|
||||
log.info("LDAP用户提交的密码错误");
|
||||
return null;
|
||||
} finally {
|
||||
ldapContext.close();
|
||||
}
|
||||
|
||||
// ou计算
|
||||
String[] rdnList = ldapUser.getDn().split(",");
|
||||
List<String> ou = new ArrayList<>();
|
||||
for (String s : rdnList) {
|
||||
if (StringUtil.startsWith(s, "ou=")) {
|
||||
ou.add(s.replace("ou=", ""));
|
||||
}
|
||||
}
|
||||
Collections.reverse(ou);
|
||||
ldapUser.setOu(ou);
|
||||
|
||||
return ldapUser;
|
||||
}
|
||||
|
||||
public static void closeContext(LdapContext ldapCtx) {
|
||||
if (ldapCtx == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
ldapCtx.close();
|
||||
} catch (NamingException e) {
|
||||
log.error("Failed to close ldap context", e);
|
||||
}
|
||||
}
|
||||
}
|
26
playedu-common/src/main/resources/mapper/LdapUserMapper.xml
Normal file
26
playedu-common/src/main/resources/mapper/LdapUserMapper.xml
Normal file
@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="xyz.playedu.common.mapper.LdapUserMapper">
|
||||
|
||||
<resultMap id="BaseResultMap" type="xyz.playedu.common.domain.LdapUser">
|
||||
<id property="id" column="id" jdbcType="INTEGER"/>
|
||||
<result property="uuid" column="uuid" jdbcType="VARCHAR"/>
|
||||
<result property="userId" column="user_id" jdbcType="INTEGER"/>
|
||||
<result property="cn" column="cn" jdbcType="VARCHAR"/>
|
||||
<result property="dn" column="dn" jdbcType="VARCHAR"/>
|
||||
<result property="ou" column="ou" jdbcType="VARCHAR"/>
|
||||
<result property="uid" column="uid" jdbcType="VARCHAR"/>
|
||||
<result property="email" column="email" jdbcType="VARCHAR"/>
|
||||
<result property="createdAt" column="created_at" jdbcType="TIMESTAMP"/>
|
||||
<result property="updatedAt" column="updated_at" jdbcType="TIMESTAMP"/>
|
||||
</resultMap>
|
||||
|
||||
<sql id="Base_Column_List">
|
||||
id,uuid,user_id,
|
||||
cn,dn,ou,
|
||||
uid,email,created_at,
|
||||
updated_at
|
||||
</sql>
|
||||
</mapper>
|
@ -223,6 +223,70 @@ public class AppConfigCheck implements CommandLineRunner {
|
||||
}
|
||||
},
|
||||
});
|
||||
put(
|
||||
"LDAP配置",
|
||||
new AppConfig[] {
|
||||
new AppConfig() {
|
||||
{
|
||||
setName("启用");
|
||||
setSort(10);
|
||||
setFieldType(BackendConstant.APP_CONFIG_FIELD_TYPE_SWITCH);
|
||||
setKeyName(ConfigConstant.LDAP_ENABLED);
|
||||
setKeyValue("0");
|
||||
setHelp("注意:目前仅支持OpenLDAP服务");
|
||||
}
|
||||
},
|
||||
new AppConfig() {
|
||||
{
|
||||
setName("服务地址");
|
||||
setSort(20);
|
||||
setFieldType(BackendConstant.APP_CONFIG_FIELD_TYPE_TEXT);
|
||||
setKeyName(ConfigConstant.LDAP_URL);
|
||||
setKeyValue("");
|
||||
setHelp("LDAP的对外服务地址。例如:ldap.example.com");
|
||||
}
|
||||
},
|
||||
new AppConfig() {
|
||||
{
|
||||
setName("用户名");
|
||||
setSort(40);
|
||||
setFieldType(BackendConstant.APP_CONFIG_FIELD_TYPE_TEXT);
|
||||
setKeyName(ConfigConstant.LDAP_ADMIN_USER);
|
||||
setKeyValue("");
|
||||
setHelp("用户登录到LDAP。例子:cn=admin,dc=playedu,dc=xyz");
|
||||
}
|
||||
},
|
||||
new AppConfig() {
|
||||
{
|
||||
setName("密码");
|
||||
setSort(50);
|
||||
setFieldType(BackendConstant.APP_CONFIG_FIELD_TYPE_TEXT);
|
||||
setKeyName(ConfigConstant.LDAP_ADMIN_PASS);
|
||||
setKeyValue("");
|
||||
setIsPrivate(1);
|
||||
}
|
||||
},
|
||||
new AppConfig() {
|
||||
{
|
||||
setName("基本DN");
|
||||
setSort(60);
|
||||
setFieldType(BackendConstant.APP_CONFIG_FIELD_TYPE_TEXT);
|
||||
setKeyName(ConfigConstant.LDAP_BASE_DN);
|
||||
setKeyValue("");
|
||||
setHelp("从LDAP根节点搜索用户");
|
||||
}
|
||||
},
|
||||
new AppConfig() {
|
||||
{
|
||||
setName("附件用户DN");
|
||||
setSort(70);
|
||||
setFieldType(BackendConstant.APP_CONFIG_FIELD_TYPE_TEXT);
|
||||
setKeyName(ConfigConstant.LDAP_USER_DN_PREFIX);
|
||||
setKeyValue("");
|
||||
setHelp("搜索用户时,基于基础DN的搜索范围限制");
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -88,7 +88,7 @@ public class MigrationCheck implements CommandLineRunner {
|
||||
`perm_id` int(10) unsigned NOT NULL DEFAULT '0',
|
||||
KEY `role_id` (`role_id`),
|
||||
KEY `perm_id` (`perm_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
""");
|
||||
}
|
||||
});
|
||||
@ -125,7 +125,7 @@ public class MigrationCheck implements CommandLineRunner {
|
||||
`role_id` int(10) unsigned NOT NULL DEFAULT '0',
|
||||
KEY `admin_id` (`admin_id`),
|
||||
KEY `role_id` (`role_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
""");
|
||||
}
|
||||
});
|
||||
@ -198,7 +198,7 @@ public class MigrationCheck implements CommandLineRunner {
|
||||
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`updated_at` timestamp NULL DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
""");
|
||||
}
|
||||
});
|
||||
@ -215,7 +215,7 @@ public class MigrationCheck implements CommandLineRunner {
|
||||
`dep_id` int(11) NOT NULL DEFAULT '0',
|
||||
KEY `course_id` (`course_id`),
|
||||
KEY `dep_id` (`dep_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
""");
|
||||
}
|
||||
});
|
||||
@ -239,7 +239,7 @@ public class MigrationCheck implements CommandLineRunner {
|
||||
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `course_id` (`course_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
""");
|
||||
}
|
||||
});
|
||||
@ -264,7 +264,7 @@ public class MigrationCheck implements CommandLineRunner {
|
||||
`updated_at` timestamp NULL DEFAULT NULL,
|
||||
`deleted_at` timestamp NULL DEFAULT NULL COMMENT '删除时间',
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
""");
|
||||
}
|
||||
});
|
||||
@ -285,7 +285,7 @@ public class MigrationCheck implements CommandLineRunner {
|
||||
`created_at` timestamp NULL DEFAULT NULL,
|
||||
`updated_at` timestamp NULL DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
""");
|
||||
}
|
||||
});
|
||||
@ -306,7 +306,7 @@ public class MigrationCheck implements CommandLineRunner {
|
||||
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`updated_at` timestamp NULL DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
""");
|
||||
}
|
||||
});
|
||||
@ -323,7 +323,7 @@ public class MigrationCheck implements CommandLineRunner {
|
||||
`rid` int(11) NOT NULL,
|
||||
KEY `cid` (`cid`),
|
||||
KEY `rid` (`rid`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
""");
|
||||
}
|
||||
});
|
||||
@ -340,7 +340,7 @@ public class MigrationCheck implements CommandLineRunner {
|
||||
`category_id` int(11) NOT NULL DEFAULT '0',
|
||||
KEY `course_id` (`course_id`),
|
||||
KEY `category_id` (`category_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
""");
|
||||
}
|
||||
});
|
||||
@ -358,7 +358,7 @@ public class MigrationCheck implements CommandLineRunner {
|
||||
`duration` int(10) unsigned NOT NULL COMMENT '视频时长[s]',
|
||||
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
UNIQUE KEY `rid` (`rid`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
""");
|
||||
}
|
||||
});
|
||||
@ -412,7 +412,7 @@ public class MigrationCheck implements CommandLineRunner {
|
||||
`updated_at` timestamp NULL DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `u_h_c_id` (`user_id`,`hour_id`,`course_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
""");
|
||||
}
|
||||
});
|
||||
@ -436,7 +436,7 @@ public class MigrationCheck implements CommandLineRunner {
|
||||
`created_at` timestamp NULL DEFAULT NULL,
|
||||
`updated_at` timestamp NULL DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
""");
|
||||
}
|
||||
});
|
||||
@ -453,7 +453,7 @@ public class MigrationCheck implements CommandLineRunner {
|
||||
`dep_id` int(11) unsigned NOT NULL DEFAULT '0',
|
||||
KEY `user_id` (`user_id`),
|
||||
KEY `dep_id` (`dep_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
""");
|
||||
}
|
||||
});
|
||||
@ -478,7 +478,7 @@ public class MigrationCheck implements CommandLineRunner {
|
||||
`hour_id` int(11) NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `u_d` (`user_id`,`created_date`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
""");
|
||||
}
|
||||
});
|
||||
@ -497,7 +497,7 @@ public class MigrationCheck implements CommandLineRunner {
|
||||
`created_date` date NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `u_d` (`user_id`,`created_date`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
""");
|
||||
}
|
||||
});
|
||||
@ -524,7 +524,7 @@ public class MigrationCheck implements CommandLineRunner {
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `jti` (`jti`),
|
||||
KEY `user_id` (`user_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
""");
|
||||
}
|
||||
});
|
||||
@ -548,7 +548,7 @@ public class MigrationCheck implements CommandLineRunner {
|
||||
`name` varchar(255) NOT NULL DEFAULT '' COMMENT '文件名',
|
||||
`created_at` timestamp NULL DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
""");
|
||||
}
|
||||
});
|
||||
@ -581,7 +581,7 @@ public class MigrationCheck implements CommandLineRunner {
|
||||
`updated_at` timestamp NULL DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `email` (`email`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
""");
|
||||
}
|
||||
});
|
||||
@ -635,7 +635,7 @@ public class MigrationCheck implements CommandLineRunner {
|
||||
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `course_id` (`course_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
""");
|
||||
}
|
||||
});
|
||||
@ -660,7 +660,7 @@ public class MigrationCheck implements CommandLineRunner {
|
||||
`ip` varchar(45) NOT NULL DEFAULT '' COMMENT '下载ip',
|
||||
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
""");
|
||||
}
|
||||
});
|
||||
@ -676,6 +676,31 @@ public class MigrationCheck implements CommandLineRunner {
|
||||
""");
|
||||
}
|
||||
});
|
||||
add(
|
||||
new HashMap<>() {
|
||||
{
|
||||
put("table", "ldap_user");
|
||||
put("name", "20230831_11_51_17_ldap_user");
|
||||
put(
|
||||
"sql",
|
||||
"""
|
||||
CREATE TABLE `ldap_user` (
|
||||
`id` int unsigned NOT NULL AUTO_INCREMENT,
|
||||
`uuid` varchar(64) NOT NULL DEFAULT '' COMMENT '唯一特征值',
|
||||
`user_id` int(11) NOT NULL DEFAULT 0 COMMENT '用户ID',
|
||||
`cn` varchar(120) NOT NULL DEFAULT '' COMMENT 'cn',
|
||||
`dn` varchar(120) NOT NULL DEFAULT '' COMMENT 'dn',
|
||||
`ou` varchar(255) NOT NULL DEFAULT '' COMMENT 'ou',
|
||||
`uid` varchar(120) NOT NULL DEFAULT '' COMMENT 'uid',
|
||||
`email` varchar(120) NOT NULL DEFAULT '' COMMENT '邮箱',
|
||||
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `unique_uuid` (`uuid`) USING BTREE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
""");
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
7
pom.xml
7
pom.xml
@ -49,12 +49,6 @@
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-devtools</artifactId>
|
||||
<scope>runtime</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
@ -170,6 +164,7 @@
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<finalName>playedu-api</finalName>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
|
Loading…
x
Reference in New Issue
Block a user