管理员关闭用户 mfa

This commit is contained in:
bwcx_jzy 2022-02-06 17:04:21 +08:00
parent dfc5a4f8a4
commit ac6a178560
No known key found for this signature in database
GPG Key ID: 5E48E9372088B9E5
7 changed files with 116 additions and 44 deletions

View File

@ -183,7 +183,7 @@ public class LoginControl extends BaseServerController {
} }
// 验证 // 验证
if (userService.simpleLogin(userName, userPwd) != null) { if (userService.simpleLogin(userName, userPwd) != null) {
updateModel = userModel.unLock(); updateModel = UserModel.unLock(userName);
this.ipSuccess(); this.ipSuccess();
// 判断是否开启 两步验证 // 判断是否开启 两步验证
boolean bindMfa = userService.hasBindMfa(userName); boolean bindMfa = userService.hasBindMfa(userName);

View File

@ -26,6 +26,7 @@ import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil; import cn.hutool.crypto.SecureUtil;
import cn.jiangzeyin.common.JsonMessage; import cn.jiangzeyin.common.JsonMessage;
import cn.jiangzeyin.common.validator.ValidatorItem;
import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONArray;
import io.jpom.common.BaseServerController; import io.jpom.common.BaseServerController;
import io.jpom.common.Const; import io.jpom.common.Const;
@ -40,10 +41,7 @@ import io.jpom.service.user.UserService;
import io.jpom.system.ServerExtConfigBean; import io.jpom.system.ServerExtConfigBean;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import java.util.List; import java.util.List;
@ -76,6 +74,12 @@ public class UserListController extends BaseServerController {
@Feature(method = MethodFeature.LIST) @Feature(method = MethodFeature.LIST)
public String getUserList() { public String getUserList() {
PageResultDto<UserModel> userModelPageResultDto = userService.listPage(getRequest()); PageResultDto<UserModel> userModelPageResultDto = userService.listPage(getRequest());
userModelPageResultDto.each(userModel -> {
boolean bindMfa = userService.hasBindMfa(userModel.getId());
if (bindMfa) {
userModel.setTwoFactorAuthKey("true");
}
});
return JsonMessage.getString(200, "", userModelPageResultDto); return JsonMessage.getString(200, "", userModelPageResultDto);
} }
@ -208,14 +212,27 @@ public class UserListController extends BaseServerController {
* @param id id * @param id id
* @return json * @return json
*/ */
@RequestMapping(value = "unlock", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE) @GetMapping(value = "unlock", produces = MediaType.APPLICATION_JSON_VALUE)
@Feature(method = MethodFeature.EDIT) @Feature(method = MethodFeature.EDIT)
public String unlock(String id) { public String unlock(@ValidatorItem String id) {
UserModel userModel = userService.getByKey(id); UserModel update = UserModel.unLock(id);
Assert.notNull(userModel, "修改失败:-1"); userService.update(update);
userService.update(userModel.unLock());
return JsonMessage.getString(200, "解锁成功"); return JsonMessage.getString(200, "解锁成功");
} }
/**
* 关闭用户 mfa
*
* @param id id
* @return json
*/
@GetMapping(value = "close_user_mfa", produces = MediaType.APPLICATION_JSON_VALUE)
@Feature(method = MethodFeature.EDIT)
@SystemPermission(superUser = true)
public String closeMfa(@ValidatorItem String id) {
UserModel update = new UserModel(id);
update.setTwoFactorAuthKey(StrUtil.EMPTY);
userService.update(update);
return JsonMessage.getString(200, "关闭成功");
}
} }

View File

@ -127,18 +127,19 @@ public class UserModel extends BaseStrikeDbModel {
this.systemUser = ObjectUtil.defaultIfNull(systemUser, 0) == 1 ? systemUser : 0; this.systemUser = ObjectUtil.defaultIfNull(systemUser, 0) == 1 ? systemUser : 0;
} }
/** // /**
* 解锁 // * 解锁
*/ // */
public UserModel unLock() { // public UserModel unLock() {
UserModel newModel = new UserModel(this.getId()); // UserModel newModel = new UserModel(this.getId());
return UserModel.unLock(newModel); // return UserModel.unLock(newModel);
} // }
/** /**
* 解锁 * 解锁
*/ */
public static UserModel unLock(UserModel newModel) { public static UserModel unLock(String id) {
UserModel newModel = new UserModel(id);
newModel.setPwdErrorCount(0); newModel.setPwdErrorCount(0);
newModel.setLockTime(0L); newModel.setLockTime(0L);
newModel.setLastPwdErrorTime(0L); newModel.setLastPwdErrorTime(0L);

View File

@ -131,11 +131,10 @@ public class UserService extends BaseDbService<UserModel> {
*/ */
public void updatePwd(String id, String newPwd) { public void updatePwd(String id, String newPwd) {
String salt = this.generateSalt(); String salt = this.generateSalt();
UserModel userModel = new UserModel(); UserModel userModel = UserModel.unLock(id);
userModel.setId(id); // userModel.setId(id);
userModel.setSalt(salt); userModel.setSalt(salt);
userModel.setPassword(SecureUtil.sha1(newPwd + salt)); userModel.setPassword(SecureUtil.sha1(newPwd + salt));
UserModel.unLock(userModel);
super.update(userModel); super.update(userModel);
} }

View File

@ -46,7 +46,6 @@ export function closeMfa() {
}); });
} }
// 生成 两步验证信息 // 生成 两步验证信息
export function generateMfa() { export function generateMfa() {
return axios({ return axios({
@ -177,8 +176,21 @@ export function sendEmailCode(email) {
export function unlockUser(id) { export function unlockUser(id) {
return axios({ return axios({
url: "/user/unlock", url: "/user/unlock",
method: "post", method: "get",
data: { id }, params: { id },
});
}
/**
* 关闭用户 mfa 两步验证
* @param {String} id 管理员 ID
* @returns
*/
export function closeUserMfa(id) {
return axios({
url: "/user/close_user_mfa",
method: "get",
params: { id },
}); });
} }

View File

@ -483,6 +483,10 @@ export default {
}); });
this.temp = { ...this.temp }; this.temp = { ...this.temp };
this.showQrCode(); this.showQrCode();
this.$notification.info({
// placement: "",
message: "需要输入验证码,确认绑定后才生效奥",
});
} }
}); });
} else { } else {
@ -499,7 +503,7 @@ export default {
this.$notification.success({ this.$notification.success({
message: res.msg, message: res.msg,
}); });
this.temp.status = false; this.temp = { ...this.temp, needVerify: false, status: false };
} }
}); });
}, },

View File

@ -1,15 +1,6 @@
<template> <template>
<div class="full-content"> <div class="full-content">
<div ref="filter" class="filter"> <!-- <div ref="filter" class="filter"></div> -->
<a-space>
<a-input v-model="listQuery.id" placeholder="用户名ID" class="search-input-item" />
<a-input v-model="listQuery['%name%']" placeholder="用户名" class="search-input-item" />
<a-tooltip title="按住 Ctr 或者 Alt 键点击按钮快速回到第一页">
<a-button type="primary" :loading="loading" @click="loadData">搜索</a-button>
</a-tooltip>
<a-button type="primary" @click="handleAdd">新增</a-button>
</a-space>
</div>
<!-- 数据表格 --> <!-- 数据表格 -->
<a-table <a-table
:data-source="list" :data-source="list"
@ -19,16 +10,42 @@
bordered bordered
:rowKey="(record, index) => index" :rowKey="(record, index) => index"
> >
<template slot="title">
<a-space>
<a-input v-model="listQuery.id" placeholder="用户名ID" class="search-input-item" />
<a-input v-model="listQuery['%name%']" placeholder="用户名" class="search-input-item" />
<a-tooltip title="按住 Ctr 或者 Alt 键点击按钮快速回到第一页">
<a-button type="primary" :loading="loading" @click="loadData">搜索</a-button>
</a-tooltip>
<a-button type="primary" @click="handleAdd">新增</a-button>
</a-space></template
>
<template slot="operation" slot-scope="text, record"> <template slot="operation" slot-scope="text, record">
<a-space> <a-space>
<a-button type="primary" @click="handleEdit(record)">编辑</a-button> <a-button type="primary" @click="handleEdit(record)">编辑</a-button>
<a-button type="danger" @click="handleDelete(record)">删除</a-button> <a-dropdown>
<a-button type="danger" :disabled="record.pwdErrorCount === 0" @click="handleUnlock(record)">解锁</a-button> <a class="ant-dropdown-link" @click="(e) => e.preventDefault()"> 更多 <a-icon type="down" /> </a>
<a-menu slot="overlay">
<a-menu-item>
<a-button type="danger" :disabled="record.parent === 'sys'" @click="handleDelete(record)">删除</a-button>
</a-menu-item>
<a-menu-item>
<a-button type="danger" :disabled="record.pwdErrorCount === 0" @click="handleUnlock(record)">解锁</a-button>
</a-menu-item>
<a-menu-item>
<a-button type="danger" :disabled="record.twoFactorAuthKey ? false : true" @click="handleCloseMfa(record)">关闭MFA</a-button>
</a-menu-item>
</a-menu></a-dropdown
>
</a-space> </a-space>
</template> </template>
<template slot="systemUser" slot-scope="text, record"> <template slot="systemUser" slot-scope="text, record">
<a-switch size="small" checked-children="" un-checked-children="" :checked="record.systemUser == 1" /> <a-switch size="small" checked-children="" un-checked-children="" :checked="record.systemUser == 1" />
</template> </template>
<template slot="twoFactorAuthKey" slot-scope="text, record">
<a-switch size="small" checked-children="" un-checked-children="" :checked="record.twoFactorAuthKey ? true : false" />
</template>
<a-tooltip slot="id" slot-scope="text" :title="text"> <a-tooltip slot="id" slot-scope="text" :title="text">
<span>{{ text }}</span> <span>{{ text }}</span>
</a-tooltip> </a-tooltip>
@ -114,7 +131,7 @@
</div> </div>
</template> </template>
<script> <script>
import { getUserList, editUser, deleteUser, unlockUser, workspaceList } from "@/api/user"; import { getUserList, editUser, deleteUser, unlockUser, closeUserMfa, workspaceList } from "@/api/user";
import { getWorkSpaceListAll } from "@/api/workspace"; import { getWorkSpaceListAll } from "@/api/workspace";
import { getMonitorOperateTypeList } from "@/api/monitor"; import { getMonitorOperateTypeList } from "@/api/monitor";
import { parseTime } from "@/utils/time"; import { parseTime } from "@/utils/time";
@ -156,10 +173,12 @@ export default {
editUserVisible: false, editUserVisible: false,
listQuery: Object.assign({}, PAGE_DEFAULT_LIST_QUERY), listQuery: Object.assign({}, PAGE_DEFAULT_LIST_QUERY),
columns: [ columns: [
{ title: "ID", dataIndex: "id", ellipsis: true, width: 150, scopedSlots: { customRender: "id" } }, { title: "ID", dataIndex: "id", ellipsis: true, scopedSlots: { customRender: "id" } },
{ title: "昵称", dataIndex: "name", ellipsis: true }, { title: "昵称", dataIndex: "name", ellipsis: true },
{ title: "管理员", dataIndex: "systemUser", ellipsis: true, width: 90, scopedSlots: { customRender: "systemUser" } }, { title: "管理员", dataIndex: "systemUser", ellipsis: true, width: 90, scopedSlots: { customRender: "systemUser" } },
{ title: "邮箱", dataIndex: "email", ellipsis: true, width: 150, scopedSlots: { customRender: "email" } }, { title: "两步验证", dataIndex: "twoFactorAuthKey", ellipsis: true, width: 90, scopedSlots: { customRender: "twoFactorAuthKey" } },
{ title: "邮箱", dataIndex: "email", ellipsis: true, scopedSlots: { customRender: "email" } },
{ title: "创建人", dataIndex: "parent", ellipsis: true, width: 150 }, { title: "创建人", dataIndex: "parent", ellipsis: true, width: 150 },
{ {
title: "修改时间", title: "修改时间",
@ -171,7 +190,7 @@ export default {
}, },
width: 170, width: 170,
}, },
{ title: "操作", dataIndex: "operation", scopedSlots: { customRender: "operation" }, width: 260 }, { title: "操作", dataIndex: "operation", scopedSlots: { customRender: "operation" }, width: 150 },
], ],
// //
rules: { rules: {
@ -399,6 +418,26 @@ export default {
}, },
}); });
}, },
//
handleCloseMfa(record) {
this.$confirm({
title: "系统提示",
content: "真的关闭当前用户的两步验证么?",
okText: "确认",
cancelText: "取消",
onOk: () => {
//
closeUserMfa(record.id).then((res) => {
if (res.code === 200) {
this.$notification.success({
message: res.msg,
});
this.loadData();
}
});
},
});
},
// //
changePage(pagination, filters, sorter) { changePage(pagination, filters, sorter) {
this.listQuery.page = pagination.current; this.listQuery.page = pagination.current;
@ -413,7 +452,7 @@ export default {
}; };
</script> </script>
<style scoped> <style scoped>
.filter { /* .filter {
margin-bottom: 10px; margin-bottom: 10px;
} } */
</style> </style>