SpringBoot+MyBatis+Shiro框架配置

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 中获得的是以“,&lt;SP&gt;”分隔 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();
   }
  • 1
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值