登陆认证权限控制(2)—— 基于Spring security 安全框架的权限管理 注解式权限控制 RABC模型

在这里插入图片描述

前言

登陆认证,权限控制是一个系统必不可少的部分,一个开放访问的系统能否在上线后稳定持续运行其实很大程度上取决于登陆认证和权限控制措施是否到位,不然可能系统刚刚上线就会夭折。

Spring Security 是一个能够为基于 Spring 的企业应用系统提供声明式的安全访问控制解决方案的安全框架。

本篇博客介绍Spring Security的使用,结合MySQL,Redis实现基于JWT的注解式的权限认证,并且可以实现给不同的用户显示不同的前端页面。

代码仓库:https://gitee.com/pet365/spring-security-demo

在这里插入图片描述

其他相关的权限文章如下:

  • 从http请求 到 cookie 到 session & 用 session控制 删改数据的权限

  • 使用token的权限验证方法 & 用户+角色+权限表设计 & SpringBoot项目应用

  • 登陆认证&权限控制(1)——从session到token认证的变迁 & session的问题分析 + CSRF攻击的认识

文章目录

  • 前言
  • 引出
  • 初识spring security安全框架
    • 快速入门
  • spring security的项目应用
    • 安全框架的配置
    • UserDetailsService接口
      • 1、基本概念
      • 2、UserDetailsService获取用户名,密码,权限
      • 3、UserInfo extends User
    • 设置拦截器handler
      • 1、登录成功的拦截器
      • 2、请求访问通过OncePerRequestFilter过滤器
  • 权限相关表设计
    • RBAC基于角色的访问控制
    • 数据库表设计
    • 查询语句
    • 权限的注解式控制
      • 配置打开注解
      • 使用注解控制权限
    • 给不同的用户显示不同的页面
      • 数据库表设计
      • 查询语句
      • 不同用户显示的效果
  • 总结
    • 附录:
      • 1、前端页面
      • 2、MySQL数据库语句

引出


1.Spring Security的使用,结合MySQL,Redis实现基于JWT的注解式的权限认证,并且可以实现给不同的用户显示不同的前端页面;
2.项目中的快速应用和使用,拦截器的设置,安全框架的配置;
3.权限表的设计,基于角色的访问控制RABC模型;
4.启动注解的配置,通过注解实现权限的控制;
5.给不同的用户显示不同的页面,相关的SQL以及前端页面;

初识spring security安全框架

Spring Security 是一个能够为基于 Spring 的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在 Spring 应用上下文中配置的 Bean,充分利用了 Spring IoC(Inversion of Control 控制反转),DI(Dependency Injection 依赖注入)和 AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作。

Spring Security 拥有以下特性:

  • 对身份验证和授权的全面且可扩展的支持
  • 防御会话固定、点击劫持,跨站请求伪造等攻击
  • 支持 Servlet API 集成
  • 支持与 Spring Web MVC 集成

Spring、Spring Boot 和 Spring Security 三者的关系如下图所示:

在这里插入图片描述

快速入门

1、引入依赖

<!--springboot整合security坐标-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

2、创建一个控制器

@RestController
public class HelloController {
    @GetMapping("hello")
    public String hello(){
        return "Hello Spring security";
    }
}

3、启动项目

访问:http://localhost:8080/hello 结果打开的是一个登录页面,其实这时候我们的请求已经被保护起来了,要想访问,需要先登录。

在这里插入图片描述

Spring Security 默认提供了一个用户名为 user 的用户,其密码在控制台可以找到

spring security的项目应用

安全框架的配置

在这里插入图片描述

Spring Security 内置的 Password Encoder 有:

加密算法名称PasswordEncoder
NOOPNoOpPasswordEncoder.getInstance()
SHA256new StandardPasswordEncoder()
BCRYPT(官方推荐)new BCryptPasswordEncoder()
LDAPnew LdapShaPasswordEncoder()
PBKDF2new Pbkdf2PasswordEncoder()
SCRYPTnew SCryptPasswordEncoder()
MD4new Md4PasswordEncoder()
MD5new MessageDigestPasswordEncoder(“MD5”)
SHA_1new MessageDigestPasswordEncoder(“SHA-1”)
SHA_256new MessageDigestPasswordEncoder(“SHA-256”)

上述 Password Encoder 中有一个『无意义』的加密器:NoOpPasswordEncoder 。它对原始密码没有做任何处理(现在也被标记为废弃)。

记得使用 @SuppressWarnings(“deprecation”) 去掉 IDE 的警告信息。

package com.tianju.config;

import com.tianju.config.security.handler.*;
import com.tianju.config.security.service.MyUserDetail;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.annotation.Resource;

/**
 * 框架中自定义用户名和密码
 */

@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true,jsr250Enabled = true,prePostEnabled = true) // 开启注解式权限控制
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Resource
    private MyUserDetail myUserDetail;

    @Autowired
    private LoginSuccessHandler loginSuccessHandler;

    @Autowired
    private JwtHandler jwtHandler;

    @Value("${security.isOpen}")
    private Boolean isOpen;

    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        /**
         * security硬性要求密码必须是密文
         */
        PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        // 对明文加密
//        String encode = passwordEncoder.encode("123");
//        System.out.println("加密后为:"+encode);
//        auth.inMemoryAuthentication().withUser("pet").password(encode).roles("admin");
        auth.userDetailsService(myUserDetail);
    }

    /**
     * 自定义表单,前端的页面
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception{
        if (isOpen){
            hasSecurity(http);
        }else {
            noSecurity(http);
        }
    }

    /**
     * 不加安全框架,放行所有请求
     * @param http
     */
    private void noSecurity(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .anyRequest().permitAll()
                .and()
                .csrf().disable();
    }


    private void hasSecurity(HttpSecurity http) throws Exception {
        // 自定义页面
        http.formLogin()  // 需要自定义表单
                .loginPage("/login.html") // 自己登陆页面
                .loginProcessingUrl("/api/user/login") // 登陆请求地址
                .successHandler(loginSuccessHandler) // 给前端返回json字符串
                .failureHandler(new LoginFailHandler()) // 登陆失败给前端的字符串Json
                .permitAll() // 对上面两个进行放行
        ;

        http.exceptionHandling()
                .accessDeniedHandler(new NoAuthorityHandler()) // 登陆后没有权限的返回字符串Json
                .authenticationEntryPoint(new NotLoginHandler()) // 未登录
        ;

        //给接口配置权限
        //1:注解的方式
        //2:编码的方式
        http.authorizeRequests()
                // 无需登陆
                .antMatchers("/find","/api/img/upload","/api/img/hi","/api/map/fastMatch",
                        "/api/myLogin/username","/api/ali/pay","/api/ali/notify").permitAll() // 无需登陆
                .anyRequest().authenticated(); // 所有请求都拦截

        // 指定目标过滤器,填自己的过滤器
        http.addFilterBefore(jwtHandler, UsernamePasswordAuthenticationFilter.class);

        // 前后端项目中装禁用掉session(改用jwt)
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        // CSRF是指跨站请求伪造(Cross-site request forgery)
        // https://www.jianshu.com/p/5ac8deb775b0
        http.csrf().disable(); // 关闭csrf过滤器
    }


    /**
     * 加密的类必须放入IOC容器中
     * @return
     */
    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }
}

UserDetailsService接口

1、基本概念

  • AuthenticationManager

    它是 “表面上” 的做认证和鉴权比对工作的那个人,它是认证和鉴权比对工作的起点。

    ProvierderManager 是 AuthenticationManager 接口的具体实现。

在这里插入图片描述

在这里插入图片描述

  • AuthenticationProvider

    它是 “实际上” 的做认证和鉴权比对工作的那个人。从命名上很容易看出,Provider 受 ProviderManager 的管理,ProviderManager 调用 Provider 进行认证和鉴权的比对工作。

    我们最常用到 DaoAuthenticationProvider 是 AuthenticationProvider 接口的具体实现。

在这里插入图片描述

  • UserDetailsService

    虽然 AuthenticationProvider 负责进行用户名和密码的比对工作,但是它并不清楚用户名和密码的『标准答案』,而标准答案则是由 UserDetailsService 来提供。简单来说,UserDetailsService 负责提供标准答案 ,以供 AuthenticationProvider 使用。

在这里插入图片描述

  • UserDetails

    UserDetails 它是存放用户认证信息和权限信息的标准答案的 “容器” ,它也是 UserDetailService “应该” 返回的内容。

在这里插入图片描述

  • PasswordEncoder

    Spring Security 要求密码不能是明文,必须经过加密器加密。这样,AuthenticationProvider 在做比对时,就必须知道『当初』密码时使用哪种加密器加密的。所以,AuthenticationProvider 除了要向 UserDetailsService 『要』用户名密码的标准答案之外,它还需要知道配套的加密算法(加密器)是什么

在这里插入图片描述

2、UserDetailsService获取用户名,密码,权限

在这里插入图片描述

3、UserInfo extends User

在这里插入图片描述

package com.tianju.config.security.entity;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;

import java.util.Collection;

public class UserInfo extends User {
    private Integer userId; // 用户的id
    private String realName; // 真实姓名

    public UserInfo(String username, String password,
                    Collection<? extends GrantedAuthority> authorities, Integer userId, String realName) {
        super(username, password, authorities);
        this.userId = userId;
        this.realName = realName;
    }

    public Integer getUserId() {
        return userId;
    }

    public String getRealName() {
        return realName;
    }
}

设置拦截器handler

在这里插入图片描述

1、登录成功的拦截器

在这里插入图片描述

2、请求访问通过OncePerRequestFilter过滤器

  • 1:是否携带了jwt

  • 2:解密清求头jwt

    不能解开:放行(到下一个过滤器》

    能解开:走到下一步

  • 3:对比redis中的jwt

    不一样:放行(到下一个过滤器)

    一样:走到下一步

  • 4:给jwt续期

  • 5:让容器中放入一个凭证(登陆凭证)

package com.tianju.config.security.handler;

import com.tianju.config.security.service.MyUserDetail;
import com.tianju.util.JwtUtil;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 1:是否携带了jwt
 * 2:解密清求头jwt
 *      不能解开:放行(到下一个过滤器》
 *      能解开:走到下一步
 * 3:对比redis中的jwt
 *      不一样:放行(到下一个过滤器)
 *      一样:走到下一步
 * 4:给jwt续期
 * 5:让容器中放入一个凭证(登陆凭证)
 */
@Component
@Slf4j
public class JwtHandler extends OncePerRequestFilter {
    @Autowired
    private RedisTemplate<String,Object> stringRedisTemplate;
    @Autowired
    private MyUserDetail myUserDetail;

    @SneakyThrows
    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain filterChain)
            throws ServletException, IOException {
        //1.获取请求头中的前端jwt
        String jwt = request.getHeader("jwt");
        //2.判断能否获取到jwt
        if (jwt == null) {
            filterChain.doFilter(request, response);
            log.warn("未携带Jwt");
            return;
        }
        //3.判断能否解密jwt
        if (!JwtUtil.decode(jwt)) {
            filterChain.doFilter(request, response);
            log.warn("未能解密jwt");
            return;
        }
        //4.通过jwt获取用户信息,然后根据key获取redis中的jwt
        Map userInfo = JwtUtil.getUserInfo(jwt);
        Long userId = (Long) userInfo.get("userId");
        String redisJwt = (String) stringRedisTemplate.opsForValue().get("jwt" + userId.intValue());
        //5.核验两个jwt
        if (!jwt.equals(redisJwt)) {
            filterChain.doFilter(request, response);
            log.warn("redis里的和前端的不一致");
            return;
        }
        //6.给jwt续期
        stringRedisTemplate.opsForValue().set("jwt" + userId, jwt, 30, TimeUnit.DAYS);
        //7.往security容器中放登录凭证
        //实现步骤:获取security上下文类,往里面放一个凭证
        String username = (String) userInfo.get("username");
        UserDetails userDetails = myUserDetail.loadUserByUsername(username);

        UsernamePasswordAuthenticationToken upa = new UsernamePasswordAuthenticationToken(userDetails.getUsername(),
                                                  userDetails.getPassword(),userDetails.getAuthorities());

        SecurityContextHolder.getContext().setAuthentication(upa);
        /**
         * 记得放行
         */
        filterChain.doFilter(request, response);
    }
}

权限相关表设计

RBAC基于角色的访问控制

RBAC(Role-Based Access Control,基于角色的访问控制)模型是一种广泛用于访问控制的安全模型。它基于角色的概念,将权限授权和访问管理组织起来。

在RBAC模型中,有以下几个核心概念:

  • 角色(Role):角色是一组权限的集合,代表了用户在系统中所扮演的角色或身份。用户可以被分配一个或多个角色。
  • 权限(Permission):权限是对系统资源的操作权限。例如,读取、写入、执行等。权限定义了用户或角色可以进行的操作。
  • 用户(User):用户是系统的最终使用者,他们可以被分配一个或多个角色。
  • 资源(Resource):资源是受到访问控制保护的对象,可以是系统中的数据、功能、服务等。

数据库表设计

在这里插入图片描述

查询语句

SELECT 
ut.id,
ut.username,
ut.realname,
ut.password,
art.role_name,
art.role_note,
at.auth_name,
at.auth_url

FROM auth_user_tab ut
LEFT JOIN auth_role_user_tab arut ON arut.user_id=ut.id
LEFT JOIN auth_role_tab art ON art.role_id=arut.role_id
LEFT JOIN auth_role_privs_tab arpt ON arpt.rp_role=arut.role_id
LEFT JOIN auth_tab at ON at.auth_id=arpt.rp_privs

权限的注解式控制

配置打开注解

Spring Security 支持三套注解:

注解类型注解
jsr250 注解@DenyAll、@PermitAll、@RolesAllowed
secured 注解@Secured
prePost 注解@PreAuthorize、@PostAuthorize

使用什么注解在@EnableGlobalMethodSecurity开启,默认是关闭的,例如

@EnableWebSecurity
@EnableGlobalMethodSecurity(jsr250Enabled  = true,securedEnabled=true) //开启jsr250和secured注解
public class SecurityConfig extends WebSecurityConfigurerAdapter {
}

实际开发中最常用的写法 使用 @PreAuthorize(“hasRole(‘admin’)”)

在这里插入图片描述

使用注解控制权限

在这里插入图片描述

package com.tianju.controller;

import com.baomidou.mybatisplus.extension.api.R;
import com.tianju.entity.GoodType;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/good/type")
public class GoodTypeController {

    @GetMapping
    @PreAuthorize("hasAnyAuthority('/good/query')")
    public String get(Integer id){
        return "查询的方法:"+id;
    }

    @PostMapping
    @PreAuthorize("hasAnyAuthority('/good/add')")
    public String add(GoodType goodType){
        return "新增商品的方法:"+goodType;
    }

    @DeleteMapping
    @PreAuthorize("hasAnyAuthority('/good/del')")
    public String deleteById(Integer id){
        return "删除的方法:"+id;
    }

    @PutMapping
    @PreAuthorize("hasAnyAuthority('/good/update')")
    public String updateById(GoodType goodType){
        return "修改的方法:"+goodType;
    }

}

给不同的用户显示不同的页面

数据库表设计

在这里插入图片描述

查询语句

SELECT
		aut.username,
		t_menu.id,
		t_menu.name,
		t_menu.link,
		t_menu.parentid,
		t_menu.icon
FROM auth_user_tab AS aut
				 LEFT JOIN t_employee_menu ON aut.id = t_employee_menu.employeeId
				 LEFT JOIN t_menu ON t_employee_menu.menuId = t_menu.id

不同用户显示的效果

admin用户登录

在这里插入图片描述

peter用户登录

在这里插入图片描述


总结

1.Spring Security的使用,结合MySQL,Redis实现基于JWT的注解式的权限认证,并且可以实现给不同的用户显示不同的前端页面;
2.项目中的快速应用和使用,拦截器的设置,安全框架的配置;
3.权限表的设计,基于角色的访问控制RABC模型;
4.启动注解的配置,通过注解实现权限的控制;
5.给不同的用户显示不同的页面,相关的SQL以及前端页面;

附录:

1、前端页面

在这里插入图片描述

<template>
  <el-container style="height: 100%">
    <el-header style="background-color: rgb(249, 195, 195);">

    </el-header>
    <el-container>
      <el-aside width="200px" style="background-color: rgb(241, 241, 202);">
        <el-col width="200px">
          <el-menu
            router
            default-active="2"
            class="el-menu-vertical-demo">
            <el-submenu :index="menu.id+''" 
            v-for="menu in menuList" :key="menu.id+''">
              <template slot="title">
                <i :class="menu.icon"></i>
                <span>{{ menu.name }}</span>
              </template>
                  <el-menu-item :index="cmenu.link" 
                  v-for="cmenu in menu.childrenMenu" :key="cmenu.id+''">
                    {{ cmenu.name }}
                  </el-menu-item>
            </el-submenu>
          </el-menu>
      </el-col>

      </el-aside>
      <el-main style="background-color: rgb(182, 182, 231);">
        <router-view></router-view>
      </el-main>
    </el-container>
  </el-container>

</template>

<script>
  export default{
    data() {
      return {

        // 定义一个数据,菜单从数据中循环出来table tr
        // 找出一级菜单和二级菜单循环体
        menuList:[
          {id:'1',name:'公共模块',icon:'el-icon-menu',
          childrenMenu:[
            {id:'2',name:'调查问卷',link:'/bookList'},
            {id:'3',name:'资料中心',link:'bcd'},
            {id:'4',name:'工资中心',link:'cde'},
          ]},
          {id:'5',name:'业务目标',icon:'el-icon-document',
          childrenMenu:[
            {id:'5',name:'短期目标',link:'def'},
            {id:'6',name:'长期目标',link:'efg'},
          ]},
        ]

      };
    },
    methods: {
      // 查询用户拥有菜单
      queryUserMenu(){
        console.log("findMenu")
        this.$axios.get('/api/menu')
        .then(response=>{
          let resp = response.data
          console.log(resp)
          if (resp.resultCode.code==20000) {
            this.menuList = resp.results

          }
        })
      },
    },
    created(){
      this.queryUserMenu()
    }
    

}
</script>

<style scoped>

</style>

2、MySQL数据库语句

/*
 Navicat Premium Data Transfer

 Source Server         : 127.0.0.1
 Source Server Type    : MySQL
 Source Server Version : 80022
 Source Host           : 127.0.0.1:3306
 Source Schema         : auth_pet

 Target Server Type    : MySQL
 Target Server Version : 80022
 File Encoding         : 65001

 Date: 06/11/2023 15:44:35
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for auth_role_privs_tab
-- ----------------------------
DROP TABLE IF EXISTS `auth_role_privs_tab`;
CREATE TABLE `auth_role_privs_tab`  (
  `rp_id` bigint NOT NULL AUTO_INCREMENT COMMENT '角色权限的中间表relationship',
  `rp_role` bigint NULL DEFAULT NULL COMMENT '对应的是角色id',
  `rp_privs` bigint NULL DEFAULT NULL COMMENT '对应的权限id',
  `created` datetime NULL DEFAULT NULL,
  `updated` datetime NULL DEFAULT NULL,
  `create_by` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
  PRIMARY KEY (`rp_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 17 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of auth_role_privs_tab
-- ----------------------------
INSERT INTO `auth_role_privs_tab` VALUES (1, 1, 1, '2023-08-06 17:26:48', '2023-08-06 17:26:53', 'admin');
INSERT INTO `auth_role_privs_tab` VALUES (2, 1, 2, '2023-08-06 17:27:09', '2023-08-06 17:27:12', 'admin');
INSERT INTO `auth_role_privs_tab` VALUES (3, 1, 3, '2023-08-06 17:28:14', '2023-08-06 17:28:21', 'admin');
INSERT INTO `auth_role_privs_tab` VALUES (4, 1, 4, '2023-08-06 17:28:17', '2023-08-06 17:28:24', 'admin');
INSERT INTO `auth_role_privs_tab` VALUES (5, 1, 5, '2023-08-06 17:28:19', '2023-08-06 17:28:26', 'admin');
INSERT INTO `auth_role_privs_tab` VALUES (6, 1, 6, '2023-10-07 19:32:23', '2023-10-07 19:32:26', 'admin');
INSERT INTO `auth_role_privs_tab` VALUES (7, 1, 7, '2023-10-07 19:32:29', '2023-10-07 19:32:31', 'admin');
INSERT INTO `auth_role_privs_tab` VALUES (8, 2, 1, '2023-10-07 19:53:21', NULL, NULL);
INSERT INTO `auth_role_privs_tab` VALUES (9, 2, 2, '2023-10-07 19:53:25', NULL, NULL);
INSERT INTO `auth_role_privs_tab` VALUES (10, 2, 3, '2023-10-07 19:53:28', NULL, NULL);
INSERT INTO `auth_role_privs_tab` VALUES (11, 2, 4, '2023-10-07 19:53:30', NULL, NULL);
INSERT INTO `auth_role_privs_tab` VALUES (12, 5, 5, NULL, NULL, NULL);
INSERT INTO `auth_role_privs_tab` VALUES (13, 5, 6, NULL, NULL, NULL);
INSERT INTO `auth_role_privs_tab` VALUES (14, 5, 7, NULL, NULL, NULL);
INSERT INTO `auth_role_privs_tab` VALUES (15, 3, NULL, NULL, NULL, NULL);
INSERT INTO `auth_role_privs_tab` VALUES (16, 4, 1, NULL, NULL, NULL);

-- ----------------------------
-- Table structure for auth_role_tab
-- ----------------------------
DROP TABLE IF EXISTS `auth_role_tab`;
CREATE TABLE `auth_role_tab`  (
  `role_id` bigint NOT NULL AUTO_INCREMENT COMMENT '角色的id,角色表',
  `role_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '角色的name',
  `role_note` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '备注一下',
  `state` int NULL DEFAULT NULL COMMENT '状态,0启用,1删除',
  `created` datetime NULL DEFAULT NULL,
  `updated` datetime NULL DEFAULT NULL,
  `create_by` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
  PRIMARY KEY (`role_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 8 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of auth_role_tab
-- ----------------------------
INSERT INTO `auth_role_tab` VALUES (1, '老板Boss', '全部权限', 0, '2023-08-06 17:24:43', '2023-08-06 17:24:46', 'admin');
INSERT INTO `auth_role_tab` VALUES (2, '商品管理员工', '商品相关', 0, '2023-08-06 17:25:03', '2023-08-06 17:25:05', 'admin');
INSERT INTO `auth_role_tab` VALUES (3, '未实名用户', '无任何权限', 0, '2023-08-06 17:25:38', '2023-08-06 17:25:41', 'admin');
INSERT INTO `auth_role_tab` VALUES (4, '普通用户', '商品查询', 0, '2023-10-07 19:49:34', '2023-10-07 19:49:37', 'admin');
INSERT INTO `auth_role_tab` VALUES (5, '人事部门员工', '人事相关', 0, '2023-10-07 19:49:40', '2023-10-07 19:49:41', 'admin');
INSERT INTO `auth_role_tab` VALUES (7, '老板Boss1', NULL, 0, '2023-11-05 22:27:04', '2023-11-05 22:44:52', 'admin');

-- ----------------------------
-- Table structure for auth_role_user_tab
-- ----------------------------
DROP TABLE IF EXISTS `auth_role_user_tab`;
CREATE TABLE `auth_role_user_tab`  (
  `id` int NOT NULL AUTO_INCREMENT COMMENT '角色,用户关联表,用户可能有多个角色',
  `user_id` int NULL DEFAULT NULL COMMENT '用户表的主键id',
  `role_id` int NULL DEFAULT NULL COMMENT '角色表的主键id',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 6 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of auth_role_user_tab
-- ----------------------------
INSERT INTO `auth_role_user_tab` VALUES (1, 1, 1);
INSERT INTO `auth_role_user_tab` VALUES (2, 2, 2);
INSERT INTO `auth_role_user_tab` VALUES (3, 3, 5);
INSERT INTO `auth_role_user_tab` VALUES (4, 4, 4);
INSERT INTO `auth_role_user_tab` VALUES (5, 5, 3);

-- ----------------------------
-- Table structure for auth_tab
-- ----------------------------
DROP TABLE IF EXISTS `auth_tab`;
CREATE TABLE `auth_tab`  (
  `auth_id` int NOT NULL AUTO_INCREMENT COMMENT '权限表的id',
  `auth_name` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '权限的名称',
  `auth_url` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '权限的url,安全框架用',
  PRIMARY KEY (`auth_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 8 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of auth_tab
-- ----------------------------
INSERT INTO `auth_tab` VALUES (1, '商品查询', '/good/query');
INSERT INTO `auth_tab` VALUES (2, '商品新增', '/good/add');
INSERT INTO `auth_tab` VALUES (3, '商品删除', '/good/del');
INSERT INTO `auth_tab` VALUES (4, '商品修改', '/good/update');
INSERT INTO `auth_tab` VALUES (5, '员工查询', '/emp/query');
INSERT INTO `auth_tab` VALUES (6, '员工新增', '/emp/add');
INSERT INTO `auth_tab` VALUES (7, '员工开除', '/emp/del');

-- ----------------------------
-- Table structure for auth_user_tab
-- ----------------------------
DROP TABLE IF EXISTS `auth_user_tab`;
CREATE TABLE `auth_user_tab`  (
  `id` int NOT NULL AUTO_INCREMENT,
  `username` varchar(25) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '用户名',
  `realname` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '真实姓名',
  `password` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '密码',
  `tel` varchar(25) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '手机号',
  `gender` varchar(5) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '性别',
  `identity` varchar(25) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '身份证号',
  `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',
  `update_time` datetime NULL DEFAULT NULL COMMENT '修改时间',
  `operator` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '操作人',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 6 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of auth_user_tab
-- ----------------------------
INSERT INTO `auth_user_tab` VALUES (1, 'admin', '超级管理', '123', '19800305334', '女', '433123456787', NULL, NULL, NULL);
INSERT INTO `auth_user_tab` VALUES (2, 'peter', '商品管理', '123', '18802229403', '男', '433123456788', NULL, NULL, NULL);
INSERT INTO `auth_user_tab` VALUES (3, 'shirley', '人事管理', '123', NULL, NULL, NULL, NULL, NULL, NULL);
INSERT INTO `auth_user_tab` VALUES (4, 'tom', '普通登陆用户', '123', NULL, NULL, NULL, NULL, NULL, NULL);
INSERT INTO `auth_user_tab` VALUES (5, 'test', '未实名认证', NULL, NULL, NULL, NULL, NULL, NULL, NULL);

-- ----------------------------
-- Table structure for t_employee_menu
-- ----------------------------
DROP TABLE IF EXISTS `t_employee_menu`;
CREATE TABLE `t_employee_menu`  (
  `id_menu` int NOT NULL AUTO_INCREMENT,
  `employeeId` int NULL DEFAULT NULL COMMENT '员工id',
  `menuId` int NULL DEFAULT NULL COMMENT '菜单表id',
  PRIMARY KEY (`id_menu`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 31 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of t_employee_menu
-- ----------------------------
INSERT INTO `t_employee_menu` VALUES (1, 1, 1);
INSERT INTO `t_employee_menu` VALUES (2, 1, 2);
INSERT INTO `t_employee_menu` VALUES (3, 1, 3);
INSERT INTO `t_employee_menu` VALUES (4, 1, 4);
INSERT INTO `t_employee_menu` VALUES (5, 1, 5);
INSERT INTO `t_employee_menu` VALUES (6, 1, 6);
INSERT INTO `t_employee_menu` VALUES (7, 1, 7);
INSERT INTO `t_employee_menu` VALUES (9, 1, 9);
INSERT INTO `t_employee_menu` VALUES (10, 1, 10);
INSERT INTO `t_employee_menu` VALUES (11, 1, 11);
INSERT INTO `t_employee_menu` VALUES (12, 1, 12);
INSERT INTO `t_employee_menu` VALUES (13, 1, 13);
INSERT INTO `t_employee_menu` VALUES (14, 1, 14);
INSERT INTO `t_employee_menu` VALUES (15, 1, 15);
INSERT INTO `t_employee_menu` VALUES (16, 1, 16);
INSERT INTO `t_employee_menu` VALUES (17, 1, 17);
INSERT INTO `t_employee_menu` VALUES (18, 1, 18);
INSERT INTO `t_employee_menu` VALUES (19, 1, 19);
INSERT INTO `t_employee_menu` VALUES (20, 1, 20);
INSERT INTO `t_employee_menu` VALUES (21, 1, 21);
INSERT INTO `t_employee_menu` VALUES (22, 1, 22);
INSERT INTO `t_employee_menu` VALUES (23, 1, 23);
INSERT INTO `t_employee_menu` VALUES (24, 1, 24);
INSERT INTO `t_employee_menu` VALUES (25, 1, 25);
INSERT INTO `t_employee_menu` VALUES (27, 1, 27);
INSERT INTO `t_employee_menu` VALUES (29, 2, 1);
INSERT INTO `t_employee_menu` VALUES (30, 2, 2);

-- ----------------------------
-- Table structure for t_menu
-- ----------------------------
DROP TABLE IF EXISTS `t_menu`;
CREATE TABLE `t_menu`  (
  `id` int NOT NULL AUTO_INCREMENT,
  `name` varchar(15) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '权限名称',
  `link` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '菜单链接',
  `parentid` int NULL DEFAULT NULL COMMENT '父级',
  `status` int NULL DEFAULT NULL COMMENT '1:正常 0:禁用',
  `percode` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '权限编码',
  `icon` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '图标',
  PRIMARY KEY (`id`) USING BTREE,
  INDEX `FK_ParentID`(`parentid`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 29 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of t_menu
-- ----------------------------
INSERT INTO `t_menu` VALUES (1, '系统管理', '', NULL, NULL, NULL, 'el-icon-location');
INSERT INTO `t_menu` VALUES (2, '商品类别', 'goodsType', 1, NULL, NULL, '');
INSERT INTO `t_menu` VALUES (3, '计量单位', 'unit', 1, NULL, NULL, NULL);
INSERT INTO `t_menu` VALUES (4, '仓库管理', 'warehouse', NULL, NULL, NULL, 'el-icon-s-data');
INSERT INTO `t_menu` VALUES (5, '原材料入库', 'rawMaterialList', 4, NULL, NULL, NULL);
INSERT INTO `t_menu` VALUES (6, '原材料领库', 'pickRawList', 4, NULL, NULL, NULL);
INSERT INTO `t_menu` VALUES (7, '订单管理', NULL, NULL, NULL, NULL, 'el-icon-s-tools');
INSERT INTO `t_menu` VALUES (9, '区域管理', 'district', 1, NULL, NULL, NULL);
INSERT INTO `t_menu` VALUES (10, '商品资料', 'goodsList', 1, NULL, NULL, NULL);
INSERT INTO `t_menu` VALUES (11, '供应商资料', 'supplier', 1, NULL, NULL, NULL);
INSERT INTO `t_menu` VALUES (12, '客户资料', 'customer', 1, NULL, NULL, NULL);
INSERT INTO `t_menu` VALUES (13, '仓库管理', 'warehouse', 1, NULL, NULL, NULL);
INSERT INTO `t_menu` VALUES (14, '部门管理', 'department', 1, NULL, NULL, NULL);
INSERT INTO `t_menu` VALUES (15, '员工管理', 'employee', 1, NULL, NULL, NULL);
INSERT INTO `t_menu` VALUES (16, '成品入库', 'finishedPickRaw', 4, NULL, NULL, NULL);
INSERT INTO `t_menu` VALUES (17, '次品入库', 'defectiveGoodsIn', 4, NULL, NULL, NULL);
INSERT INTO `t_menu` VALUES (18, '成品发货', 'finishedGoodsOut', 4, NULL, NULL, NULL);
INSERT INTO `t_menu` VALUES (19, '次品发货', 'defectiveGoodsSend', 4, NULL, NULL, NULL);
INSERT INTO `t_menu` VALUES (20, '财务管理', NULL, NULL, NULL, NULL, 'el-icon-s-data');
INSERT INTO `t_menu` VALUES (21, '应收款管理', 'income', 20, NULL, NULL, NULL);
INSERT INTO `t_menu` VALUES (22, '应付款管理', 'expenses', 20, NULL, NULL, NULL);
INSERT INTO `t_menu` VALUES (23, '数据统计', NULL, NULL, NULL, NULL, 'el-icon-s-data');
INSERT INTO `t_menu` VALUES (24, '仓库报表', 'storeStatistics', 23, NULL, NULL, NULL);
INSERT INTO `t_menu` VALUES (25, '财务报表', 'financialStatistics', 23, NULL, NULL, NULL);
INSERT INTO `t_menu` VALUES (27, '订单查询', 'orderSearch', 7, NULL, NULL, NULL);

SET FOREIGN_KEY_CHECKS = 1;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/122501.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

学习Opencv(蝴蝶书/C++)相关——2.用clang++或g++命令行编译程序

文章目录 1. c/cpp程序的执行1.1 cpp程序的编译过程1.2 预处理指令1.3 编译过程的细节2. macOS下使用Clang看cpp程序的编译过程2.1 示例2.1.1 第一步 预处理器-preprocessor2.1.2 第二步 编译器-compiler2.1.3 第三步 汇编器-assembler2.1.4 第四步 链接器-linker2.1.5 链接其他…

【机器学习基础】机器学习概述

目录 前言 一、机器学习概念 二、机器学习分类 三、机器学习术语 &#x1f308;嗨&#xff01;我是Filotimo__&#x1f308;。很高兴与大家相识&#xff0c;希望我的博客能对你有所帮助。 &#x1f4a1;本文由Filotimo__✍️原创&#xff0c;首发于CSDN&#x1f4da;。 &#x…

【论文阅读】PSDF Fusion:用于动态 3D 数据融合和场景重建的概率符号距离函数

【论文阅读】PSDF Fusion&#xff1a;用于动态 3D 数据融合和场景重建的概率符号距离函数 Abstract1 Introduction3 Overview3.1 Hybrid Data Structure3.2 3D Representations3.3 Pipeline 4 PSDF Fusion and Surface Reconstruction4.1 PSDF Fusion4.2 Inlier Ratio Evaluati…

MCU平台使用SPI-DirectC实现FPGA在线升级

本文介绍在MCU平台上使用SPI-DirectC实现FPGA的在线升级功能。 对于使用Microchip FPGA若想使用离线方式对FPGA进行Bitstream的烧写,就不得不使用官方提供的DirectC组件(开源,包含JTAG-DirectC和SPI-DirectC),本文是在MCU(32bit)上实现的,采用的是SPI-DirectC组件。 …

分布式数据库·Hive和MySQL的安装与配置

一、版本要求&#xff1a;Hadoop:hadoop-2.10.1、MySQL&#xff1a;mysql-8.0.35、 HIVE&#xff1a;apache-hive-3.1.2、MySQL驱动&#xff1a;mysql-connector-java-5.1.49 安装包网盘链接&#xff1a;阿里云盘分享 安装位置 Hive:master、MySQL:slave1 二、卸载已安装的…

实现第三方app开机自启

添加 <category android:name"android.intent.category.LAUNCHER_APP" />

【数据结构】单链表OJ题(一)

&#x1f525;博客主页&#xff1a; 小羊失眠啦. &#x1f3a5;系列专栏&#xff1a;《C语言》 《数据结构》 《Linux》《Cpolar》 ❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 文章目录 前言一、移除链表元素二、寻找链表中间结点三、输出链表倒数第k个结点四、反转单链表五…

【Web】在前端中CSS的语法

CSS规则是由两个主要的部分构成&#xff1a;选择器、以及一条或多条声明。 选择器通常是需要改变的HTML元素。 每条声明由一个属性和一个值组成。 属性&#xff08;Property&#xff09;是需要设置的样式属性&#xff08;Style attribute&#xff09;。每一个属性有一个值。…

Vue3+vite+cesium环境搭建

引言 目前有不少vue3cesium的配置教学&#xff0c;存在以下两个问题&#xff1a; &#xff08;1&#xff09;vue3cli方式&#xff0c;随着项目的迭代&#xff0c;npm run serve 启动调试很慢&#xff1b; &#xff08;2&#xff09;vue3vite 确实能将调试启动提升不少的&…

万宾科技智能井盖监测仪器助力建设数字化城市

市政公共设施建设在近几年来发展迅速&#xff0c;市政设备的更新换代&#xff0c;资产管理等也成为其中的重要一项。在市政设施建设过程中&#xff0c;井盖也是不可忽视的&#xff0c;一方面&#xff0c;根据传统的管理井盖模式来讲&#xff0c;缺乏有效的远程监控管理方法和手…

zookeeper:启动原理

主类&#xff1a; QuorumPeerMain, 其中调用了main对象的initializeAndRun方法&#xff0c; 首先定义了QuorumPeerConfig对象&#xff0c;然后调用了parse方法&#xff0c;parse方法代码如下&#xff1a; 其中调用的parseProperties方法的代码如下&#xff1a; 可以看到&am…

Docker实战

一、Docker安装 以下均以CentOS 7为例 1、安装Docker yum install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin 2、启动和校验 # 启动Docker systemctl start docker# 停止Docker systemctl stop docker# 重启 systemctl resta…

【GEE】7、利用GEE进行遥感影像分类【随机森林分类】

1简介 在本模块中&#xff0c;我们将讨论以下概念&#xff1a; 监督和非监督图像分类之间的区别。Google Earth Engine 提供的各种分类算法的定义和应用。如何使用 randomForest 设置和运行分类&#xff0c;以 aspen 存在和不存在作为示例数据集。 2背景 图像分类 人类自然倾向…

kubernetes集群编排(7)

目录 k8s认证授权 pod绑定sa 认证 授权 k8s认证授权 pod绑定sa [rootk8s2 ~]# kubectl create sa admin //在当前 Kubernetes 集群中创建一个名为 "admin" 的新服务账户[rootk8s2 secret]# vim pod3.yaml apiVersion: v1 kind: Pod metadata:name: mypod spec…

Leetcode—剑指OfferII LCR 044.在每个树行中找最大值【中等】

2023每日刷题&#xff08;二十三&#xff09; Leetcode—LCR 044.在每个树行中找最大值 DFS实现代码 /*** Definition for a binary tree node.* struct TreeNode {* int val;* struct TreeNode *left;* struct TreeNode *right;* };*/ /*** Note: The returned …

单应用多语言切换(语言国际化)

目录 编写语言管理类 编写Activity 的父类 DEMO 实验界面--首页Activity DEMO 实验界面--设置语言Activity Demo 语言资源文件 参考连接 编写语言管理类 package com.example.languageapplicationimport android.content.Context import android.content.ContextWrapper i…

Oracle Primavera Unifier 23.10 新特征

根据官方的说法&#xff0c;Unifier 23.7 ~ 23.9 更多为对功能bug的修复&#xff0c;以下将对23.10进行重点介绍 Cost Sheets Cost Sheets Support Conditional Formatting Conditional formatting of table data is now supported in cost sheets with features such as ce…

Excel下拉填充时,如何使得数字不递增?

问题描述&#xff1a;Excel下拉填充时&#xff0c;如何使得数字不递增&#xff1f; 解决办法&#xff1a;先下拉填充数据之后&#xff0c;看到最后一个单元格的右下角有个填充设置的符号&#xff0c;右键选择复制单元格即可。其中这里的填充序列就是递增数字的操作。

高性能网络编程 - 关于单台服务器并发TCP连接数理论值的讨论

文章目录 概述操作系统的限制因素文件句柄限制1. 进程限制2. 全局限制 端口号范围限制 概述 单台服务器可以支持的并发TCP连接数取决于多个因素&#xff0c;包括硬件性能、操作系统限制、网络带宽和应用程序设计。以下是一些影响并发TCP连接数的因素&#xff1a; 服务器硬件性…