修改密码更新逻辑.

This commit is contained in:
lijiahang
2023-09-20 18:23:28 +08:00
parent d29cdac130
commit 6c8ccb3864
16 changed files with 176 additions and 41 deletions

View File

@@ -23,6 +23,14 @@ public interface SecurityFrameworkService {
*/ */
boolean hasPermission(String permission); boolean hasPermission(String permission);
/**
* 检查是否有任意权限
*
* @param permissions 权限
* @return has
*/
boolean hasAnyPermission(String... permissions);
/** /**
* 检查是否有角色 * 检查是否有角色
* *

View File

@@ -22,6 +22,11 @@ public class SecurityFrameworkServiceDelegate implements SecurityFrameworkServic
return delegate.hasPermission(permission); return delegate.hasPermission(permission);
} }
@Override
public boolean hasAnyPermission(String... permissions) {
return delegate.hasAnyPermission(permissions);
}
@Override @Override
public boolean hasRole(String role) { public boolean hasRole(String role) {
return delegate.hasRole(role); return delegate.hasRole(role);

View File

@@ -4,6 +4,8 @@
:width="430" :width="430"
:mask-closable="false" :mask-closable="false"
:unmount-on-close="true" :unmount-on-close="true"
:ok-button-props="{ disabled: loading }"
:cancel-button-props="{ disabled: loading }"
:on-before-ok="handlerOk" :on-before-ok="handlerOk"
@cancel="handleClose"> @cancel="handleClose">
<a-spin :loading="loading"> <a-spin :loading="loading">

View File

@@ -58,7 +58,7 @@ public class HostKeyController {
@GetMapping("/get") @GetMapping("/get")
@Operation(summary = "查询主机秘钥详情") @Operation(summary = "查询主机秘钥详情")
@Parameter(name = "id", description = "id", required = true) @Parameter(name = "id", description = "id", required = true)
@PreAuthorize("@ss.hasPermission('asset:host-key:detail')") @PreAuthorize("@ss.hasAnyPermission('asset:host-key:detail', 'asset:host-key:update')")
public HostKeyVO getHostKey(@RequestParam("id") Long id) { public HostKeyVO getHostKey(@RequestParam("id") Long id) {
return hostKeyService.getHostKeyById(id); return hostKeyService.getHostKeyById(id);
} }

View File

@@ -47,4 +47,7 @@ public class HostKeyUpdateRequest implements Serializable {
@Schema(description = "密码") @Schema(description = "密码")
private String password; private String password;
@Schema(description = "是否使用新密码")
private Boolean useNewPassword;
} }

View File

@@ -3,6 +3,7 @@ package com.orion.ops.module.asset.service.impl;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.orion.lang.define.wrapper.DataGrid; import com.orion.lang.define.wrapper.DataGrid;
import com.orion.lang.utils.Booleans;
import com.orion.lang.utils.Strings; import com.orion.lang.utils.Strings;
import com.orion.ops.framework.common.constant.Const; import com.orion.ops.framework.common.constant.Const;
import com.orion.ops.framework.common.constant.ErrorMessage; import com.orion.ops.framework.common.constant.ErrorMessage;
@@ -72,9 +73,16 @@ public class HostKeyServiceImpl implements HostKeyService {
HostKeyDO updateRecord = HostKeyConvert.MAPPER.to(request); HostKeyDO updateRecord = HostKeyConvert.MAPPER.to(request);
// 查询数据是否冲突 // 查询数据是否冲突
this.checkHostKeyPresent(updateRecord); this.checkHostKeyPresent(updateRecord);
String password = updateRecord.getPassword(); if (Booleans.isTrue(request.getUseNewPassword())) {
if (!Strings.isBlank(password)) { // 使用新密码
updateRecord.setPassword(CryptoUtils.encryptAsString(password)); String password = updateRecord.getPassword();
if (Strings.isBlank(password)) {
updateRecord.setPassword(Const.EMPTY);
} else {
updateRecord.setPassword(CryptoUtils.encryptAsString(password));
}
} else {
updateRecord.setPassword(null);
} }
// 更新 // 更新
int effect = hostKeyDAO.updateById(updateRecord); int effect = hostKeyDAO.updateById(updateRecord);
@@ -101,7 +109,7 @@ public class HostKeyServiceImpl implements HostKeyService {
HostKeyDO record = hostKeyDAO.selectById(id); HostKeyDO record = hostKeyDAO.selectById(id);
Valid.notNull(record, ErrorMessage.DATA_ABSENT); Valid.notNull(record, ErrorMessage.DATA_ABSENT);
String password = record.getPassword(); String password = record.getPassword();
if (password != null) { if (!Strings.isBlank(password)) {
record.setPassword(CryptoUtils.decryptAsString(password)); record.setPassword(CryptoUtils.decryptAsString(password));
} }
return record; return record;

View File

@@ -36,6 +36,12 @@ public class SecurityFrameworkServiceImpl implements SecurityFrameworkService {
return permissionService.hasPermission(permission); return permissionService.hasPermission(permission);
} }
@Override
public boolean hasAnyPermission(String... permissions) {
// 检查是否有权限
return permissionService.hasAnyPermission(permissions);
}
@Override @Override
public boolean hasRole(String role) { public boolean hasRole(String role) {
// 检查是否有角色 // 检查是否有角色

View File

@@ -59,6 +59,14 @@ public interface PermissionService {
*/ */
boolean hasPermission(String permission); boolean hasPermission(String permission);
/**
* 检查当前用户是否含任意权限 (有效性判断)
*
* @param permissions permissions
* @return 是否包含
*/
boolean hasAnyPermission(String... permissions);
/** /**
* 获取用户菜单 * 获取用户菜单
* *

View File

@@ -1,5 +1,6 @@
package com.orion.ops.module.infra.service.impl; package com.orion.ops.module.infra.service.impl;
import com.orion.lang.utils.Arrays1;
import com.orion.lang.utils.collect.Lists; import com.orion.lang.utils.collect.Lists;
import com.orion.ops.framework.common.constant.Const; import com.orion.ops.framework.common.constant.Const;
import com.orion.ops.framework.common.security.LoginUser; import com.orion.ops.framework.common.security.LoginUser;
@@ -129,17 +130,25 @@ public class PermissionServiceImpl implements PermissionService {
return true; return true;
} }
// 检查普通角色是否有此权限 // 检查普通角色是否有此权限
for (String role : roles) { return roles.stream().anyMatch(s -> this.checkRoleHasPermission(s, permission));
// 获取角色权限列表 }
List<SystemMenuCacheDTO> menus = roleMenuCache.get(role);
if (Lists.isEmpty(menus)) { @Override
continue; public boolean hasAnyPermission(String... permissions) {
} if (Arrays1.isEmpty(permissions)) {
boolean has = menus.stream() return true;
.filter(s -> MenuStatusEnum.ENABLED.getStatus().equals(s.getStatus())) }
.map(SystemMenuCacheDTO::getPermission) // 获取用户角色
.filter(Objects::nonNull) List<String> roles = this.getUserEnabledRoles();
.anyMatch(permission::equals); if (roles.isEmpty()) {
return false;
}
// 检查是否为超级管理员
if (RoleDefine.containsAdmin(roles)) {
return true;
}
for (String permission : permissions) {
final boolean has = roles.stream().anyMatch(s -> this.checkRoleHasPermission(s, permission));
if (has) { if (has) {
return true; return true;
} }
@@ -212,6 +221,27 @@ public class PermissionServiceImpl implements PermissionService {
.build(); .build();
} }
/**
* 检查角色是否有权限
*
* @param role role
* @param permission permission
* @return 是否有权限
*/
private boolean checkRoleHasPermission(String role, String permission) {
// 获取角色权限列表
List<SystemMenuCacheDTO> menus = roleMenuCache.get(role);
if (Lists.isEmpty(menus)) {
return false;
}
// 检查是否有此权限
return menus.stream()
.filter(s -> MenuStatusEnum.ENABLED.getStatus().equals(s.getStatus()))
.map(SystemMenuCacheDTO::getPermission)
.filter(Objects::nonNull)
.anyMatch(permission::equals);
}
/** /**
* 获取用户启用的角色 * 获取用户启用的角色
* *

View File

@@ -9,6 +9,7 @@ export interface HostKeyCreateRequest {
publicKey?: string; publicKey?: string;
privateKey?: string; privateKey?: string;
password?: string; password?: string;
useNewPassword?: boolean;
} }
/** /**

View File

@@ -1,13 +1,18 @@
<template> <template>
<a-drawer :visible="visible" <a-drawer class="drawer-body-padding-0"
:visible="visible"
:title="title" :title="title"
:width="470" :width="470"
:mask-closable="false" :mask-closable="false"
:unmount-on-close="true" :unmount-on-close="true"
:ok-button-props="{ disabled: loading || isViewHandler }"
:cancel-button-props="{ disabled: loading }"
:on-before-ok="handlerOk" :on-before-ok="handlerOk"
@cancel="handleClose"> @cancel="handleClose">
<a-spin :loading="loading"> <a-spin class="form-wrapper" :loading="loading">
<a-alert style="margin-bottom: 18px;">请使用 ssh-keygen -m PEM -t rsa 生成秘钥</a-alert> <a-alert class="keygen-alert">
请使用 ssh-keygen -m PEM -t rsa 生成秘钥
</a-alert>
<a-form :model="formModel" <a-form :model="formModel"
ref="formRef" ref="formRef"
label-align="right" label-align="right"
@@ -17,19 +22,23 @@
:rules="formRules"> :rules="formRules">
<!-- 名称 --> <!-- 名称 -->
<a-form-item field="name" label="名称"> <a-form-item field="name" label="名称">
<a-input v-model="formModel.name" placeholder="请输入名称" /> <a-input v-model="formModel.name"
:disabled="isViewHandler"
placeholder="请输入名称" />
</a-form-item> </a-form-item>
<!-- 公钥文本 --> <!-- 公钥文本 -->
<a-form-item field="publicKey" label="公钥"> <a-form-item field="publicKey" label="公钥">
<a-upload :auto-upload="false" <a-upload :auto-upload="false"
:show-file-list="false" :show-file-list="false"
draggable :draggable="true"
:disabled="isViewHandler"
@change="selectPublicFile" @change="selectPublicFile"
@click.prevent="() => {}"> @click.prevent="() => {}">
<template #upload-button> <template #upload-button>
<a-textarea v-model="formModel.publicKey" <a-textarea v-model="formModel.publicKey"
:disabled="isViewHandler"
placeholder="请输入公钥文本或将文件拖拽到此处" placeholder="请输入公钥文本或将文件拖拽到此处"
:auto-size="{ minRows: 7, maxRows: 7}" /> :auto-size="{ minRows: 8, maxRows: 8}" />
</template> </template>
</a-upload> </a-upload>
</a-form-item> </a-form-item>
@@ -37,19 +46,38 @@
<a-form-item field="privateKey" label="私钥"> <a-form-item field="privateKey" label="私钥">
<a-upload :auto-upload="false" <a-upload :auto-upload="false"
:show-file-list="false" :show-file-list="false"
draggable :draggable="true"
:disabled="isViewHandler"
@change="selectPrivateFile" @change="selectPrivateFile"
@click.prevent="() => {}"> @click.prevent="() => {}">
<template #upload-button> <template #upload-button>
<a-textarea v-model="formModel.privateKey" <a-textarea v-model="formModel.privateKey"
:disabled="isViewHandler"
placeholder="请输入私钥文本或将文件拖拽到此处" placeholder="请输入私钥文本或将文件拖拽到此处"
:auto-size="{ minRows: 8, maxRows: 8}" /> :auto-size="{ minRows: 8, maxRows: 8}" />
</template> </template>
</a-upload> </a-upload>
</a-form-item> </a-form-item>
<!-- 密码 --> <!-- 密码 -->
<a-form-item field="password" label="密码"> <a-form-item v-if="!isViewHandler"
<a-input-password v-model="formModel.password" placeholder="请输入私钥密码" /> field="password"
label="密码"
style="justify-content: space-between;">
<a-input-password v-model="formModel.password"
:disabled="!formModel.useNewPassword"
class="password-input"
placeholder="请输入私钥密码" />
<a-switch v-model="formModel.useNewPassword"
class="password-switch"
type="round"
size="large">
<template #checked>
使用新密码
</template>
<template #unchecked>
使用原密码
</template>
</a-switch>
</a-form-item> </a-form-item>
</a-form> </a-form>
</a-spin> </a-spin>
@@ -78,6 +106,7 @@
const title = ref<string>(); const title = ref<string>();
const isAddHandle = ref<boolean>(true); const isAddHandle = ref<boolean>(true);
const isViewHandler = ref<boolean>(false);
const defaultForm = () => { const defaultForm = () => {
return { return {
@@ -86,6 +115,7 @@
publicKey: undefined, publicKey: undefined,
privateKey: undefined, privateKey: undefined,
password: undefined, password: undefined,
useNewPassword: false
}; };
}; };
@@ -98,6 +128,7 @@
const openAdd = () => { const openAdd = () => {
title.value = '添加主机秘钥'; title.value = '添加主机秘钥';
isAddHandle.value = true; isAddHandle.value = true;
isViewHandler.value = false;
renderForm({ ...defaultForm() }); renderForm({ ...defaultForm() });
setVisible(true); setVisible(true);
}; };
@@ -106,10 +137,24 @@
const openUpdate = async (record: any) => { const openUpdate = async (record: any) => {
title.value = '修改主机秘钥'; title.value = '修改主机秘钥';
isAddHandle.value = false; isAddHandle.value = false;
isViewHandler.value = false;
await render(record.id);
};
// 打开查看
const openView = async (record: any) => {
title.value = '主机秘钥';
isAddHandle.value = false;
isViewHandler.value = true;
await render(record.id);
};
// 渲染数据
const render = async (id: number) => {
setVisible(true); setVisible(true);
setLoading(true); setLoading(true);
try { try {
const { data } = await getHostKey(record.id); const { data } = await getHostKey(id);
renderForm({ ...data }); renderForm({ ...data });
} catch (e) { } catch (e) {
setVisible(false); setVisible(false);
@@ -125,7 +170,7 @@
}); });
}; };
defineExpose({ openAdd, openUpdate }); defineExpose({ openAdd, openUpdate, openView });
// 选择公钥文件 // 选择公钥文件
const selectPublicFile = async (fileList: FileItem[]) => { const selectPublicFile = async (fileList: FileItem[]) => {
@@ -182,5 +227,21 @@
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
.form-wrapper {
width: 100%;
padding: 12px 12px 0 12px;
}
.keygen-alert {
margin: 0 0 12px 16px;
width: calc(100% - 42px);
}
.password-input {
width: 240px;
}
.password-switch {
margin-left: 16px;
}
</style> </style>

View File

@@ -52,6 +52,13 @@
<!-- 操作 --> <!-- 操作 -->
<template #handle="{ record }"> <template #handle="{ record }">
<div class="table-handle-wrapper"> <div class="table-handle-wrapper">
<!-- 查看 -->
<a-button type="text"
size="mini"
v-permission="['asset:host-key:detail', 'asset:host-key:update']"
@click="emits('openView', record)">
查看
</a-button>
<!-- 修改 --> <!-- 修改 -->
<a-button type="text" <a-button type="text"
size="mini" size="mini"
@@ -90,13 +97,10 @@
import useLoading from '@/hooks/loading'; import useLoading from '@/hooks/loading';
import columns from '../types/table.columns'; import columns from '../types/table.columns';
import { defaultPagination } from '@/types/table'; import { defaultPagination } from '@/types/table';
import {} from '../types/enum.types';
import {} from '../types/const';
import { toOptions } from '@/utils/enum';
const tableRenderData = ref<HostKeyQueryResponse[]>(); const tableRenderData = ref<HostKeyQueryResponse[]>();
const { loading, setLoading } = useLoading(); const { loading, setLoading } = useLoading();
const emits = defineEmits(['openAdd', 'openUpdate']); const emits = defineEmits(['openAdd', 'openUpdate', 'openView']);
const pagination = reactive(defaultPagination()); const pagination = reactive(defaultPagination());
@@ -105,7 +109,6 @@
name: undefined, name: undefined,
publicKey: undefined, publicKey: undefined,
privateKey: undefined, privateKey: undefined,
password: undefined,
}); });
// 删除当前行 // 删除当前行

View File

@@ -2,12 +2,13 @@
<div class="layout-container"> <div class="layout-container">
<!-- 表格 --> <!-- 表格 -->
<host-key-table ref="table" <host-key-table ref="table"
@openAdd="() => drawer.openAdd()" @openView="(e) => drawer.openView(e)"
@openUpdate="(e) => drawer.openUpdate(e)" /> @openAdd="() => drawer.openAdd()"
@openUpdate="(e) => drawer.openUpdate(e)" />
<!-- 添加修改模态框 --> <!-- 添加修改模态框 -->
<host-key-form-drawer ref="drawer" <host-key-form-drawer ref="drawer"
@added="() => table.addedCallback()" @added="() => table.addedCallback()"
@updated="() => table.updatedCallback()" /> @updated="() => table.updatedCallback()" />
</div> </div>
</template> </template>

View File

@@ -32,7 +32,7 @@ const columns = [
}, { }, {
title: '操作', title: '操作',
slotName: 'handle', slotName: 'handle',
width: 130, width: 180,
align: 'center', align: 'center',
fixed: 'right', fixed: 'right',
}, },

View File

@@ -12,17 +12,14 @@ const columns = [
title: '主机名称', title: '主机名称',
dataIndex: 'name', dataIndex: 'name',
slotName: 'name', slotName: 'name',
align: 'center',
}, { }, {
title: '主机编码', title: '主机编码',
dataIndex: 'code', dataIndex: 'code',
slotName: 'code', slotName: 'code',
align: 'center',
}, { }, {
title: '主机地址', title: '主机地址',
dataIndex: 'address', dataIndex: 'address',
slotName: 'address', slotName: 'address',
align: 'center',
}, { }, {
title: '标签', title: '标签',
dataIndex: 'tag', dataIndex: 'tag',

View File

@@ -30,7 +30,9 @@
<a-input v-model="formModel.name" placeholder="请输入菜单名称" /> <a-input v-model="formModel.name" placeholder="请输入菜单名称" />
</a-form-item> </a-form-item>
<!-- 菜单类型 --> <!-- 菜单类型 -->
<a-form-item field="type" label="菜单类型"> <a-form-item v-if="isAddHandle"
field="type"
label="菜单类型">
<a-radio-group type="button" <a-radio-group type="button"
v-model="formModel.type" v-model="formModel.type"
:options="toOptions(MenuTypeEnum)" /> :options="toOptions(MenuTypeEnum)" />