SpringBoot+layui实现后台管理平台
一、前提
Layui 作为一款优秀的开源前端库现在被敏捷开发团队用的越来越多。丰富的插件让开发不再从头造轮子。https://www.layui.com/(LayUI官网)。
二、SpringBoot项目
本项目使用SpringToolSuite,maven开发。
三、主要技术
1. 后台使用SpringBoot 2.1.7 集成了 shiro做权限管理,使用JsonWebToken 做接口鉴权。
2. 数据库是MYSQL.数据层使用了MyBatis。
3.代码简略。
pom.xml
<?xml version="1.0" encoding="UTF-8"?> <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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.7.RELEASE</version> </parent> <groupId>com.goodness</groupId> <artifactId>cboot</artifactId> <version>0.0.1-SNAPSHOT</version> <name>cboot</name> <description>cboot Spring Boot</description> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> <shiro.spring.version>1.4.0</shiro.spring.version> <jwt.auth0.version>3.2.0</jwt.auth0.version> <!-- 第一行报错 --> <maven-jar-plugin.version>3.1.1</maven-jar-plugin.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-quartz</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <!--thymeleaf中使用shiro--> <dependency> <groupId>com.github.theborakompanioni</groupId> <artifactId>thymeleaf-extras-shiro</artifactId> <version>2.0.0</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.0</version> </dependency> <dependency> <groupId>com.github.abel533</groupId> <artifactId>mapper</artifactId> <version>2.3.0</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>${jwt.auth0.version}</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring-boot-web-starter</artifactId> <version>${shiro.spring.version}</version> </dependency> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.5</version> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.7</version> </dependency> <!-- https://mvnrepository.com/artifact/org.apache.commons/commons-io --> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-io</artifactId> <version>1.3.2</version> </dependency> <!-- 与数据库操作相关的依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <!-- https://mvnrepository.com/artifact/org.mybatis.spring.boot/mybatis-spring-boot-starter --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.10</version> </dependency> <!-- <dependency> <groupId>com.lz.ht</groupId> <artifactId>htcodeplayer</artifactId> <version>1.0-SNAPSHOT</version> <scope>compile</scope> </dependency> --> <!-- https://mvnrepository.com/artifact/com.google.code.gson/gson --> <dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> <version>2.8.5</version> </dependency> <!-- https://mvnrepository.com/artifact/org.codehaus.jackson/jackson-core-asl --> <dependency> <groupId>org.codehaus.jackson</groupId> <artifactId>jackson-core-asl</artifactId> <version>1.9.13</version> </dependency> <!-- https://mvnrepository.com/artifact/org.freemarker/freemarker --> <dependency> <groupId>org.freemarker</groupId> <artifactId>freemarker</artifactId> <version>2.3.22</version> </dependency> </dependencies> <build> <resources> <resource> <directory>src/main/resources</directory> <includes> <include>static/**</include> <include>*/*/*.xml</include> <include>application.yml</include> <include>application-*.yml</include> <include>logback-spring.xml</include> <include>templates/**</include> </includes> </resource> </resources> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
com.lz.ht.configuration
package com.lz.ht.configuration; import com.alibaba.druid.pool.DruidDataSource; import com.alibaba.druid.support.http.StatViewServlet; import com.alibaba.druid.support.http.WebStatFilter; import com.github.abel533.sql.SqlMapper; import org.apache.ibatis.session.SqlSession; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.boot.web.servlet.ServletRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import javax.sql.DataSource; import java.sql.SQLException; @SuppressWarnings("all") @Configuration public class DruidConfig { private static final Logger log = LoggerFactory.getLogger(DruidConfig.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.driver-class-name}") 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") @Primary 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) { log.error("druid configuration initialization filter", e); } datasource.setConnectionProperties(connectionProperties); return datasource; } /** * http://127.0.0.1:8090/monitor/druid/login.html * * @throws * @Title: druidServlet * @Description: 注册一个StatViewServlet 相当于在web.xml中声明了一个servlet * @param: void * @return: ServletRegistrationBean */ @Bean public ServletRegistrationBean druidServlet() { ServletRegistrationBean reg = new ServletRegistrationBean(); reg.setServlet(new StatViewServlet()); reg.addUrlMappings("/druid/*"); /** 白名单 */ // IP白名单 reg.addInitParameter("allow", "*"); // IP黑名单(共同存在时,deny优先于allow) reg.addInitParameter("deny", "192.168.1.100"); /** /druid/login.html登录时账号密码 */ reg.addInitParameter("loginUsername", "zhaozhou"); reg.addInitParameter("loginPassword", "zhaozhou"); /** 是否能够重置数据 禁用HTML页面上的“Reset All”功能 */ reg.addInitParameter("resetEnable", "false"); return reg; } /** * 注册一个:filterRegistrationBean 相当于在web.xml中声明了一个Filter */ @Bean public FilterRegistrationBean druidStatFilter() { FilterRegistrationBean druidStatFilter = new FilterRegistrationBean(); druidStatFilter.setFilter(new WebStatFilter()); /** 添加过滤规则. */ druidStatFilter.addUrlPatterns("/*"); /** 监控选项滤器 */ druidStatFilter.addInitParameter("DruidWebStatFilter", "/*"); /** 添加不需要忽略的格式信息. */ druidStatFilter.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*"); /** 配置profileEnable能够监控单个url调用的sql列表 */ druidStatFilter.addInitParameter("profileEnable", "true"); /** 当前的cookie的用户 */ druidStatFilter.addInitParameter("principalCookieName", "USER_COOKIE"); /** 当前的session的用户 */ druidStatFilter .addInitParameter("principalSessionName", "USER_SESSION"); return druidStatFilter; } @Bean(name = "transactionManager") @Primary public DataSourceTransactionManager transactionManager() { return new DataSourceTransactionManager(dataSource()); } @Bean(name = "sqlMapper") public SqlMapper sqlMapper(SqlSession sqlSession){ SqlMapper sqlMapper = new SqlMapper(sqlSession); log.info("-------------SqlMapper ------------"); return sqlMapper; } }
package com.lz.ht.configuration; import com.lz.ht.filter.MyFilter; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.ArrayList; import java.util.List; /** * @author Administrator */ @Configuration public class JwtConfig { @Bean public FilterRegistrationBean basicFilterRegistrationBean() { FilterRegistrationBean registrationBean = new FilterRegistrationBean(); MyFilter myFilter = new MyFilter(); registrationBean.setFilter(myFilter); List<String> urlPatterns = new ArrayList<String>(); urlPatterns.add("/api/*"); registrationBean.setUrlPatterns(urlPatterns); return registrationBean; } }
package com.lz.ht.configuration; import com.lz.ht.realm.CustomRealm; import at.pollux.thymeleaf.shiro.dialect.ShiroDialect; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.spring.web.ShiroFilterFactoryBean; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.LinkedHashMap; import java.util.Map; @Configuration public class ShiroConfig { @Bean public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); // 必须设置 SecurityManager shiroFilterFactoryBean.setSecurityManager(securityManager); // setLoginUrl 如果不设置值,默认会自动寻找Web工程根目录下的"/login.jsp"页面 或 "/login" 映射 shiroFilterFactoryBean.setLoginUrl("/login"); // 设置无权限时跳转的 url; shiroFilterFactoryBean.setUnauthorizedUrl("/error"); // 设置拦截器 Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>(); //游客,开发权限 filterChainDefinitionMap.put("/alllayui/**","anon"); filterChainDefinitionMap.put("/system/**","anon"); //用户,需要角色权限 “user” filterChainDefinitionMap.put("/user/**", "anon"); //管理员,需要角色权限 “admin” filterChainDefinitionMap.put("/admin/**", "anon"); //开放登陆接口 filterChainDefinitionMap.put("/login", "anon"); //其余接口一律拦截 //主要这行代码必须放在所有权限设置的最后,不然会导致所有 url 都被拦截 filterChainDefinitionMap.put("/**", "authc"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); System.out.println("Shiro拦截器工厂类注入成功"); return shiroFilterFactoryBean; } /** * 注入 securityManager */ @Bean public org.apache.shiro.web.mgt.DefaultWebSecurityManager securityManager() { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); // 设置realm. securityManager.setRealm(customRealm()); return securityManager; } /** * 自定义身份认证 realm; * <p> * 必须写这个类,并加上 @Bean 注解,目的是注入 CustomRealm, * 否则会影响 CustomRealm类 中其他类的依赖注入 */ @Bean public CustomRealm customRealm() { return new CustomRealm(); } /*** * 添加thymeleaf shiro标签支持 * 别忘记在html 标签中添加xmlns xmlns:shiro="http://www.pollix.at/thymeleaf/shiro" * @return */ @Bean(name = "shiroDialect") public ShiroDialect shiroDialect(){ return new ShiroDialect(); } }
package com.lz.ht.configuration; import com.lz.ht.base.dateconverter.DateConverter; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Configuration; import org.springframework.format.FormatterRegistry; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; /** Web 配置 */ @Configuration public class WebMvcConfig extends WebMvcConfigurerAdapter { @Value("${systemConstant.imageRootPath}") private String localImageServerDir; @Value("${systemConstant.staticHtmlPath}") private String localStaticHtmlServerDir; @Value("${systemConstant.simditorImagePath}") private String simditorServerDir; @Override public void addFormatters(FormatterRegistry registry) { registry.addConverter(new DateConverter()); } /*** * 配置静态资源路径 * @param registry */ @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { if(!registry.hasMappingForPattern("/static/**")){ registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/"); } registry.addResourceHandler("/localImage/image/**").addResourceLocations("file:///" + localImageServerDir + "/"); registry.addResourceHandler("/simditor/image/**").addResourceLocations("file:///" + simditorServerDir + "/"); registry.addResourceHandler("/localPage/staticHtml/**").addResourceLocations("file:///" + localStaticHtmlServerDir + "/"); super.addResourceHandlers(registry); } }
#redis #spring.redis.host=localhost #spring.redis.port=6379 #spring.redis.lettuce.pool.max-active=5 #spring.redis.lettuce.pool.max-idle=1 #spring.redis.lettuce.pool.min-idle=1 #spring.redis.lettuce.pool.max-wait=5000 #spring.redis.timeout=30000 server: port: 10086 tomcat: uri-encoding: utf-8 servlet: session: timeout: 30m spring: #jdbc配置 datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/cboot?useUnicode=true&characterEncoding=UTF-8&useSSL=true&useAffectedRows=true&tinyInt1isBit=false&serverTimezone=GMT%2B8 username: root password: root type: com.alibaba.druid.pool.DruidDataSource # 数据源其他配置 initialSize: 5 minIdle: 5 maxActive: 20 maxWait: 60000 timeBetweenEvictionRunsMillis: 60000 minEvictableIdleTimeMillis: 300000 validationQuery: SELECT 1 FROM DUAL testWhileIdle: true testOnBorrow: false testOnReturn: false poolPreparedStatements: true # 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙 filters: stat,wall maxPoolPreparedStatementPerConnectionSize: 20 useGlobalDataSourceStat: true connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500 servlet: multipart: max-file-size: 10MB max-request-size: 10MB thymeleaf: prefix: classpath:/templates/ suffix: .html mode: HTML encoding: UTF-8 cache: false servlet: content-type: text/html mybatis: typeAliasesPackage: com.lz.ht.dao mapperLocations: classpath:xml/mapper/*.xml configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl systemConstant: imageRootPath: E:/imageRootPath staticHtmlPath: E:/staticHtmlPath simditorImagePath: E:/simditorImagePath logging: level: root: debug
四、界面展示
五、完整代码
https://gitee.com/cailun-hx/cboot