1.引言
基於SpringBoot與MyBatis框架在Java開發中越來越流行,最近公司剛好需要技術變革,筆者也是頗費了寫心血做了框架的搭建和幾次框架的一直工作,本框架除了SpringBoot和MyBatista另外也揉入了當下比較流行的權限安全認證框架Shiro,附帶架構設計,希望能幫助到有需要的人。
1.1 框架版本說明
技術的變革瞬息萬變,此處有必要對各個第三方框架的版本做一下說明:SpringBoot1.5.8,Mybatis1.3.2,shiro1.3.2,其他工具jar中帶有版本信息,不在贅述。
2. SpringBoot框架配置說明
2.1 主要配置的包和文件位置
項目結構規範參考阿里開發手冊
2.2 配置前準備工作
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("||======服務啓動成功======||");
}
}
3. 框架技術體系(第三方框架)配置
3.1 shiro相關配置
參考博客地址: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;
}
}
3.2 Mybatis相關配置
參考博客地址: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);
}
}
3.3 分頁器相關配置
參考博客地址: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<Customer> 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 druid(數據庫連接池)相關配置
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 log(日誌管理)相關配置
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 http相關配置
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 OSS相關配置
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 freemarker相關配置
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
4 框架開發配置模塊
4.1 攔截器相關配置(所在包:config.intercepter)
用途:對前端發送的請求進行攔截和驗證
(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());
}
}
}
4.2 監聽器相關配置(所在包:config.listener)
用途:對整個服務流程進行監聽
(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 {
}
}
}
4.3 過濾器相關配置(所在包:config.filter)
用途:結合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();
}
}
}
}
4.4 異常處理相關配置(所在包:config.exception)
用途:對一些常見異常的封裝和處理
(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);
}
4.5 Spring相關配置(所在包:config.application)
(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);
}
}
4.6 IndexController.java(所在包:controller,必配)
=集合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";
}
}
5 常用常量類說明
5.1 JSONResult.java(所在包:config.domain)
import java.util.HashMap;
import java.util.Map;
import net.sf.json.JSONObject;
/**
* @Description: 返回前端json對象
* 前端同步、異步請求後端數據,返回前端JSONResult對象
* 返回類型JSONResult或者String(使用自帶方法將對象手動轉String)類型
* 返回字符串,裝入params,設置狀態true,返回消息,和param
* 返回單個對象,裝入obj
* List<Role> roleList = roleService.queryAllRole(role);
* return JSONResult.success("success", roleList);
* 返回多個對象,裝入objects
* Map<String, Object> objects = new HashMap<>();
* List<Role> roleList = roleService.queryAllRole(role);
* List<Company> 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>
5.2 Result.java(所在包:config.domain)
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 如果成功,則返回<code>true</code>
*/
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();
/**
* <p>
* 獲取特定類型的 model 對象
* </p>
*
* @param <T>
* @param key
* 字符串 key
* @param clazz
* 數據類型
* @return
*/
<T> T getModel(String key, Class<T> clazz);
<T> T getModel(Class<T> clazz);
}
5.3 ResultSupport.java(對Result的實現)
(所在包: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());
}
}
5.4 Myconst.java(系統中一些常用的靜態常量)
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");
}
5.5 SerNoConst.java(系統中一些序號定義類)
(所在包: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;
}
}
5.6 DateConv.java(系統中一些時間類型轉化類)
(所在包: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 + "]無效");
}
}
}
5.7 ServletUtil.java(客戶ip處理,需要獲取用戶ip必配)
(所在包: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";
/**
* <p>
* Web 服務器反向代理中用於存放客戶端原始 IP 地址的 Http header 名字。
* </p>
*/
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() {
}
/**
* <p>
* 獲取請求的客戶端的 IP 地址。若應用服務器前端配有反向代理的 Web 服務器 * 需要在 Web 服務器中將客戶端原始請求的 IP 地址加入到 HTTP header 中。 *詳見{@link ServletUtil#PROXY_REMOTE_IP_ADDRESS}
* </p>
*
* @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();
}
/**
* <p>
* 從 HTTP Header 的 X-Forward-IP 頭中截取客戶端連接 IP 地址。如果經過多次反向代理,
* 在 X-Forward-IP 中獲得的是以“,<SP>”分隔 IP 地址鏈,第一段爲 * 客戶端 IP
* 地址。
* </p>
*
* @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();
}
/**
* <p>
* 獲取應用服務器的 HTTP 監聽端口號
* </p>
*
* @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();
}