diff --git a/itbaima-monitor-server/src/main/java/com/example/controller/MonitorController.java b/itbaima-monitor-server/src/main/java/com/example/controller/MonitorController.java index 362be8a..5fe38e7 100644 --- a/itbaima-monitor-server/src/main/java/com/example/controller/MonitorController.java +++ b/itbaima-monitor-server/src/main/java/com/example/controller/MonitorController.java @@ -1,6 +1,7 @@ package com.example.controller; import com.example.entity.RestBean; +import com.example.entity.dto.Account; import com.example.entity.vo.request.RenameClientVO; import com.example.entity.vo.request.RenameNodeVO; import com.example.entity.vo.request.RuntimeDetailVO; @@ -8,7 +9,9 @@ import com.example.entity.vo.response.ClientDetailsVO; import com.example.entity.vo.response.ClientPreviewVO; import com.example.entity.vo.response.ClientSimpleVO; import com.example.entity.vo.response.RuntimeDetailsVO; +import com.example.service.AccountService; import com.example.service.ClientService; +import com.example.utils.Const; import jakarta.annotation.Resource; import jakarta.validation.Valid; import org.springframework.web.bind.annotation.*; @@ -22,51 +25,121 @@ public class MonitorController { @Resource ClientService service; + @Resource + AccountService accountService; + @GetMapping("/list") - public RestBean> listAllClient() { - return RestBean.success(service.listClients()); + public RestBean> listAllClient(@RequestAttribute(Const.ATTR_USER_ID) int userId, + @RequestAttribute(Const.ATTR_USER_ROLE) String userRole) { + List clients = this.accountAccessClients(userId); + List data = service.listClients(); + if(this.isAdminAccount(userRole)) { + return RestBean.success(data); + } else { + return RestBean.success(data.stream() + .filter(vo -> clients.contains(vo.getId())) + .toList()); + } } @GetMapping("/simple-list") - public RestBean> simpleClientList() { - return RestBean.success(service.listSimpleList()); + public RestBean> simpleClientList(@RequestAttribute(Const.ATTR_USER_ROLE) String userRole) { + if (this.isAdminAccount(userRole)) { + return RestBean.success(service.listSimpleList()); + } else { + return RestBean.noPermission(); + } } @PostMapping("/rename") - public RestBean renameClient(@RequestBody @Valid RenameClientVO vo) { - service.renameClient(vo); - return RestBean.success(); + public RestBean renameClient(@RequestBody @Valid RenameClientVO vo, + @RequestAttribute(Const.ATTR_USER_ID) int userId, + @RequestAttribute(Const.ATTR_USER_ROLE) String userRole) { + if(this.permissionCheck(userId, userRole, vo.getId())) { + service.renameClient(vo); + return RestBean.success(); + } else { + return RestBean.noPermission(); + } } @PostMapping("/node") - public RestBean renameNode(@RequestBody @Valid RenameNodeVO vo) { - service.renameNode(vo); - return RestBean.success(); + public RestBean renameNode(@RequestBody @Valid RenameNodeVO vo, + @RequestAttribute(Const.ATTR_USER_ID) int userId, + @RequestAttribute(Const.ATTR_USER_ROLE) String userRole) { + if(this.permissionCheck(userId, userRole, vo.getId())) { + service.renameNode(vo); + return RestBean.success(); + } else { + return RestBean.noPermission(); + } } @GetMapping("/details") - public RestBean clientDetails(int clientId) { - return RestBean.success(service.clientDetails(clientId)); + public RestBean clientDetails(int clientId, + @RequestAttribute(Const.ATTR_USER_ID) int userId, + @RequestAttribute(Const.ATTR_USER_ROLE) String userRole) { + if(this.permissionCheck(userId, userRole, clientId)) { + return RestBean.success(service.clientDetails(clientId)); + } else { + return RestBean.noPermission(); + } } @GetMapping("/runtime-history") - public RestBean runtimeDetailsHistory(int clientId) { - return RestBean.success(service.clientRuntimeDetailsHistory(clientId)); + public RestBean runtimeDetailsHistory(int clientId, + @RequestAttribute(Const.ATTR_USER_ID) int userId, + @RequestAttribute(Const.ATTR_USER_ROLE) String userRole) { + if(this.permissionCheck(userId, userRole, clientId)) { + return RestBean.success(service.clientRuntimeDetailsHistory(clientId)); + } else { + return RestBean.noPermission(); + } } @GetMapping("/runtime-now") - public RestBean runtimeDetailsNow(int clientId) { - return RestBean.success(service.clientRuntimeDetailsNow(clientId)); + public RestBean runtimeDetailsNow(int clientId, + @RequestAttribute(Const.ATTR_USER_ID) int userId, + @RequestAttribute(Const.ATTR_USER_ROLE) String userRole) { + if(this.permissionCheck(userId, userRole, clientId)) { + return RestBean.success(service.clientRuntimeDetailsNow(clientId)); + } else { + return RestBean.noPermission(); + } } @GetMapping("/register") - public RestBean registerToken() { - return RestBean.success(service.registerToken()); + public RestBean registerToken(@RequestAttribute(Const.ATTR_USER_ROLE) String userRole) { + if (this.isAdminAccount(userRole)) { + return RestBean.success(service.registerToken()); + } else { + return RestBean.noPermission(); + } } @GetMapping("/delete") - public RestBean deleteClient(int clientId) { - service.deleteClient(clientId); - return RestBean.success(); + public RestBean deleteClient(int clientId, + @RequestAttribute(Const.ATTR_USER_ROLE) String userRole) { + if (this.isAdminAccount(userRole)) { + service.deleteClient(clientId); + return RestBean.success(); + } else { + return RestBean.noPermission(); + } + } + + private List accountAccessClients(int uid) { + Account account = accountService.getById(uid); + return account.getClientList(); + } + + private boolean isAdminAccount(String role) { + role = role.substring(5); + return Const.ROLE_ADMIN.equals(role); + } + + private boolean permissionCheck(int uid, String role, int clientId) { + if(this.isAdminAccount(role)) return true; + return this.accountAccessClients(uid).contains(clientId); } } diff --git a/itbaima-monitor-server/src/main/java/com/example/entity/RestBean.java b/itbaima-monitor-server/src/main/java/com/example/entity/RestBean.java index d4f9fd3..8c227c3 100644 --- a/itbaima-monitor-server/src/main/java/com/example/entity/RestBean.java +++ b/itbaima-monitor-server/src/main/java/com/example/entity/RestBean.java @@ -34,6 +34,10 @@ public record RestBean (long id, int code, T data, String message) { return new RestBean<>(requestId(), code, null, message); } + public static RestBean noPermission() { + return new RestBean<>(requestId(), 401, null, "权限不足,拒绝访问"); + } + /** * 快速将当前实体转换为JSON字符串格式 * @return JSON字符串 diff --git a/itbaima-monitor-server/src/main/java/com/example/entity/dto/Account.java b/itbaima-monitor-server/src/main/java/com/example/entity/dto/Account.java index 818588a..45dd1f1 100644 --- a/itbaima-monitor-server/src/main/java/com/example/entity/dto/Account.java +++ b/itbaima-monitor-server/src/main/java/com/example/entity/dto/Account.java @@ -1,5 +1,6 @@ package com.example.entity.dto; +import com.alibaba.fastjson2.JSONArray; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; @@ -7,7 +8,9 @@ import com.example.entity.BaseData; import lombok.AllArgsConstructor; import lombok.Data; +import java.util.Collections; import java.util.Date; +import java.util.List; /** * 数据库中的用户信息 @@ -24,4 +27,9 @@ public class Account implements BaseData { String role; Date registerTime; String clients; + + public List getClientList() { + if(clients == null) return Collections.emptyList(); + return JSONArray.parse(clients).toList(Integer.class); + } } diff --git a/itbaima-monitor-server/src/main/java/com/example/entity/vo/request/CreateSubAccountVO.java b/itbaima-monitor-server/src/main/java/com/example/entity/vo/request/CreateSubAccountVO.java index 627f2a9..b65ee29 100644 --- a/itbaima-monitor-server/src/main/java/com/example/entity/vo/request/CreateSubAccountVO.java +++ b/itbaima-monitor-server/src/main/java/com/example/entity/vo/request/CreateSubAccountVO.java @@ -16,5 +16,5 @@ public class CreateSubAccountVO { @Length(min = 6, max = 20) String password; @Size(min = 1) - List clients; + List clients; } diff --git a/itbaima-monitor-server/src/main/java/com/example/entity/vo/response/AuthorizeVO.java b/itbaima-monitor-server/src/main/java/com/example/entity/vo/response/AuthorizeVO.java index 88cd2b3..0eeceaf 100644 --- a/itbaima-monitor-server/src/main/java/com/example/entity/vo/response/AuthorizeVO.java +++ b/itbaima-monitor-server/src/main/java/com/example/entity/vo/response/AuthorizeVO.java @@ -10,6 +10,7 @@ import java.util.Date; @Data public class AuthorizeVO { String username; + String email; String role; String token; Date expire; diff --git a/itbaima-monitor-server/src/main/java/com/example/service/impl/AccountServiceImpl.java b/itbaima-monitor-server/src/main/java/com/example/service/impl/AccountServiceImpl.java index 023e55b..06715f9 100644 --- a/itbaima-monitor-server/src/main/java/com/example/service/impl/AccountServiceImpl.java +++ b/itbaima-monitor-server/src/main/java/com/example/service/impl/AccountServiceImpl.java @@ -156,7 +156,7 @@ public class AccountServiceImpl extends ServiceImpl impl return this.list(Wrappers.query().eq("role", "user")) .stream().map(account -> { SubAccountVO vo = account.asViewObject(SubAccountVO.class); - vo.setClientList(JSONArray.parse(account.getClients())); + vo.setClientList(JSONArray.copyOf(account.getClientList())); return vo; }).toList(); } diff --git a/itbaima-monitor-web/package-lock.json b/itbaima-monitor-web/package-lock.json index 2d63f7a..8c4ddfc 100644 --- a/itbaima-monitor-web/package-lock.json +++ b/itbaima-monitor-web/package-lock.json @@ -14,6 +14,8 @@ "echarts": "^5.4.3", "element-plus": "^2.3.9", "flag-icon-css": "^4.1.7", + "pinia": "^2.1.7", + "pinia-plugin-persistedstate": "^3.2.0", "vue": "^3.3.4", "vue-router": "^4.2.4" }, @@ -1526,6 +1528,58 @@ "node": ">=6" } }, + "node_modules/pinia": { + "version": "2.1.7", + "resolved": "https://registry.npmmirror.com/pinia/-/pinia-2.1.7.tgz", + "integrity": "sha512-+C2AHFtcFqjPih0zpYuvof37SFxMQ7OEG2zV9jRI12i9BOy3YQVAHwdKtyyc8pDcDyIc33WCIsZaCFWU7WWxGQ==", + "dependencies": { + "@vue/devtools-api": "^6.5.0", + "vue-demi": ">=0.14.5" + }, + "peerDependencies": { + "@vue/composition-api": "^1.4.0", + "typescript": ">=4.4.4", + "vue": "^2.6.14 || ^3.3.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/pinia-plugin-persistedstate": { + "version": "3.2.0", + "resolved": "https://registry.npmmirror.com/pinia-plugin-persistedstate/-/pinia-plugin-persistedstate-3.2.0.tgz", + "integrity": "sha512-tZbNGf2vjAQcIm7alK40sE51Qu/m9oWr+rEgNm/2AWr1huFxj72CjvpQcIQzMknDBJEkQznCLAGtJTIcLKrKdw==", + "peerDependencies": { + "pinia": "^2.0.0" + } + }, + "node_modules/pinia/node_modules/vue-demi": { + "version": "0.14.6", + "resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.6.tgz", + "integrity": "sha512-8QA7wrYSHKaYgUxDA5ZC24w+eHm3sYCbp0EzcDwKqN3p6HqtTCGR/GVsPyZW92unff4UlcSh++lmqDWN3ZIq4w==", + "hasInstallScript": true, + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, "node_modules/pkg-types": { "version": "1.0.3", "resolved": "https://registry.npmmirror.com/pkg-types/-/pkg-types-1.0.3.tgz", diff --git a/itbaima-monitor-web/package.json b/itbaima-monitor-web/package.json index d003eb2..a6ac19c 100644 --- a/itbaima-monitor-web/package.json +++ b/itbaima-monitor-web/package.json @@ -14,6 +14,8 @@ "echarts": "^5.4.3", "element-plus": "^2.3.9", "flag-icon-css": "^4.1.7", + "pinia": "^2.1.7", + "pinia-plugin-persistedstate": "^3.2.0", "vue": "^3.3.4", "vue-router": "^4.2.4" }, diff --git a/itbaima-monitor-web/src/main.js b/itbaima-monitor-web/src/main.js index aeaff3e..9193ab5 100644 --- a/itbaima-monitor-web/src/main.js +++ b/itbaima-monitor-web/src/main.js @@ -6,11 +6,16 @@ import axios from "axios"; import 'flag-icon-css/css/flag-icons.min.css' import 'element-plus/theme-chalk/dark/css-vars.css' import '@/assets/css/element.less' +import {createPinia} from "pinia"; +import piniaPluginPersistedstate from 'pinia-plugin-persistedstate' axios.defaults.baseURL = 'http://localhost:8080' const app = createApp(App) +const pinia = createPinia() +app.use(pinia) +pinia.use(piniaPluginPersistedstate) app.use(router) app.mount('#app') diff --git a/itbaima-monitor-web/src/net/index.js b/itbaima-monitor-web/src/net/index.js index 29a4395..86f9aa9 100644 --- a/itbaima-monitor-web/src/net/index.js +++ b/itbaima-monitor-web/src/net/index.js @@ -1,5 +1,6 @@ import axios from "axios"; import {ElMessage} from "element-plus"; +import {useStore} from "@/store"; const authItemName = "authorize" @@ -74,6 +75,8 @@ function login(username, password, remember, success, failure = defaultFailure){ 'Content-Type': 'application/x-www-form-urlencoded' }, (data) => { storeAccessToken(remember, data.token, data.expire) + const store = useStore() + Object.assign(store.user, data) ElMessage.success(`登录成功,欢迎 ${data.username} 来到我们的系统`) success(data) }, failure) diff --git a/itbaima-monitor-web/src/store/index.js b/itbaima-monitor-web/src/store/index.js new file mode 100644 index 0000000..04eb67f --- /dev/null +++ b/itbaima-monitor-web/src/store/index.js @@ -0,0 +1,19 @@ +import {defineStore} from "pinia"; + +export const useStore = defineStore('general', { + state: () => { + return { + user: { + role: '', + username: '', + email: '' + } + } + }, + getters: { + isAdmin() { + return this.user.role === 'admin' + } + }, + persist: true +}) diff --git a/itbaima-monitor-web/src/views/IndexView.vue b/itbaima-monitor-web/src/views/IndexView.vue index 2dc179e..f7b145c 100755 --- a/itbaima-monitor-web/src/views/IndexView.vue +++ b/itbaima-monitor-web/src/views/IndexView.vue @@ -12,6 +12,14 @@ v-model="dark" active-color="#424242" :active-action-icon="Moon" :inactive-action-icon="Sunny"/> +
+
+ 管理员 + 子账户 + {{store.user.username}} +
+
{{store.user.email}}
+
@@ -45,6 +53,9 @@ import TabItem from "@/component/TabItem.vue"; import {ref} from "vue"; import {useDark} from "@vueuse/core"; import {useRoute} from "vue-router"; +import {useStore} from "@/store"; + +const store = useStore() function userLogout() { logout(() => router.push("/")) diff --git a/itbaima-monitor-web/src/views/main/Manage.vue b/itbaima-monitor-web/src/views/main/Manage.vue index 790459d..1422568 100644 --- a/itbaima-monitor-web/src/views/main/Manage.vue +++ b/itbaima-monitor-web/src/views/main/Manage.vue @@ -6,7 +6,9 @@ import ClientDetails from "@/component/ClientDetails.vue"; import {Plus} from "@element-plus/icons-vue"; import RegisterCard from "@/component/RegisterCard.vue"; import {useRoute} from "vue-router"; +import {useStore} from "@/store"; +const store = useStore() const route = useRoute() const list = ref([]) @@ -40,7 +42,8 @@ updateList()
在这里管理所有已经注册的主机实例,实时监控主机运行状态,快速进行管理和操作。
- 添加新主机 + 添加新主机
diff --git a/itbaima-monitor-web/src/views/main/Security.vue b/itbaima-monitor-web/src/views/main/Security.vue index 5dc10ca..30b3610 100644 --- a/itbaima-monitor-web/src/views/main/Security.vue +++ b/itbaima-monitor-web/src/views/main/Security.vue @@ -5,6 +5,9 @@ import {ElMessage} from "element-plus"; import {Delete, Lock, Plus, Switch} from "@element-plus/icons-vue"; import router from "@/router"; import CreateSubAccount from "@/component/CreateSubAccount.vue"; +import {useStore} from "@/store"; + +const store = useStore() const formRef = ref() const valid = ref(false) @@ -57,10 +60,12 @@ const initSubAccounts = () => const createAccount = ref(false) const simpleList = ref([]) -get('/api/monitor/simple-list', list => { - simpleList.value = list - initSubAccounts() -}) +if(store.isAdmin) { + get('/api/monitor/simple-list', list => { + simpleList.value = list + initSubAccounts() + }) +} function deleteAccount(id) { get(`/api/user/sub/delete?uid=${id}`, () => { @@ -117,10 +122,14 @@ function deleteAccount(id) { 添加更多子用户 - - 添加子用户 - +
+ + 添加子用户 + + +
+