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 7eaa6f1..9cd5041 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.RuntimeHistoryVO; +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 = service.listClients(); + if(this.isAdminAccount(userRole)) { + return RestBean.success(clients); + } else { + List ids = this.accountAccessClients(userId); + return RestBean.success(clients.stream() + .filter(vo -> ids.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 details(int clientId) { - return RestBean.success(service.clientDetails(clientId)); + public RestBean details(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/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-web/package-lock.json b/itbaima-monitor-web/package-lock.json index 7e23087..726e29d 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" }, @@ -1515,6 +1517,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 3737a68..fce6be5 100644 --- a/itbaima-monitor-web/src/main.js +++ b/itbaima-monitor-web/src/main.js @@ -6,11 +6,16 @@ import axios from "axios"; import '@/assets/css/element.less' import 'flag-icon-css/css/flag-icons.min.css' import 'element-plus/theme-chalk/dark/css-vars.css' +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..eb81053 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,10 @@ 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() + store.user.role = data.role + store.user.username = data.username + store.user.email = data.email 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 ba1346c..13b1032 100755 --- a/itbaima-monitor-web/src/views/IndexView.vue +++ b/itbaima-monitor-web/src/views/IndexView.vue @@ -10,6 +10,14 @@ v-model="dark" active-color="#424242" :active-action-icon="Moon" :inactive-action-icon="Sunny"/> +
+
+ 管理员 + 子账户 + {{store.user.username}} +
+
{{store.user.email}}
+
@@ -44,6 +52,9 @@ import {ref} from "vue"; import {useDark} from "@vueuse/core"; import TabItem from "@/component/TabItem.vue"; import {useRoute} from "vue-router"; +import {useStore} from "@/store"; + +const store = useStore() const route = useRoute() const dark = ref(useDark()) diff --git a/itbaima-monitor-web/src/views/main/Manage.vue b/itbaima-monitor-web/src/views/main/Manage.vue index d613c12..00ef62e 100644 --- a/itbaima-monitor-web/src/views/main/Manage.vue +++ b/itbaima-monitor-web/src/views/main/Manage.vue @@ -6,6 +6,7 @@ import ClientDetails from "@/component/ClientDetails.vue"; import RegisterCard from "@/component/RegisterCard.vue"; import {Plus} from "@element-plus/icons-vue"; import {useRoute} from "vue-router"; +import {useStore} from "@/store"; const locations = [ {name: 'cn', desc: '中国大陆'}, @@ -19,6 +20,7 @@ const locations = [ const checkedNodes = ref([]) const list = ref([]) +const store = useStore() const route = useRoute() @@ -62,7 +64,7 @@ const refreshToken = () => get('/api/monitor/register', token => register.token
在这里管理所有已经注册的主机实例,实时监控主机运行状态,快速进行管理和操作。
- 添加新主机
diff --git a/itbaima-monitor-web/src/views/main/Security.vue b/itbaima-monitor-web/src/views/main/Security.vue index e99dfd3..a34a6ec 100644 --- a/itbaima-monitor-web/src/views/main/Security.vue +++ b/itbaima-monitor-web/src/views/main/Security.vue @@ -5,6 +5,9 @@ import {get, logout, post} from "@/net"; import {ElMessage} from "element-plus"; import router from "@/router"; import CreateSubAccount from "@/component/CreateSubAccount.vue"; +import {useStore} from "@/store"; + +const store = useStore() const formRef = ref() const valid = ref(false) @@ -52,10 +55,12 @@ function resetPassword() { } 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() + }) +} const accounts = ref([]) const initSubAccounts = () => @@ -123,10 +128,13 @@ function deleteAccount(id) { 添加更多子用户 - - 添加子用户 - +
+ + 添加子用户 + + +