From 7896c7c5c2b4648d47020df36b0a3ea7eb040d59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=83=A1=E5=9C=A3=E9=94=8B?= <361882884@qq.com> Date: Sat, 15 Nov 2025 22:36:33 +0800 Subject: [PATCH] =?UTF-8?q?=E8=AE=BE=E7=BD=AE=E6=97=B6=E9=97=B4=EF=BC=8C?= =?UTF-8?q?=E8=BF=87=E6=9C=9F=E9=99=90=E5=88=B6=E5=AD=A6=E5=91=98=E7=99=BB?= =?UTF-8?q?=E5=BD=95=EF=BC=8C=E4=BD=86=E6=98=AF=E4=B8=8D=E9=99=90=E5=88=B6?= =?UTF-8?q?=E7=B3=BB=E7=BB=9F=E7=94=A8=E6=88=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ruoyi-ui/src/store/modules/user.js | 29 +++--- ruoyi-ui/src/utils/request.js | 14 ++- .../controller/system/SysLoginController.java | 11 +++ .../framework/aspectj/DataScopeAspect.java | 13 ++- .../framework/config/SecurityConfig.java | 4 +- .../web/service/SysLoginService.java | 93 +++++++++++++++++++ 6 files changed, 145 insertions(+), 19 deletions(-) diff --git a/ruoyi-ui/src/store/modules/user.js b/ruoyi-ui/src/store/modules/user.js index e30d47e4..c2c483ee 100644 --- a/ruoyi-ui/src/store/modules/user.js +++ b/ruoyi-ui/src/store/modules/user.js @@ -98,17 +98,24 @@ const user = { commit('SET_NAME', user.userName) commit('SET_NICK_NAME', user.nickName) commit('SET_AVATAR', avatar) - /* 初始密码提示 */ - if(res.isDefaultModifyPwd) { - MessageBox.confirm('您的密码还是初始密码,请修改密码!', '安全提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(() => { - router.push({ name: 'Profile', params: { activeTab: 'resetPwd' } }) - }).catch(() => {}) - } - /* 过期密码提示 */ - if(!res.isDefaultModifyPwd && res.isPasswordExpired) { - MessageBox.confirm('您的密码已过期,请尽快修改密码!', '安全提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(() => { - router.push({ name: 'Profile', params: { activeTab: 'resetPwd' } }) - }).catch(() => {}) + + // 检查是否是学员角色 + const isStudentRole = res.roles && res.roles.some(role => role === 'student' || (typeof role === 'string' && role.includes('学员'))) + + // 学员角色不需要修改密码提示 + if (!isStudentRole) { + /* 初始密码提示 */ + if(res.isDefaultModifyPwd) { + MessageBox.confirm('您的密码还是初始密码,请修改密码!', '安全提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(() => { + router.push({ name: 'Profile', params: { activeTab: 'resetPwd' } }) + }).catch(() => {}) + } + /* 过期密码提示 */ + if(!res.isDefaultModifyPwd && res.isPasswordExpired) { + MessageBox.confirm('您的密码已过期,请尽快修改密码!', '安全提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(() => { + router.push({ name: 'Profile', params: { activeTab: 'resetPwd' } }) + }).catch(() => {}) + } } resolve(res) }).catch(error => { diff --git a/ruoyi-ui/src/utils/request.js b/ruoyi-ui/src/utils/request.js index b5310141..827aa207 100644 --- a/ruoyi-ui/src/utils/request.js +++ b/ruoyi-ui/src/utils/request.js @@ -84,17 +84,21 @@ service.interceptors.response.use(res => { return res.data } if (code === 401) { - if (!isRelogin.show) { + // 登录接口返回401时,不应该显示"登录状态已过期"提示,因为用户还没有登录 + const isLoginRequest = res.config && res.config.url && + (res.config.url.includes('/login') || res.config.url.includes('/student/login') || res.config.url.includes('/register')) + + if (!isLoginRequest && !isRelogin.show) { isRelogin.show = true MessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录', '系统提示', { confirmButtonText: '重新登录', cancelButtonText: '取消', type: 'warning' }).then(() => { isRelogin.show = false store.dispatch('LogOut').then(() => { location.href = '/index' }) - }).catch(() => { - isRelogin.show = false - }) - } + }).catch(() => { + isRelogin.show = false + }) + } return Promise.reject('无效的会话,或者会话已过期,请重新登录。') } else if (code === 500) { Message({ message: msg, type: 'error' }) diff --git a/ry-news-admin/src/main/java/com/ddnai/web/controller/system/SysLoginController.java b/ry-news-admin/src/main/java/com/ddnai/web/controller/system/SysLoginController.java index d9b93eee..67b09f04 100644 --- a/ry-news-admin/src/main/java/com/ddnai/web/controller/system/SysLoginController.java +++ b/ry-news-admin/src/main/java/com/ddnai/web/controller/system/SysLoginController.java @@ -10,6 +10,7 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; import com.ddnai.common.constant.Constants; import com.ddnai.common.core.domain.AjaxResult; +import com.ddnai.common.exception.ServiceException; import com.ddnai.common.core.domain.entity.SysMenu; import com.ddnai.common.core.domain.entity.SysUser; import com.ddnai.common.core.domain.model.LoginBody; @@ -165,6 +166,16 @@ public class SysLoginController // 根据学员编号查找用户(使用user_name作为学员编号) SysUser user = userService.selectUserByUserName(studentNo.trim()); + // 检查系统有效时间(系统管理员不受限制) + try + { + loginService.validateSystemExpireTime(studentNo, user != null ? user.getUserId() : null); + } + catch (ServiceException e) + { + return AjaxResult.error(e.getMessage()); + } + // 如果找不到,返回错误 if (user == null) { diff --git a/ry-news-framework/src/main/java/com/ddnai/framework/aspectj/DataScopeAspect.java b/ry-news-framework/src/main/java/com/ddnai/framework/aspectj/DataScopeAspect.java index 32ce39bf..92a5e8ad 100644 --- a/ry-news-framework/src/main/java/com/ddnai/framework/aspectj/DataScopeAspect.java +++ b/ry-news-framework/src/main/java/com/ddnai/framework/aspectj/DataScopeAspect.java @@ -66,7 +66,18 @@ public class DataScopeAspect protected void handleDataScope(final JoinPoint joinPoint, DataScope controllerDataScope) { // 获取当前的用户 - LoginUser loginUser = SecurityUtils.getLoginUser(); + LoginUser loginUser = null; + try + { + loginUser = SecurityUtils.getLoginUser(); + } + catch (Exception e) + { + // 如果无法获取登录用户(例如匿名访问或登录过程中),则跳过数据权限过滤 + // 这种情况通常发生在登录接口、注册接口等匿名访问的场景 + return; + } + if (StringUtils.isNotNull(loginUser)) { SysUser currentUser = loginUser.getUser(); diff --git a/ry-news-framework/src/main/java/com/ddnai/framework/config/SecurityConfig.java b/ry-news-framework/src/main/java/com/ddnai/framework/config/SecurityConfig.java index 0b88598e..05918047 100644 --- a/ry-news-framework/src/main/java/com/ddnai/framework/config/SecurityConfig.java +++ b/ry-news-framework/src/main/java/com/ddnai/framework/config/SecurityConfig.java @@ -134,8 +134,8 @@ public class SecurityConfig // 注解标记允许匿名访问的url .authorizeHttpRequests((requests) -> { permitAllUrl.getUrls().forEach(url -> requests.antMatchers(url).permitAll()); - // 对于登录login 注册register 验证码captchaImage 允许匿名访问 - requests.antMatchers("/login", "/register", "/captchaImage").permitAll() + // 对于登录login 学员登录student/login 注册register 验证码captchaImage 允许匿名访问 + requests.antMatchers("/login", "/student/login", "/register", "/captchaImage").permitAll() // API接口,可匿名访问 .antMatchers("/api/**").permitAll() // 静态资源,可匿名访问 diff --git a/ry-news-framework/src/main/java/com/ddnai/framework/web/service/SysLoginService.java b/ry-news-framework/src/main/java/com/ddnai/framework/web/service/SysLoginService.java index 597e7681..afe682a1 100644 --- a/ry-news-framework/src/main/java/com/ddnai/framework/web/service/SysLoginService.java +++ b/ry-news-framework/src/main/java/com/ddnai/framework/web/service/SysLoginService.java @@ -1,6 +1,9 @@ package com.ddnai.framework.web.service; +import java.util.Date; import javax.annotation.Resource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.BadCredentialsException; @@ -25,6 +28,7 @@ import com.ddnai.common.utils.ip.IpUtils; import com.ddnai.framework.manager.AsyncManager; import com.ddnai.framework.manager.factory.AsyncFactory; import com.ddnai.framework.security.context.AuthenticationContextHolder; +import com.ddnai.common.core.domain.entity.SysUser; import com.ddnai.system.service.ISysConfigService; import com.ddnai.system.service.ISysUserService; @@ -36,6 +40,8 @@ import com.ddnai.system.service.ISysUserService; @Component public class SysLoginService { + private static final Logger log = LoggerFactory.getLogger(SysLoginService.class); + @Autowired private TokenService tokenService; @@ -135,6 +141,8 @@ public class SysLoginService */ public void loginPreCheck(String username, String password) { + // 检查系统有效时间(系统管理员不受限制) + validateSystemExpireTime(username, null); // 用户名或密码为空 错误 if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) { @@ -164,6 +172,91 @@ public class SysLoginService } } + /** + * 校验系统有效时间 + * 检查 system.top 参数,如果存在且截止时间小于当前时间,则阻止登录 + * 系统管理员(用户ID=1)不受此限制 + * + * @param username 用户名(用于记录日志) + * @param userId 用户ID(如果为1则跳过检查,为null时通过用户名查找用户判断) + */ + public void validateSystemExpireTime(String username, Long userId) + { + // 如果提供了用户ID,判断是否是系统管理员 + if (userId != null) + { + if (com.ddnai.common.utils.SecurityUtils.isAdmin(userId)) + { + // 系统管理员不受限制,直接返回 + return; + } + } + else if (StringUtils.isNotEmpty(username)) + { + // 如果没有提供用户ID,通过用户名查找用户判断是否是系统管理员 + try + { + SysUser user = userService.selectUserByUserName(username); + if (user != null && user.isAdmin()) + { + // 系统管理员不受限制,直接返回 + return; + } + } + catch (Exception e) + { + // 查找用户失败,继续检查系统有效时间 + log.debug("查找用户失败,继续检查系统有效时间: {}", username, e); + } + } + + // 获取系统有效时间配置 + String expireTimeStr = configService.selectConfigByKey("system.top"); + if (StringUtils.isNotEmpty(expireTimeStr)) + { + try + { + // 解析日期时间(支持 yyyy-MM-dd 和 yyyy-MM-dd HH:mm:ss 格式) + Date expireTime = DateUtils.parseDate(expireTimeStr); + Date now = DateUtils.getNowDate(); + + // 如果配置的是日期格式(yyyy-MM-dd),则将其转换为当天的结束时间(23:59:59) + // 这样当天仍然可以登录 + if (expireTime != null) + { + // 如果配置值只包含日期部分(长度为10),则认为是日期格式 + if (expireTimeStr.trim().length() == 10) + { + // 将日期设置为当天的23:59:59 + java.util.Calendar cal = java.util.Calendar.getInstance(); + cal.setTime(expireTime); + cal.set(java.util.Calendar.HOUR_OF_DAY, 23); + cal.set(java.util.Calendar.MINUTE, 59); + cal.set(java.util.Calendar.SECOND, 59); + cal.set(java.util.Calendar.MILLISECOND, 999); + expireTime = cal.getTime(); + } + + // 如果当前时间在过期时间之后,则系统已过期 + if (now.after(expireTime)) + { + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, "系统有效时间已过期")); + throw new ServiceException("系统出现问题,请联系管理员"); + } + } + } + catch (ServiceException e) + { + throw e; + } + catch (Exception e) + { + // 日期解析失败,记录日志但不阻止登录 + log.warn("系统有效时间配置解析失败: {}", expireTimeStr, e); + } + } + } + /** * 记录登录信息 *