领先的免费Web技术教程,涵盖HTML到ASP.NET

网站首页 > 知识剖析 正文

权限管理系统 权限管理系统er图

nixiaole 2024-11-12 13:43:05 知识剖析 20 ℃

要求

效果图

实际

功能

  1. 白天/暗夜模式
  2. 注册
    1. 未跳转,还是本页面注册,点击注册切换按钮即可。
  1. 登录
    1. 和注册在同页面。
    2. 登录实现了动态菜单和路由守卫。
    3. 多角色登录
  1. 忘记密码
    1. 弹窗形式
  1. 首页
    1. 日历查看
    2. 公告显示
  1. 个人中心
    1. 可通过其进行更改个人信息。
  1. 权限管理
    1. 权限菜单
      1. 分页查询
      2. 搜索/重置
      3. 新增/导出
      4. 修改/删除
    1. 权限分配
      1. 分页查询
      2. 搜索/重置
      3. 修改/删除
      4. 角色权限查看
      5. 分配权限
      6. 新增角色
  1. 系统功能
    1. 用户管理
      1. 分页查询
      2. 搜索/重置
      3. 修改/删除/批量删除
      4. 新增用户
      5. 导入/导出
      6. 分配角色
    1. 公告管理
      1. 分页查询
      2. 搜索/重置
      3. 修改/删除/批量删除/新增
      4. 公告内容查看
      5. 导入/导出
      6. 发布按钮功能
    1. 系统日志
      1. 分页查询
      2. 搜索/重置/删除/批量删除

效果图

数据库

数据库:permission、字符集:utf8mb4、规则:utf8mb4_bin

user (用户信息表)、perm (权限信息表)、role(角色信息表)、perm_role(角色权限表)、notice(公告信息表)、logs(日志信息表)

user表

字段

类型

备注

实体类对应类型

id

int(11)

主键、自增

用户id

Integer

username

varchar(255)


账号

String

password

varchar(255)


密码

String

phone

varchar(255)


手机号

String

role

varchar(255)


角色(默认为SYS-YONGHU)

String

name

varchar(255)


昵称

String

avatar

longtext


头像(base64编码上传)

String

role表

字段

类型

备注

实体类对应类型

id

int(11)

主键、自增

用户id

Integer

role_name

varchar(255)

!null

角色名称

String

perm_role_id

int(11)

!null、外键(关联perm_role表role_id)同步更新

角色权限id

Integer

remark

longtext


角色备注

String

perm_role表

字段

类型

备注

实体类对应类型

id

int(11)

主键、自增

角色权限id

Integer

role_id

int(11)


角色关联字段

Integer

perm_id

int(11)


权限id

Integer

perm表

字段

类型

备注

实体类对应类型

id

int(11)

主键、自增

权限id

Integer

name

varcahr(255)

!null

权限名称

String

visit_path

varcahr(255)

!null

访问路径

String

path

varcahr(255)

!null

文件路径

String

icon

varcahr(255)


菜单图标

String

p_no

varcahr(255)


父级菜单编号

String

is_one

int(11)

!null

是否为一级菜单(1是/0不是)

Integer

notice表

字段

类型

备注

实体类对应类型

id

int(11)

主键、自增

公告id

Integer

title

varchar(255)


标题

String

content

text


内容

String

user_id

int(11)


用户id

Integer

time

varchar(255)


创建时间

String

open

tinyint(4)


是否发布

boolean

logs表

字段

类型

备注

实体类对应类型

id

int(11)

主键、自增

日志id

Integer

operation

varchar(255)


操作名称

String

type

varchar(255)


操作类型

String

user

varchar(255)


操作人

String

time

varchar(255)


操作时间

String

ip

varchar(255)


ip地址

String

place

varchar(255)


操作人位置

String

关系图

后端

使用技术

  1. SpringBoot框架
  2. Mybatis框架
  3. jdk17

依赖

<!-- ali地域 -->
<dependency>
    <groupId>com.maxmind.geoip2</groupId>
    <artifactId>geoip2</artifactId>
    <version>2.6.0</version>
</dependency>
<!-- JSON格式的数据结构(key-value 结构) -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>2.0.12</version>
</dependency>
<!-- excel操作依赖 -->
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>5.2.5</version>
</dependency>
<!-- aop系统日志 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!-- 数据加密jwt -->
<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>4.3.0</version>
</dependency>
<!--hutool工具库-->
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.8.26</version>
</dependency>
<!--mybatis框架-->
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>3.0.3</version>
</dependency>
<!--热部署-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
    <scope>runtime</scope>
    <optional>true</optional>
</dependency>
<!--数据库连接-->
<dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
    <scope>runtime</scope>
</dependency>

登录业务层代码

/**
 * 登录
 * @param user 用户信息(username、password)
 */
@Override
public User login(User user) {
// 通过用户名查询用户信息
User dbUser = findByUserName(user.getUsername(),null);
if (dbUser == null) {
    throw new ServiceException("用户不存在");
}
if (!dbUser.getPassword().equals(user.getPassword())) {
    throw new ServiceException("密码错误!");
}
// 权限获取,并包装传前端主要代码
String token = JwtUtil.createToken(String.valueOf(dbUser.getId()), dbUser.getPassword());
// 获取用户权限
List<Role> role = roleMapper.findRole(null, dbUser.getRole());
if (role == null) {
    throw new ServiceException("系统错误");
}
// 通过role表的perm_role_id查perm_role表的role_id的数据
List<PermRole> permRoleList = permRoleMapper.findPermRole(null, role.get(0).getPermRoleId());
List<Perm> permList = new ArrayList<>();
// 数据为空查所有
if (permRoleList != null && !permRoleList.isEmpty()) {
    for (PermRole permRole : permRoleList) {
        // 通过perm_id查perm表数据,并添加到permList中
        List<Perm> perm = permMapper.findPerm(permRole.getPermId(), null, null, null, 1);
        permList.add(perm.get(0));
    }
} else {
    permList = permMapper.findPerm(null,null,null,null,1);
}
// 包装数据
ArrayList<Perm> permList1 = new ArrayList<>();
// 父级
for (Perm perm : permList) {
    if (perm.getIsOne() == 0 && perm.getPNo() == null) {
        permList1.add(perm);
    }
}
// 子级
for (Perm perm1 : permList1) {
    ArrayList<Perm> permList2 = new ArrayList<>();
    for (Perm perm : permList) {
        if (StrUtil.equals(perm1.getPath(),perm.getPNo())) {
            permList2.add(perm);
        }
    }
    perm1.setPerms(permList2);
}
// 一级菜单
for (Perm perm : permList) {
    if (perm.getIsOne() == 1) {
        permList1.add(perm);
    }
}
dbUser.setPermList(permList1);
dbUser.setToken(token);
return dbUser;
}

权限获取,并包装传前端思路

  1. 用户登录成功后获取数据库该用户数据
  2. 使用该用户的role角色名称,查询出该用户的角色对应的编号
  3. 使用该角色编号查询,角色对应的权限id,(可能多个使用List集合)
  4. 将查询出的数据进行分类,分为父级、子级、一级
  5. 通过pNo和isOne进行判断分类(pNo为null和isOne为0,则为父级/pNo不为null和isOne为0,则为子级,isOne为1,则为一级)
  6. 使用2层循环将子级菜单加入到父级菜单中。

后端目录

前端

使用技术

  1. Vue3框架
  2. ElementPlus组件库
  3. Axios交互

request.js代码

import axios from 'axios'
import router from "@/router";

// 创建可一个新的axios对象
const request = axios.create({
    baseURL: 'http://localhost:6767',   // 后端的接口地址  ip:port
    timeout: 30000
})

// request 拦截器
// 可以自请求发送前对请求做一些处理
// 比如统一加token,对请求参数统一加密
request.interceptors.request.use(config => {
    config.headers['Content-Type'] = 'application/json;charset=utf-8';
    // 发送token数据(添加到请求头上,使已经添加到请求头上的token在每次访问时加上token)
    let user = JSON.parse(sessionStorage.getItem("user-data") || '{}')
    config.headers['token'] = user.token  // 设置请求头

    return config;
}, error => {
    console.error('request error: ' + error) // for debug
    return Promise.reject(error)
});

// response 拦截器
// 可以在接口响应后统一处理结果
request.interceptors.response.use(
    response => {
        let res = response.data;

        // 兼容服务端返回的字符串数据  JSON.parse(res)转化为json对象
        if (typeof res === 'string') {
            res = res ? JSON.parse(res) : res;
        }

        // 拦截没有token的请求
        if (res.code === "401") {
            router.push('/login')
        }
        return res;
    },
    error => {
        console.error('response error: ' + error) // for debug
        return Promise.reject(error)
    }
)


export default request

动态菜单生成

import router from '@/router'
import HomeView from '@/views/HomeView.vue'

export async function activeRouter(permList){
  let root = {
    path: '/',
    name: '首页',
    component: HomeView,
    redirect: '/home',
    children: [],
  }

  permList.forEach(p1 => {
    if (p1.perms == null) {
      let obj = {
        path: p1.visitPath,
        name: p1.path,
        meta: {name: p1.name},
        component: () => import("@/views/" + p1.path),
      };
      console.log("一级:", obj);
      root.children.push(obj);
    } else {
      p1.perms.forEach(p2 => {
        let obj = {
          path: p2.visitPath,
          name: p2.path,
          meta:{name: p2.name},
          component: ()=> import("@/views/"+ p2.path),
        }
        console.log("二级:",obj)
        root.children.push(obj);
      });
    }
  });
  router.addRoute(root);
}
<div style="width: 130px;position: absolute;right: 0;">
  <el-dropdown>
    <div style="cursor: pointer;align-items: center;display: inline-flex;margin-left: -30px">
      <el-avatar v-if="user.avatar" :src="user.avatar" />
      <el-avatar v-else :icon="UserFilled" />
      <p v-if="user.name != null" style="margin-left: 10px">{{user.name}}</p>
      <div style="margin-left: 10px">
        <el-switch
          v-model="isDark"
          :active-icon="Moon"
          :inactive-icon="Sunny"
          inline-prompt
          @change="toggleDark"
        />
      </div>
    </div>
    <template #dropdown>
      <el-dropdown-menu>
      <el-dropdown-item @click="$router.push('/person')">个人中心</el-dropdown-item>
      <el-dropdown-item @click="logout">退出</el-dropdown-item>
      </el-dropdown-menu>
    </template>
  </el-dropdown>
</div>

路由

import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'

const routes = [
  {
    path: '/',
    name: 'home',
    component: HomeView,
    redirect: 'home',
    /* 子路由 */
    children:[
      {path: 'home',name: 'Home',meta:{name: '主页'},component:() => import('../views/Home.vue')},
    ]
  },
  {
    path: '/login',
    name: 'Login',
    meta: {name: '登录'},
    component: () => import('../views/Login.vue')
  },
  {
    path: '/:catchAll(.*)',
    name: '404',
    component: () => import('../views/404.vue'),
    meta: {name: '无法访问'},
  },
];

const router = createRouter({
  mode: 'history',
  history: createWebHistory(process.env.BASE_URL),
  routes
})

function activeRouter(){
  let userStr = sessionStorage.getItem('user-data');
  let user = JSON.parse(userStr);
  if (user) {
    let root = {
      path: '/',
      name: '首页',
      component: HomeView,
      redirect: '/home',
      children: [],
    }
    // 一级
    user.permList.forEach(p1 => {
      if (p1.perms == null) {
        let obj = {
          path: p1.visitPath,
          name: p1.path,
          meta: {name: p1.name},
          component: () => import("@/views/" + p1.path),
        };
        root.children.push(obj);
      } else {
        // 二级
        p1.perms.forEach(p2 => {
          let obj = {
            path: p2.visitPath,
            name: p2.path,
            meta:{name: p2.name},
            component: ()=> import("@/views/"+ p2.path),
          }
          root.children.push(obj);
        });
      }
    });
    if (router) {
      router.addRoute(root);
    }
  }
}
activeRouter();

// 路由守卫
router.beforeEach((to, from, next) => {
  /*
       to: 到达的路由信息
       from: 来源的路由信息
       next: 跳转路由的函数
   */
  let user = JSON.parse(sessionStorage.getItem('user-data'));
  if (user == null) {
    if (to.path === '/login') {
      next()
    } else {
      next('/login')
    }
  } else {
    if (routes.indexOf(to.path) > -1) {
      next('/login')
    } else {
      next()
    }
  }
});
export default router

前端功能及对应路径

功能:

首页--> /home

个人中心--> /person

权限管理--> perm

权限菜单--> /permMenu

权限分配--> /permAllot

系统功能--> system

用户管理--> /userManage

公告管理--> /noticeManage

系统日志--> /log

前端文件目录

思路

分配权限

前端应显示权限的名称,其对应的应该是权限的id.

分配

选择完后点击分配,应获取该列的perm_role_id值,并在perm_role中通过role_id添加perm_id(对应选择的id)

最近发表
标签列表