feat: 初始密码支持自定义修改策略

This commit is contained in:
insistence
2025-09-03 16:44:31 +08:00
parent 2699039c0b
commit 385013db9d
9 changed files with 57 additions and 14 deletions
@@ -165,6 +165,7 @@ async def reset_system_user_pwd(
edit_user = EditUserModel( edit_user = EditUserModel(
userId=reset_user.user_id, userId=reset_user.user_id,
password=PwdUtil.get_password_hash(reset_user.password), password=PwdUtil.get_password_hash(reset_user.password),
pwdUpdateDate=datetime.now(),
updateBy=current_user.user.user_name, updateBy=current_user.user.user_name,
updateTime=datetime.now(), updateTime=datetime.now(),
type='pwd', type='pwd',
@@ -304,6 +305,7 @@ async def reset_system_user_password(
userId=current_user.user.user_id, userId=current_user.user.user_id,
oldPassword=reset_password.old_password, oldPassword=reset_password.old_password,
password=reset_password.new_password, password=reset_password.new_password,
pwdUpdateDate=datetime.now(),
updateBy=current_user.user.user_name, updateBy=current_user.user.user_name,
updateTime=datetime.now(), updateTime=datetime.now(),
) )
@@ -32,6 +32,7 @@ class SysUser(Base):
del_flag = Column(CHAR(1), nullable=True, server_default='0', comment='删除标志(0代表存在 2代表删除)') 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_ip = Column(String(128), nullable=True, server_default="''", comment='最后登录IP')
login_date = Column(DateTime, nullable=True, comment='最后登录时间') 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_by = Column(String(64), nullable=True, server_default="''", comment='创建者')
create_time = Column(DateTime, nullable=True, comment='创建时间', default=datetime.now()) create_time = Column(DateTime, nullable=True, comment='创建时间', default=datetime.now())
update_by = Column(String(64), nullable=True, server_default="''", comment='更新者') update_by = Column(String(64), nullable=True, server_default="''", comment='更新者')
@@ -40,6 +40,7 @@ class UserModel(BaseModel):
del_flag: Optional[Literal['0', '2']] = Field(default=None, description='删除标志(0代表存在 2代表删除)') del_flag: Optional[Literal['0', '2']] = Field(default=None, description='删除标志(0代表存在 2代表删除)')
login_ip: Optional[str] = Field(default=None, description='最后登录IP') login_ip: Optional[str] = Field(default=None, description='最后登录IP')
login_date: Optional[datetime] = Field(default=None, description='最后登录时间') 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_by: Optional[str] = Field(default=None, description='创建者')
create_time: Optional[datetime] = Field(default=None, description='创建时间') create_time: Optional[datetime] = Field(default=None, description='创建时间')
update_by: Optional[str] = Field(default=None, description='更新者') update_by: Optional[str] = Field(default=None, description='更新者')
@@ -125,6 +126,7 @@ class CurrentUserModel(BaseModel):
permissions: List = Field(description='权限信息') permissions: List = Field(description='权限信息')
roles: List = Field(description='角色信息') roles: List = Field(description='角色信息')
user: Union[UserInfoModel, None] = Field(description='用户信息') user: Union[UserInfoModel, None] = Field(description='用户信息')
is_default_modify_pwd: bool = Field(default=False, description='是否初始密码修改提醒')
class UserDetailModel(BaseModel): class UserDetailModel(BaseModel):
@@ -218,7 +218,7 @@ class LoginService:
else: else:
# 此方法可实现同一账号同一时间只能登录一次 # 此方法可实现同一账号同一时间只能登录一次
redis_token = await request.app.state.redis.get( 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 token == redis_token:
if AppConfig.app_same_time_login: if AppConfig.app_same_time_login:
@@ -229,7 +229,7 @@ class LoginService:
) )
else: else:
await request.app.state.redis.set( 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, redis_token,
ex=timedelta(minutes=JwtConfig.jwt_redis_expire_minutes), 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')]) 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')]) 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')] 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( current_user = CurrentUserModel(
permissions=permissions, permissions=permissions,
@@ -253,12 +256,27 @@ class LoginService:
dept=CamelCaseUtil.transform_result(query_user.get('user_dept_info')), dept=CamelCaseUtil.transform_result(query_user.get('user_dept_info')),
role=CamelCaseUtil.transform_result(query_user.get('user_role_info')), role=CamelCaseUtil.transform_result(query_user.get('user_role_info')),
), ),
isDefaultModifyPwd=is_default_modify_pwd,
) )
return current_user return current_user
else: else:
logger.warning('用户token已失效,请重新登录') logger.warning('用户token已失效,请重新登录')
raise AuthException(data='', message='用户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 @classmethod
async def get_current_user_routers(cls, user_id: int, query_db: AsyncSession): async def get_current_user_routers(cls, user_id: int, query_db: AsyncSession):
""" """
@@ -69,6 +69,7 @@ create table sys_user (
del_flag char(1) default '0', del_flag char(1) default '0',
login_ip varchar(128) default '', login_ip varchar(128) default '',
login_date timestamp(0), login_date timestamp(0),
pwd_update_date timestamp(0),
create_by varchar(64) default '', create_by varchar(64) default '',
create_time timestamp(0), create_time timestamp(0),
update_by varchar(64) default '', 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.del_flag is '删除标志(0代表存在 2代表删除)';
comment on column sys_user.login_ip is '最后登录IP'; comment on column sys_user.login_ip is '最后登录IP';
comment on column sys_user.login_date is '最后登录时间'; 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_by is '创建者';
comment on column sys_user.create_time is '创建时间'; comment on column sys_user.create_time is '创建时间';
comment on column sys_user.update_by 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 column sys_user.remark is '备注';
comment on table sys_user 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、岗位信息表 -- 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(3, 'hr', '人力资源', 3, '0', 'admin', current_timestamp, '', null, '');
insert into sys_post values(4, 'user', '普通员工', 4, '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、角色信息表 -- 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(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(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(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、系统访问记录 -- 14、系统访问记录
+4 -2
View File
@@ -54,6 +54,7 @@ create table sys_user (
del_flag char(1) default '0' comment '删除标志(0代表存在 2代表删除)', del_flag char(1) default '0' comment '删除标志(0代表存在 2代表删除)',
login_ip varchar(128) default '' comment '最后登录IP', login_ip varchar(128) default '' comment '最后登录IP',
login_date datetime comment '最后登录时间', login_date datetime comment '最后登录时间',
pwd_update_date datetime comment '密码最后更新时间',
create_by varchar(64) default '' comment '创建者', create_by varchar(64) default '' comment '创建者',
create_time datetime comment '创建时间', create_time datetime comment '创建时间',
update_by varchar(64) default '' 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(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(), '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(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(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(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:提醒用户,如果未修改初始密码,则在登录时就会提醒修改密码对话框');
-- ---------------------------- -- ----------------------------
+1 -1
View File
@@ -77,7 +77,7 @@ export const constantRoutes = [
redirect: 'noredirect', redirect: 'noredirect',
children: [ children: [
{ {
path: 'profile', path: 'profile/:activeTab?',
component: () => import('@/views/system/user/profile/index'), component: () => import('@/views/system/user/profile/index'),
name: 'Profile', name: 'Profile',
meta: { title: '个人中心', icon: 'user' } meta: { title: '个人中心', icon: 'user' }
@@ -1,3 +1,5 @@
import router from '@/router'
import { ElMessageBox } from 'element-plus'
import { login, logout, getInfo } from '@/api/login' import { login, logout, getInfo } from '@/api/login'
import { getToken, setToken, removeToken } from '@/utils/auth' import { getToken, setToken, removeToken } from '@/utils/auth'
import { isHttp, isEmpty } from "@/utils/validate" import { isHttp, isEmpty } from "@/utils/validate"
@@ -51,6 +53,12 @@ const useUserStore = defineStore(
this.name = user.userName this.name = user.userName
this.nickName = user.nickName this.nickName = user.nickName
this.avatar = avatar this.avatar = avatar
/* 初始密码提示 */
if(res.isDefaultModifyPwd) {
ElMessageBox.confirm('您的密码还是初始密码,请修改密码!', '安全提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(() => {
router.push({ name: 'Profile', params: { activeTab: 'resetPwd' } })
}).catch(() => {})
}
resolve(res) resolve(res)
}).catch(error => { }).catch(error => {
reject(error) reject(error)
@@ -48,7 +48,7 @@
<span>基本资料</span> <span>基本资料</span>
</div> </div>
</template> </template>
<el-tabs v-model="activeTab"> <el-tabs v-model="selectedTab">
<el-tab-pane label="基本资料" name="userinfo"> <el-tab-pane label="基本资料" name="userinfo">
<userInfo :user="state.user" /> <userInfo :user="state.user" />
</el-tab-pane> </el-tab-pane>
@@ -68,7 +68,8 @@ import userInfo from "./userInfo";
import resetPwd from "./resetPwd"; import resetPwd from "./resetPwd";
import { getUserProfile } from "@/api/system/user"; import { getUserProfile } from "@/api/system/user";
const activeTab = ref("userinfo"); const route = useRoute()
const selectedTab = ref("userinfo")
const state = reactive({ const state = reactive({
user: {}, user: {},
roleGroup: {}, roleGroup: {},
@@ -83,5 +84,11 @@ function getUser() {
}); });
}; };
getUser(); onMounted(() => {
const activeTab = route.params && route.params.activeTab
if (activeTab) {
selectedTab.value = activeTab
}
getUser()
})
</script> </script>