diff --git a/ruoyi-fastapi-backend/module_admin/controller/user_controller.py b/ruoyi-fastapi-backend/module_admin/controller/user_controller.py index ed7cf7a..91d6611 100644 --- a/ruoyi-fastapi-backend/module_admin/controller/user_controller.py +++ b/ruoyi-fastapi-backend/module_admin/controller/user_controller.py @@ -165,6 +165,7 @@ async def reset_system_user_pwd( edit_user = EditUserModel( userId=reset_user.user_id, password=PwdUtil.get_password_hash(reset_user.password), + pwdUpdateDate=datetime.now(), updateBy=current_user.user.user_name, updateTime=datetime.now(), type='pwd', @@ -304,6 +305,7 @@ async def reset_system_user_password( userId=current_user.user.user_id, oldPassword=reset_password.old_password, password=reset_password.new_password, + pwdUpdateDate=datetime.now(), updateBy=current_user.user.user_name, updateTime=datetime.now(), ) diff --git a/ruoyi-fastapi-backend/module_admin/entity/do/user_do.py b/ruoyi-fastapi-backend/module_admin/entity/do/user_do.py index 246461f..c71b5b7 100644 --- a/ruoyi-fastapi-backend/module_admin/entity/do/user_do.py +++ b/ruoyi-fastapi-backend/module_admin/entity/do/user_do.py @@ -32,6 +32,7 @@ class SysUser(Base): del_flag = Column(CHAR(1), nullable=True, server_default='0', comment='删除标志(0代表存在 2代表删除)') login_ip = Column(String(128), nullable=True, server_default="''", comment='最后登录IP') login_date = Column(DateTime, nullable=True, comment='最后登录时间') + pwd_update_date = Column(DateTime, nullable=True, comment='密码最后更新时间') create_by = Column(String(64), nullable=True, server_default="''", comment='创建者') create_time = Column(DateTime, nullable=True, comment='创建时间', default=datetime.now()) update_by = Column(String(64), nullable=True, server_default="''", comment='更新者') diff --git a/ruoyi-fastapi-backend/module_admin/entity/vo/user_vo.py b/ruoyi-fastapi-backend/module_admin/entity/vo/user_vo.py index 4eac953..9ef8867 100644 --- a/ruoyi-fastapi-backend/module_admin/entity/vo/user_vo.py +++ b/ruoyi-fastapi-backend/module_admin/entity/vo/user_vo.py @@ -40,6 +40,7 @@ class UserModel(BaseModel): del_flag: Optional[Literal['0', '2']] = Field(default=None, description='删除标志(0代表存在 2代表删除)') login_ip: Optional[str] = Field(default=None, description='最后登录IP') login_date: Optional[datetime] = Field(default=None, description='最后登录时间') + pwd_update_date: Optional[datetime] = Field(default=None, description='密码最后更新时间') create_by: Optional[str] = Field(default=None, description='创建者') create_time: Optional[datetime] = Field(default=None, description='创建时间') update_by: Optional[str] = Field(default=None, description='更新者') @@ -125,6 +126,7 @@ class CurrentUserModel(BaseModel): permissions: List = Field(description='权限信息') roles: List = Field(description='角色信息') user: Union[UserInfoModel, None] = Field(description='用户信息') + is_default_modify_pwd: bool = Field(default=False, description='是否初始密码修改提醒') class UserDetailModel(BaseModel): diff --git a/ruoyi-fastapi-backend/module_admin/service/login_service.py b/ruoyi-fastapi-backend/module_admin/service/login_service.py index 9c61f22..e5a5f30 100644 --- a/ruoyi-fastapi-backend/module_admin/service/login_service.py +++ b/ruoyi-fastapi-backend/module_admin/service/login_service.py @@ -218,7 +218,7 @@ class LoginService: else: # 此方法可实现同一账号同一时间只能登录一次 redis_token = await request.app.state.redis.get( - f"{RedisInitKeyConfig.ACCESS_TOKEN.key}:{query_user.get('user_basic_info').user_id}" + f'{RedisInitKeyConfig.ACCESS_TOKEN.key}:{query_user.get("user_basic_info").user_id}' ) if token == redis_token: if AppConfig.app_same_time_login: @@ -229,7 +229,7 @@ class LoginService: ) else: await request.app.state.redis.set( - f"{RedisInitKeyConfig.ACCESS_TOKEN.key}:{query_user.get('user_basic_info').user_id}", + f'{RedisInitKeyConfig.ACCESS_TOKEN.key}:{query_user.get("user_basic_info").user_id}', redis_token, ex=timedelta(minutes=JwtConfig.jwt_redis_expire_minutes), ) @@ -242,6 +242,9 @@ class LoginService: post_ids = ','.join([str(row.post_id) for row in query_user.get('user_post_info')]) role_ids = ','.join([str(row.role_id) for row in query_user.get('user_role_info')]) roles = [row.role_key for row in query_user.get('user_role_info')] + is_default_modify_pwd = await cls.__init_password_is_modify( + request, query_user.get('user_basic_info').pwd_update_date + ) current_user = CurrentUserModel( permissions=permissions, @@ -253,12 +256,27 @@ class LoginService: dept=CamelCaseUtil.transform_result(query_user.get('user_dept_info')), role=CamelCaseUtil.transform_result(query_user.get('user_role_info')), ), + isDefaultModifyPwd=is_default_modify_pwd, ) return current_user else: logger.warning('用户token已失效,请重新登录') raise AuthException(data='', message='用户token已失效,请重新登录') + @classmethod + async def __init_password_is_modify(cls, request: Request, pwd_update_date: datetime): + """ + 判断当前用户是否初始密码登录 + + :param request: Request对象 + :param pwd_update_date: 密码最后更新时间 + :return: 是否初始密码登录 + """ + init_password_is_modify = await request.app.state.redis.get( + f'{RedisInitKeyConfig.SYS_CONFIG.key}:sys.account.initPasswordModify' + ) + return init_password_is_modify == '1' and pwd_update_date is None + @classmethod async def get_current_user_routers(cls, user_id: int, query_db: AsyncSession): """ diff --git a/ruoyi-fastapi-backend/sql/ruoyi-fastapi-pg.sql b/ruoyi-fastapi-backend/sql/ruoyi-fastapi-pg.sql index a47c14f..fbe559b 100644 --- a/ruoyi-fastapi-backend/sql/ruoyi-fastapi-pg.sql +++ b/ruoyi-fastapi-backend/sql/ruoyi-fastapi-pg.sql @@ -69,6 +69,7 @@ create table sys_user ( del_flag char(1) default '0', login_ip varchar(128) default '', login_date timestamp(0), + pwd_update_date timestamp(0), create_by varchar(64) default '', create_time timestamp(0), update_by varchar(64) default '', @@ -91,6 +92,7 @@ comment on column sys_user.status is '帐号状态(0正常 1停用)'; comment on column sys_user.del_flag is '删除标志(0代表存在 2代表删除)'; comment on column sys_user.login_ip is '最后登录IP'; comment on column sys_user.login_date is '最后登录时间'; +comment on column sys_user.pwd_update_date is '密码最后更新时间'; comment on column sys_user.create_by is '创建者'; comment on column sys_user.create_time is '创建时间'; comment on column sys_user.update_by is '更新者'; @@ -98,6 +100,12 @@ comment on column sys_user.update_time is '更新时间'; comment on column sys_user.remark is '备注'; comment on table sys_user is '用户信息表'; +-- ---------------------------- +-- 初始化-用户信息表数据 +-- ---------------------------- +insert into sys_user values(1, 103, 'admin', '超级管理员', '00', 'niangao@163.com', '15888888888', '1', '', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '127.0.0.1', current_timestamp, current_timestamp, 'admin', current_timestamp, '', null, '管理员'); +insert into sys_user values(2, 105, 'niangao', '年糕', '00', 'niangao@qq.com', '15666666666', '1', '', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '127.0.0.1', current_timestamp, current_timestamp, 'admin', current_timestamp, '', null, '测试员'); + -- ---------------------------- -- 3、岗位信息表 -- ---------------------------- @@ -136,12 +144,6 @@ insert into sys_post values(2, 'se', '项目经理', 2, '0', 'admin', current insert into sys_post values(3, 'hr', '人力资源', 3, '0', 'admin', current_timestamp, '', null, ''); insert into sys_post values(4, 'user', '普通员工', 4, '0', 'admin', current_timestamp, '', null, ''); --- ---------------------------- --- 初始化-用户信息表数据 --- ---------------------------- -insert into sys_user values(1, 103, 'admin', '超级管理员', '00', 'niangao@163.com', '15888888888', '1', '', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '127.0.0.1', current_timestamp, 'admin', current_timestamp, '', null, '管理员'); -insert into sys_user values(2, 105, 'niangao', '年糕', '00', 'niangao@qq.com', '15666666666', '1', '', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '127.0.0.1', current_timestamp, 'admin', current_timestamp, '', null, '测试员'); - -- ---------------------------- -- 4、角色信息表 -- ---------------------------- @@ -704,6 +706,7 @@ insert into sys_config values(3, '主框架页-侧边栏主题', 'sys. insert into sys_config values(4, '账号自助-验证码开关', 'sys.account.captchaEnabled', 'true', 'Y', 'admin', current_timestamp, '', null, '是否开启验证码功能(true开启,false关闭)'); insert into sys_config values(5, '账号自助-是否开启用户注册功能', 'sys.account.registerUser', 'false', 'Y', 'admin', current_timestamp, '', null, '是否开启注册用户功能(true开启,false关闭)'); insert into sys_config values(6, '用户登录-黑名单列表', 'sys.login.blackIPList', '', 'Y', 'admin', current_timestamp, '', null, '设置登录IP黑名单限制,多个匹配项以;分隔,支持匹配(*通配、网段)'); +insert into sys_config values(7, '用户管理-初始密码修改策略', 'sys.account.initPasswordModify', '1', 'Y', 'admin', current_timestamp, '', null, '0:初始密码修改策略关闭,没有任何提示,1:提醒用户,如果未修改初始密码,则在登录时就会提醒修改密码对话框'); -- ---------------------------- -- 14、系统访问记录 diff --git a/ruoyi-fastapi-backend/sql/ruoyi-fastapi.sql b/ruoyi-fastapi-backend/sql/ruoyi-fastapi.sql index 7b796a7..2795698 100644 --- a/ruoyi-fastapi-backend/sql/ruoyi-fastapi.sql +++ b/ruoyi-fastapi-backend/sql/ruoyi-fastapi.sql @@ -54,6 +54,7 @@ create table sys_user ( del_flag char(1) default '0' comment '删除标志(0代表存在 2代表删除)', login_ip varchar(128) default '' comment '最后登录IP', login_date datetime comment '最后登录时间', + pwd_update_date datetime comment '密码最后更新时间', create_by varchar(64) default '' comment '创建者', create_time datetime comment '创建时间', update_by varchar(64) default '' comment '更新者', @@ -65,8 +66,8 @@ create table sys_user ( -- ---------------------------- -- 初始化-用户信息表数据 -- ---------------------------- -insert into sys_user values(1, 103, 'admin', '超级管理员', '00', 'niangao@163.com', '15888888888', '1', '', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '127.0.0.1', sysdate(), 'admin', sysdate(), '', null, '管理员'); -insert into sys_user values(2, 105, 'niangao', '年糕', '00', 'niangao@qq.com', '15666666666', '1', '', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '127.0.0.1', sysdate(), 'admin', sysdate(), '', null, '测试员'); +insert into sys_user values(1, 103, 'admin', '超级管理员', '00', 'niangao@163.com', '15888888888', '1', '', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '127.0.0.1', sysdate(), sysdate(), 'admin', sysdate(), '', null, '管理员'); +insert into sys_user values(2, 105, 'niangao', '年糕', '00', 'niangao@qq.com', '15666666666', '1', '', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '127.0.0.1', sysdate(), sysdate(), 'admin', sysdate(), '', null, '测试员'); -- ---------------------------- @@ -554,6 +555,7 @@ insert into sys_config values(3, '主框架页-侧边栏主题', 'sys. insert into sys_config values(4, '账号自助-验证码开关', 'sys.account.captchaEnabled', 'true', 'Y', 'admin', sysdate(), '', null, '是否开启验证码功能(true开启,false关闭)'); insert into sys_config values(5, '账号自助-是否开启用户注册功能', 'sys.account.registerUser', 'false', 'Y', 'admin', sysdate(), '', null, '是否开启注册用户功能(true开启,false关闭)'); insert into sys_config values(6, '用户登录-黑名单列表', 'sys.login.blackIPList', '', 'Y', 'admin', sysdate(), '', null, '设置登录IP黑名单限制,多个匹配项以;分隔,支持匹配(*通配、网段)'); +insert into sys_config values(7, '用户管理-初始密码修改策略', 'sys.account.initPasswordModify', '1', 'Y', 'admin', sysdate(), '', null, '0:初始密码修改策略关闭,没有任何提示,1:提醒用户,如果未修改初始密码,则在登录时就会提醒修改密码对话框'); -- ---------------------------- diff --git a/ruoyi-fastapi-frontend/src/router/index.js b/ruoyi-fastapi-frontend/src/router/index.js index e8b982d..f31c43f 100644 --- a/ruoyi-fastapi-frontend/src/router/index.js +++ b/ruoyi-fastapi-frontend/src/router/index.js @@ -77,7 +77,7 @@ export const constantRoutes = [ redirect: 'noredirect', children: [ { - path: 'profile', + path: 'profile/:activeTab?', component: () => import('@/views/system/user/profile/index'), name: 'Profile', meta: { title: '个人中心', icon: 'user' } diff --git a/ruoyi-fastapi-frontend/src/store/modules/user.js b/ruoyi-fastapi-frontend/src/store/modules/user.js index 2c6398f..36b562f 100644 --- a/ruoyi-fastapi-frontend/src/store/modules/user.js +++ b/ruoyi-fastapi-frontend/src/store/modules/user.js @@ -1,3 +1,5 @@ +import router from '@/router' +import { ElMessageBox } from 'element-plus' import { login, logout, getInfo } from '@/api/login' import { getToken, setToken, removeToken } from '@/utils/auth' import { isHttp, isEmpty } from "@/utils/validate" @@ -51,6 +53,12 @@ const useUserStore = defineStore( this.name = user.userName this.nickName = user.nickName this.avatar = avatar + /* 初始密码提示 */ + if(res.isDefaultModifyPwd) { + ElMessageBox.confirm('您的密码还是初始密码,请修改密码!', '安全提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(() => { + router.push({ name: 'Profile', params: { activeTab: 'resetPwd' } }) + }).catch(() => {}) + } resolve(res) }).catch(error => { reject(error) diff --git a/ruoyi-fastapi-frontend/src/views/system/user/profile/index.vue b/ruoyi-fastapi-frontend/src/views/system/user/profile/index.vue index 5851f05..26f4bd4 100644 --- a/ruoyi-fastapi-frontend/src/views/system/user/profile/index.vue +++ b/ruoyi-fastapi-frontend/src/views/system/user/profile/index.vue @@ -48,7 +48,7 @@ 基本资料 - + @@ -68,7 +68,8 @@ import userInfo from "./userInfo"; import resetPwd from "./resetPwd"; import { getUserProfile } from "@/api/system/user"; -const activeTab = ref("userinfo"); +const route = useRoute() +const selectedTab = ref("userinfo") const state = reactive({ user: {}, roleGroup: {}, @@ -83,5 +84,11 @@ function getUser() { }); }; -getUser(); +onMounted(() => { + const activeTab = route.params && route.params.activeTab + if (activeTab) { + selectedTab.value = activeTab + } + getUser() +})