项目说明:
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的各种应用场景...