diff --git a/src/main/java/com/xuxd/kafka/console/dao/init/DataInit.java b/src/main/java/com/xuxd/kafka/console/dao/init/DataInit.java new file mode 100644 index 0000000..a3e515e --- /dev/null +++ b/src/main/java/com/xuxd/kafka/console/dao/init/DataInit.java @@ -0,0 +1,84 @@ +package com.xuxd.kafka.console.dao.init; + +import com.xuxd.kafka.console.config.AuthConfig; +import com.xuxd.kafka.console.dao.SysPermissionMapper; +import com.xuxd.kafka.console.dao.SysRoleMapper; +import com.xuxd.kafka.console.dao.SysUserMapper; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.SmartInitializingSingleton; +import org.springframework.stereotype.Component; + +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; + +/** + * @author: xuxd + * @date: 2023/5/17 13:10 + **/ +@Slf4j +@Component +public class DataInit implements SmartInitializingSingleton { + + private final AuthConfig authConfig; + + private final SysUserMapper userMapper; + + private final SysRoleMapper roleMapper; + + private final SysPermissionMapper permissionMapper; + + private final DataSource dataSource; + + private final SqlParse sqlParse; + + + public DataInit(AuthConfig authConfig, + SysUserMapper userMapper, + SysRoleMapper roleMapper, + SysPermissionMapper permissionMapper, + DataSource dataSource) { + this.authConfig = authConfig; + this.userMapper = userMapper; + this.roleMapper = roleMapper; + this.permissionMapper = permissionMapper; + this.dataSource = dataSource; + this.sqlParse = new SqlParse(); + } + + @Override + public void afterSingletonsInstantiated() { + if (!authConfig.isEnable()) { + log.info("Disable login authentication, no longer try to initialize the data"); + return; + } + try { + Connection connection = dataSource.getConnection(); + Integer userCount = userMapper.selectCount(null); + if (userCount == null || userCount == 0) { + initData(connection, SqlParse.USER_TABLE); + } + + Integer roleCount = roleMapper.selectCount(null); + if (roleCount == null || roleCount == 0) { + initData(connection, SqlParse.ROLE_TABLE); + } + + Integer permCount = permissionMapper.selectCount(null); + if (permCount == null || permCount == 0) { + initData(connection, SqlParse.PERM_TABLE); + } + } catch (SQLException e) { + throw new RuntimeException(e); + } + + } + + private void initData(Connection connection, String table) throws SQLException { + log.info("Init default data for {}", table); + String sql = sqlParse.getMergeSql(table); + PreparedStatement statement = connection.prepareStatement(sql); + statement.execute(); + } +} diff --git a/src/main/java/com/xuxd/kafka/console/dao/init/SqlParse.java b/src/main/java/com/xuxd/kafka/console/dao/init/SqlParse.java new file mode 100644 index 0000000..1ce6975 --- /dev/null +++ b/src/main/java/com/xuxd/kafka/console/dao/init/SqlParse.java @@ -0,0 +1,85 @@ +package com.xuxd.kafka.console.dao.init; + +import com.google.common.io.Files; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.util.ResourceUtils; +import scala.collection.mutable.StringBuilder; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @author: xuxd + * @date: 2023/5/17 21:22 + **/ +@Slf4j +public class SqlParse { + + private final String FILE = "classpath:db/data-h2.sql"; + + private final Map> sqlMap = new HashMap<>(); + + public static final String ROLE_TABLE = "t_sys_role"; + public static final String USER_TABLE = "t_sys_user"; + public static final String PERM_TABLE = "t_sys_permission"; + + public SqlParse() { + sqlMap.put(ROLE_TABLE, new ArrayList<>()); + sqlMap.put(USER_TABLE, new ArrayList<>()); + sqlMap.put(PERM_TABLE, new ArrayList<>()); + + String table = null; + try { + File file = ResourceUtils.getFile(FILE); + List lines = Files.readLines(file, Charset.forName("UTF-8")); + for (String str : lines) { + if (StringUtils.isNotEmpty(str)) { + if (str.indexOf("start--") > 0) { + if (str.indexOf(ROLE_TABLE) > 0) { + table = ROLE_TABLE; + } + if (str.indexOf(USER_TABLE) > 0) { + table = USER_TABLE; + } + if (str.indexOf(PERM_TABLE) > 0) { + table = PERM_TABLE; + } + } + if (isSql(str)) { + if (table == null) { + log.error("Table is null, can not load sql: {}", str); + continue; + } + sqlMap.get(table).add(str); + } + } + } + } catch (FileNotFoundException e) { + throw new RuntimeException(e); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public List getSqlList(String table) { + return sqlMap.get(table); + } + + public String getMergeSql(String table) { + List list = getSqlList(table); + StringBuilder sb = new StringBuilder(); + list.forEach(sql -> sb.append(sql)); + return sb.toString(); + } + + private boolean isSql(String str) { + return StringUtils.isNotEmpty(str) && str.startsWith("insert"); + } +} diff --git a/src/main/java/com/xuxd/kafka/console/service/impl/UserManageServiceImpl.java b/src/main/java/com/xuxd/kafka/console/service/impl/UserManageServiceImpl.java index beb65e3..be23f1d 100644 --- a/src/main/java/com/xuxd/kafka/console/service/impl/UserManageServiceImpl.java +++ b/src/main/java/com/xuxd/kafka/console/service/impl/UserManageServiceImpl.java @@ -81,7 +81,7 @@ public class UserManageServiceImpl implements UserManageService { } userDO.setSalt(UUIDStrUtil.random()); userDO.setPassword(UUIDStrUtil.generate(userDTO.getPassword(), userDO.getSalt())); - userMapper.insert(userDTO.toDO()); + userMapper.insert(userDO); } else { SysUserDO userDO = userMapper.selectById(userDTO.getId()); if (userDO == null) { diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 304feee..e372073 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -50,6 +50,6 @@ cron: # 权限认证设置,设置为true,需要先登录才能访问 auth: - enable: false + enable: true # 登录用户token的过期时间,单位:小时 expire-hours: 24 \ No newline at end of file diff --git a/src/main/resources/db/data-h2.sql b/src/main/resources/db/data-h2.sql index ac396b6..e350493 100644 --- a/src/main/resources/db/data-h2.sql +++ b/src/main/resources/db/data-h2.sql @@ -1,14 +1,11 @@ --- DELETE FROM t_kafka_user; --- --- INSERT INTO t_kafka_user (id, username, password) VALUES --- (1, 'Jone', 'p1'), --- (2, 'Jack', 'p2'); - +-- 不要随便修改下面的注释,要根据这个注释初始化加载数据 +-- t_sys_permission start-- insert into t_sys_permission(id, name,type,parent_id,permission) values(0,'主页',0,null,'home'); insert into t_sys_permission(id, name,type,parent_id,permission) values(11,'集群',0,null,'cluster'); insert into t_sys_permission(id, name,type,parent_id,permission) values(12,'属性配置',1,11,'cluster:property-config'); insert into t_sys_permission(id, name,type,parent_id,permission) values(13,'日志配置',1,11,'cluster:log-config'); +insert into t_sys_permission(id, name,type,parent_id,permission) values(14,'编辑配置',1,11,'cluster:edit'); insert into t_sys_permission(id, name,type,parent_id,permission) values(21,'Topic',0,null,'topic'); insert into t_sys_permission(id, name,type,parent_id,permission) values(22,'刷新',1,21,'topic:load'); @@ -49,11 +46,26 @@ insert into t_sys_permission(id, name,type,parent_id,permission) values(83,'客 insert into t_sys_permission(id, name,type,parent_id,permission) values(84,'新增配置',1,83,'quota:client:add'); insert into t_sys_permission(id, name,type,parent_id,permission) values(85,'用户和客户端ID',1,80,'quota:user-client'); insert into t_sys_permission(id, name,type,parent_id,permission) values(86,'新增配置',1,85,'quota:user-client:add'); +insert into t_sys_permission(id, name,type,parent_id,permission) values(87,'删除',1,80,'quota:del'); +insert into t_sys_permission(id, name,type,parent_id,permission) values(88,'修改',1,80,'quota:edit'); insert into t_sys_permission(id, name,type,parent_id,permission) values(100,'Acl',0,null,'acl'); insert into t_sys_permission(id, name,type,parent_id,permission) values(101,'资源授权',1,100,'acl:authority'); -insert into t_sys_permission(id, name,type,parent_id,permission) values(102,'新增权限',1,101,'acl:authority:add'); +insert into t_sys_permission(id, name,type,parent_id,permission) values(102,'新增主体权限',1,101,'acl:authority:add-principal'); +insert into t_sys_permission(id, name,type,parent_id,permission) values(103,'权限详情',1,101,'acl:authority:detail'); +insert into t_sys_permission(id, name,type,parent_id,permission) values(104,'管理生产权限',1,101,'acl:authority:producer'); +insert into t_sys_permission(id, name,type,parent_id,permission) values(105,'管理消费权限',1,101,'acl:authority:consumer'); +insert into t_sys_permission(id, name,type,parent_id,permission) values(106,'增加权限',1,101,'acl:authority:add'); +insert into t_sys_permission(id, name,type,parent_id,permission) values(107,'清除权限',1,101,'acl:authority:clean'); +insert into t_sys_permission(id, name,type,parent_id,permission) values(108,'SaslScram用户管理',1,100,'acl:sasl-scram'); +insert into t_sys_permission(id, name,type,parent_id,permission) values(109,'新增/更新用户',1,108,'acl:sasl-scram:add-update'); +insert into t_sys_permission(id, name,type,parent_id,permission) values(110,'详情',1,108,'acl:sasl-scram:detail'); +insert into t_sys_permission(id, name,type,parent_id,permission) values(111,'删除',1,108,'acl:sasl-scram:del'); +insert into t_sys_permission(id, name,type,parent_id,permission) values(112,'管理生产权限',1,108,'acl:sasl-scram:producer'); +insert into t_sys_permission(id, name,type,parent_id,permission) values(113,'管理消费权限',1,108,'acl:sasl-scram:consumer'); +insert into t_sys_permission(id, name,type,parent_id,permission) values(114,'增加权限',1,108,'acl:sasl-scram:add-auth'); +insert into t_sys_permission(id, name,type,parent_id,permission) values(115,'彻底删除',1,108,'acl:sasl-scram:pure'); insert into t_sys_permission(id, name,type,parent_id,permission) values(140,'用户',0,null,'user-manage'); insert into t_sys_permission(id, name,type,parent_id,permission) values(141,'用户列表',1,140,'user-manage:user'); @@ -78,8 +90,13 @@ insert into t_sys_permission(id, name,type,parent_id,permission) values(167,'解 insert into t_sys_permission(id, name,type,parent_id,permission) values(168,'首选副本作leader',1,160,'op:replication-preferred'); insert into t_sys_permission(id, name,type,parent_id,permission) values(169,'副本变更详情',1,160,'op:replication-update-detail'); insert into t_sys_permission(id, name,type,parent_id,permission) values(170,'副本重分配',1,160,'op:replication-reassign'); +-- t_sys_permission end-- +-- t_sys_role start-- +insert into t_sys_role(id, role_name, description, permission_ids) VALUES (1,'超级管理员','超级管理员','12,13,14,22,23,24,25,26,27,28,29,30,31,32,33,42,43,44,45,46,47,48,49,50,62,63,64,65,81,82,83,84,85,86,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,141,142,143,144,145,146,147,148,149,150,161,162,163,164,165,166,167,168,169,170'); +-- insert into t_sys_role(id, role_name, description, permission_ids) VALUES (2,'访客','访客','12,13,22,26,29,32,44,45,50,62,63,81,83,85,141,146,149,150,161,163'); +-- t_sys_role end-- -insert into t_sys_role(id, role_name, description, permission_ids) VALUES ( 1,'超级管理员','超级管理员','12,13,22,23,24,25,26,27,28,29,30,31,32,33,42,43,44,45,46,47,48,49,50,62,63,64,65,81,82,83,84,85,86,101,102,141,142,143,144,145,146,147,148,149,150,161,162,163,164,165,166,167,168,169,170' ); - -insert into t_sys_user(id, username, password, salt, role_ids) VALUES ( 1,'admin','3a3e4d32-5247-321b-9efb-9cbf60b2bf6c','e6973cfc-7583-4baa-8802-65ded1268ab6','1' ); \ No newline at end of file +-- t_sys_user start-- +insert into t_sys_user(id, username, password, salt, role_ids) VALUES (1,'admin','3a3e4d32-5247-321b-9efb-9cbf60b2bf6c','e6973cfc-7583-4baa-8802-65ded1268ab6','1' ); +-- t_sys_user end-- \ No newline at end of file diff --git a/ui/src/App.vue b/ui/src/App.vue index 78fcc5b..8253c21 100644 --- a/ui/src/App.vue +++ b/ui/src/App.vue @@ -15,8 +15,10 @@ >限流 |Acl - |用户 + |用户 |运维
@@ -44,7 +46,13 @@ import { KafkaClusterApi, AuthApi } from "@/utils/api"; import request from "@/utils/request"; import { mapMutations, mapState } from "vuex"; -import {deleteToken, deleteUsername, getClusterInfo, getPermissions, getUsername} from "@/utils/local-cache"; +import { + deleteToken, + deleteUsername, + getClusterInfo, + getPermissions, + getUsername, +} from "@/utils/local-cache"; import notification from "ant-design-vue/lib/notification"; import { AUTH, CLUSTER } from "@/store/mutation-types"; @@ -64,6 +72,7 @@ export default { enableSasl: (state) => state.clusterInfo.enableSasl, showUsername: (state) => state.auth.enable && state.auth.username, username: (state) => state.auth.username, + showUserMenu: (state) => state.auth.enable, }), }, methods: { diff --git a/ui/src/utils/api.js b/ui/src/utils/api.js index 27549b7..86a618b 100644 --- a/ui/src/utils/api.js +++ b/ui/src/utils/api.js @@ -357,4 +357,4 @@ export const AuthApi = { url: "/auth/login", method: "post", }, -}; \ No newline at end of file +}; diff --git a/ui/src/utils/auth.js b/ui/src/utils/auth.js new file mode 100644 index 0000000..7ec19f0 --- /dev/null +++ b/ui/src/utils/auth.js @@ -0,0 +1,16 @@ +import Store from "@/store"; + +export function isUnauthorized(permission) { + const enableAuth = Store.state.auth.enable; + const permissions = Store.state.auth.permissions; + return enableAuth && (!permissions || permissions.indexOf(permission) < 0); +} + +export function isAuthorized(permission) { + const enableAuth = Store.state.auth.enable; + if (!enableAuth) { + return true; + } + const permissions = Store.state.auth.permissions; + return permissions && permissions.indexOf(permission) >= 0; +} diff --git a/ui/src/utils/request.js b/ui/src/utils/request.js index 8bf2a3d..6010991 100644 --- a/ui/src/utils/request.js +++ b/ui/src/utils/request.js @@ -39,7 +39,7 @@ request.interceptors.request.use((config) => { config.headers["X-Cluster-Info-Id"] = clusterInfo.id; // config.headers["X-Cluster-Info-Name"] = encodeURIComponent(clusterInfo.clusterName); } - const token = localStorage.getItem('access_token') + const token = localStorage.getItem("access_token"); if (token) { config.headers["X-Auth-Token"] = token; } diff --git a/ui/src/views/acl/Acl.vue b/ui/src/views/acl/Acl.vue index 02cd1ce..19c8a30 100644 --- a/ui/src/views/acl/Acl.vue +++ b/ui/src/views/acl/Acl.vue @@ -1,10 +1,14 @@