Spring Boot项目学习之通用权限管理项目01

权限管理系统是一个十分常见的系统,在这个系统中是基于角色访问控制的,用户是通过角色与权限进行关联。换句话说,就是一个用户拥有若干个角色,每一个角色拥有若干权限,这样就可以形成一个关系模型:用户-角色-权限。他们之间的关系:

  • 一个用户对应多个角色,一个角色对应多个用户,多对多关系。
  • 一个角色对应多个资源,一个资源对应多个角色,多对多关系。

1. 创建数据库脚本

DROP TABLE IF EXISTS `urms_system`;
CREATE TABLE `urms_system` (
  `system_id` bigint(10) unsigned NOT NULL COMMENT '编号',
  `icon` varchar(50) DEFAULT NULL COMMENT '图标',
  `banner` varchar(150) DEFAULT NULL COMMENT '背景',
  `theme` varchar(50) DEFAULT NULL COMMENT '主题',
  `basepath` varchar(100) NOT NULL COMMENT '根目录',
  `status` tinyint(4) DEFAULT NULL COMMENT '状态(-1:黑名单,1:正常)',
  `name` varchar(20) NOT NULL COMMENT '系统名称',
  `title` varchar(20) NOT NULL COMMENT '系统标题',
  `description` varchar(300) NOT NULL COMMENT '系统描述',
  `ctime` datetime DEFAULT NULL COMMENT '创建时间',
  `orders` bigint(20) NOT NULL COMMENT '排序',
  PRIMARY KEY (`system_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='系统';


DROP TABLE IF EXISTS `urms_organization`;
CREATE TABLE `urms_organization` (
  `organization_id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '编号',
  `pid` int(10) DEFAULT NULL COMMENT '所属上级',
  `name` varchar(40) DEFAULT NULL COMMENT '组织名称',
  `description` varchar(500) DEFAULT NULL COMMENT '组织描述',
  `ctime` datetime DEFAULT NULL COMMENT '创建时间',
  PRIMARY KEY (`organization_id`)
) ENGINE=InnoDB AUTO_INCREMENT=100 DEFAULT CHARSET=utf8 COMMENT='组织';


DROP TABLE IF EXISTS `urms_permission`;
CREATE TABLE `urms_permission` (
  `permission_id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '编号',
  `system_id` int(10) unsigned NOT NULL COMMENT '所属系统',
  `pid` int(10) DEFAULT NULL COMMENT '所属上级',
  `name` varchar(20) NOT NULL COMMENT '名称',
  `type` tinyint(4) DEFAULT NULL COMMENT '类型(1:目录,2:菜单,3:按钮)',
  `permission_value` varchar(50) DEFAULT NULL COMMENT '权限值',
  `uri` varchar(100) DEFAULT NULL COMMENT '路径',
  `icon` varchar(50) DEFAULT NULL COMMENT '图标',
  `status` tinyint(4) DEFAULT NULL COMMENT '状态(0:禁止,1:正常)',
  `ctime` datetime DEFAULT NULL COMMENT '创建时间',
  `orders` bigint(20) NOT NULL COMMENT '排序',
  PRIMARY KEY (`permission_id`)
) ENGINE=InnoDB AUTO_INCREMENT=100 DEFAULT CHARSET=utf8 COMMENT='权限';


DROP TABLE IF EXISTS `urms_role`;
CREATE TABLE `urms_role` (
  `role_id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '编号',
  `name` varchar(20) NOT NULL COMMENT '角色名称',
  `title` varchar(20) NOT NULL COMMENT '角色标题',
  `description` varchar(500) DEFAULT NULL COMMENT '角色描述',
  `ctime` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `orders` bigint(20) NOT NULL COMMENT '排序',
  PRIMARY KEY (`role_id`)
) ENGINE=InnoDB AUTO_INCREMENT=100 DEFAULT CHARSET=utf8 COMMENT='角色';


DROP TABLE IF EXISTS `urms_role_permission`;
CREATE TABLE `urms_role_permission` (
  `role_permission_id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '编号',
  `role_id` int(10) unsigned NOT NULL COMMENT '角色编号',
  `permission_id` int(10) unsigned NOT NULL COMMENT '权限编号',
  PRIMARY KEY (`role_permission_id`)
) ENGINE=InnoDB AUTO_INCREMENT=100 DEFAULT CHARSET=utf8 COMMENT='角色权限关联表';



DROP TABLE IF EXISTS `urms_user`;
CREATE TABLE `urms_user` (
  `user_id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '编号',
  `username` varchar(20) NOT NULL COMMENT '帐号',
  `password` varchar(32) NOT NULL COMMENT '密码MD5(密码+盐)',
  `salt` varchar(32) DEFAULT NULL COMMENT '盐',
  `realname` varchar(20) NOT NULL COMMENT '姓名',
  `avatar` varchar(150) DEFAULT NULL COMMENT '头像',
  `phone` varchar(20) NOT NULL COMMENT '电话',
  `email` varchar(50) NOT NULL COMMENT '邮箱',
  `sex` tinyint(4) NOT NULL COMMENT '性别',
  `locked` tinyint(4) DEFAULT NULL COMMENT '状态(0:正常,1:锁定)',
  `ctime` datetime DEFAULT NULL COMMENT '创建时间',
  `organization_id` int(10) DEFAULT NULL COMMENT '所属组织机构',
  PRIMARY KEY (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=100 DEFAULT CHARSET=utf8 COMMENT='用户';



DROP TABLE IF EXISTS `urms_user_role`;
CREATE TABLE `urms_user_role` (
  `user_role_id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '编号',
  `user_id` int(10) unsigned NOT NULL COMMENT '用户编号',
  `role_id` int(10) unsigned NOT NULL COMMENT '角色编号',
  PRIMARY KEY (`user_role_id`)
) ENGINE=InnoDB AUTO_INCREMENT=100 DEFAULT CHARSET=utf8 COMMENT='用户角色关联表';


DROP TABLE IF EXISTS `urms_log`;
CREATE TABLE `urms_log` (
  `log_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '编号',
  `description` varchar(100) DEFAULT NULL COMMENT '操作描述',
  `username` varchar(20) DEFAULT NULL COMMENT '操作用户',
  `permissions` varchar(100) DEFAULT NULL COMMENT '权限值',
  `start_time` datetime DEFAULT NULL COMMENT '操作时间',
  `spend_time` int(11) DEFAULT NULL COMMENT '消耗时间',
  `base_path` varchar(500) DEFAULT NULL COMMENT '根路径',
  `uri` varchar(500) DEFAULT NULL COMMENT 'URI',
  `method` varchar(7) DEFAULT NULL COMMENT '请求类型',
  `parameter` text  DEFAULT NULL COMMENT '参数',
  `user_agent` varchar(500) DEFAULT NULL COMMENT '用户标识',
  `ip` varchar(30) DEFAULT NULL COMMENT 'IP地址',
  `result` tinyint(4) DEFAULT NULL COMMENT '响应状态',
  PRIMARY KEY (`log_id`),
  KEY `log_id` (`log_id`)
) ENGINE=InnoDB AUTO_INCREMENT=100 DEFAULT CHARSET=utf8 COMMENT='操作日志';





INSERT INTO `urms_organization` VALUES (1,0,'xxx科技有限公司','深圳总部','2020-05-02 05:55:02'),(2,1,'总公司','深圳总部','2020-05-02 05:55:02'),(4,1,'河北分公司','河北石家庄','2020-05-02 05:55:02'),(5,1,'河南分公司','河南郑州','2020-05-02 05:55:02'),(6,1,'湖北分公司','湖北武汉','2020-05-02 05:55:02'),(7,1,'湖南分公司','湖南长沙','2020-05-02 05:55:02'),(8,2,'总公司研发部','深圳总部研发部门','2020-05-02 19:55:02'),(9,2,'总公司市场部','深圳总部市场部门','2020-05-02 05:55:02'),(10,2,'总公司财务部','深圳总部财务部门','2020-05-02 05:55:02'),(11,2,'总公司运维部','深圳总部运维部门','2020-05-02 05:55:02'),(12,4,'石家庄支公司','河北省石家庄市','2020-05-10 12:35:36'),(13,6,'武汉支公司','湖北省武汉市','2020-05-10 12:36:40'),(14,13,'武汉营销部','武汉支公司市场营销部','2020-05-10 12:37:38');

INSERT INTO `urms_permission` VALUES (1,1,0,'系统资源管理',1,'','','fa fa-gear',1,'2020-05-02 05:55:02',1),(2,1,1,'系统管理',2,'system:upmsSystem:upmsSystem','/system/upmsSystem','',1,'2020-05-02 05:55:02',1),(3,1,1,'资源管理',2,'system:upmsPermission:upmsPermission','/system/upmsPermission','',1,'2020-05-02 05:55:02',2),(4,1,0,'组织角色管理',1,NULL,'','fa fa-users',1,'2020-05-02 05:55:02',4),(5,1,4,'角色管理',2,'system:upmsRole:upmsRole','/system/upmsRole','',1,'2020-05-02 05:55:02',5),(6,1,4,'用户管理',2,'system:upmsUser:upmsUser','/system/upmsUser','',1,'2020-05-02 05:55:02',6),(7,1,4,'组织管理',2,'system:upmsOrganization:read','/system/upmsOrganization','',1,'2020-05-02 05:55:02',3),(8,1,3,'修改',3,'system:upmsPermission:edit','','',1,'2020-05-07 22:26:23',1),(9,1,3,'删除',3,'system:upmsPermission:remove','','',1,'2020-05-07 22:34:02',1),(10,1,2,'添加',3,'system:upmsSystem:add','','',1,'2020-05-07 22:44:41',3),(15,1,0,'日志监控管理',1,'','','fa fa-video-camera',1,'2020-05-14 21:44:33',10),(16,1,15,'用户操作日志',2,'system:upmsLog:upmsLog','/system/upmsLog','',1,'2020-05-14 21:46:41',2),(17,1,3,'添加',3,'system:upmsPermission:add','','',1,'2020-05-14 23:32:57',3),(18,1,2,'修改',3,'system:upmsSystem:edit','','',1,'2020-05-14 23:51:47',3),(19,1,5,'添加',3,'system:upmsRole:add','','',1,'2020-05-14 23:54:46',3),(20,1,5,'修改',3,'system:upmsRole:edit','','',1,'2020-05-14 23:55:16',3),(21,1,6,'添加',3,'system:upmsUser:add','','',1,'2020-05-15 21:24:40',3),(22,1,6,'修改',3,'system:upmsUser:edit','','',1,'2020-05-15 21:25:02',3),(23,1,7,'添加',3,'system:upmsOrganization:add','','',1,'2020-05-15 21:25:34',1),(24,1,7,'修改',3,'system:upmsOrganization:edit','','',1,'2020-05-15 21:27:10',3),(25,1,2,'删除',3,'system:upmsSystem:remove','','',1,'2020-06-03 21:16:13',3),(26,1,7,'删除',3,'system:upmsOrganization:remove','','',1,'2020-06-03 21:17:59',3),(27,1,5,'删除',3,'system:upmsRole:remove','','',1,'2020-06-03 21:18:51',3),(28,1,6,'删除',3,'system:upmsUser:remove','','',1,'2020-06-03 21:19:25',3),(29,1,16,'删除',3,'system:upmsLog:remove','','',1,'2020-06-03 21:20:25',1),(30,1,15,'数据库监控',2,'','/druid/index','',1,'2020-06-04 22:46:10',2),(31,1,15,'API接口文档',2,'','/swagger-ui.html','',1,'2020-06-05 22:15:51',3);

INSERT INTO `urms_role` VALUES (1,'super','超级管理员','拥有所有权限','2020-05-02 05:55:02',1),(2,'admin','管理员','拥有除权限管理系统外的所有权限','2020-05-02 05:55:02',2),(6,'test','测试用户','用于测试账号','2020-05-01 21:55:02',3);

INSERT INTO `urms_role_permission` VALUES (227,6,1),(228,6,2),(229,6,10),(230,6,18),(231,6,3),(232,6,8),(233,6,17),(234,2,4),(235,2,7),(236,2,23),(237,2,24),(238,2,26),(239,2,5),(240,2,19),(241,2,20),(242,2,27),(243,2,6),(244,2,21),(245,2,22),(246,2,28),(247,2,15),(248,2,16),(249,2,29),(276,1,1),(277,1,2),(278,1,10),(279,1,18),(280,1,25),(281,1,3),(282,1,8),(283,1,9),(284,1,17),(285,1,4),(286,1,7),(287,1,23),(288,1,24),(289,1,26),(290,1,5),(291,1,19),(292,1,20),(293,1,27),(294,1,6),(295,1,21),(296,1,22),(297,1,28),(298,1,15),(299,1,16),(300,1,29),(301,1,30),(302,1,31);

INSERT INTO `urms_user` VALUES (1,'admin','1d45e9bc108bcf0237885d37df5488c9','66f1b370c660445a8657bf8bf1794486','管理员','/img/profile_small01.jpg','12311112222','jie_ming514@163.com',1,0,'2020-05-02 05:55:02',8),(9,'test','b5f7a38359cbafdb41452e32b0a1cc5d','l5266XyOiKpXT2FDYMtoOezjgIslyS','测试用户','/img/profile_small03.jpg','13311111111','jie_ming514@163.com',0,0,'2020-05-13 20:30:40',10);

INSERT INTO `urms_user_role` VALUES (1,1,1),(2,1,2),(15,9,1),(17,9,6),(19,1,6);

INSERT INTO `urms_system` VALUES (1,'','','#29A176','http://localhost/upms/index',1,'upms','权限管理系统','用户权限管理系统(RBAC细粒度用户权限、统一后台、单点登录、会话管理)','2020-05-02 19:55:02',1);

2.集成MyBatis框架

创建完数据库及表后,集成MyBatis框架,并且使用druid连接池完成数据持久层的操作。

2.1 添加依赖

        <!-- 添加 MyBatis 依赖 -->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.2</version>
        </dependency>
        <!-- 添加 jdbc 依赖 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <!-- 添加 druid 依赖 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.14</version>
        </dependency>

然后在主配置文件中,添加MyBatis的基础配置。

mybatis:
  configuration:
    map-underscore-to-camel-case: true
  # 指定 mapper 文件的位置
  mapper-locations: mapper/*Mapper.xml
  # POJO 扫描包来让 MyBatis 自动扫描到自定义 POJO
  type-aliases-package: com.picacho.urms

MyBatis 默认是属性名和数据库字段名一一对应的,但是在 java 中一般使用驼峰命名,那么 map-underscore-to-camel-case 属性设置为 true 可以开启驼峰功能。例如:user_name(数据库属性规范)可以转化为 userName(驼峰命名规范)。

2.2 使用mybatis-generator 插件

MyBatis 框架中包含了 mybatis-generator 插件,这个插件只需要简单的配置就可以自动生成 model,dao 和 mapper。这样可以大大地提高开发的效率。

在 pom.xml 中添加 mybatis-generator 插件。后续就可以通过插件指令来生成我们需要的代码了。

<!-- mybatis-generator 自动生成代码插件 -->
            <plugin>
                <groupId>org.mybatis.generator</groupId>
                <artifactId>mybatis-generator-maven-plugin</artifactId>
                <version>1.3.2</version>
                <configuration>
                    <configurationFile>${basedir}/src/main/resources/generator/generatorConfig.xml</configurationFile>
                    <overwrite>true</overwrite>
                    <verbose>true</verbose>
                </configuration>
            </plugin>

添加 generatorConfig.xml 配置文件
在 urms/src/main/resources/generator 目录下创建 generatorConfig.xml 配置文件。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
        PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
        "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">

<generatorConfiguration>
    <!-- 数据库驱动:选择你的本地数据库驱动包 -->
    <classPathEntry  location="/Users/xuzhi/devTools/local-maven/mysql/mysql-connector-java/8.0.22/mysql-connector-java-8.0.22.jar"/>
    <context id="DB2Tables"  targetRuntime="MyBatis3">
        <commentGenerator>
            <property name="suppressDate" value="true"/>
            <!-- 是否去除自动生成的注释 true:是; false:否 -->
            <property name="suppressAllComments" value="true"/>
        </commentGenerator>
        <!-- 数据库链接URL,用户名、密码 -->
        <jdbcConnection driverClass="com.mysql.cj.jdbc.Driver" connectionURL="jdbc:mysql://127.0.0.1:3306/urms" userId="root" password="12345678">
        </jdbcConnection>
        <javaTypeResolver>
            <property name="forceBigDecimals" value="false"/>
        </javaTypeResolver>
        <!-- 生成模型的包名和位置 -->
        <javaModelGenerator targetPackage="com.picacho.urms.domain" targetProject="src/main/java">
            <property name="enableSubPackages" value="true"/>
            <property name="trimStrings" value="true"/>
        </javaModelGenerator>
        <!-- 生成映射文件的包名和位置 -->
        <sqlMapGenerator targetPackage="mapper" targetProject="src/main/resources">
            <property name="enableSubPackages" value="true"/>
        </sqlMapGenerator>
        <!-- 生成DAO的包名和位置 -->
        <javaClientGenerator type="XMLMAPPER" targetPackage="com.picacho.urms.dao" targetProject="src/main/java">
            <property name="enableSubPackages" value="true"/>
        </javaClientGenerator>


        <!-- 要生成的表 tableName 是数据库中的表名或视图名 domainObjectName 是实体类名 -->
        <table tableName="urms_user" domainObjectName="UrmsUser" enableCountByExample="false"
               enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false"
               selectByExampleQueryId="false"></table>
        <table tableName="urms_organization" domainObjectName="UrmsOrganization" enableCountByExample="false"
               enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false"
               selectByExampleQueryId="false"/>
        <table tableName="urms_permission" domainObjectName="UrmsPermission" enableCountByExample="false"
               enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false"
               selectByExampleQueryId="false"/>
        <table tableName="urms_role" domainObjectName="UrmsRole" enableCountByExample="false"
               enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false"
               selectByExampleQueryId="false"/>
        <table tableName="urms_role_permission" domainObjectName="UrmsRolePermission" enableCountByExample="false"
               enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false"
               selectByExampleQueryId="false"/>
        <table tableName="urms_user_organization" domainObjectName="UrmsUserOrganization" enableCountByExample="false"
               enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false"
               selectByExampleQueryId="false"/>
        <table tableName="urms_user_role" domainObjectName="UrmsUserRole" enableCountByExample="false"
               enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false"
               selectByExampleQueryId="false"/>
        <table tableName="urms_system" domainObjectName="UrmsSystem" enableCountByExample="false"
               enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false"
               selectByExampleQueryId="false"/>
        <table tableName="urms_log" domainObjectName="UrmsLog" enableCountByExample="false"
               enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false"
               selectByExampleQueryId="false">
            <columnOverride column="start_time" javaType="java.util.Date" jdbcType="TIMESTAMP" />
            <columnOverride column="parameter" javaType="java.lang.String" jdbcType="LONGVARCHAR" />
            <columnOverride column="result" javaType="java.lang.Integer" jdbcType="TINYINT" />
        </table>
    </context>
</generatorConfiguration>

按照如下方式启动插件,生成我们需要的代码。
在这里插入图片描述
如下图为生成后的文件:
在这里插入图片描述
注意生成的Dao接口还没有被注入到Spring容器中,所以需要我们手动添加注解,提示Sprin扫描这些Dao注入到容器中。

  • 第一种是在每个Dao接口上添加@Mapper注解。
  • 第二种在启动类上添加@MapperScan注解,并且指定扫描路径,这里是"com.picacho.urms.dao"。

这里我们选择第二种较为方便些。

3.配置druid数据库连接池

这里选用druid连接池可以更好的监控SQL的执行情况,这个连接池提供了默认的操作界面,可以非常方便地观察我们的数据库连接与执行情况。

3.1 druid的配置

这里是druid数据库连接池比较基本的配置信息。

spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driverClassName: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/urms?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC
    username: root
    password: 12345678
    initialSize: 1
    minIdle: 3
    maxActive: 20
    # 配置获取连接等待超时的时间
    maxWait: 60000
    # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
    timeBetweenEvictionRunsMillis: 60000
    # 配置一个连接在池中最小生存的时间,单位是毫秒
    minEvictableIdleTimeMillis: 30000
    validationQuery: select 'x'
    testWhileIdle: true
    testOnBorrow: false
    testOnReturn: false
    # 打开 PSCache,并且指定每个连接上 PSCache 的大小
    poolPreparedStatements: true
    maxPoolPreparedStatementPerConnectionSize: 20
    # 配置监控统计拦截的 filters,去掉后监控界面 SQL 无法统计,'wall' 用于防火墙
    filters: stat,wall,slf4j

3.2 添加配置类

创建config 包,并在包下添加 DruidDBConfig.java 类。

@SuppressWarnings("AlibabaRemoveCommentedCode")
@Configuration
public class DruidDBConfig {

    private static final Logger logger = LoggerFactory.getLogger(DruidDBConfig.class);

    @Value("${spring.datasource.url}")
    private String dbUrl;

    @Value("${spring.datasource.username}")
    private String username;

    @Value("${spring.datasource.password}")
    private String password;

    @Value("${spring.datasource.driverClassName}")
    private String driverClassName;

    @Value("${spring.datasource.initialSize}")
    private int initialSize;

    @Value("${spring.datasource.minIdle}")
    private int minIdle;

    @Value("${spring.datasource.maxActive}")
    private int maxActive;

    @Value("${spring.datasource.maxWait}")
    private int maxWait;

    @Value("${spring.datasource.timeBetweenEvictionRunsMillis}")
    private int timeBetweenEvictionRunsMillis;

    @Value("${spring.datasource.minEvictableIdleTimeMillis}")
    private int minEvictableIdleTimeMillis;

    @Value("${spring.datasource.validationQuery}")
    private String validationQuery;

    @Value("${spring.datasource.testWhileIdle}")
    private boolean testWhileIdle;

    @Value("${spring.datasource.testOnBorrow}")
    private boolean testOnBorrow;

    @Value("${spring.datasource.testOnReturn}")
    private boolean testOnReturn;

    @Value("${spring.datasource.poolPreparedStatements}")
    private boolean poolPreparedStatements;

    @Value("${spring.datasource.maxPoolPreparedStatementPerConnectionSize}")
    private int maxPoolPreparedStatementPerConnectionSize;

    @Value("${spring.datasource.filters}")
    private String filters;

    @Value("{spring.datasource.connectionProperties}")
    private String connectionProperties;



    @Bean(initMethod = "init", destroyMethod = "close")   // 声明其为 Bean 实例
    @Primary  // 在同样的 DataSource 中,首先使用被标注的 DataSource
    public DataSource dataSource() {

        DruidDataSource datasource = new DruidDataSource();

        datasource.setUrl(this.dbUrl);
        datasource.setUsername(username);
        datasource.setPassword(password);
        datasource.setDriverClassName(driverClassName);

        //configuration
        datasource.setInitialSize(initialSize);
        datasource.setMinIdle(minIdle);
        datasource.setMaxActive(maxActive);
        datasource.setMaxWait(maxWait);
        datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
        datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
        datasource.setValidationQuery(validationQuery);
        datasource.setTestWhileIdle(testWhileIdle);
        datasource.setTestOnBorrow(testOnBorrow);
        datasource.setTestOnReturn(testOnReturn);
        datasource.setPoolPreparedStatements(poolPreparedStatements);
        datasource.setMaxPoolPreparedStatementPerConnectionSize(maxPoolPreparedStatementPerConnectionSize);
        try {
            datasource.setFilters(filters);
        } catch (SQLException e) {
            logger.error("druid configuration initialization filter", e);
        }
        datasource.setConnectionProperties(connectionProperties);

        return datasource;
    }

    @Bean
    public ServletRegistrationBean<StatViewServlet> druidServlet() {
        ServletRegistrationBean<StatViewServlet> reg = new ServletRegistrationBean<StatViewServlet>();
        reg.setServlet(new StatViewServlet());
        reg.addUrlMappings("/druid/*");
        reg.addInitParameter("allow", ""); // 白名单
        return reg;
    }

    @Bean
    public FilterRegistrationBean<WebStatFilter> filterRegistrationBean() {
        FilterRegistrationBean<WebStatFilter> filterRegistrationBean = new FilterRegistrationBean<WebStatFilter>();
        filterRegistrationBean.setFilter(new WebStatFilter());
        filterRegistrationBean.addUrlPatterns("/*");
        filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
        filterRegistrationBean.addInitParameter("profileEnable", "true");
        filterRegistrationBean.addInitParameter("principalCookieName","USER_COOKIE");
        filterRegistrationBean.addInitParameter("principalSessionName","USER_SESSION");
        filterRegistrationBean.addInitParameter("DruidWebStatFilter","/*");
        return filterRegistrationBean;
    }
}

启动项目后,我们可以通过http://localhost:8080/druid/index.html,就可以看到我们druid数据库连接池的相关信息。
在这里插入图片描述

4.全局统一处理

一个项目一般需要统一处理一些数据,比如返回结果,异常,日期时间转换等。

4.1 统一返回json结果

在返回给前端的 json 报文中,一般需要包含 code(响应码)、msg(响应信息)、data(数据)等字段。所以后端一般需要封装一个返回类。

创建common包,在该包下创建R.java类。

public class R extends HashMap<String, Object> {
    
    private static final long serialVersionUID = 1L;

    public R() {
        put("code", 0);
        put("msg", "操作成功");
    }

    public static R error() {
        return error(1, "操作失败");
    }

    public static R error(String msg) {
        return error(500, msg);
    }

    public static R error(int code, String msg) {
        R r = new R();
        r.put("code", code);
        r.put("msg", msg);
        return r;
    }

    public static R ok(String msg) {
        R r = new R();
        r.put("msg", msg);
        return r;
    }

    public static R ok(Map<String, Object> map) {
        R r = new R();
        r.putAll(map);
        return r;
    }

    public static R ok() {
        return new R();
    }

    @Override
    public R put(String key, Object value) {
        super.put(key, value);
        return this;
    }
}

创建controller包,在该包下创建HelloController.java类。

@RestController
public class HelloController {
    
    @Value("${picacho.name}")
    private String appName;

    @Value("${picacho.version}")
    private String appVersion;

    @Value("${picacho.description}")
    private String appDescription;

    @GetMapping("/hello")
    public R hello(){
        Map<String, Object> resMap = new HashMap<>();
        resMap.put("appName", appName);
        resMap.put("appVersion", appVersion);
        resMap.put("appDescription", appDescription);
        resMap.put("time", new Date());;

        return R.ok(resMap);
    }
}

启动项目,测试效果。
在这里插入图片描述

4.2 统一异常处理

不做任何处理时,当后端发生异常或者请求路径出错,Spring Boot项目会返回一个错误页面,但是这个页面过于笼统,不利于我们找到错误到底在哪里发生的,十分不友好;所以需要统一处理异常。
在这里插入图片描述
Spring Boot 提供了一个统一处理异常的接口类 ErrorController,并通过重写 getErrorPath() 方法实现异常页面跳转。

在controller包下创建MyErrorController.java类。

@RestController
public class MyErrorController implements ErrorController {

    private final String ERROR_PATH ="/error";

    /**
     * 出现错误,跳转到如下映射中
     * @return
     */
    @Override
    public String getErrorPath() {
        return ERROR_PATH;
    }

    /**
     * Web 页面错误处理
     */
    @RequestMapping(value = ERROR_PATH, produces = {"text/html"})
    public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
        int code = response.getStatus();
        if (404 == code) {
            return new ModelAndView("error/404");
        } else if (403 == code) {
            return new ModelAndView("error/403");
        } else if (401 == code) {
            return new ModelAndView("login");
        } else {
            return new ModelAndView("error/500");
        }
    }

    @RequestMapping(value =ERROR_PATH)
    public R handleError(HttpServletRequest request, HttpServletResponse response) {
        int code = response.getStatus();
        if (404 == code) {
            return R.error(404, "未找到资源");
        } else if (403 == code) {
            return R.error(403, "没有访问权限");
        } else if (401 == code) {
            return R.error(401, "登录过期");
        } else {
            return R.error(500, "服务器错误");
        }
    }
}

接着添加常见的错误页面,例如404.html,403.html等。前端页面这里我们就选用bootstrap来构建。其下载地址:bootstrap下载地址。将其资源文件放在自己的项目中,在需要的地方引入即可。
404.html

<!DOCTYPE html>
<html  xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>ming - 404 页面</title>
    <meta name="keywords" content="">
    <meta name="description" content="">

    <!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
    <link href="static/css/bootstrap.min.css" th:href="@{/css/bootstrap.min.css}" rel="stylesheet">
    <link href="static/css/bootstrap.min.css" th:href="@{/css/style.css}" rel="stylesheet">
</head>

<body class="gray-bg">
<div class="middle-box text-center animated fadeInDown">
    <h1>404</h1>
    <h3 class="font-bold">页面未找到!</h3>

    <div class="error-desc">
        抱歉,页面好像去火星了~
        <form class="form-inline m-t" role="form">
            <div class="form-group">
                <input type="email" class="form-control" placeholder="请输入您需要查找的内容 …">
            </div>
            <button type="submit" class="btn btn-primary">搜索</button>
        </form>
    </div>
</div>
<!-- 全局js -->
<!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
<!-- jQuery -->
<script h:src="@{https://lf9-cdn-tos.bytecdntp.com/cdn/expire-1-y/jquery/3.5.1/jquery.min.js}"></script>
<script src="static/js/bootstrap.min.js?v=3.3.7" th:src="@{/js/bootstrap.min.js}"></script>
</body>

</html>

启动项目,验证一下效果。
在这里插入图片描述
在这里插入图片描述

4.3 统一日期处理

在处理日期时,一般我们都需要对日期时间进行转换;另一个是考虑时区问题。这些每次处理起来也是很麻烦的。所以一般采用如下方式解决上面的两个问题。

  • 当返回值时,Date 转换为 String。
  • 当传入参数时,String 转换为 Date。

传入参数时 String 转换为 Date

@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss", timezone="GMT+8")
private Date date;

这样做的话,每次都需要在 Date 类型中添加这样一行注解,显得有点麻烦。所以可以创建一个日期转化的配置类。
在config包下,创建DateConverterConfig.Java类。

@Component
public class DateConverterConfig implements Converter<String, Date> {

    private static final List<String> formarts = new ArrayList<>(4);
    
    static {
        formarts.add("yyyy-MM");
        formarts.add("yyyy-MM-dd");
        formarts.add("yyyy-MM-dd hh:mm");
        formarts.add("yyyy-MM-dd hh:mm:ss");
    }

    @Override
    public Date convert(String source) {
        String value = source.trim();
        if ("".equals(value)) {
            return null;
        }
        if (source.matches("^\\d{4}-\\d{1,2}$")) {
            return parseDate(source, formarts.get(0));
        } else if (source.matches("^\\d{4}-\\d{1,2}-\\d{1,2}$")) {
            return parseDate(source, formarts.get(1));
        } else if (source.matches("^\\d{4}-\\d{1,2}-\\d{1,2} {1}\\d{1,2}:\\d{1,2}$")) {
            return parseDate(source, formarts.get(2));
        } else if (source.matches("^\\d{4}-\\d{1,2}-\\d{1,2} {1}\\d{1,2}:\\d{1,2}:\\d{1,2}$")) {
            return parseDate(source, formarts.get(3));
        } else {
            throw new IllegalArgumentException("Invalid boolean value '" + source + "'");
        }
    }

    /**
     * 格式化日期
     *
     * @param dateStr String 字符型日期
     * @param format  String 格式
     * @return Date 日期
     */
    public Date parseDate(String dateStr, String format) {
        Date date = null;
        try {
            DateFormat dateFormat = new SimpleDateFormat(format);
            date = dateFormat.parse(dateStr);
        } catch (Exception e) {
            throw new IllegalArgumentException("Invalid boolean value ");
        }
        return date;
    }
}

以后前端提交的表单信息中包含日期格式时,系统会自动将字符串类型转化为 java 日期类型。

返回值时 Date 转换为 String
这里我们只需要在 application.yml 配置文件中添加。

spring:
  jackson:
    date-format: yyyy-MM-dd HH:mm:ss
    time-zone: GMT+8

启动项目,验证一下效果。
在这里插入图片描述
到这里项目学习的基本环境搭建就基本完成了,接下来就是正式的功能实现了。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
课程简介:历经半个多月的时间,Debug亲自撸的 “企业员工角色权限管理平台” 终于完成了。正如字面意思,本课程讲解的是一个真正意义上的、企业级的项目实战,主要介绍了企业级应用系统中后端应用权限的管理,其中主要涵盖了六大核心业务模块、十几张数据库表。 其中的核心业务模块主要包括用户模块、部门模块、岗位模块、角色模块、菜单模块和系统日志模块;与此同时,Debug还亲自撸了额外的附属模块,包括字典管理模块、商品分类模块以及考勤管理模块等等,主要是为了更好地巩固相应的技术栈以及企业应用系统业务模块的开发流程! 核心技术栈列表: 值得介绍的是,本课程在技术栈层面涵盖了前端和后端的大部分常用技术,包括Spring BootSpring MVC、Mybatis、Mybatis-Plus、Shiro(身份认证与资源授权跟会话等等)、Spring AOP、防止XSS攻击、防止SQL注入攻击、过滤器Filter、验证码Kaptcha、热部署插件Devtools、POI、Vue、LayUI、ElementUI、JQuery、HTML、Bootstrap、Freemarker、一键打包部署运行工具Wagon等等,如下图所示: 课程内容与收益: 总的来说,本课程是一门具有很强实践性质的“项目实战”课程,即“企业应用员工角色权限管理平台”,主要介绍了当前企业级应用系统中员工、部门、岗位、角色、权限、菜单以及其他实体模块的管理;其中,还重点讲解了如何基于Shiro的资源授权实现员工-角色-操作权限、员工-角色-数据权限的管理;在课程的最后,还介绍了如何实现一键打包上传部署运行项目等等。如下图所示为本权限管理平台的数据库设计图: 以下为项目整体的运行效果截图: 值得一提的是,在本课程中,Debug也向各位小伙伴介绍了如何在企业级应用系统业务模块的开发中,前端到后端再到数据库,最后再到服务器的上线部署运行等流程,如下图所示:

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

picacho_pkq

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值