1. 程式人生 > 其它 >spring boot sa-token練手專案

spring boot sa-token練手專案

rbac

基於SpringBoot、sa-token 、MyBatis-plus 的許可權後臺管理系統

專案說明

一個練手的專案,具有RBAC的功能許可權系統

技術選型

1、系統環境

  • Java EE 8
  • Servlet 3.0
  • Apache Maven 3

2、主框架

  • Spring Boot 2.3.x
  • Spring Framework 5.2.x
  • sa-token 1.28.0

3、持久層

  • MyBatis-plus 3.5.x
  • Hibernate Validation 6.0.x
  • Alibaba Druid 1.2.x

4、檢視層

  • Bootstrap 3.3.7
  • Thymeleaf 3.0.x

1、專案依賴與系統配置

1.1、專案依賴
pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.pj</groupId>
    <artifactId>rbac-service</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    <!-- SpringBoot -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.1</version>
    </parent>

    <!-- 指定一些屬性 -->
    <properties>
        <java.version>1.8</java.version>
        <maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
        <druid.version>1.2.6</druid.version>
        <fastjson.version>1.2.76</fastjson.version>
    </properties>

    <dependencies>

        <!-- SpringBoot Web模組 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- Mysql驅動包 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

        <!--阿里資料庫連線池 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>${druid.version}</version>
        </dependency>

        <!-- 阿里JSON解析器 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>${fastjson.version}</version>
        </dependency>

        <!-- mybatis-plus-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.2</version>
        </dependency>

        <!-- 程式碼生成器 -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.4.1</version>
        </dependency>

        <!--freemarker-->
        <dependency>
            <groupId>org.freemarker</groupId>
            <artifactId>freemarker</artifactId>
            <version>2.3.31</version>
        </dependency>

        <!-- Sa-Token 許可權認證, 線上文件:http://sa-token.dev33.cn/ -->
        <dependency>
            <groupId>cn.dev33</groupId>
            <artifactId>sa-token-spring-boot-starter</artifactId>
            <version>1.28.0</version>
        </dependency>
        <!-- Sa-Token 整合 Redis (使用jackson序列化方式) -->
        <dependency>
            <groupId>cn.dev33</groupId>
            <artifactId>sa-token-dao-redis-jackson</artifactId>
            <version>1.28.0</version>
        </dependency>

        <!-- 提供Redis連線池 -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
            <version>2.6.2</version>
        </dependency>

        <!-- 神lombok外掛 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <!-- hutool - 線上文件:https://hutool.cn/docs/#/ -->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>4.5.15</version>
        </dependency>

        <!-- validation -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
            <version>2.5.1</version>
        </dependency>

        <!--Swagger3-->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-boot-starter</artifactId>
            <version>3.0.0</version>
        </dependency>
        <!-- swagger ui-->
        <dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>swagger-bootstrap-ui</artifactId>
            <version>1.8.5</version>
        </dependency>

        <!--驗證碼 -->
        <dependency>
            <groupId>com.github.penggle</groupId>
            <artifactId>kaptcha</artifactId>
            <version>2.3.2</version>
        </dependency>

        <!-- easyexcel -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
            <version>2.2.6</version>
        </dependency>

        <!--aspectj-->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.4</version>
        </dependency>

    </dependencies>

</project>

1.2、系統配置
application.yml

# 自定義配置
custom:
  # 檔案路徑 示例( Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath)
  profile: D:/ruoyi/uploadPath
  # 驗證碼型別 math 陣列計算 char 字元驗證
  captchaType: math

server:
  port: 8080
# Spring配置
spring:
  # 檔案上傳
  servlet:
    multipart:
      # 單個檔案大小
      max-file-size:  10MB
      # 設定總上傳的檔案大小
      max-request-size:  20MB
    # 資料來源配置
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driverClassName: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/demo-rbac?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
    username: root
    password: root
    druid:
      # 初始連線數
      initialSize: 5
      # 最小連線池數量
      minIdle: 10
      # 最大連線池數量
      maxActive: 20
      # 配置獲取連線等待超時的時間
      maxWait: 60000
      # 配置間隔多久才進行一次檢測,檢測需要關閉的空閒連線,單位是毫秒
      timeBetweenEvictionRunsMillis: 60000
      # 配置一個連線在池中最小生存的時間,單位是毫秒
      minEvictableIdleTimeMillis: 300000
      # 配置一個連線在池中最大生存的時間,單位是毫秒
      maxEvictableIdleTimeMillis: 900000
      # 配置檢測連線是否有效
      validationQuery: SELECT 1 FROM DUAL
      testWhileIdle: true
      testOnBorrow: false
      testOnReturn: false
      webStatFilter:
        enabled: true
      statViewServlet:
        enabled: true
        # 設定白名單,不填則允許所有訪問
        allow:
        url-pattern: /druid/*
        # 控制檯管理使用者名稱和密碼
        login-username: galen
        login-password: galen
      filter:
        stat:
          enabled: true
          # 慢SQL記錄
          log-slow-sql: true
          slow-sql-millis: 1000
          merge-sql: true
        wall:
          config:
            multi-statement-allow: true
  # redis配置
  redis:
    # Redis資料庫索引(預設為0)
    database: 1
    # Redis伺服器地址
    host: 127.0.0.1
    # Redis伺服器連線埠
    port: 6379
    # Redis伺服器連線密碼(預設為空)
    # password:
    # 連線超時時間
    timeout: 10s
    lettuce:
      pool:
        # 連線池最大連線數
        max-active: 200
        # 連線池最大阻塞等待時間(使用負值表示沒有限制)
        max-wait: -1ms
        # 連線池中的最大空閒連線
        max-idle: 10
        # 連線池中的最小空閒連線
        min-idle: 0

# Logger Config
logging:
  level:
    com.heyuht.hospital.mapper: debug

# Sa-Token 配置
sa-token:
  # token名稱 (同時也是cookie名稱)
  token-name: satoken
  # token有效期,單位s 預設30天, -1代表永不過期
  timeout: 2592000
  # token臨時有效期 (指定時間內無操作就視為token過期) 單位: 秒
  activity-timeout: -1
  # 是否允許同一賬號併發登入 (為true時允許一起登入, 為false時新登入擠掉舊登入)
  is-concurrent: true
  # 在多人登入同一賬號時,是否共用一個token (為true時所有登入共用一個token, 為false時每次登入新建一個token)
  is-share: true
  # token風格
  token-style: uuid
  # 是否輸出操作日誌
  is-log: false
  # cookie裡讀取token
  is-read-cookie: false
  # cookie裡讀取token
  is-print: false

2、配置連線池

2.1配置檔案
DruidConfig.java


import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties;
import com.alibaba.druid.util.Utils;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.*;
import javax.sql.DataSource;
import java.io.IOException;

/**
 * druid 配置多資料來源
 *
 * @author heyuht
 */
@Configuration
public class DruidConfig
{
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSource druid(){
        return new DruidDataSource();
    }

    /**
     * 去除監控頁面底部的廣告
     */
    @SuppressWarnings({ "rawtypes", "unchecked" })
    @Bean
    @ConditionalOnProperty(name = "spring.datasource.druid.statViewServlet.enabled", havingValue = "true")
    public FilterRegistrationBean removeDruidFilterRegistrationBean(DruidStatProperties properties)
    {
        // 獲取web監控頁面的引數
        DruidStatProperties.StatViewServlet config = properties.getStatViewServlet();
        // 提取common.js的配置路徑
        String pattern = config.getUrlPattern() != null ? config.getUrlPattern() : "/druid/*";
        String commonJsPattern = pattern.replaceAll("\\*", "js/common.js");
        final String filePath = "support/http/resources/js/common.js";
        // 建立filter進行過濾
        Filter filter = new Filter()
        {
            @Override
            public void init(javax.servlet.FilterConfig filterConfig) throws ServletException
            {
            }
            @Override
            public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
                    throws IOException, ServletException
            {
                chain.doFilter(request, response);
                // 重置緩衝區,響應頭不會被重置
                response.resetBuffer();
                // 獲取common.js
                String text = Utils.readFromResource(filePath);
                // 正則替換banner, 除去底部的廣告資訊
                text = text.replaceAll("<a.*?banner\"></a><br/>", "");
                text = text.replaceAll("powered.*?shrek.wang</a>", "");
                response.getWriter().write(text);
            }
            @Override
            public void destroy()
            {
            }
        };
        FilterRegistrationBean registrationBean = new FilterRegistrationBean();
        registrationBean.setFilter(filter);
        registrationBean.addUrlPatterns(commonJsPattern);
        return registrationBean;
    }
}

3、配置 mybatis plus 程式碼生成

1、自定義模板controller
controller.java.ftl

package ${package.Controller};


import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import ${package.Service}.${table.serviceName};
import ${package.Entity}.${entity};
import com.heyuht.hospital.utils.AjaxResult;
import com.heyuht.hospital.utils.TableDataInfo;
<#if swagger2>
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
</#if>

/**
 * <p>
 * ${table.comment!} 前端控制器
 * </p>
 *
 * @author ${author}
 * @since ${date}
 */
<#if swagger2>
@Api(tags = "${table.comment!}")
</#if>
@RestController
@RequestMapping("${table.entityPath}")
public class ${table.controllerName} {

    @Autowired
    private ${table.serviceName} ${table.entityPath}Service;

    // 查詢
    <#if swagger2>
    @ApiOperation(value = "查詢",notes = "查詢列表")
    </#if>
    @GetMapping("/list")
    public TableDataInfo list(Integer pageNum,Integer pageSize){
    Page<${entity}> page = new Page<>();
        if (pageNum != null && pageSize != null){
        page = new Page<>(pageNum, pageSize);
        }
        Page<${entity}> ${table.entityPath}Page = ${table.entityPath}Service.page(page);
            return new TableDataInfo(${table.entityPath}Page);
        }
    // 新增
    <#if swagger2>
    @ApiOperation(value = "新增",notes = "新增資料")
    </#if>
    @PostMapping("/add")
    public AjaxResult add(${entity} ${table.entityPath}){
    boolean flag = ${table.entityPath}Service.save(${table.entityPath});
    return AjaxResult.toAjax(flag);
    }
    // 修改
    <#if swagger2>
    @ApiOperation(value = "修改",notes = "修改資料")
    </#if>
    @PostMapping("/update")
    public AjaxResult update(${entity} ${table.entityPath}){
    return AjaxResult.toAjax(${table.entityPath}Service.updateById(${table.entityPath}));
    }
    // 刪除
    <#if swagger2>
    @ApiOperation(value = "刪除",notes = "刪除資料")
    </#if>
    @PostMapping("/delete/{id}")
    public AjaxResult delete(@PathVariable Long id){
    return AjaxResult.toAjax(${table.entityPath}Service.removeById(id));
    }
}

2.程式碼生成
CodeGenerator.java


import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.po.TableFill;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;

import java.util.ArrayList;
import java.util.List;

// 演示例子,執行 main 方法控制檯輸入模組表名回車自動生成對應專案目錄中
public class CodeGenerator {

    public static void main(String[] args) {

        // 表名,通過,分割
        String tableName = "sys_attachment";

        // 程式碼生成器
        AutoGenerator mpg = new AutoGenerator();

        // 全域性配置
        GlobalConfig gc = new GlobalConfig();
        String projectPath = "D:/hy-project/new-base-cloud/demo/rbac-service/src/main";
        gc.setOutputDir(projectPath + "/java/");
        gc.setAuthor("galen");
        gc.setOpen(false);
        gc.setSwagger2(true); //實體屬性 Swagger2 註解
        mpg.setGlobalConfig(gc);



        // 資料來源配置
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setUrl("jdbc:mysql://localhost:3306/demo-rbac?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8");
        // dsc.setSchemaName("public");
        dsc.setDriverName("com.mysql.cj.jdbc.Driver");
        dsc.setUsername("root");
        dsc.setPassword("root");
        mpg.setDataSource(dsc);

        // 包配置
        PackageConfig pc = new PackageConfig();
        pc.setParent("com.heyuht.hospital");
        mpg.setPackageInfo(pc);
        // 自定義配置
        InjectionConfig cfg = new InjectionConfig() {
            @Override
            public void initMap() {
                // to do nothing
            }
        };

        // 如果模板引擎是 freemarker
        String templatePath = "/templates/mapper.xml.ftl";
        // 如果模板引擎是 velocity
        // String templatePath = "/templates/mapper.xml.vm";
        // 自定義輸出配置
        List<FileOutConfig> focList = new ArrayList<>();
        // 自定義配置會被優先輸出
        focList.add(new FileOutConfig(templatePath) {
            @Override
            public String outputFile(TableInfo tableInfo) {
                // 自定義輸出檔名 , 如果你 Entity 設定了前後綴、此處注意 xml 的名稱會跟著發生變化!!
                return projectPath + "/resources/mapper/"
                        + "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
            }
        });
        cfg.setFileOutConfigList(focList);
        mpg.setCfg(cfg);
        // 配置模板
        TemplateConfig templateConfig = new TemplateConfig();

        // 配置自定義輸出模板
        //指定自定義模板路徑,注意不要帶上.ftl/.vm, 會根據使用的模板引擎自動識別
        // templateConfig.setEntity("templates/entity2.java");
        // templateConfig.setService();
        templateConfig.setController("/templates/ftl/controller.java");

        templateConfig.setXml(null);
        mpg.setTemplate(templateConfig);

        // 自定義需要填充的欄位 資料庫中的欄位
        List<TableFill> tableFillList = new ArrayList<>();
        tableFillList.add(new TableFill("create_time", FieldFill.INSERT));
        tableFillList.add(new TableFill("create_by", FieldFill.INSERT));
        tableFillList.add(new TableFill("update_time", FieldFill.UPDATE));
        tableFillList.add(new TableFill("update_by", FieldFill.UPDATE));


        // 策略配置
        StrategyConfig strategy = new StrategyConfig();
        strategy.setNaming(NamingStrategy.underline_to_camel);
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
        strategy.setEntityLombokModel(true);
        strategy.setRestControllerStyle(true);
        strategy.setInclude(tableName.split(","));
        strategy.setControllerMappingHyphenStyle(true);
        strategy.setTablePrefix(pc.getModuleName() + "_");
        //自動填充設定
        strategy.setTableFillList(tableFillList);
        mpg.setStrategy(strategy);
        mpg.setTemplateEngine(new FreemarkerTemplateEngine());
        mpg.execute();
    }

}

mybatis plus 其它配置
1、配置分頁
MybatisPlusConfig.java

package com.heyuht.hospital.config.mybatisPlus;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

//Spring boot方式
@Configuration
@MapperScan("com.heyuht.hospital.*.mapper*")
public class MybatisPlusConfig {

    // 舊版
//    @Bean
//    public PaginationInterceptor paginationInterceptor() {
//        PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
//        // 設定請求的頁面大於最大頁後操作, true調回到首頁,false 繼續請求  預設false
//        // paginationInterceptor.setOverflow(false);
//        // 設定最大單頁限制數量,預設 500 條,-1 不受限制
//        // paginationInterceptor.setLimit(500);
//        // 開啟 count 的 join 優化,只針對部分 left join
//        paginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true));
//        return paginationInterceptor;
//    }

    // 分頁最新版
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }

}

2、配置欄位填充
MyMetaObjectHandler.java

package com.heyuht.hospital.config.mybatisPlus;

import cn.dev33.satoken.stp.StpUtil;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.Date;

/**
 * 自動填充功能
 */
@Slf4j
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {

    @Override
    public void insertFill(MetaObject metaObject) {
        log.info("start insert fill ....");
        this.strictInsertFill(metaObject, "createTime", () -> new Date(), Date.class); // 起始版本 3.3.3(推薦)
        this.strictInsertFill(metaObject, "createBy", () -> StpUtil.getLoginIdAsString(), String.class);
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        log.info("start update fill ....");
        this.strictUpdateFill(metaObject, "updateTime", () -> new Date(), Date.class); // 起始版本 3.3.3(推薦)
        this.strictUpdateFill(metaObject, "updateBy", () -> StpUtil.getLoginIdAsString(), String.class);
    }
}

5、系統管理

1、生成使用者,許可權,選單表

1、編寫對應表的CRUD介面

2、新增登入介面

LoginController.java

package com.heyuht.hospital.controller;


import cn.dev33.satoken.secure.SaSecureUtil;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.util.SaResult;
import com.alibaba.fastjson.JSONObject;
import com.heyuht.hospital.config.satoken.StpInterfaceImpl;
import com.heyuht.hospital.entity.SysUser;
import com.heyuht.hospital.entity.vo.LoginVo;
import com.heyuht.hospital.service.ISysUserService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.Optional;

/**
 *
 * 登入相關
 *
 * @author galen
 * @since 2022-01-22
 */
@Api(tags = "登入相關")
@RestController
@RequestMapping("/")
public class LoginController {

    @Autowired
    ISysUserService sysUserService;
    @Autowired
    StpInterfaceImpl stpInterface;

    @ApiOperation(value = "登入",notes = "用來登入")
    @PostMapping("login")
    public SaResult login(@RequestBody LoginVo loginVo){
        String username = loginVo.getUserName();
        String password = loginVo.getPassword();
        if (username == null || username.trim().length() == 0 || password == null || password.trim().length() == 0){
            return SaResult.error("使用者或密碼不能為空");
        }
        SysUser one = sysUserService.lambdaQuery()
                .eq(SysUser::getUserName, username)
                .eq(SysUser::getPassword, SaSecureUtil.sha1(password)).one();
        if (one != null){
            StpUtil.login(one.getUserId());
            return SaResult.ok("登入成功");
        }
        return SaResult.error("使用者名稱或密碼錯誤");
    }

    @ApiOperation(value = "退出登入")
    @GetMapping("logout")
    public SaResult logout(){
        // 當前會話登出登入
        StpUtil.logout();
        return SaResult.ok("退出成功");
    }
}

StpInterfaceImpl.java

package com.heyuht.hospital.config.satoken;

import cn.dev33.satoken.stp.StpInterface;
import com.heyuht.hospital.service.ISysRoleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;

/**
 * 自定義許可權驗證介面擴充套件
 * 許可權認證。
 */
// 保證此類被SpringBoot掃描,完成Sa-Token的自定義許可權驗證擴充套件
@Component
public class StpInterfaceImpl implements StpInterface {

    @Autowired
    ISysRoleService sysRoleService;

    /**
     * 返回一個賬號所擁有的許可權碼集合
     */
    @Override
    public List<String> getPermissionList(Object loginId, String loginType) {
        // 在判斷許可權時,會呼叫該方法
        List<String> list = new ArrayList<String>();
        Integer userId = Integer.valueOf(loginId.toString());
        if (userId == 1){
            list.add("*");
            return list;
        }
        list = sysRoleService.queryRoleMenu(userId);
//        list.add("101");
//        list.add("user:add");
//        list.add("user:delete");
//        list.add("user:update");
        return list;
    }

    /**
     * 返回一個賬號所擁有的角色標識集合 (許可權與角色可分開校驗)
     */
    @Override
    public List<String> getRoleList(Object loginId, String loginType) {
        // 本list僅做模擬,實際專案中要根據具體業務邏輯來查詢角色
        List<String> list = new ArrayList<String>();
        list.add("admin");
        list.add("super:admin");
        return list;
    }

}

新增攔截器 SaTokenConfigure.java

package com.heyuht.hospital.config.satoken;

import cn.dev33.satoken.interceptor.SaAnnotationInterceptor;
import cn.dev33.satoken.interceptor.SaRouteInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class SaTokenConfigure implements WebMvcConfigurer {
    // 註冊攔截器
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 註冊Sa-Token的路由攔截器
        registry.addInterceptor(new SaRouteInterceptor()).addPathPatterns("/**")
                .excludePathPatterns("/login","/captchaImage");

        // 註冊註解攔截器,並排除不需要註解鑑權的介面地址 (與登入攔截器無關)
        registry.addInterceptor(new SaAnnotationInterceptor()).addPathPatterns("/**");
    }
}

3、新增驗證碼

CaptchaConfig.java

package com.heyuht.hospital.config.captcha;

import com.google.code.kaptcha.impl.DefaultKaptcha;
import com.google.code.kaptcha.util.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import static com.google.code.kaptcha.Constants.*;

import java.util.Properties;

/**
 * 驗證碼配置
 * https://www.cnblogs.com/louis80/p/5230507.html
 * @author galen
 */
@Configuration
public class CaptchaConfig
{
    @Bean(name = "captchaProducer")
    public DefaultKaptcha getKaptchaBean()
    {
        DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
        Properties properties = new Properties();
        // 是否有邊框 預設為true 我們可以自己設定yes,no
        properties.setProperty(KAPTCHA_BORDER, "yes");
        // 驗證碼文字字元顏色 預設為Color.BLACK
        properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_COLOR, "black");
        // 驗證碼圖片寬度 預設為200
        properties.setProperty(KAPTCHA_IMAGE_WIDTH, "160");
        // 驗證碼圖片高度 預設為50
        properties.setProperty(KAPTCHA_IMAGE_HEIGHT, "60");
        // 驗證碼文字字元大小 預設為40
        properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_SIZE, "38");
        // KAPTCHA_SESSION_KEY
        properties.setProperty(KAPTCHA_SESSION_CONFIG_KEY, "kaptchaCode");
        // 驗證碼文字字元長度 預設為5
        properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "4");
        // 驗證碼文字字型樣式 預設為new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize)
        properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_NAMES, "Arial,Courier");
        // 圖片樣式 水紋com.google.code.kaptcha.impl.WaterRipple 魚眼com.google.code.kaptcha.impl.FishEyeGimpy 陰影com.google.code.kaptcha.impl.ShadowGimpy
        properties.setProperty(KAPTCHA_OBSCURIFICATOR_IMPL, "com.google.code.kaptcha.impl.ShadowGimpy");
        Config config = new Config(properties);
        defaultKaptcha.setConfig(config);
        return defaultKaptcha;
    }

    @Bean(name = "captchaProducerMath")
    public DefaultKaptcha getKaptchaBeanMath()
    {
        DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
        Properties properties = new Properties();
        // 是否有邊框 預設為true 我們可以自己設定yes,no
        properties.setProperty(KAPTCHA_BORDER, "yes");
        // 邊框顏色 預設為Color.BLACK
        properties.setProperty(KAPTCHA_BORDER_COLOR, "105,179,90");
        // 驗證碼文字字元顏色 預設為Color.BLACK
        properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_COLOR, "blue");
        // 驗證碼圖片寬度 預設為200
        properties.setProperty(KAPTCHA_IMAGE_WIDTH, "160");
        // 驗證碼圖片高度 預設為50
        properties.setProperty(KAPTCHA_IMAGE_HEIGHT, "60");
        // 驗證碼文字字元大小 預設為40
        properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_SIZE, "35");
        // KAPTCHA_SESSION_KEY
        properties.setProperty(KAPTCHA_SESSION_CONFIG_KEY, "kaptchaCodeMath");
        // 驗證碼文字生成器
        properties.setProperty(KAPTCHA_TEXTPRODUCER_IMPL, "com.heyuht.hospital.config.captcha.KaptchaTextCreator");
        // 驗證碼文字字元間距 預設為2
        properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_SPACE, "3");
        // 驗證碼文字字元長度 預設為5
        properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "6");
        // 驗證碼文字字型樣式 預設為new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize)
        properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_NAMES, "Arial,Courier");
        // 驗證碼噪點顏色 預設為Color.BLACK
        properties.setProperty(KAPTCHA_NOISE_COLOR, "white");
        // 干擾實現類
        properties.setProperty(KAPTCHA_NOISE_IMPL, "com.google.code.kaptcha.impl.NoNoise");
        // 圖片樣式 水紋com.google.code.kaptcha.impl.WaterRipple 魚眼com.google.code.kaptcha.impl.FishEyeGimpy 陰影com.google.code.kaptcha.impl.ShadowGimpy
        properties.setProperty(KAPTCHA_OBSCURIFICATOR_IMPL, "com.google.code.kaptcha.impl.ShadowGimpy");
        Config config = new Config(properties);
        defaultKaptcha.setConfig(config);
        return defaultKaptcha;
    }
}

KaptchaTextCreator.java

package com.heyuht.hospital.config.captcha;

import com.google.code.kaptcha.text.impl.DefaultTextCreator;

import java.util.Random;

/**
 * 驗證碼文字生成器
 *
 * @author ruoyi
 */
public class KaptchaTextCreator extends DefaultTextCreator
{
    private static final String[] CNUMBERS = "0,1,2,3,4,5,6,7,8,9,10".split(",");

    @Override
    public String getText()
    {
        Integer result = 0;
        Random random = new Random();
        int x = random.nextInt(10);
        int y = random.nextInt(10);
        StringBuilder suChinese = new StringBuilder();
        int randomoperands = (int) Math.round(Math.random() * 2);
        if (randomoperands == 0)
        {
            result = x * y;
            suChinese.append(CNUMBERS[x]);
            suChinese.append("*");
            suChinese.append(CNUMBERS[y]);
        }
        else if (randomoperands == 1)
        {
            if (!(x == 0) && y % x == 0)
            {
                result = y / x;
                suChinese.append(CNUMBERS[y]);
                suChinese.append("/");
                suChinese.append(CNUMBERS[x]);
            }
            else
            {
                result = x + y;
                suChinese.append(CNUMBERS[x]);
                suChinese.append("+");
                suChinese.append(CNUMBERS[y]);
            }
        }
        else if (randomoperands == 2)
        {
            if (x >= y)
            {
                result = x - y;
                suChinese.append(CNUMBERS[x]);
                suChinese.append("-");
                suChinese.append(CNUMBERS[y]);
            }
            else
            {
                result = y - x;
                suChinese.append(CNUMBERS[y]);
                suChinese.append("-");
                suChinese.append(CNUMBERS[x]);
            }
        }
        else
        {
            result = x + y;
            suChinese.append(CNUMBERS[x]);
            suChinese.append("+");
            suChinese.append(CNUMBERS[y]);
        }
        suChinese.append("=?@" + result);
        return suChinese.toString();
    }
}

4、新增許可權相關介面

1、角色授權選單功能
3、使用者授權角色功能

5、測試認證

1、新增角色選單

2、新增使用者角色

3、新增許可權驗證

6、新增swagger3

1、整合swagger3+UI美化包

package com.heyuht.hospital.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;

/**
 * Swagger3(可選配置)
 */
@Configuration
public class Swagger3 {

    @Bean
    public Docket docket() {
        return new Docket(DocumentationType.OAS_30).apiInfo(
                new ApiInfoBuilder()
                        .contact(new Contact("Galen", "", ""))
                        .title("Swagger3介面測試專案")
                        .build()
        );
    }
}

7、新增系統日誌

1、新增日誌表

2、日誌儲存方法

3、新增日誌切面

8、新增檔案上傳

1、檔案上傳服務

9、新增匯出匯入功能

1、引用EasyExcel依賴。

參考文件https://blog.csdn.net/qidasheng2012/article/details/102707394

10、字典管理

1、新增字典主表和字典明細表。

6、系統監控

1、定時任務

2、資料監控

3、服務監控

4、快取監控