项目说明:
1.前后端分离的web项目-后台管理系统
2.外置tomcat,保留web.xml
3.打包方式:war
4.yml配置
应用场景:
1.支持多数据源
2.文件上传
3.定时
4.异步
5.shiro 权限整合
6.log4j
二.案例
项目结构:
1.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.0.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>demo</groupId>
<artifactId>demo</artifactId>
<version>1.0.0</version>
<packaging>war</packaging>
<name>demo</name>
<description>demo</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<jackson.version>2.9.5</jackson.version>
<log4j2.version>2.6.2</log4j2.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
<finalName>demo##${project.version}</finalName>
</build>
<dependencies>
<!-- JDBC -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!-- web项目 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<!-- 剔除掉springBoot内嵌的tomcat-->
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- tomcat -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<!-- 日志 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
<!-- mybatis -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<!-- 数据库连接 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>
<version>2.1.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.18</version>
</dependency>
<!-- 测试 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- 针对tomcat的版本问题而导入包 -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.4.3.Final</version>
</dependency>
<!-- 文件上传 -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.2</version>
</dependency>
<!-- redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- spring session + redis -->
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
<!-- jedis -->
<!-- <dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>-->
<!-- alibaba的druid数据库连接池 -->
<!-- <dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.9</version>
</dependency>-->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.16</version>
</dependency>
<!-- shiro -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.1</version>
</dependency>
<!-- <dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>-->
</dependencies>
</project>
2.application.yml
#切换不同运行环境的配置
spring:
profiles:
active: local
---
#开发配置
spring:
profiles: local
# double mysql datasource
datasource:
# 数据源1
db1:
jdbc-url:
username:
password:
driver-class-name: com.mysql.cj.jdbc.Driver
# type: com.alibaba.druid.pool.DruidDataSource
type: org.apache.commons.dbcp2.BasicDataSource
dbcp2:
initialSize: 2
maxTotal: 4
minIdle: 2
maxWaitMillis: 5000
validationQuery: select 1
maxConnLifetimeMillis: 43200000
timeBetweenEvictionRunsMillis: 14400000
numTestsPerEvictionRun: 500
# 数据源2
#db2:
# session
session:
store-type: none
#停用jmx监控,tomcat多个项目使用相同数据源名时会部署失败;
jmx:
enabled: false
# mybatis backpage
mybatis:
typeAliasesPackage: demo.model
#mapperLocations: classpath:mapper/*.xml
---
#test environment
spring:
profiles: test
datasource:
# db1
db1:
jdbc-url:
username:
password:
driver-class-name: com.mysql.cj.jdbc.Driver
# type: com.alibaba.druid.pool.DruidDataSource
type: org.apache.commons.dbcp2.BasicDataSource
dbcp2:
initialSize: 2
maxTotal: 4
minIdle: 2
maxWaitMillis: 5000
validationQuery: select 1
maxConnLifetimeMillis: 43200000
timeBetweenEvictionRunsMillis: 14400000
numTestsPerEvictionRun: 500
# db2
#db2:
# session
session:
store-type: none
jmx:
enabled: false
# mybatis backpage
mybatis:
typeAliasesPackage: demo.model
#config-location:
3.web.xml
依旧在webapp->WEB-INF下
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID"
version="3.0">
<display-name>demo</display-name>
<filter>
<filter-name>characterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/</url-pattern>
</filter-mapping>
<session-config>//session的有效时间
<session-timeout>300</session-timeout>
<cookie-config>
<name>BACKEND</name>
<path>/</path>
</cookie-config>
</session-config>
</web-app>
4.应用主程序: SpringBootApplication
**
* 启动类
*/
// controller,service等扫描路径 : 默认扫描和启动类同个包下文件
@EnableScheduling // 启用spring定时
@EnableTransactionManagement//数据库事务
@EnableAsync //异步
@SpringBootApplication
public class SpringBootBackApplication extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(SpringBootBackApplication.class, args);
}
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(SpringBootBackApplication.class);
}
/**
* 去除multipartResolver 冲突,上传文件配置
*
* @return
*/
@Bean(name = "multipartResolver")
public CommonsMultipartResolver multipartResolver() {
CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
multipartResolver.setDefaultEncoding("UTF-8");
// resolveLazily属性启用是为了推迟文件解析,以在在UploadAction中捕获文件大小异常
// multipartResolver.setResolveLazily(true);
multipartResolver.setMaxInMemorySize(5120);
// 上传文件大小 30M 30*1024*1024
multipartResolver.setMaxUploadSize(30 * 1024 * 1024);
return multipartResolver;
}
/**
* 文件上传配置
*
* @return
*/
// @Bean
// public MultipartConfigElement multipartConfigElement() {
// MultipartConfigFactory factory = new MultipartConfigFactory();
// // 单个文件最大
// factory.setMaxFileSize("30MB"); // KB,MB
// /// 设置总上传数据总大小
// factory.setMaxRequestSize("30MB");
// return factory.createMultipartConfig();
// }
/**
* redis
*
* @return
*/
// @Bean
// public static ConfigureRedisAction configureRedisAction() {
// return ConfigureRedisAction.NO_OP;
// }
}
5.config 配置类
5.1数据源注册
/*
* db1的数据源,事务,工厂等配置
*/
@Configuration
@MapperScan(basePackages = "hotkidclub.mapper", sqlSessionTemplateRef = "db1SqlSessionTemplate")
public class MasterDataSourceConfig {
@Bean
@ConfigurationProperties(prefix = "spring.datasource.db1")
@Primary
public DataSource db1DataSource() {
return DataSourceBuilder.create().build();
}
@Bean
@Primary
public SqlSessionFactory db1SqlSessionFactory(@Qualifier("db1DataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:hotkidclub/mapper/*.xml"));
return bean.getObject();
}
@Bean
@Primary
public DataSourceTransactionManager db1TransactionManager(@Qualifier("db1DataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
@Bean
@Primary
public SqlSessionTemplate db1SqlSessionTemplate(@Qualifier("db1SqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
5.2 Mvc 可根据具体应用场景配置
/**
* Mvc相关配置
*
* Interceptors:拦截器
*
* ArgumentResolvers: 参数绑定解析器
*
* ViewControllers:页面跳转
*
* ResourceHandlers:静态资源
*
* configureDefaultServletHandling:默认静态资源处理器
*
* configureViewResolvers:视图解析器
*
* configureContentNegotiation:配置内容裁决的一些参数
*
* addCorsMappings:跨域
*
* configureMessageConverters:信息转换器
*/
@Configuration
public class WebConfigurer implements WebMvcConfigurer {
@Autowired
private LoggedInInterceptor loggedInInterceptor;
@Autowired
private LoginUserInfoResolver loginUserInfoResolver;
/**
* 配置静态资源:html,js,css,等等
*
* @param registry
*/
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
}
/**
* 注册拦截器:自定义的拦截器需要通过这里添加注册才能生效
*
*
* @param registry
*/
// @Override
// public void addInterceptors(InterceptorRegistry registry) {
// registry.addInterceptor(loggedInInterceptor).addPathPatterns("/**").excludePathPatterns("/login.ctrl");// 添加请求路径拦截,以及不包含的路径(不拦截)
// registry.addInterceptor(loggedInInterceptor);// 在注册时不指定拦截路径,通过拦截器中判断是否函数带有isLogin(自定义)的注解来拦截
// }
/**
* 注册:参数绑定解析器
*
* @param argumentResolvers
*/
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
//argumentResolvers.add(loginUserInfoResolver);
}
}
6.shiro 相关
6.1 自定义realm
/**
* shiro 核心组件-realm : 处理用户资源权限,实现认证和授权
*/
public class ShiroRealm extends AuthorizingRealm {
@Autowired
RoleService roleService;
/**
* 认证器 :
* <p>
* 本平台用户登入接口调用-subject.login() : 回调该函数
* </p>
*
* @param authcToken 凭证信息:可存放用户账号密码
* @return 认证信息:包含当前请求登入的用户信息
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException {
Subject subject = SecurityUtils.getSubject();
// 1.获取认证的主体信息:账户密码
UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
// 2.可校验数据库存储的账号密码(本系统在登入接口的service层已处理)
// 3.创建简单认证信息对象可存放相关信息 : (object, object, 字符串);可传递自定义对象,字符内容;
// Session session = subject.getSession();
// session.setAttribute("user", token.getPrincipal());
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(token.getPrincipal(), token.getCredentials(), getName());
return info;
}
/**
* 授权器 : 校验用户的角色和权限
* <p>
* 本平台接口配置角色/权限的授权注解时 : 回调该函数
* </p>
*
* @param principalCollection
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
String principal = (String) principalCollection.getPrimaryPrincipal();
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
// 1.通过用户名去数据库查询该用户的角色和权限
Map<String, Object> verifyInfo = roleService.getAuthorizationInfo(principal);
Set<String> rs = (Set) verifyInfo.get("roleNames");
Set<String> ps = (Set) verifyInfo.get("permissionCodes");
// 2.设置角色信息
if (rs != null && rs.size() > 0) {
info.addRoles(rs);
}
// 3.设置权限信息
if (ps != null && ps.size() > 0) {
info.addStringPermissions(ps);
}
return info;
}
}
6.2自定义异常
public class AuthenticationFilter extends FormAuthenticationFilter {
//未登入
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
response.setContentType("application/json; charset=utf-8");
response.setCharacterEncoding("UTF-8");
response.getWriter().print("{\"errorCode\":444,\"errorMsg\":\"未登入\"}");
response.flushBuffer();
return false;
}
}
/**
* 全局未授权异常-捕获&处理
*
* 本项目使用场景: shiro鉴权时抛出的未授权异常未处理直接响应客户端,使用全局异常注解@ControllerAdvice 捕获处理
*
*/
@RestControllerAdvice
public class UnauthorizedExceptionHandler {
/**
* 处理UnauthorizedException
*
* @param req
* @param e
* @return
*/
@ExceptionHandler(value = UnauthorizedException.class)
public Map<?, ?> defaultExceptionHandler(HttpServletRequest req, Exception e) {
Map<String, Object> exception_response = new HashMap<>();
exception_response.put("errorCode", 445);
exception_response.put("errorMsg", "拒绝访问-权限不足");
return exception_response;
}
}
后期持续更新springBoot的各种应用场景...