设置时间,过期限制学员登录,但是不限制系统用户

This commit is contained in:
胡圣锋 2025-11-15 22:36:33 +08:00
parent 52b3a345bf
commit 7896c7c5c2
6 changed files with 145 additions and 19 deletions

View File

@ -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 => {

View File

@ -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' })

View File

@ -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)
{

View File

@ -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();

View File

@ -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()
// 静态资源可匿名访问

View File

@ -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);
}
}
}
/**
* 记录登录信息
*