基于SpringBoot与MyBatis框架在Java开发中越来越流行,最近公司刚好需要技术变革,笔者也是颇费了写心血做了框架的搭建和几次框架的一直工作,本框架除了SpringBoot和MyBatista另外也揉入了当下比较流行的权限安全认证框架Shiro,附带架构设计,希望能帮助到有需要的人。
技术的变革瞬息万变,此处有必要对各个第三方框架的版本做一下说明:SpringBoot1.5.8,Mybatis1.3.2,shiro1.3.2,其他工具jar中带有版本信息,不在赘述。
2.2.1 已构建一个maven环境
略,网上资料很多
2.2.2 规范项目结构
项目结构规范参考阿里开发手册
2.2.3 在pom.xml文件引入准备jar包
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>fanshion</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>fanshion</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<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>1.3.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- http调用依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- shiro 依赖 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.3.2</version>
</dependency>
<!-- shiro缓存管理依赖 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>1.3.2</version>
</dependency>
<!-- 日志管理依赖 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
</dependencies>
<!--编译相关配置-->
<build>
<finalName>ROOT</finalName>
<plugins>
<!-- add linux shell plugin -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<executable>true</executable>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.5.1</version><!--$NO-MVN-MAN-VER$-->
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<encoding>${project.build.sourceEncoding}</encoding>
<compilerArguments>
<verbose />
<bootclasspath>${java.home}/lib/rt.jar;${java.home}/lib/jce.jar</bootclasspath>
</compilerArguments>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.18.1</version><!--$NO-MVN-MAN-VER$-->
<configuration>
<skipTests>true</skipTests>
</configuration>
</plugin>
</plugins>
</build>
</project>
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>fanshion</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>fanshion</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<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>1.3.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- http调用依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- shiro 依赖 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.3.2</version>
</dependency>
<!-- shiro缓存管理依赖 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>1.3.2</version>
</dependency>
<!-- 日志管理依赖 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
</dependencies>
<!--编译相关配置-->
<build>
<finalName>ROOT</finalName>
<plugins>
<!-- add linux shell plugin -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<executable>true</executable>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.5.1</version><!--$NO-MVN-MAN-VER$-->
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<encoding>${project.build.sourceEncoding}</encoding>
<compilerArguments>
<verbose />
<bootclasspath>${java.home}/lib/rt.jar;${java.home}/lib/jce.jar</bootclasspath>
</compilerArguments>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.18.1</version><!--$NO-MVN-MAN-VER$-->
<configuration>
<skipTests>true</skipTests>
</configuration>
</plugin>
</plugins>
</build>
</project>
2.2.4 在application-dev.yml文件中添加项目相关配置
(1) 新建文件application.yml
spring:
profiles:
active: dev #对应loc表示本地,dev表示生产或者测试表的配置
(2)新建文件application-loc.yml
server:
port: 8080
context-path: /
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/ejchina_pro_test?useSSL=false
username: root
password: fyx
zeroDateTimeBehavior: convertToNull
session:
store-type: none
redis:
database: 0
host: 118.190.64.183
port: 6379
password: redis1906
pool:
max-active: 8
max-idle: 8
max-wait: -1
min-idle: 0
timeout: 0
freemarker:
enabled: true
cache: false
charset: CESU-8
content-type: text/html
template-loader-path: classpath:/templates/
suffix: .ftl
request-context-attribute: request
expose-session-attributes: true
expose-request-attributes: true
expose-spring-macro-helpers: true
allow-request-override: true
allow-session-override: true
settings:
date_format: yyyy-MM-dd
time_format: HH:mm:ss
datetime_format: yyyy-MM-dd HH:mm:ss
mvc:
static-path-pattern: /static/**
devtools:
restart:
enabled: false
additional-paths: src/main/java
additional-exclude: target/classes
(3)新建文件application-dev.yml
server: #配置服务
port: 8089 #配置服务端口
context-path: / #项目路径,Spring boot默认是/,这样直接通过http://ip:port/就可以访问到index页面
spring: #Spring相关配置
http: # 集成HttpClient,封装常用客户端工具类
multipart:
enabled: true #是否激活
max-file-size: -1 #限制文件大小
max-request-size: -1 #限制文件大小
datasource: #配置数据源
driver-class-name: com.mysql.jdbc.Driver #数据库驱动名称
url: jdbc:mysql://47.104.13.151:3306/ej_comment?useSSL=false #数据库地址
username: ej_comment #数据库名
password: fZoPCyNINC+g8yH0A8Y68ApYTJgmtjM9PnkZlc3I1DxSLAUATn/TnEbiU3QlNq/ZUeKcSTjcjbVslQtvM+j6tQ== #数据库密码(加密后)
zeroDateTimeBehavior: convertToNull #把日期异常转换为null代替异常处理
type: com.alibaba.druid.pool.DruidDataSource #数据源/连接池类型
initialSize: 2 #定义初始连接数
minIdle: 5 #定义最小空闲 minIdle=1
maxActive: 20 #定义最大连接数
maxWait: 60000 #定义最长等待时间
timeBetweenEvictionRunsMillis: 60000 #每60秒运行一次空闲连接回收器
minEvictableIdleTimeMillis: 300000 #池中的连接空闲300秒后被回收
validationQuery: SELECT 1 FROM DUAL #验证使用的SQL语句
testWhileIdle: true #指明连接是否被空闲连接回收器(如果有)进行检验.如果检测失败,则连接将被从池中去除.
testOnBorrow: false #借出连接时不要测试,否则很影响性能
testOnReturn: false #归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能
poolPreparedStatements: true #是否缓存preparedStatement,也就是PSCache。PSCache对支持游标的数据库性能提升巨大,比如说oracle。在mysql下建议关闭。
maxPoolPreparedStatementPerConnectionSize: 20 #指定每个连接上PSCache的大小
filters: config,stat,log4j # 配置监控统计拦截的filters,去掉后监控界面sql无法统计
#
decryptkey: MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKkalCG0DLhIMQ75ixjyvXLwx28Th+KxPMG2reDuYSCOWeV8yVQNRhmV7YXTGnrzroGFl4nU5mioZZijKPGPpg8CAwEAAQ==
# 通过connectProperties属性来打开mergeSql功能;慢SQL记录 #
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000;config.decrypt.key=${spring.datasource.decryptkey};config.decrypt=true
useGlobalDataSourceStat: true #合并多个DruidDataSource的监控数据
druidLoginName: ejsino # SQL监控后台登录用户名
druidPassword: ej1906
说明:application.yml中的active属性值中配置的loc和dev分别对应application-loc.yml和application-dev.yml,这两个配置文件理论上配置方式一模一样,参数值可不同,可用于区分0本地和测试机。
(4)启动类application-dev.yml
package fanshion;
import org.mybatis.spring.annotation.MapperScan;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.support.SpringBootServletInitializer;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@SpringBootApplication
//@EnableRedisHttpSession
@EnableTransactionManagement
@MapperScan("fanshion.dao")
public class FanshionApplication extends SpringBootServletInitializer{
private static Logger log = LoggerFactory.getLogger(FanshionApplication.class);
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(FanshionApplication.class);
}
public static void main(String[] args) {
SpringApplication.run(FanshionApplication.class, args);
log.info("||======服务启动成功======||");
}
}
参考博客地址:https://blog.csdn.net/u012343297/article/details/78919966
3.1.1 引入jar包依赖
<!-- shiro 依赖 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.3.2</version>
</dependency>
<!-- shiro缓存管理依赖 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>1.3.2</version>
</dependency>
<!-- 日志管理依赖 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
3.1.2 shiro缓存管理配置文件(ehcache-shiro.xml)
所在包:resources/config。
<?xml version="1.0" encoding="utf-8"?>
<ehcache name="shirocache">
<diskStore path="java.io.tmpdir"/>
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="false"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120"
/>
<!-- 系统缓存 -->
<cache name="systemCache"
maxElementsInMemory="1000"
eternal="false"
timeToIdleSeconds="3600"
timeToLiveSeconds="0"
overflowToDisk="false"
diskExpiryThreadIntervalSeconds="300" />
<cache name="shiro-activeSessionCache"
maxElementsInMemory="10000"
overflowToDisk="true"
eternal="true"
timeToLiveSeconds="0"
timeToIdleSeconds="0"
diskPersistent="true"
diskExpiryThreadIntervalSeconds="600" />
<cache name="org.apache.shiro.realm.text.PropertiesRealm-0-accounts"
maxElementsInMemory="1000"
eternal="true"
overflowToDisk="true"/>
<!-- 登录记录缓存 锁定10分钟 -->
<cache name="passwordRetryCache"
maxEntriesLocalHeap="2000"
eternal="false"
timeToIdleSeconds="3600"
timeToLiveSeconds="0"
overflowToDisk="false"
statistics="true" />
<cache name="configCache"
maxElementsInMemory="1000"
overflowToDisk="true"
eternal="true"
timeToLiveSeconds="0"
timeToIdleSeconds="0"
diskPersistent="true" />
</ehcache>
3.1.3 配置实现类(所在包:config.shiro)
(1)Authentication.java
package com.ejsino.xxx.config.shiro;
import com.ejsino.xxx.entity.user.CustInfo;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
/**
* @description validate user 验证用户
* @author YDLiang
* @date 20171117
*/
public class Authentication {
public static final String USER_SESSION_NAME = "csp";
public static final String USER_ID_SESSION_NAME = "csp_uid";
/**
* @description 获取登录用户 ID
* @return
*/
public static long getUserId() {
Subject subject = SecurityUtils.getSubject();
if (!subject.isAuthenticated()) {
return 0;
}
return (long) subject.getSession(false).getAttribute(USER_ID_SESSION_NAME);
}
/**
* @description 获取登录用户 username
* @return
*/
public static String getUserName() {
CustInfo profile = getUserInfo();
if (null == profile) {
return null;
}
return profile.getCustName();
}
/**
* @description 获取登录用户资料
* @return
*/
public static CustInfo getUserInfo() {
Subject subject = SecurityUtils.getSubject();
return (CustInfo) subject.getSession().getAttribute(USER_SESSION_NAME);
}
/**
* @description 获取登录用户资料
* @return
*/
public static void setUserInfo(CustInfo info) {
Subject subject = SecurityUtils.getSubject();
subject.getSession(false).setAttribute(USER_SESSION_NAME, info);
}
}
(2)ShiroRealm.java
package com.ejsino.xxx.config.shiro;
import com.ejsino.xxx.entity.user.Customer;
import com.ejsino.xxx.service.user.CustAuthorizationService;
import com.ejsino.xxx.service.user.CustService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
/**
* @author Paulo.Yuan
* @version V1.0
* @Title:
* @Package
* @Description: shiro 权限验证
* @date 20171106
*/
public class ShiroRealm extends AuthorizingRealm {
//引入日志
private Logger log = LoggerFactory.getLogger(getClass());
@Autowired
private CustService custService;
@Autowired
private CustAuthorizationService custAuthorizationService;
/**
* @param authcToken 被用来证明身份
* @return AuthenticationInfo
* @description Authentication 信息(身份证明)
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException {
UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
if (StringUtils.isEmpty(token.getUsername())) {
log.debug("current token's username is null.");
throw new AuthenticationException();
} else {
log.debug("current token is : {}", token.getUsername());
}
// 从token中拿到用户基本信息
Customer cust = custService.queryCustomer(token.getUsername());
if (cust == null) {
throw new UnknownAccountException();
}
if (Boolean.TRUE.equals(cust.isLocked())) {
throw new LockedAccountException();
}
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(cust.getCustName(), cust.getPassword(), getName());
ByteSource salt = ByteSource.Util.bytes(cust.getSalt());
authenticationInfo.setCredentialsSalt(salt);
// 如果验证通过则防如当前用户session中
SecurityUtils.getSubject().getSession(true).setAttribute("uid", cust.getCustNo());
return authenticationInfo;
}
/**
* @param principals
* @return
* @description 此方法调用 hasRole,hasPermission的时候才会进行回调.
* Authorization 是授权访问控制,用于对用户进行的操作授权,证明该用户是否允许进行当前操作,如访问某个链接,某个资源文件等
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
String username = (String) principals.getPrimaryPrincipal();
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
authorizationInfo.addRoles(custAuthorizationService.queryCustRoleList(username));
authorizationInfo.addStringPermissions(custAuthorizationService.queryCustPermissionList(username));
return authorizationInfo;
}
/**
* 清理所有授权和授权缓存
*/
public void clearAllCache() {
clearAllCachedAuthenticationInfo();
clearAllCachedAuthorizationInfo();
PrincipalCollection principals = SecurityUtils.getSubject().getPrincipals();
super.clearCache(principals);
}
/**
* 清理所有授权缓存
*/
public void clearAllCachedAuthorizationInfo() {
getAuthorizationCache().clear();
}
}
(3)ShiroConfig.java
package com.ejsino.xxx.config.shiro;
import com.ejsino.xxx.config.exception.DaoException;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authc.pam.AuthenticationStrategy;
import org.apache.shiro.authc.pam.FirstSuccessfulStrategy;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.Cookie;
import org.apache.shiro.web.servlet.ShiroHttpSession;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
importorg.springframework.web.servlet.handler.SimpleMappingExceptionResolver;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Properties;
/**
* @author Paulo.Yuan
* @version V1.0
* @Title:
* @Package
* @Description: shiro config
* @date 20171106
*/
@Configuration
public class ShiroConfig {
/**
* 后台身份认证realm;
*/
@Bean(name = "shiroRealm")
public ShiroRealm shiroRealm() {
ShiroRealm shiroRealm = new ShiroRealm();
shiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
return shiroRealm;
}
/**
* shiro ecache manager;
* 需要注入对应的其它的实体类中
* securityManager: 安全管理器
* securityManager is the core of shiro
*/
@Bean(name = "ehCacheManager")
public EhCacheManager ehCacheManager() {
EhCacheManager cacheManager = new EhCacheManager();
cacheManager.setCacheManagerConfigFile("classpath:config/ehcache-shiro.xml");
return cacheManager;
}
@Bean
public SecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//设置realm.
securityManager.setRealm(shiroRealm());
// 自定义缓存实现 使用ehCache
securityManager.setCacheManager(ehCacheManager());
// 自定义session管理 使用redis
// securityManager.setSessionManager(sessionManager());
return securityManager;
}
/**
* Shiro默认提供了三种 AuthenticationStrategy 实现:
* AtLeastOneSuccessfulStrategy :其中一个通过则成功。
* FirstSuccessfulStrategy :其中一个通过则成功,但只返回第一个通过的Realm提供的验证信息。
* AllSuccessfulStrategy :凡是配置到应用中的Realm都必须全部通过。
* authenticationStrategy
*/
@Bean(name = "authenticationStrategy")
public AuthenticationStrategy authenticationStrategy() {
return new FirstSuccessfulStrategy();
}
/**
* @see 默认session管理器
*/
@Bean(name = "sessionManager")
public DefaultWebSessionManager defaultWebSessionManager() {
DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
//sessionManager.setSessionDAO(new CustomSessionDAO());
//单位为毫秒(1秒=1000毫秒) 3600000毫秒为1个小时
sessionManager.setSessionValidationInterval(3600000 * 12);
//3600000 milliseconds = 1 hour
sessionManager.setGlobalSessionTimeout(3600000 * 12);
sessionManager.setDeleteInvalidSessions(true);
sessionManager.setSessionValidationSchedulerEnabled(true);
Cookie cookie = new SimpleCookie(ShiroHttpSession.DEFAULT_SESSION_ID_NAME);
cookie.setName("WEBID");
cookie.setHttpOnly(true);
sessionManager.setSessionIdCookie(cookie);
return sessionManager;
}
/**
* 开启shiro aop注解支持.
* 使用代理方式;所以需要开启代码支持;
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager((org.apache.shiro.mgt.SecurityManager) securityManager);
return authorizationAttributeSourceAdvisor;
}
/**
* 凭证匹配器
* 由于我们的密码校验交给Shiro的SimpleAuthenticationInfo进行处理了
* 所以我们需要修改下doGetAuthenticationInfo中的代码;
*/
@Bean
public HashedCredentialsMatcher hashedCredentialsMatcher() {
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
hashedCredentialsMatcher.setHashAlgorithmName("SHA-256");//散列算法:这里使用MD5算法;
hashedCredentialsMatcher.setHashIterations(2);//散列的次数,比如散列两次,相当于 md5(md5(""));
return hashedCredentialsMatcher;
}
/**
* 用户错误页面
*/
@Bean
public SimpleMappingExceptionResolver simpleMappingExceptionResolver() {
SimpleMappingExceptionResolver simpleMappingExceptionResolver = new SimpleMappingExceptionResolver();
simpleMappingExceptionResolver.setDefaultErrorView("/login.html");
Properties mappings = new Properties();
mappings.setProperty("org.apache.shiro.authc.UnknownAccountException","/403");
mappings.setProperty("org.apache.shiro.authc.IncorrectCredentialsException","/403");
mappings.setProperty("org.apache.shiro.authc.LockedAccountException","/403");
mappings.setProperty("org.apache.shiro.authc.ExcessiveAttemptsException","/403");
mappings.setProperty("org.apache.shiro.authc.AuthenticationException","/403");
mappings.setProperty("org.apache.shiro.authz.UnauthorizedException","/login.html");
simpleMappingExceptionResolver.setExceptionMappings(mappings);
simpleMappingExceptionResolver.setExcludedExceptions(DaoException.class);
return simpleMappingExceptionResolver;
}
/**
* ShiroFilterFactoryBean 处理拦截资源文件问题。
*/
@Bean(name = "shirFilter")
public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
// 必须设置 SecurityManager
shiroFilterFactoryBean.setSecurityManager(securityManager);
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
// 配置不会被拦截的链接 顺序判断
filterChainDefinitionMap.put("/favicon.ico", "anon");
filterChainDefinitionMap.put("/static/css/**", "anon");
<!--authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问-->
filterChainDefinitionMap.put("/author/login", "anon");
filterChainDefinitionMap.put("/login.html", "anon");
filterChainDefinitionMap.put("/logout", "logout");
//anthc:authc filter 监听,不登陆不能访问
filterChainDefinitionMap.put("/**", "authc");
filterChainDefinitionMap.put("/", "authc");
// 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
shiroFilterFactoryBean.setLoginUrl("/login.html");
// 登录成功后要跳转的链接
//对应{@link 17.indexController配置}
shiroFilterFactoryBean.setSuccessUrl("/index");
// 未授权界面;
shiroFilterFactoryBean.setUnauthorizedUrl("/403"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
}
(4)PwdHashed.java(密码加密)
package com.ejsino.xxx.config.shiro;
import org.apache.shiro.crypto.SecureRandomNumberGenerator;
import org.apache.shiro.crypto.hash.Sha256Hash;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.util.ByteSource;
/**
* @description 密码加密
* @author YDLiang
* @date 20171109
*/
public enum PwdHashed {
INSTANCE;
private String credentialsSalt;
private static final int DEFAULT_ITERATIONS = 0x2;
public String encryptToHex(String plaintext) {
return getSimpleHash(plaintext).toHex();
}
//给密码加密
public String encryptToBase64(String plaintext) {
return getSimpleHash(plaintext).toBase64();
}
//hash加密
public SimpleHash getSimpleHash(String plaintext) {
String algorithm = Sha256Hash.ALGORITHM_NAME;
credentialsSalt = new SecureRandomNumberGenerator().nextBytes().toHex();
ByteSource byteSalt = ByteSource.Util.bytes(credentialsSalt);
return new SimpleHash(algorithm, plaintext, byteSalt, DEFAULT_ITERATIONS);
}
//生成盐
public String getCredentialsSalt() {
return credentialsSalt;
}
public void setCredentialsSalt(String credentialsSalt) {
this.credentialsSalt = credentialsSalt;
}
}
参考博客地址:https://www.cnblogs.com/fifiyong/p/5805531.html
3.2.1 引入jar包依赖
<!--mybatis依赖包-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.1</version>
</dependency>
<!-- mysql 驱动包 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
3.2.2 添加mybatis相关配置文件
(1)在application-dev.yml文件中Spring节点下添加mybatis相关配置
mybatis: #mybatis相关配置
config-location: classpath:config/mybatis-config.xml #加mybatis #配置文件
mapper-locations: classpath:mapper/**/*.xml #映射xml文件
(2)配置mybatis-config.xml(所在包:resources/config)
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--setting设置,关系到mybatis的主要设置,关系到mybatis运行时的行为方式-->
<settings>
<!--是全局的映射器启用或者禁用缓存 true|false-->
<setting name="cacheEnabled" value="true" />
<!--全局启用或者禁用延迟加载 true|false-->
<setting name="lazyLoadingEnabled" value="true" />
<!-- 当启用时, 有延迟加载属性的对象在被 调用时将会完全加载任意属性。否则, 每种属性将会按需要加载 true|false-->
<setting name="aggressiveLazyLoading" value="false" />
<!--允许或不允许多种结果集从一个单独 的语句中返回(需要适合的驱动) true|false-->
<setting name="multipleResultSetsEnabled" value="true" />
<!--使用列标签代替列名 true|false-->
<setting name="useColumnLabel" value="true" />
<!--允许 JDBC 支持生成的键 true|false-->
<setting name="useGeneratedKeys" value="true" />
<!-- 配置默认的执行器-->
<setting name="defaultExecutorType" value="SIMPLE" />
<!-- 设置超时时间, 它决定驱动等待一个数 据库响应的时间-->
<setting name="defaultStatementTimeout" value="25" />
<!-- true|false-->
<setting name="defaultFetchSize" value="100" />
<!-- 允许在嵌套语句上使用RowBoundtrue|false-->
<setting name="safeRowBoundsEnabled" value="false" />
<!—声明日志实现类型-->
<setting name="logImpl" value="STDOUT_LOGGING" />
</settings>
<!--给java类型取一个别名,方便在核心配置、映射配置中来使用这个java类型-->
<typeAliases>
<typeAlias alias="Integer" type="java.lang.Integer" />
<typeAlias alias="Long" type="java.lang.Long" />
<typeAlias alias="HashMap" type="java.util.HashMap" />
<typeAlias alias="LinkedHashMap" type="java.util.LinkedHashMap" />
<typeAlias alias="ArrayList" type="java.util.ArrayList" />
<typeAlias alias="LinkedList" type="java.util.LinkedList" />
</typeAliases>
<!--类型处理器-->
<typeHandlers>
<typeHandler handler="com.ejsino.claim.config.mybatis.EnumHandlerStatus" jdbcType="VARCHAR"></typeHandler>
<typeHandler handler="com.ejsino.claim.config.mybatis.EnumHandlerGender" jdbcType="VARCHAR"></typeHandler>
</typeHandlers>
3.2.3 配置实现类(所在包:config.mybatis)
自定义枚举实现
(1)Status.java
package com.ejsino.xxx.config.mybatis;
public enum Status {
VALID("有效", "1"), INVALID("无效", "0");
private String display;
private String value;
/*
*省略get,set方法和构造方法
*/
public static String getDisplay(String value) {
for (Status status: Status.values()) {
if (status.getValue().equals(value)) {
return status.display;
}
}
return null;
}
public static String getValue(String display) {
for (Status status: Status.values()) {
if (status.getDisplay().equals(display)) {
return status.value;
}
}
return null;
}
public static Status displayOf(String display) {
if (display == null) {
throw new NullPointerException("display is null");
}
for (Status status: Status.values()) {
if (status.getDisplay().equals(display)) {
return status;
}
}
throw new IllegalArgumentException("No enum display " + display);
}
public static Status newValueOf(String value) {
if (value == null) {
throw new NullPointerException("value is null");
}
for (Status status: Status.values()) {
if (status.getValue().equals(value)) {
return status;
}
}
throw new IllegalArgumentException("No enum new value " + value);
}
}
(2)EnumHandlerStatus.java对应自己枚举类的处理
package com.ejsino.xxx.config.mybatis;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeHandler;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class EnumHandlerStatus implements TypeHandler<Status> {
@Override
public void setParameter(PreparedStatement ps, int i, Status status, JdbcType jdbcType) throws SQLException {
ps.setString(i, status.getValue());
}
@Override
public Status getResult(ResultSet rs, String columnName) throws SQLException {
String status = rs.getString(columnName);
return Status.newValueOf(status);
}
@Override
public Status getResult(ResultSet rs, int columnIndex) throws SQLException {
String status = rs.getString(columnIndex);
return Status.newValueOf(status);
}
@Override
public Status getResult(CallableStatement cs, int columnIndex) throws SQLException {
String status = cs.getString(columnIndex);
return Status.newValueOf(status);
}
}
参考博客地址:https://www.cnblogs.com/digdeep/p/4608933.html
3.3.1 引入jar包依赖
<!-- 分页器pagehelper依赖-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>4.1.6</version>
</dependency>
3.3.2 添加分页器PageHelp相关配置文件
在mybatis-config.xml中配置(也可在SpringBoot配置文件中配置,此处在mybatis-config.xml中配置)
<!--分页器插件配置-->
<plugins>
<plugin interceptor="com.github.pagehelper.PageHelper">
<property name="dialect" value="mysql" />
<property name="offsetAsPageNum" value="true" />
<property name="rowBoundsWithCount" value="true" />
<property name="pageSizeZero" value="true" />
<property name="reasonable" value="true" />
<property name="params" value="pageNum=pageHelperStart;pageSize=pageHelperRows;" />
<property name="supportMethodsArguments" value="false" />
<property name="returnPageInfo" value="none" />
</plugin>
</plugins>
</configuration>
3.3.3 配置实现类
(1)Paginator.java(所在包:config.paginator)
package com.ejsino.xxx.config.paginator;
/**
* @author YDLiang
* @descreption 分页处理器
* @date 20171108
*/
public class Paginator implements java.io.Serializable, Cloneable {
private static final long serialVersionUID = 1L;
/*默认起始页码*/
private static final int DEFAULT_PAGE_NUMBER = 1;
/*默认每页显示数量*/
private static final int DEFAULT_PAGE_SIZE = 10;
/*每页最大显示数据量*/
private static final int MAX_PAGE_SIZE = Integer.MAX_VALUE;
/**
* 页码
*/
private int pageNum = DEFAULT_PAGE_NUMBER;
/**
* 每页数据条数
*/
private int pageSize = MAX_PAGE_SIZE;
private int limit = 0;
private int offset = 0;
private String action;
/**
* 默认构造器
*/
public Paginator() {
}
public Paginator(int pageNum, int pageSize) {
this.pageNum = pageNum;
this.pageSize = pageSize;
}
/**
* @return 页码
*/
public int getPageNum() {
if(limit != 0){
return offset/limit + 1;
}
return pageNum;
}
/**
* @param pageNum the {@link #pageNum} to set
*/
public void setPageNum(int pageNum) {
this.pageNum = (pageNum > 0) ? pageNum : DEFAULT_PAGE_NUMBER;
}
/**
* @return the {@link #pageSize}
*/
public int getPageSize() {
if(limit != 0){
return limit;
}
return pageSize;
}
/**
* @param pageSize the {@link #pageSize} to set
*/
public void setPageSize(int pageSize) {
this.pageSize = (pageSize > 0) ? pageSize : DEFAULT_PAGE_SIZE;
}
//省略get,set方法
public enum Direction {
/**
* 升序
*/
ASC,
/**
* 降序
*/
DESC
}
}
(2)PageResult.java:前后端交互处理类(所在包:config.domainr)
package com.ejsino.xxx.config.domain;
import net.sf.json.JSONObject;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @description 分页返回前端对象
* 分页功能,接收参数、查询分页均无变化
* 返回分页对象的时候采用此对象 转json返回
* 例如: 返回类型String
* PageInfo custs = custService.queryUserPage(cust);
* PageResult page = new PageResult(custs.getTotal(),custs.getList());
* return PageResult.toJSONObject(page);
* @author YDLiang
* @date 20171218
*/
public class PageResult {
/**返回状态*/
private boolean success = false;
/**返回消息*/
private String message;
/**返回条数*/
private long total;
/**返回list对象*/
private List<?> rows;
/**返回参数(仅字符串)*/
private String params;
/**返回对象(多个)*/
private Map<String, Object> objects = new HashMap<String, Object>();
public PageResult(){};
public PageResult(long total, List<?> rows) {
this.success = true;
this.total = total;
this.rows = rows;
}
public PageResult(String params, long total, List<?> rows) {
this.params = params;
this.success = true;
this.total = total;
this.rows = rows;
}
public PageResult(long total, List<?> rows, Map<String, Object> objects) {
this.success = true;
this.total = total;
this.rows = rows;
this.objects = objects;
}
public PageResult(boolean success, String message, long total, List<?> rows) {
this.success = success;
this.message = message;
this.total = total;
this.rows = rows;
}
/**
* @param json
* @return
* @description To JSON Object
*/
public static String toJSONObject(PageResult json) {
return JSONObject.fromObject(json).toString();
}
3.4.1 引入jar包依赖
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.6</version>
</dependency>
3.4.2 添加druid相关配置
(1)此处的配置放在application-dev.yml中Spring节点下文件中
datasource: #配置数据源
driver-class-name: com.mysql.jdbc.Driver #数据库驱动名称
url: xxx #数据库地址
username: xxx #数据库名
password: xxxx #数据库密码(加密后)
zeroDateTimeBehavior: convertToNull #把日期异常转换为null代替异常处理
type: com.alibaba.druid.pool.DruidDataSource #数据源/连接池类型
initialSize: 2 #定义初始连接数
minIdle: 5 #定义最小空闲 minIdle=1
maxActive: 20 #定义最大连接数
maxWait: 60000 #定义最长等待时间
timeBetweenEvictionRunsMillis: 60000#每60秒运行一次空闲连接回收器
minEvictableIdleTimeMillis: 300000 #池中的连接空闲300秒后被回收
validationQuery: SELECT 1 FROM DUAL #验证使用的SQL语句
testWhileIdle: true#指明连接是否被空闲连接回收器(如果有)进行检验.如果
#检测失败,则连接将被从池中去除.
testOnBorrow: false #借出连接时不要测试,否则很影响性能
testOnReturn: false#归还连接时执行validationQuery检测连接是否效,
#做了这个配置会降低性能
poolPreparedStatements: true #是否缓存preparedStatement,也就是
#PSCache。PSCache对支持游标的数据库性能提升巨
#大,比如说oracle。在mysql下建议关闭。
maxPoolPreparedStatementPerConnectionSize: 20 #指定每个连接上
#PSCache的大小
filters: config,stat,log4j # 配置监控统计拦截的filters,去掉后监控界面sql无法统计
decryptkey: MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKkalCG0DLhIMQ75ixjyvXLwx28Th+KxPMG2reDuYSCOWeV8yVQNRhmV7YXTGnrzroGFl4nU5mioZZijKPGPpg8CAwEAAQ==
# 通过connectProperties属性来打开mergeSql功能;慢SQL记录
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000;config.decrypt.key=${spring.datasource.decryptkey};config.decrypt=true
useGlobalDataSourceStat: true #合并多个DruidDataSource的监控数据
druidLoginName: ejsino # SQL监控后台登录用户名
druidPassword: xxx # SQL监控后台登录用户密码
3.4.3 添加配置实现类
(1)DruidConfig.java
package com.ejsino.xxx.config.druid;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
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 javax.sql.DataSource;
import java.sql.SQLException;
/**
* @description druid 数据库连接池配置
* @author YDLiang
* @date 20171221
*/
@Configuration
public class DruidConfig {
private Logger log = LoggerFactory.getLogger(DruidConfig.class);
@Value("${spring.datasource.url}")
private String dbUrl;
@Value("${spring.datasource.type}")
private String dbType;
@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.filters}")
private String filters;
@Value("${spring.datasource.connectionProperties}")
private String connectionProperties;
@Value("${spring.datasource.useGlobalDataSourceStat}")
private boolean useGlobalDataSourceStat;
@Value("${spring.datasource.druidLoginName}")
private String druidLoginName;
@Value("${spring.datasource.druidPassword}")
private String druidPassword;
@Bean(name="dataSource",destroyMethod = "close", initMethod="init")
@Primary
public DataSource dataSource(){
DruidDataSource datasource = new DruidDataSource();
try {
datasource.setUrl(this.dbUrl);
datasource.setDbType(dbType);
datasource.setUsername(username);
datasource.setPassword(password);
datasource.setDriverClassName(driverClassName);
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.setFilters(filters);
datasource.setConnectionProperties(connectionProperties);
datasource.setUseGlobalDataSourceStat(useGlobalDataSourceStat);
} catch (SQLException e) {
log.error("druid configuration initialization filter", e);
}
return datasource;
}
@Bean
public ServletRegistrationBean druidServlet() {
ServletRegistrationBean reg = new ServletRegistrationBean();
reg.setServlet(new StatViewServlet());
reg.addUrlMappings("/druid/*");
// IP白名单(没有配置或者为空,则允许所有访问)
reg.addInitParameter("allow", "192.168.1.110,127.0.0.1");
// IP黑名单 (存在共同时,deny优先于allow)
reg.addInitParameter("deny", "192.168.1.111");
// 用户名
reg.addInitParameter("loginUsername", this.druidLoginName);
// 密码
reg.addInitParameter("loginPassword", this.druidPassword);
//是否能够重置数据.
reg.addInitParameter("resetEnable", "false");
return reg;
}
/**
* druid过滤器.
* @author Administrator
*
*/
@Bean(name="druidWebStatFilter")
public FilterRegistrationBean filterRegistrationBean() {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
filterRegistrationBean.setFilter(new WebStatFilter());
//添加过滤规则.
filterRegistrationBean.addUrlPatterns("/*");
////忽略资源
filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
//监控单个url调用的sql列表
filterRegistrationBean.addInitParameter("profileEnable", "true");
//使得druid知道当前的user是谁
filterRegistrationBean.addInitParameter("principalCookieName", "USER_COOKIE");
//使得druid能够知道当前的session的用户是谁filterRegistrationBean.addInitParameter("principalSessionName", "USER_SESSION");
return filterRegistrationBean;
}
}
3.5.1 引入jar包依赖
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
3.5.2 添加日志相关配置
(1) 在application-dev.yml文件中添加相关配置(一级节点)
logging: #日志相关配置
config: classpath:config/logback-config.xml #加载日志相关配置文件
path: /ejdata/comment-log/ #日志存储路径
level: ERROR #接收日志等级,从低到高:trace(追踪), #debug(试),info(自定义),warn(提示),error(错误)
match: ACCEPT #ACCEPT/DENY #日志级别筛选策略,此除ACCEPT表示匹配到 #更高级别异常允许输出
mismatch: DENY #日志级别筛选策略,此处ACCEPT表示没有匹配到更高级别异常 #不允许输出
(2)配置logback-config.xml(所在包:resources/config)
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="true">
<contextName>ej_claim</contextName>
<!-- 配置本机log地址,勿用相对路径 -->
<springProperty scope="context" name="LOG_HOME" source="logging.path"/>
<springProperty scope="context" name="LOG_LEVEL" source="logging.level"/>
<springProperty scope="context" name="LOG_ONMATCH" source="logging.match"/>
<springProperty scope="context" name="LOG_ONMISMATCH" source="logging.mismatch"/>
<!-- 彩色日志依赖的渲染类 -->
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" />
<conversionRule conversionWord="wex" converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter" />
<conversionRule conversionWord="wEx" converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter" />
<!-- 彩色日志格式 -->
<property name="CONSOLE_LOG_PATTERN" value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(-){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}" />
<!-- 控制台输出 -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
<charset>utf8</charset>
</encoder>
</appender>
<!-- 按照每天生成日志文件 -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>${LOG_LEVEL}</level>
<onMatch>${LOG_ONMATCH}</onMatch>
<onMismatch>${LOG_ONMISMATCH}</onMismatch>
</filter>
<Prudent>true</Prudent>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<FileNamePattern>${LOG_HOME}/claim-%d{yyyy-MM-dd}.log</FileNamePattern>
<MaxHistory>30</MaxHistory>
</rollingPolicy>
<layout class="ch.qos.logback.classic.PatternLayout">
<Pattern>%d{yyyy-MM-dd HH:mm:ss} -%msg%n</Pattern>
</layout>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<!--<logger name="org.apache.ibatis" level="DEBUG" />
<logger name="java.sql.Connection" level="DEBUG"/>
<logger name="java.sql.Statement" level="DEBUG"/>
<logger name="java.sql.PreparedStatement" level="DEBUG"/>-->
<logger name="freemarker" level="INFO" />
<logger name="org.springframework" level="INFO" />
<logger name="com.alibaba.druid.filter.stat.StatFilter" level="OFF" />
<!-- log输出等级 -->
<root level="${LOG_LEVEL}">
<appender-ref ref="CONSOLE" />
<appender-ref ref="FILE" />
</root>
</configuration>
3.6.1 引入相关jar包依赖
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
<dependency>
<groupId>commons-httpclient</groupId>
<artifactId>commons-httpclient</artifactId>
<version>3.1</version>
</dependency>
3.6.2 添加http相关配置
(1) 在application-dev.yml文件中添加相关配置(一级节点)
http:
maxTotal: 100 #http连接池大小
defaultMaxPerRoute: 20 #单机连接最大并发数
connectTimeout: 1000 #链接建立的超时时间(单位:毫秒)
connectionRequestTimeout: 500#http clilent中从connetcion pool中 #获得一个connection的超时时间
socketTimeout: 10000 #响应超时时间,超过此时间不再读取响应
staleConnectionCheckEnabled: true #提交测试连接是否可用
3.6.3 配置实现类(所在包:config.httpclient)
(1)HttpClientConfig.java(读取和处理http相关配置并进行处理)
package com.ejsino.xxx.config.httpclient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author Paulo.Yuan
* @version V1.0
* @Title:
* @Package
* @Description: HttpClient Config
* @date 20171206
*/
@Configuration
public class HttpClientConfig {
@Value("${http.maxTotal}")
private Integer maxTotal;
@Value("${http.defaultMaxPerRoute}")
private Integer defaultMaxPerRoute;
@Value("${http.connectTimeout}")
private Integer connectTimeout;
@Value("${http.connectionRequestTimeout}")
private Integer connectionRequestTimeout;
@Value("${http.socketTimeout}")
private Integer socketTimeout;
@Value("${http.staleConnectionCheckEnabled}")
private boolean staleConnectionCheckEnabled;
/**
* @description 实例化一个连接池管理器,设置最大连接数、并发连接数
* @return
*/
@Bean(name = "httpClientConnectionManager")
public PoolingHttpClientConnectionManager getHttpClientConnectionManager() {
PoolingHttpClientConnectionManager pool = new PoolingHttpClientConnectionManager();
pool.setMaxTotal(maxTotal);
pool.setDefaultMaxPerRoute(defaultMaxPerRoute);
return pool;
}
/**
* @description 实例化构造器,设置连接池管理器
* @param httpClientConnectionManager
* @return
*/
@Bean(name = "httpClientBuilder")
public HttpClientBuilder getHttpClientBuilder(@Qualifier("httpClientConnectionManager") PoolingHttpClientConnectionManager httpClientConnectionManager) {
//HttpClientBuilder中的构造方法被protected修饰,所以这里不能直接使用new来实例化一个HttpClientBuilder,
// 可以使用HttpClientBuilder提供的静态方法create()来获取HttpClientBuilder对象
HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
httpClientBuilder.setConnectionManager(httpClientConnectionManager);
return httpClientBuilder;
}
/**
* @description 获取httpClient实例
* @param httpClientBuilder
* @return
*/
@Bean
public CloseableHttpClient getCloseableHttpClient(@Qualifier("httpClientBuilder") HttpClientBuilder httpClientBuilder) {
return httpClientBuilder.build();
}
/**
* @description 设置RequestConfig
* Builder是RequestConfig的一个内部类
* 通过RequestConfig的custom方法来获取到一个Builder对象
* 设置builder的连接信息
* 这里还可以设置proxy,cookieSpec等属性。有需要的话可以在此设置
* @return
*/
@Bean(name = "builder")
public RequestConfig.Builder getBuilder() {
RequestConfig.Builder builder = RequestConfig.custom();
return builder.setConnectTimeout(connectTimeout)
.setSocketTimeout(socketTimeout)
.setConnectionRequestTimeout(connectionRequestTimeout)
.setStaleConnectionCheckEnabled(staleConnectionCheckEnabled);
}
/**
* @description 构建一个RequestConfig实例
* @param builder
* @return
*/
@Bean
public RequestConfig getRequestConfig(@Qualifier("builder") RequestConfig.Builder builder) {
return builder.build();
}
}
(2)HttpService.java(封装对http不同类型的请求的处理过程)
package com.ejsino.xxx.config.httpclient;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@Component
public class HttpService {
@Autowired
private CloseableHttpClient httpClient;
@Autowired
private RequestConfig config;
/**
* @description 不带参数的get请求,如果状态码为200,则返回body,如果不为200,则返回null
* @param url
* @return
* @throws Exception
*/
public String doGet(String url) throws Exception {
HttpGet httpGet = new HttpGet(url);
httpGet.setConfig(config);
CloseableHttpResponse response = this.httpClient.execute(httpGet);
if (response.getStatusLine().getStatusCode() == 200) {
return EntityUtils.toString(response.getEntity(), "UTF-8");
}
return null;
}
/**
* @description 带参数的get请求,如果状态码为200,则返回body,如果不为200,则返回null
* @param url
* @return
* @throws Exception
*/
public String doGet(String url, Map<String, Object> map) throws Exception {
URIBuilder uriBuilder = new URIBuilder(url);
if (map != null) {
// 遍历map,拼接请求参数
for (Map.Entry<String, Object> entry : map.entrySet()) {
uriBuilder.setParameter(entry.getKey(), entry.getValue().toString());
}
}
return this.doGet(uriBuilder.build().toString());
}
/**
* @description 带参数的post请求
* @param url
* @param map
* @return
* @throws Exception
*/
public HttpResult doPost(String url, Map<String, Object> map) throws Exception {
HttpPost httpPost = new HttpPost(url);
httpPost.setConfig(config);
// 判断map是否为空,不为空则进行遍历,封装from表单对象
if (map != null) {
List<NameValuePair> list = new ArrayList<NameValuePair>();
for (Map.Entry<String, Object> entry : map.entrySet()) {
list.add(new BasicNameValuePair(entry.getKey(), entry.getValue().toString()));
}
UrlEncodedFormEntity urlEncodedFormEntity = new UrlEncodedFormEntity(list, "UTF-8");
httpPost.setEntity(urlEncodedFormEntity);
}
CloseableHttpResponse response = this.httpClient.execute(httpPost);
return new HttpResult(response.getStatusLine().getStatusCode(), EntityUtils.toString(
response.getEntity(), "UTF-8"));
}
/**
* @description 不带参数post请求
* @param url
* @return
* @throws Exception
*/
public HttpResult doPost(String url) throws Exception {
return this.doPost(url, null);
}
}
(3)HttpResult.java(封装对http不同类型的请求的处理过程)
package com.ejsino.xxx.config.httpclient;
/**
* @author charles
* @version V1.0
* @Title:
* @Package
* @Description: HttpResult
* @date 20180328
*/
public class HttpResult {
/**响应码*/
private Integer code;
/**响应体*/
private String body;
public HttpResult() {
super();
}
public HttpResult(Integer code, String body) {
super();
this.code = code;
this.body = body;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getBody() {
return body;
}
public void setBody(String body) {
this.body = body;
}
}
3.7.1 引入相关jar包
<dependency>
<groupId>com.aliyun.openservices</groupId>
<artifactId>aliyun-openservices</artifactId>
<version>1.0.12</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3</version>
</dependency>
3.7.2 添加OSS相关配置
(1) 在application-dev.yml文件中添加相关配置(一级节点)
http:
maxTotal: 100
defaultMaxPerRoute: 20
connectTimeout: 1000
connectionRequestTimeout: 500
socketTimeout: 10000
staleConnectionCheckEnabled: true
3.7.3 配置实现类(所在包:config.aliyunoss)
(1) 在application-dev.yml文件中添加相关配置(一级节点)
http:
maxTotal: 100
defaultMaxPerRoute: 20
connectTimeout: 1000
connectionRequestTimeout: 500
socketTimeout: 10000
staleConnectionCheckEnabled: true
3.7.3 配置实现类(所在包:config.aliyunoss)
(1)FileInfoBean.java(项目中涉及的文件上传下载)
定义一些文件上传下载操作需要的字段/*文件流/
package com.ejsino.XXX.config.aliyunoss;
import java.io.File;
/***
* @description 上传阿里云对象
* @date 20171220
* @version V1.0
*/
public class FileInfoBean {
/**文件流*/
private byte[] fileInfo;
/**文件名*/
private String fileName;
/**文件类型*/
private String fileType;
/**压缩后缀*/
private String compresssuffix;
/**是否需要压缩*/
private String needcompress;
/**文件唯一编号*/
private String fileno;
/**文件路径*/
private String filePath;
public String getFileno() {
return fileno;
}
public void setFileno(String fileno) {
this.fileno = fileno;
}
public String getNeedcompress() {
return needcompress;
}
public void setNeedcompress(String needcompress) {
this.needcompress = needcompress;
}
public byte[] getFileInfo() {
return fileInfo;
}
public void setFileInfo(byte[] fileInfo) {
this.fileInfo = fileInfo;
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
public String getFileType() {
return fileType;
}
public void setFileType(String fileType) {
this.fileType = fileType;
}
public void deleteOwnerFile() {
File f = new File(this.fileName);
f.delete();
}
public String getCompresssuffix() {
return compresssuffix;
}
public void setCompresssuffix(String compresssuffix) {
this.compresssuffix = compresssuffix;
}
public String getFilePath() {
return filePath;
}
public void setFilePath(String filePath) {
this.filePath = filePath;
}
}
(2)OssConst.java(放一些跟文件有关的常量,自愿配置)
package com.ejsino.xxx.config.aliyunoss;
public enum OssConst {
//oss-测试机
OSS_DOMAIN_ADDRESS("oss-domainAddress","http://testimage.ejsino.net/"),
OSS_BUCKET_NAME("oss-bucketName","ejsino-test"),
OSS_PROD_FILEPATH("oss-prodFilePath","prodfile/"),
OSS_TEMP_FILEPATH("oss-tempFilePath","D:/project/");
private String name ;
private String value ;
private OssConst( String name , String value){
this.name = name ;
this.value = value ;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
(3)OssProperties.java
package com.ejsino.ejcomment.config.aliyunoss;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* @author charles
* @version V1.0
* @Title:
* @Package
* @Description: oss 配置信息
* @date 20171211
*/
@Component
@ConfigurationProperties(prefix = "oss")
public class OssProperties {
private String image_storage_dir;
private String oss_server;
private String file_storage_dir;
private String tempFilePath;
private String bucketName;
private String accessKeyId;
private String accessKeySecret;
private String endPoint;
private String productClauseFilePath;
private String prodFilePath;
private String accountLogoFilePath;
private String productModal;
private String aigPrintModal;
private String pinganPrintModal;
private String commonPrintModal;
private String docPrintModal;
private String domainAddress;
private String apicarfile;
//private String 5fservicecert;
private String bohai;
private String appointcard;
private String meinian;
private String signature;
private String mobilecust;
public String getImage_storage_dir() {
return image_storage_dir;
}
public void setImage_storage_dir(String image_storage_dir) {
this.image_storage_dir = image_storage_dir;
}
public String getOss_server() {
return oss_server;
}
public void setOss_server(String oss_server) {
this.oss_server = oss_server;
}
public String getFile_storage_dir() {
return file_storage_dir;
}
public void setFile_storage_dir(String file_storage_dir) {
this.file_storage_dir = file_storage_dir;
}
public String getTempFilePath() {
return tempFilePath;
}
public void setTempFilePath(String tempFilePath) {
this.tempFilePath = tempFilePath;
}
public String getBucketName() {
return bucketName;
}
public void setBucketName(String bucketName) {
this.bucketName = bucketName;
}
public String getAccessKeyId() {
return accessKeyId;
}
public void setAccessKeyId(String accessKeyId) {
this.accessKeyId = accessKeyId;
}
public String getAccessKeySecret() {
return accessKeySecret;
}
public void setAccessKeySecret(String accessKeySecret) {
this.accessKeySecret = accessKeySecret;
}
public String getEndPoint() {
return endPoint;
}
public void setEndPoint(String endPoint) {
this.endPoint = endPoint;
}
public String getProductClauseFilePath() {
return productClauseFilePath;
}
public void setProductClauseFilePath(String productClauseFilePath) {
this.productClauseFilePath = productClauseFilePath;
}
public String getProdFilePath() {
return prodFilePath;
}
public void setProdFilePath(String prodFilePath) {
this.prodFilePath = prodFilePath;
}
public String getAccountLogoFilePath() {
return accountLogoFilePath;
}
public void setAccountLogoFilePath(String accountLogoFilePath) {
this.accountLogoFilePath = accountLogoFilePath;
}
public String getProductModal() {
return productModal;
}
public void setProductModal(String productModal) {
this.productModal = productModal;
}
public String getAigPrintModal() {
return aigPrintModal;
}
public void setAigPrintModal(String aigPrintModal) {
this.aigPrintModal = aigPrintModal;
}
public String getPinganPrintModal() {
return pinganPrintModal;
}
public void setPinganPrintModal(String pinganPrintModal) {
this.pinganPrintModal = pinganPrintModal;
}
public String getCommonPrintModal() {
return commonPrintModal;
}
public void setCommonPrintModal(String commonPrintModal) {
this.commonPrintModal = commonPrintModal;
}
public String getDocPrintModal() {
return docPrintModal;
}
public void setDocPrintModal(String docPrintModal) {
this.docPrintModal = docPrintModal;
}
public String getDomainAddress() {
return domainAddress;
}
public void setDomainAddress(String domainAddress) {
this.domainAddress = domainAddress;
}
public String getApicarfile() {
return apicarfile;
}
public void setApicarfile(String apicarfile) {
this.apicarfile = apicarfile;
}
public String getBohai() {
return bohai;
}
public void setBohai(String bohai) {
this.bohai = bohai;
}
public String getAppointcard() {
return appointcard;
}
public void setAppointcard(String appointcard) {
this.appointcard = appointcard;
}
public String getMeinian() {
return meinian;
}
public void setMeinian(String meinian) {
this.meinian = meinian;
}
public String getSignature() {
return signature;
}
public void setSignature(String signature) {
this.signature = signature;
}
public String getMobilecust() {
return mobilecust;
}
public void setMobilecust(String mobilecust) {
this.mobilecust = mobilecust;
}
}
3.8.1 引入相关jar包
<!-- freemarker depend -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
3.8.2 添加freemarker相关配置
(1) 在application-dev.yml文件中添加相关配置(二级节点,在Spring节点下配置)
freemarker: #freemarker相关配置
enabled: true #是否允许mvc使用freemarker.
cache: false #是否开启template caching.
charset: UTF-8
content-type: text/html
template-loader-path: classpath:/templates/ #模板路径
suffix: .html #前端文件后缀
request-context-attribute: request #指定RequestContext属性的名
expose-session-attributes: true #是否在merge模板的时候,将
#HttpSession属性都添加到model中
expose-request-attributes: true #设定所有request的属性在merge到模板的时
#候,是否要都添加到model中.
expose-spring-macro-helpers: true #是否以springMacroRequestContext的
#形式暴露RequestContext给Spring’s macrolibrary使用
allow-request-override: true #指定HttpServletRequest的属性是否可以覆
#盖controller的model的同名项
allow-session-override: true #指定HttpSession的属性是否可以覆盖
#controller的model的同名项
settings: #设置几种时间类型的转化
date_format: yyyy-MM-dd
time_format: HH:mm:ss
datetime_format: yyyy-MM-dd HH:mm:ss
用途:对前端发送的请求进行拦截和验证
(1) UserSessionInterceptor.java(结合shiro中的3.1.3Authentication.java类)
package com.ejsino.xxx.config.intercepter;
import com.ejsino.xxx.config.application.SpringBeanFactory;
import com.ejsino.xxx.entity.user.CustInfo;
import com.ejsino.xxx.config.shiro.Authentication;
import com.ejsino.xxx.service.user.CustInfoService;
import com.ejsino.xxx.service.user.impl.CustInfoServiceImpl;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class UserSessionInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
setUserProfile();
if (!(handler instanceof HandlerMethod)) {
return super.preHandle(request, response, handler);
}
return true;
}
//获取用户session信息
public void setUserProfile() {
Subject subject = SecurityUtils.getSubject();
Session session = subject.getSession();
CustInfo info = (CustInfo)
if (null == info) {
String username = (String) subject.getPrincipal();
if (null == username) {
return;
}
//CustInfoServiceImpl自己定义的获取用户信息方法
CustInfoService custInfoService = SpringBeanFactory.getBean(CustInfoServiceImpl.class);
info = custInfoService.queryCustInfo(username);
//Authentication是shiro包中的配置类
session.setAttribute(Authentication.USER_SESSION_NAME, info);
session.setAttribute(Authentication.USER_ID_SESSION_NAME, info.getCustNo());
}
}
}
用途:对整个服务流程进行监听
(1)ApplicationEventListener.java
package com.ejsino.xxx.config.listener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
import org.springframework.boot.context.event.ApplicationPreparedEvent;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.ContextStartedEvent;
import org.springframework.context.event.ContextStoppedEvent;
import org.springframework.stereotype.Component;
@Component
public class ApplicationEventListener implements ApplicationListener<ApplicationEvent> {
private Logger log = LoggerFactory.getLogger(getClass());
@Override
public void onApplicationEvent(ApplicationEvent event) {
// 监听SpringBoot生命周期
if (event instanceof ApplicationEnvironmentPreparedEvent) {
log.debug("初始化环境变量");
} else if (event instanceof ApplicationPreparedEvent) {
log.info("初始化环境变量完成");
} else if (event instanceof ContextRefreshedEvent) {
log.debug("应用刷新");
} else if (event instanceof ApplicationReadyEvent) {
log.info("应用已启动完成");
} else if (event instanceof ContextStartedEvent) {
log.debug("应用启动");
} else if (event instanceof ContextStoppedEvent) {
log.debug("应用停止");
} else if (event instanceof ContextClosedEvent) {
log.debug("应用关闭");
} else {
}
}
}
用途:结合Shiro对请求的合理性进行过滤(是否超时,即session是否失效)
(1)ShiroLoginFilter.java
//避免Ajax超时的情况下向前端返回登录页面的内容,普通请求超时,返回登录页面是跳转登陆
package com.ejsino.xxx.config.filter;
import com.ejsino.ejcomment.config.domain.JSONResult;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
/**
* @author charles
* @description shiro AdviceFilter
* @date 20180329
*/
public class ShiroLoginFilter extends FormAuthenticationFilter {
private static Logger log = LoggerFactory.getLogger(ShiroLoginFilter.class);
@Override
protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
if (isLoginRequest(servletRequest, servletResponse)) {
if (isLoginSubmission(servletRequest, servletResponse)) {
if (log.isTraceEnabled()) {
log.trace("Login submission detected. Attempting to execute login.");
}
return executeLogin(servletRequest, servletResponse);
} else {
if (log.isTraceEnabled()) {
log.trace("Login page view.");
}
return true;
}
} else {
if (log.isTraceEnabled()) {
log.trace("Attempting to access a path which requires authentication. Forwarding to the " +
"Authentication url [" + getLoginUrl() + "]");
}
String requestedWith = request.getHeader("x-requested-with");
String requestedAcpt = request.getHeader("accept");
if (StringUtils.isNotEmpty(requestedWith) && StringUtils.equals(requestedWith, "XMLHttpRequest")) {
((HttpServletResponse) servletResponse).setStatus(402);
returnJson(response, JSONResult.toJSONObject(JSONResult.error("timeout", "未登录或已超时,请重新登录!")).toString());
} else if (StringUtils.isNotEmpty(requestedAcpt) && StringUtils.equals(requestedAcpt, "*/*")) {
((HttpServletResponse) servletResponse).setStatus(402);
returnJson(response, JSONResult.toJSONObject(JSONResult.error("timeout", "未登录或已超时,请重新登录!")).toString());
} else {
saveRequestAndRedirectToLogin(servletRequest, servletResponse);
}
return false;
}
}
private void returnJson(HttpServletResponse response, String json) throws Exception {
PrintWriter writer = null;
response.setCharacterEncoding("UTF-8");
response.setContentType("text/html; charset=utf-8");
try {
writer = response.getWriter();
writer.print(json);
} catch (IOException e) {
log.error("response error", e);
} finally {
if (writer != null) {
writer.close();
}
}
}
}
用途:对一些常见异常的封装和处理
(1)DaoException.java(数据库层异常)
package com.ejsino.ejcomment.config.exception;
/**
* @description DaoException
* @author charles
* @date 20171102
*/
public class DaoException extends Exception {
/*** serial id */
private static final long serialVersionUID = 1L;
/**
*
* 空构造
*/
public DaoException() {
super("Dao 异常");
}
/**
*
* 自定义错误日志
*
* @param e
*/
public DaoException(String e) {
super(e);
}
/**
* 只抛错误信息
*
* @param e
*/
public DaoException(Throwable e) {
super(e);
}
/**
* 两者皆抛
*
* @param er
* @param e
*/
public DaoException(String er, Throwable e) {
super(er, e);
}
}
(2)EhomeException.java(全局的异常处理)
package com.ejsino.xxx.config.exception;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintStream;
/**
* @description EhomeException
* @author charles
* @date 20171102
*/
public class EhomeException extends Exception {
private static final long serialVersionUID = 1L;
private int exceptionCode = 0;
private String errorCode="";
private String errorMessage="";
public EhomeException(String errMsg) {
super(errMsg);
}
public EhomeException(Throwable cause) {
super(cause);
}
public EhomeException(String errMsg, int exceptionCode) {
super(errMsg);
this.exceptionCode = exceptionCode;
}
public int getExceptionCode() {
return this.exceptionCode;
}
public void setExceptionCode(int exceptionCode) {
this.exceptionCode = exceptionCode;
}
public String getErrorCode() {
return errorCode;
}
public void setErrorCode(String errorCode) {
this.errorCode = errorCode;
}
public String getErrorMessage() {
return errorMessage;
}
public void setErrorMessage(String errorMessage) {
this.errorMessage = errorMessage;
}
public String getDetailMessage() {
ByteArrayOutputStream ostr = new ByteArrayOutputStream();
super.printStackTrace(new PrintStream(ostr));
try {
ostr.flush();
ostr.close();
return ostr.toString();
} catch (IOException ex) {
return super.getMessage();
}
}
}
(3)GlobalExceptionHandler.java(全局的异常处理)
@ExceptionHandler(value = Exception.class )
@ResponseStatus(HttpStatus.UNAUTHORIZED)
public ModelAndView processUnauthenticatedException(HttpServletRequest request, Exception e) throws Exception {
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("exception", e);
modelAndView.addObject("url", request.getRequestURL());
modelAndView.setViewName("error");
log.error("got exception: {}", e.getClass() + ":" + e.getMessage());
return modelAndView;
}
(4)TransactionRuntimeException.java(事务回滚异常处理)
private static final long serialVersionUID = 1L;
public TransactionRuntimeException(String msg) {
super(msg);
}
public TransactionRuntimeException(String msg, Throwable cause) {
super(msg, cause);
}
(1)AppConfigurer.java(Spring事物线程池等相关配置)
package com.ejsino.xxx.config.application;
import com.ejsino.ejcomment.config.intercepter.UserSessionInterceptor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import javax.sql.DataSource;
/**
* @author charles
* @version V1.0
* @Title:
* @Package
* @Description: application configuration
* @date 20171117
*/
@Configuration
public class AppConfigurer extends WebMvcConfigurerAdapter {
@Value("${sysParam.encryptKey}")
public String secretKey;
@Override
public void addInterceptors(InterceptorRegistry registry) {
/**拦截用户的session*/
registry.addInterceptor(new UserSessionInterceptor())
.addPathPatterns("/*")
.addPathPatterns("/**.html")
.addPathPatterns("/**/**.html");
/**拦截器配置菜单*/
}
@Bean(name = "transactionManager")
public PlatformTransactionManager getTransactionManager(DataSource dataSource) {
PlatformTransactionManager transactionManager = new DataSourceTransactionManager(dataSource);
return transactionManager;
}
@Bean(name = "transactionTemplate")
public TransactionTemplate getTransactionTemplate(PlatformTransactionManager transactionManager) {
TransactionTemplate transactionTemplate = new TransactionTemplate();
transactionTemplate.setTransactionManager(transactionManager);
return transactionTemplate;
}
@Bean(name = "taskExecutor")
public ThreadPoolTaskExecutor getTaskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(15);
executor.setQueueCapacity(35);
return executor;
}
@Bean(name = "taskScheduler")
public ThreadPoolTaskScheduler taskScheduler() {
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setPoolSize(20);
scheduler.setThreadNamePrefix("task-");
scheduler.setAwaitTerminationSeconds(60);
scheduler.setWaitForTasksToCompleteOnShutdown(true);
return scheduler;
}
@Bean(name = "uploadPoolExecutor")
public ThreadPoolTaskExecutor getUploadPoolExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(15);
executor.setMaxPoolSize(50);
executor.setQueueCapacity(200);
return executor;
}
}
(2)SpringBean.java(SpringBean工厂)
package com.ejsino.xxx.config.application;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import java.beans.Introspector;
@Component
public class SpringBeanFactory implements ApplicationContextAware {
private static ApplicationContext context;
public SpringBeanFactory() {
}
@Override
public void setApplicationContext(ApplicationContext context) throws BeansException {
SpringBeanFactory.context = context;
}
@SuppressWarnings("unchecked")
public static <T> T getBean(String name, Class<T> clazz) {
return (T) getBean(name);
}
public static <T> T getBean(Class<T> clazz) {
return getBean(Introspector.decapitalize(clazz.getSimpleName()), clazz);
}
public static Object getBean(String name) {
return context.getBean(name);
}
}
=集合shiro框架做登陆成功后的跳转。
package com.ejsino.xxx.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* @author Paulo.Yuan
* @version V1.0
* @Title:
* @Package
* @Description: index controller
* @date 20171103
*/
@Controller
public class IndexController {
@RequestMapping("/")
private String mainpage(){
return "index";
}
@RequestMapping("/index.html")
private String toMainpage(){
return "index";
}
}
import java.util.HashMap;
import java.util.Map;
import net.sf.json.JSONObject;
/**
* @Description: 返回前端json对象
* 前端同步、异步请求后端数据,返回前端JSONResult对象
* 返回类型JSONResult或者String(使用自带方法将对象手动转String)类型
* 返回字符串,装入params,设置状态true,返回消息,和param
* 返回单个对象,装入obj
* List roleList = roleService.queryAllRole(role);
* return JSONResult.success("success", roleList);
* 返回多个对象,装入objects
* Map objects = new HashMap<>();
* List roleList = roleService.queryAllRole(role);
* List companyList = * companyService.queryCompanyList(company);
* objects.put("roleList", roleList);
* objects.put("companyList", companyList);
* return JSONResult.success("success", objects);
* 返回失败,必须置入失败消息
* return JSONResult.error("错误信息");
* @author Paulo.Yuan
* @version V1.0
* @date 20171109
*/
public class JSONResult {
/**返回状态*/
private boolean success = false;
/**返回消息*/
private String message;
/**返回参数(近字符串)*/
private String params;
/**返回对象(单个)*/
private Object obj;
/**返回对象(多个)*/
private Map<String, Object> objects = new HashMap<String, Object>();
public JSONResult() {
}
public JSONResult(boolean success, String message) {
this.success = success;
this.message = message;
}
public JSONResult(boolean success, Object obj, String message) {
this(success, message);
this.obj = obj;
}
public JSONResult(boolean success, String params, String message) {
this(success, message);
this.params = params;
}
public static JSONResult success(String message) {
return new JSONResult(true, message);
}
public static JSONResult success(String message, Object object) {
return new JSONResult(true, object, message);
}
public static JSONResult success(String message, Object... object) {
return new JSONResult(true, object, message);
}
public static JSONResult error(String message) {
return new JSONResult(false, message);
}
public static JSONResult error(String params, String message) {
return new JSONResult(false, params, message);
}
public static JSONResult error(String message, Object object) {
return new JSONResult(false, object, message);
}
/**
* @description To JSON Object
* @param json
* @return
*/
public static JSONObject toJSONObject(JSONResult json) {
return JSONObject.fromObject(json);
}
}
需要导入jar包
<dependency>
<groupId>net.sf.json-lib</groupId>
<artifactId>json-lib</artifactId>
<version>2.4</version>
<classifier>jdk15</classifier><!--指定jdk版本-->
</dependency>
package com.ejsino.xxx.config.domain;
import java.io.Serializable;
import java.util.Map;
/**
* @description 返回结果
* @author charles
* @date 20171101
*/
public interface Result extends Serializable {
String SUCCESS = "success";
/**
* 请求是否成功。
*
* @return 如果成功,则返回true
*/
boolean isSuccess();
/**
* 设置请求成功标志。
*
* @param success
* 成功标志
*/
void setSuccess(boolean success);
/**
* 获取返回码
*
* @return 返回码
*/
String getResultCode();
/**
* 设置返回码
*
* @param code
*/
void setResultCode(String code);
/**
* 取得model对象
*
* @param key
* 字符串key
* @return model对象
*/
Object getModel(String key);
/**
* 设置model对象。
*
* @param key
* 字符串key
* @param model
* model对象
*/
void setModel(String key, Object model);
/**
* 设置model对象。
*
* @param key
* 字符串key
* @param model
* model对象
*/
void setModel(Class<?> clazz, Object model);
/**
* 取得所有model对象。
*
* @return model对象表
*/
Map<String, Object> getModels();
/**
*
* 获取特定类型的 model 对象
*
*
* @param
* @param key
* 字符串 key
* @param clazz
* 数据类型
* @return
*/
<T> T getModel(String key, Class<T> clazz);
<T> T getModel(Class<T> clazz);
}
(所在包:config.domain)
package com.ejsino.claim.config.domain;
import java.util.HashMap;
import java.util.Map;
/**
* @author YDLiang
* @date 20171102
*/
public class ResultSupport implements Result, java.io.Serializable {
private static final long serialVersionUID = -5427837161273573297L;
private boolean success = false;
private String resultCode;
private Map<String, Object> models = new HashMap<String, Object>(4);
public ResultSupport() {
}
public ResultSupport(boolean success) {
this.success = success;
}
public Object getModel(String key) {
return getModels().get(key);
}
public Map<String, Object> getModels() {
return models;
}
public String getResultCode() {
return resultCode;
}
public boolean isSuccess() {
return success;
}
public void setModel(String key, Object model) {
getModels().put(key, model);
}
public void setModel(Class<?> clazz, Object model) {
getModels().put(clazz.getSimpleName(), model);
}
public void setResultCode(String resultCode) {
this.resultCode = resultCode;
}
public void setSuccess(boolean success) {
this.success = success;
}
public void setModels(Map<String, Object> models) {
this.models = models;
}
@SuppressWarnings("unchecked")
@Override
public <T> T getModel(String key, Class<T> clazz) {
return (T) getModel(key);
}
@SuppressWarnings("unchecked")
@Override
public <T> T getModel(Class<T> clazz) {
return (T) getModel(clazz.getSimpleName());
}
}
public enum MyConst {
DOM_NODE_TYPE_STRING("数据库读取数据方式", "string"),
DOM_NODE_TYPE_DATE("数据库读取数据方式", "date"),
DOM_NODE_TYPE_TIME("数据库读取数据方式", "time"),
DOM_NODE_TYPE_FLOAT("数据库读取数据方式", "float"),
。
。
。
DAYMINHOUR("起始时间", "00:00:00"),
DAYMAXHOUR("终止时间", "23:59:59");
}
(所在包:config.pub)
package com.ejsino.xxx.config.pub;
public enum SerNoConst {
SERIAL_TYPE_ONLINE_MESSAGE("消息详情表序号","ME",10),
SERIAL_TYPE_ONLINE_USERROLE("用户表序号","UR",10),
。
。
。
SERIAL_TYPE_CLAIM_ORGAN_NO("机构代码","JG",5),
SERIAL_TYPE_GROUP_COMPANYNO("合并后保险公司编号","CG",3);
private String name ;
private String value ;
private Integer length;
private SerNoConst( String name , String value, Integer length){
this.name = name ;
this.value = value ;
this.length = length;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public Integer getLength() {
return length;
}
public void setLength(Integer length) {
this.length = length;
}
}
(所在包:config.pub)
package com.ejsino.xxx.config.pub;
import com.ejsino.xxx.config.exception.EhomeException;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Pattern;
/**
* Created by cohenkl on 2018/1/8.
*/
public class DateConv {
public static void main(String args[]) {
try {
System.out.println(DateConv.formatDate("19860504", "yyyy-MM-dd HH:mm:ss"));
} catch (Exception ex) {
ex.printStackTrace();
System.out.println("……游艺场222……………………" + ex.getMessage());
}
}
private static String errMsg = null;
private DateConv() {
TimeZone.setDefault(TimeZone.getTimeZone("GMT+8"));//夏令时问题
}
private static DateConv obj = new DateConv();
public static DateConv instance() {
return obj;
}
public static String format(String srcString, String filterChar, String strFormat) throws EhomeException {
if (srcString == null || srcString.equals("")) {
return "";
}
String retStr = null;
try {
String newString = srcString.replaceAll("/", "").replaceAll("-", "");
if (filterChar != null && !filterChar.equals("")) {
newString = newString.replaceAll(filterChar, "");
}
String tmpFormat = "yyyyMMdd";
if (newString.length() > 8) {
tmpFormat = "yyyyMMdd HH:mm:ss";
}
DateFormat df = new SimpleDateFormat(tmpFormat);
Date retDate = df.parse(newString);
retStr = new SimpleDateFormat(strFormat).format(retDate);
} catch (ParseException ex) {
throw new EhomeException(ex.getMessage());
}
return retStr;
}
public static String DateIntToString(int iDate) {
iDate -= 1;
if (iDate < 1) {
return ("18991231");
}
int iCnt = 0;
int iYear = 0;
int iMonth = 0;
int iDay = 0;
int curdays = 0;
int passdays = 0;
int i = 0;
while (true) {
if (i * 365 + iCnt >= iDate) {
break;
}
iYear = i;
curdays = iDate - (i * 365) - iCnt;
if (((i + 1900) % 400 == 0) || ((i + 1900) % 4 == 0 && (i + 1900) % 100 != 0)) {
iCnt++;
}
i++;
}
for (i = 1; i <= 12; i++) {
iDay = curdays - passdays;
if (i == 1 || i == 3 || i == 5 || i == 7 || i == 8 || i == 10 || i == 12) {
passdays += 31;
} else if (i == 4 || i == 6 || i == 9 || i == 11) {
passdays += 30;
} else if (((iYear + 1900) % 400 == 0) || ((iYear + 1900) % 4 == 0 && (iYear + 1900) % 100 != 0)) {
passdays += 29;
} else {
passdays += 28;
}
iMonth = i;
if (passdays >= curdays) {
break;
}
}
iYear += 1900;
StringBuffer outstr = new StringBuffer();
outstr.append(iYear);
Integer iTmp = new Integer(iMonth);
String strMonth = iTmp.toString();
if (strMonth.length() < 2) {
outstr.append("0");
outstr.append(strMonth);
} else {
outstr.append(strMonth);
}
iTmp = new Integer(iDay);
String strDay = iTmp.toString();
if (strDay.length() < 2) {
outstr.append("0");
outstr.append(strDay);
} else {
outstr.append(strDay);
}
return outstr.toString();
}
public static String DateStringConv(String strDate) {
if (strDate == null || strDate.equals("")) {
return "";
}
String strRet = new String(strDate);
return strRet.replaceAll("/", "").replaceAll("-", "");
}
public static String DateTimeToDate(String strDate) {
if (strDate == null || strDate.equals("")) {
return "";
}
String strRet = strDate.replaceAll("/", "").replaceAll("-", "");
return strRet.substring(0, 8);
}
public static int DateStringToInt(String strDate) {
String strTmpDate = new String(strDate);
if (strTmpDate.length() != 8) {
errMsg = strDate + " 日期数据不正确,正确格式[YYYYMMDD]";
return (-1);
}
String strYear = strTmpDate.substring(0, 4);
String strMonth = strTmpDate.substring(4, 6);
String strDay = strTmpDate.substring(6);
int iYear = Integer.parseInt(strYear);
int iMonth = Integer.parseInt(strMonth);
int iDay = Integer.parseInt(strDay);
if (iYear < 1900 || iYear >= 2900) {
errMsg = "日期的年份不正确";
return (-1);
}
if (iMonth < 1 || iMonth > 12) {
errMsg = "日期的月份应在0-12之间";
return (-1);
}
if (iMonth == 1 || iMonth == 3 || iMonth == 5 || iMonth == 7 || iMonth == 8 || iMonth == 10 || iMonth == 12) {
if (iDay < 1 || iDay > 31) {
errMsg = "日期天数不正确";
return (-1);
}
} else if (iMonth == 2) {
// 闰年
if ((iYear % 400 == 0) || (iYear % 4 == 0 && iYear % 100 != 0)) {
if (iDay < 1 || iDay > 29) {
errMsg = "日期天数不正确";
return (-1);
}
} else {
if (iDay < 1 || iDay > 28) {
errMsg = "日期天数不正确";
return (-1);
}
}
} else {
if (iDay < 1 || iDay > 30) {
errMsg = "日期天数不正确";
return (-1);
}
}
int iDate = iDay;
for (int i = 0; i < iMonth; i++) {
switch (i) {
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12: {
iDate += 31;
break;
}
case 4:
case 6:
case 9:
case 11: {
iDate += 30;
break;
}
case 2: {
// 闰年
if ((iYear % 400 == 0) || (iYear % 4 == 0 && iYear % 100 != 0)) {
iDate += 29;
} else {
iDate += 28;
}
break;
}
} // switch
} // for iMonth
for (int i = 0; i < iYear - 1900; i++) {
if (((i + 1900) % 400 == 0) || ((i + 1900) % 4 == 0 && (i + 1900) % 100 != 0)) {
iDate += 366;
} else {
iDate += 365;
}
}
iDate += 1;
return ((int) iDate);
}
public static String TimeToStrTime(long localtime, String strFormat) {
SimpleDateFormat formatter = null;
if (strFormat != null && strFormat.indexOf("-") >= 0) {
formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
} else {
formatter = new SimpleDateFormat("yyyyMMdd HH:mm:ss");
}
Date myDate = new Date();
myDate.setTime(localtime);
String outDate = formatter.format(myDate);
return outDate;
}
// 校验时间或日期格式 timeOrDate==true yyyyMMdd HH:mm:ss false:yyyyMMdd
public static boolean checkDateFormat(String str, boolean bTimeOrDate) {
try {
if (bTimeOrDate) {
checkDataTimt(str);
} else {
if (!Pattern.compile("^\\d{8}$").matcher(str).matches()) {
return false;
}
String format = "yyyyMMdd";
SimpleDateFormat formatter = new SimpleDateFormat(format);
formatter.setLenient(false);// 严格规定时间格式
formatter.parse(str);
}
} catch (EhomeException io) {
return false;
} catch (ParseException io) {
return false;
}
return true;
}
public static Date StrTimeToDate(String strTime) throws EhomeException {
// strTime不足时分秒的部分,需先补齐时分秒" HH:mm:ss",再调用此方法
// 时间串格式:"yyyyMMdd HH:mm:ss"
String tmpTime = DateStringConv(strTime);
SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMdd HH:mm:ss");
Date myDate = null;
try {
// formatter.setLenient(false);// 严格规定时间格式
checkDataTimt(tmpTime);
myDate = formatter.parse(tmpTime);
} catch (EhomeException io) {
throw new EhomeException(io.getMessage());
} catch (ParseException io) {
throw new EhomeException("格式化日期时间字符串到time型失败:" + strTime);
}
return myDate;
}
/**
* 功能:获取工作日批改时间;
* @return
* @throws EhomeException
*/
/* public static boolean checkNotWorkDay() throws EhomeException {
Calendar c = Calendar.getInstance();
return checkHoliday(c);
}*/
public static void checkDataTimt(String strDateTime) throws EhomeException {
if(strDateTime.length() == 20 && strDateTime.lastIndexOf(":00")==17){
strDateTime=strDateTime.substring(0,17);
}//特殊处理, 接口用户中有客户传输航班时间没有按要求传输(yyyy-MM-dd HH:mm)。以后逐步改掉
if (strDateTime.length() != 17) {
throw new EhomeException("日期格式错误:" + strDateTime);
} else {
String strDate = strDateTime.substring(0, 8);
String strTime = strDateTime.substring(9);
SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMdd");
try {
formatter.setLenient(false);// 严格规定日期格式
formatter.parse(strDate);
} catch (ParseException io) {
throw new EhomeException("格式化日期时间字符串到time型失败:" + strTime);
}
if (strTime.indexOf(":") < 0) {
throw new EhomeException("日期格式错误:" + strDateTime);
} else {
String strHour = strTime.substring(0, strTime.indexOf(":"));
String strMin = strTime.substring(strTime.indexOf(":") + 1, strTime.lastIndexOf(":"));
String strSecond = strTime.substring(strTime.lastIndexOf(":") + 1);
if (strHour.length()!=2 || Integer.parseInt(strHour) > 23 || strMin.length()!=2 || Integer.parseInt(strMin) > 59 || strSecond.length()!=2 || Integer.parseInt(strSecond) > 59) {
throw new EhomeException("日期格式错误:" + strDateTime);
}
}
}
}
public static long StrTimeToTime(String strTime) throws EhomeException {
return StrTimeToDate(strTime).getTime();
}
// 日期时间加减相应的秒数得到对应的日期时间串,天、时、分都换算成秒传入secs参数
public static String dateTimeAdded(String strTime, int secs) throws EhomeException {
// 例子:dateTimeAdded("20091231 00:00:00", 24*3600);加一天
long time = StrTimeToTime(strTime);
String newtime = TimeToStrTime(time + (long) secs * 1000, "-");
return newtime;
}
// 日期时间加减月数得到对应的日期时间串
public static String dateTimeMonthAdded(String strTime, int months, boolean bFlag) throws EhomeException {
// bFlag:return结果为对应秒的前1秒 !bFlag:return结果为对应秒
StrTimeToDate(strTime);// 校验入参的格式合法性
String dateStr = strTime.substring(0, 8);
String timeStr = strTime.substring(9);
int iNewDate = IncMonth(DateStringToInt(dateStr), months);
String newDateTime = DateIntToString(iNewDate) + " " + timeStr;
if (bFlag) {
return dateTimeAdded(newDateTime, -1).replaceAll("-", "");
} else {
return newDateTime;
}
}
public static String dateTimeAddedByType(String strDateTime, int iNum, int dType, boolean bFlag) throws EhomeException {
if (dType == 1) {
// 加天数
int secs = iNum * 24 * 3600;
if (bFlag) {
secs -= 1;
}
return dateTimeAdded(strDateTime, secs);
} else if (dType == 2) {
// 加月数
return dateTimeMonthAdded(strDateTime, iNum, bFlag);
} else {
throw new EhomeException("[service]加减日期计算方法调用有误");
}
}
public static String IncDay(String strStartDate, int iNum) {
int iDate = DateStringToInt(strStartDate);
if (iDate < 0) {
errMsg = "日期数据不正确,正确格式[YYYYMMDD]";
return ("18991231");
}
iDate = iDate + iNum;
String strDate = DateIntToString(iDate);
if (strDate.equals("")) {
return ("18991231");
}
return (strDate);
}
public static String getNextSysDate(int nextday) {
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.DATE, nextday);
return (new SimpleDateFormat("yyyy-MM-dd").format(calendar.getTime()));
}
public static String getErrMsg() {
return errMsg;
}
public static String getCurrSysTime() {
return (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
}
public static String getSysTime() {
return (new SimpleDateFormat("yyyyMMdd HH:mm:ss").format(new Date()));
}
public static String getSysDate() {
return (new SimpleDateFormat("yyyyMMdd").format(new Date()));
}
public static String getCurrSysDate() {
return (new SimpleDateFormat("yyyy-MM-dd").format(new Date()));
}
public static String getCharisCurrSysDate() {
Locale locale = new Locale("en", "US");
DateFormat format = DateFormat.getDateInstance(DateFormat.MONTH_FIELD, locale);
String value = format.format(new Date()).replaceAll(",", "").toUpperCase();
String[] values = value.split(" ");
if (values[1].length() == 1) {
values[1] = "0" + values[1];
}
return values[1] + " " + values[0] + " " + values[2];
// return (new SimpleDateFormat("MM/dd/yyyy").format(new Date()));
}
public static String formatDateTime(long dateTime, String format) {
return (new SimpleDateFormat(format).format(dateTime));
}
public static String formatDateDate(Date dateTime, String format) {
return (new SimpleDateFormat(format).format(dateTime));
}
// 根据保险的期限,加对应月后得到对应的月数对应的一天,即对应月的前一天
public static String incMonthGetRelateDay(String startDate, int iNum) {
return DateIntToString(IncMonth(DateStringToInt(startDate), iNum) - 1);
}
public static String IncMonth(String startDate, int iNum) {
return DateIntToString(IncMonth(DateStringToInt(startDate), iNum));
}
public static int IncMonth(int iStartDate, int iNum) {
String strDate = DateIntToString(iStartDate);
if (strDate.equals(""))
return (-1);
String strYear = strDate.substring(0, 4);
String strMonth = strDate.substring(4, 6);
String strDay = strDate.substring(6);
int iYear = Integer.parseInt(strYear);
int iMonth = Integer.parseInt(strMonth);
int iDay = Integer.parseInt(strDay);
iMonth += iNum;
while (iMonth > 12) {
iMonth -= 12;
iYear += 1;
}
while (iMonth <= 0) {
iMonth += 12;
iYear -= 1;
}
int iDate = 0;
int iCnt = 0;
while (true) {
strMonth = String.valueOf(iMonth);
if (strMonth.length() < 2) {
strMonth = "0" + strMonth;
}
strDay = String.valueOf(iDay);
if (strDay.length() < 2) {
strDay = '0' + strDay;
}
String strTmp = iYear + strMonth + strDay;
iDate = DateStringToInt(strTmp);
if (iDate >= 0)
break;
iDay -= 1;
if (iCnt > 3) {
errMsg = "日期加月数计算对应的日期出错";
return (-1);
}
iCnt++;
}
return (iDate);
}
// 根据dGetType计算两个日期时间型之间的间隔年、月、周、天数
public static int calDateTimeSpace(String startDateTime, String endDateTime, int dGetType, boolean bFlag) throws EhomeException {
if (startDateTime == null || endDateTime == null || startDateTime.equals("") || endDateTime.equals("")) {
throw new EhomeException("计算日期差的入参都不能为空");
}
// bFlag:不满一天算一天 !bFlag:满一天了才算一天 同理:年、月、周也如此
// dGetType:1年 2月 3周 4天
switch (dGetType) {
case (1): {
// 年
int iMonth = calDateTimeSpace(startDateTime, endDateTime, 2, bFlag);
if (bFlag) {
return (int) (iMonth + 11) / 12;
} else {
return (int) iMonth / 12;
}
}
case (2): {
// 月
int iNum = 0;
if (bFlag) {
while (true) {
String tmpDate = dateTimeMonthAdded(startDateTime, iNum, true);
if (tmpDate.compareTo(DateStringConv(endDateTime)) >= 0)
break;
iNum++;
}
return iNum;
} else {
while (true) {
String tmpDate = dateTimeMonthAdded(startDateTime, iNum + 1, true);
if (tmpDate.compareTo(DateStringConv(endDateTime)) > 0)
break;
iNum++;
}
return iNum;
}
}
case (3): {
// 周
int days = calDateTimeSpace(startDateTime, endDateTime, 4, bFlag);
if (bFlag) {
return (int) (days + 6) / 7;
} else {
return (int) days / 7;
}
}
case (4): {
// 天
double days = (double) (StrTimeToTime(endDateTime) - StrTimeToTime(startDateTime) + 1000) / (24 * 3600 * 1000);
if (bFlag) {
return (int) Math.ceil(days);
} else {
return (int) Math.floor(days);
}
}
default:
throw new EhomeException("server日期时间计算类型参数不匹配");
}
}
// 计算日期之间的间隔数
public static int GetDateSpace(int dCalTimeType, String strStartDate, String strEndDate, int dGetType, boolean bFlag) {
return GetDateSpace(dCalTimeType, DateStringToInt(strStartDate), DateStringToInt(strEndDate), dGetType, bFlag);
}
// dCalTimeType:: 1 超过才算满期 2 一样才算满期 3 前一天就算满期
public static int GetDateSpace(int dCalTimeType, int iStartDate, int iEndDate, int dGetType, boolean bFlag) {
if (iStartDate == 0)
return 0;
if (dCalTimeType == 1) {
iEndDate -= 2;
} else if (dCalTimeType == 2) {
iEndDate -= 1;
} else if (dCalTimeType != 3) {
return -2;
}
int intMonth = 0;
if (bFlag) { // 不满一年算一年
if (dGetType == 1) { // 取年数
// 不足一个月算一个月;
while (true) {
int tmpdate = IncMonth(iStartDate, intMonth) - 1;
if (tmpdate == -2) {
return -1;
}
if (iEndDate <= tmpdate)
break;
intMonth++;
}
// 不足一个年算一年;
if ((intMonth % 12) == 0)
return (intMonth / 12); // 整年
else
return (intMonth / 12 + 1); // 整年;
} else if (dGetType == 2) { // 取月数
// 不足一个月算一个月;
while (true) {
int tmpdate = IncMonth(iStartDate, intMonth) - 1;
if (tmpdate == -2) {
return -1;
}
if (iEndDate <= tmpdate)
break;
intMonth++;
}
return intMonth;
} else if (dGetType == 3) { // 取星期数
int iDays = iEndDate - iStartDate + 1;
return (int) (iDays + 6) / 7;
} else if (dGetType == 4) { // 取天数
int iDays = iEndDate - iStartDate + 1;
return iDays;
}
} else {
if (dGetType == 1) {
// 满一个月才算一个月;
while (true) {
int tmpdate = IncMonth(iStartDate, intMonth + 1) - 1;
if (tmpdate == -2) {
return -1;
}
if (iEndDate < tmpdate)
break;
intMonth++;
}
// 满一个年才算一年;
if ((intMonth % 12) == 0)
return ((int) intMonth / 12); // 整年
else
return ((int) intMonth / 12); // 整年
} else if (dGetType == 2) {
// 满一个月才算一个月;
while (true) {
int tmpdate = IncMonth(iStartDate, intMonth + 1) - 1;
if (tmpdate == -2) {
return -1;
}
if (iEndDate < tmpdate)
break;
intMonth++;
}
return intMonth;
} else if (dGetType == 3) { // 取星期数,满一个星期才算一个星期
int iDays = iEndDate - iStartDate + 1;
return (int) iDays / 7;
} else if (dGetType == 4) { // 取天数,满一天才算一天
int iDays = iEndDate - iStartDate;
return iDays;
}
}
return -1;
}
public static String getMonthFirstDay() {
String today = new SimpleDateFormat("yyyyMMdd").format(new Date());
return today.substring(0, 6) + "01";
}
public static String getMonthLastDay() {
String today = new SimpleDateFormat("yyyyMMdd").format(new Date());
String year = today.substring(0, 4);
String month = today.substring(4, 6);
String day = "31";
if (month.equals("02")) {
int iYear = Integer.parseInt(year);
if (iYear % 400 == 0 || (iYear % 4 == 0 && iYear % 100 != 0)) {
day = "29";
} else {
day = "28";
}
} else if (month.equals("04") || month.equals("06") || month.equals("09") || month.equals("11")) {
day = "30";
}
return year + month + day;
}
public static String getLastMonthLastDay() {
Calendar calendar = Calendar.getInstance();
int day=calendar.get(Calendar.DATE);
calendar.add(Calendar.DATE, -day);
return (new SimpleDateFormat("yyyy-MM-dd").format(calendar.getTime()));
}
public static String getTimeOfDateTime(String strDateTime){
String strTime = "";
if(strDateTime.length()>8 && strDateTime.lastIndexOf(":")>8){
String newString = strDateTime.replaceAll("/", "").replaceAll("-", "");
strTime = newString.substring(9);
}
return strTime;
}
//通用日期或时间各种格式的格式化,返回dateFormat约定的格式,如yyyyMMdd HH:mm:ss //added by zhaohaibin on 2016-12-15
public static String formatDate(String dateStr, String dateFormat) throws EhomeException {
if (dateStr == null || dateStr.equals("")) {
return "";
}
HashMap<String, String> dateRegFormat = new HashMap<String, String>();
dateRegFormat.put(
"^\\d{4}\\D+\\d{1,2}\\D+\\d{1,2}\\D+\\d{1,2}\\D+\\d{1,2}\\D+\\d{1,2}\\D*$",
"yyyy-MM-dd-HH-mm-ss");//2014年3月12日 13时5分34秒,2014-03-12 12:05:34,2014/3/12 12:5:34
dateRegFormat.put("^\\d{4}\\D+\\d{1,2}\\D+\\d{1,2}\\D+\\d{1,2}\\D+\\d{1,2}$",
"yyyy-MM-dd-HH-mm");//2014-03-12 12:05
dateRegFormat.put("^\\d{4}\\D+\\d{1,2}\\D+\\d{1,2}\\D+\\d{1,2}$",
"yyyy-MM-dd-HH");//2014-03-12 12
dateRegFormat.put("^\\d{4}\\D+\\d{1,2}\\D+\\d{1,2}$", "yyyy-MM-dd");//2014-03-12
dateRegFormat.put("^\\d{4}\\D+\\d{1,2}$", "yyyy-MM");//2014-03
dateRegFormat.put("^\\d{4}$", "yyyy");//2014
dateRegFormat.put("^\\d{14}$", "yyyyMMddHHmmss");//20140312120534
dateRegFormat.put("^\\d{12}$", "yyyyMMddHHmm");//201403121205
dateRegFormat.put("^\\d{10}$", "yyyyMMddHH");//2014031212
dateRegFormat.put("^\\d{8}$", "yyyyMMdd");//20140312
dateRegFormat.put("^\\d{6}$", "yyyyMM");//201403
dateRegFormat.put("^\\d{8}\\D+\\d{1,2}\\D+\\d{1,2}\\D+\\d{1,2}$", "yyyyMMdd-HH-mm-ss");//20140312 12:05:34
dateRegFormat.put("^\\d{8}\\D+\\d{1,2}\\D+\\d{1,2}$", "yyyyMMdd-HH-mm");//20140312 12:05
dateRegFormat.put("^\\d{8}\\D+\\d{1,2}$", "yyyyMMdd-HH");//20140312 12
dateRegFormat.put("^\\d{1,2}\\s*:\\s*\\d{1,2}\\s*:\\s*\\d{1,2}$",
"yyyy-MM-dd-HH-mm-ss");//13:05:34 拼接当前日期
dateRegFormat.put("^\\d{1,2}\\s*:\\s*\\d{1,2}$", "yyyy-MM-dd-HH-mm");//13:05 拼接当前日期
dateRegFormat.put("^\\d{2}\\D+\\d{1,2}\\D+\\d{1,2}$", "yy-MM-dd");//14.10.18(年.月.日)
dateRegFormat.put("^\\d{1,2}\\D+\\d{1,2}$", "yyyy-dd-MM");//30.12(日.月) 拼接当前年份
dateRegFormat.put("^\\d{1,2}\\D+\\d{1,2}\\D+\\d{4}$", "dd-MM-yyyy");//12.21.2013(日.月.年)
DateFormat formatter1 = new SimpleDateFormat(dateFormat);
DateFormat formatter2 = null;
String dateReplace = dateStr;
String strSuccess = "";
try {
for (String key : dateRegFormat.keySet()) {
if (Pattern.compile(key).matcher(dateStr).matches()) {
formatter2 = new SimpleDateFormat(dateRegFormat.get(key));
String curDate = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
if (key.equals("^\\d{1,2}\\s*:\\s*\\d{1,2}\\s*:\\s*\\d{1,2}$")
|| key.equals("^\\d{1,2}\\s*:\\s*\\d{1,2}$")) {//13:05:34 或 13:05 拼接当前日期
dateReplace = curDate + " " + dateStr;
} else if (key.equals("^\\d{1,2}\\D+\\d{1,2}$")) {//21.1 (日.月) 拼接当前年份
dateReplace = curDate.substring(0, 4) + "-" + dateStr;
}
dateReplace = dateReplace.replaceAll("\\D+", "-");
strSuccess = formatter1.format(formatter2.parse(dateReplace));
return strSuccess;
}
}
throw new EhomeException("输入日期格式[" + dateStr + "]未能匹配解析");
} catch (EhomeException e) {
throw e;
} catch (Exception e) {
e.printStackTrace();
throw new EhomeException("输入日期格式[" + dateStr + "]无效");
}
}
}
(所在包:utils)
package com.ejsino.xxx.utils;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
/**
* @author charles
* @version V1.0
* @Title:
* @Package
* @Description: servlet util
* @date 20171108
*/
public class ServletUtil {
//private final static String INVOKE_MESSAGE = "invokeMessage";
/**
*
* Web 服务器反向代理中用于存放客户端原始 IP 地址的 Http header 名字。
*
*/
private final static String[] PROXY_REMOTE_IP_ADDRESS = { "X-Forwarded-For", "X-Real-IP", "Proxy-Client-IP",
"WL-Proxy-Client-IP", "HTTP_CLIENT_IP", "HTTP_X_FORWARDED_FOR" };
private ServletUtil() {
}
/**
*
* 获取请求的客户端的 IP 地址。若应用服务器前端配有反向代理的 Web 服务器 * 需要在 Web 服务器中将客户端原始请求的 IP 地址加入到 HTTP header 中。 *详见{@link ServletUtil#PROXY_REMOTE_IP_ADDRESS}
*
*
* @param request
* @return
*
* @author gaobaowen
*/
public static String getRemoteIp(HttpServletRequest request) {
for (int i = 0; i < PROXY_REMOTE_IP_ADDRESS.length; i++) {
String ip = request.getHeader(PROXY_REMOTE_IP_ADDRESS[i]);
if (!StringUtils.isEmpty(ip) && !"unknown".equals(ip)) {
return getRemoteIpFromForward(ip);
}
}
return request.getRemoteHost();
}
/**
*
* 从 HTTP Header 的 X-Forward-IP 头中截取客户端连接 IP 地址。如果经过多次反向代理,
* 在 X-Forward-IP 中获得的是以“,<SP>”分隔 IP 地址链,第一段为 * 客户端 IP
* 地址。
*
*
* @param xforwardIp
* @return
*/
private static String getRemoteIpFromForward(String xforwardIp) {
int commaOffset = xforwardIp.indexOf(',');
if (commaOffset < 0) {
return xforwardIp;
}
return xforwardIp.substring(0, commaOffset);
}
public static String getRemoteIp() {
HttpServletRequest request = getServletRequest();
return getRemoteIp(request);
}
public static String getRemoteHostName() {
return getServletRequest().getServerName();
}
/**
*
* 获取应用服务器的 HTTP 监听端口号
*
*
* @param request
* @return
*
* @author gaobaowen
*/
public static int getServerPort(HttpServletRequest request) {
return request.getServerPort();
}
/**
* 将指定 key 对应value放入值栈中
*
* @param key
* @param value
*/
public static void setInvokeMessage(String key, String value) {
getServletRequest().setAttribute(key, value);
}
public static HttpServletRequest getServletRequest() {
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
return requestAttributes.getRequest();
}
public static HttpServletResponse getServletResponse() {
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
return requestAttributes.getResponse();
}
public static HttpSession getServletSession() {
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
return requestAttributes.getRequest().getSession();
}