Browse Source

initial commit

albertshaw 9 years ago
parent
commit
6876d68424
79 changed files with 5243 additions and 12 deletions
  1. 7 12
      .gitignore
  2. 327 0
      pom.xml
  3. 91 0
      src/main/java/com/goafanti/common/bo/Error.java
  4. 53 0
      src/main/java/com/goafanti/common/bo/Result.java
  5. 27 0
      src/main/java/com/goafanti/common/constant/ErrorConstants.java
  6. 16 0
      src/main/java/com/goafanti/common/constant/ShiroConstants.java
  7. 31 0
      src/main/java/com/goafanti/common/controller/BaseApiController.java
  8. 75 0
      src/main/java/com/goafanti/common/controller/BaseController.java
  9. 18 0
      src/main/java/com/goafanti/common/controller/WebpageController.java
  10. 17 0
      src/main/java/com/goafanti/common/dao/PermissionMapper.java
  11. 17 0
      src/main/java/com/goafanti/common/dao/RoleMapper.java
  12. 17 0
      src/main/java/com/goafanti/common/dao/UserMapper.java
  13. 87 0
      src/main/java/com/goafanti/common/error/BusinessException.java
  14. 74 0
      src/main/java/com/goafanti/common/error/SystemExceptionResolver.java
  15. 71 0
      src/main/java/com/goafanti/common/mapper/PermissionMapper.xml
  16. 71 0
      src/main/java/com/goafanti/common/mapper/RoleMapper.xml
  17. 128 0
      src/main/java/com/goafanti/common/mapper/UserMapper.xml
  18. 29 0
      src/main/java/com/goafanti/common/model/BaseModel.java
  19. 39 0
      src/main/java/com/goafanti/common/model/Permission.java
  20. 33 0
      src/main/java/com/goafanti/common/model/Role.java
  21. 107 0
      src/main/java/com/goafanti/common/model/User.java
  22. 43 0
      src/main/java/com/goafanti/common/utils/ArrayUtils.java
  23. 53 0
      src/main/java/com/goafanti/common/utils/DateUtils.java
  24. 121 0
      src/main/java/com/goafanti/common/utils/LoggerUtils.java
  25. 41 0
      src/main/java/com/goafanti/common/utils/PasswordUtil.java
  26. 71 0
      src/main/java/com/goafanti/common/utils/SerializeUtil.java
  27. 49 0
      src/main/java/com/goafanti/common/utils/SpringContextUtils.java
  28. 11 0
      src/main/java/com/goafanti/common/utils/StringUtils.java
  29. 327 0
      src/main/java/com/goafanti/common/utils/VerifyCodeUtils.java
  30. 165 0
      src/main/java/com/goafanti/core/config/INI4j.java
  31. 244 0
      src/main/java/com/goafanti/core/mybatis/BaseMybatisDao.java
  32. 25 0
      src/main/java/com/goafanti/core/mybatis/page/Dialect.java
  33. 49 0
      src/main/java/com/goafanti/core/mybatis/page/MysqlDialect.java
  34. 56 0
      src/main/java/com/goafanti/core/mybatis/page/Paginable.java
  35. 38 0
      src/main/java/com/goafanti/core/mybatis/page/Pagination.java
  36. 122 0
      src/main/java/com/goafanti/core/mybatis/page/SimplePage.java
  37. 58 0
      src/main/java/com/goafanti/core/shiro/CustomShiroSessionDAO.java
  38. 135 0
      src/main/java/com/goafanti/core/shiro/cache/JedisManager.java
  39. 109 0
      src/main/java/com/goafanti/core/shiro/cache/JedisShiroCache.java
  40. 100 0
      src/main/java/com/goafanti/core/shiro/cache/JedisShiroSessionRepository.java
  41. 10 0
      src/main/java/com/goafanti/core/shiro/cache/ShiroCacheManager.java
  42. 32 0
      src/main/java/com/goafanti/core/shiro/cache/impl/CustomShiroCacheManager.java
  43. 31 0
      src/main/java/com/goafanti/core/shiro/cache/impl/JedisShiroCacheManager.java
  44. 51 0
      src/main/java/com/goafanti/core/shiro/filter/LoginFilter.java
  45. 74 0
      src/main/java/com/goafanti/core/shiro/filter/PermissionFilter.java
  46. 47 0
      src/main/java/com/goafanti/core/shiro/filter/RoleFilter.java
  47. 71 0
      src/main/java/com/goafanti/core/shiro/filter/ShiroFilterUtils.java
  48. 65 0
      src/main/java/com/goafanti/core/shiro/filter/SimpleAuthFilter.java
  49. 41 0
      src/main/java/com/goafanti/core/shiro/listener/CustomSessionListener.java
  50. 16 0
      src/main/java/com/goafanti/core/shiro/service/ShiroManager.java
  51. 96 0
      src/main/java/com/goafanti/core/shiro/service/impl/ShiroManagerImpl.java
  52. 208 0
      src/main/java/com/goafanti/core/shiro/session/CustomSessionManager.java
  53. 23 0
      src/main/java/com/goafanti/core/shiro/session/SessionStatus.java
  54. 31 0
      src/main/java/com/goafanti/core/shiro/session/ShiroSessionRepository.java
  55. 27 0
      src/main/java/com/goafanti/core/shiro/token/ShiroToken.java
  56. 160 0
      src/main/java/com/goafanti/core/shiro/token/TokenManager.java
  57. 76 0
      src/main/java/com/goafanti/core/shiro/token/UserRealm.java
  58. 6 0
      src/main/java/com/goafanti/core/websocket/Constants.java
  59. 87 0
      src/main/java/com/goafanti/core/websocket/SystemWebSocketHandler.java
  60. 27 0
      src/main/java/com/goafanti/core/websocket/WebSocketConfig.java
  61. 37 0
      src/main/java/com/goafanti/core/websocket/WebSocketHandshakeInterceptor.java
  62. 21 0
      src/main/java/com/goafanti/user/controller/UserApiController.java
  63. 5 0
      src/main/java/com/goafanti/user/service/UserService.java
  64. 10 0
      src/main/java/com/goafanti/user/service/impl/UserServiceImpl.java
  65. 24 0
      src/main/resources/applicationContext.xml
  66. 24 0
      src/main/resources/log4j.properties
  67. 17 0
      src/main/resources/mybatis-config.xml
  68. 53 0
      src/main/resources/props/config_dev.properties
  69. 93 0
      src/main/resources/props/config_local.properties
  70. 53 0
      src/main/resources/props/config_prod.properties
  71. 30 0
      src/main/resources/props/error.properties
  72. 9 0
      src/main/resources/shiro_base_auth.ini
  73. 107 0
      src/main/resources/spring/spring-mvc.xml
  74. 90 0
      src/main/resources/spring/spring-mybatis.xml
  75. 157 0
      src/main/resources/spring/spring-shiro.xml
  76. 19 0
      src/main/resources/spring/spring-task.xml
  77. 18 0
      src/main/webapp/WEB-INF/views/error.jsp
  78. 19 0
      src/main/webapp/WEB-INF/views/index.jsp
  79. 126 0
      src/main/webapp/WEB-INF/web.xml

+ 7 - 12
.gitignore

@@ -1,12 +1,7 @@
-*.class
-
-# Mobile Tools for Java (J2ME)
-.mtj.tmp/
-
-# Package Files #
-*.jar
-*.war
-*.ear
-
-# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
-hs_err_pid*
+/target/
+.classpath
+.project
+.settings
+/.externalToolBuilders/
+/bin/
+/build/

+ 327 - 0
pom.xml

@@ -0,0 +1,327 @@
+<?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/maven-v4_0_0.xsd">
+
+	<modelVersion>4.0.0</modelVersion>
+	<groupId>com.goafanti</groupId>
+	<artifactId>aft</artifactId>
+	<name>aft</name>
+
+	<packaging>war</packaging>
+	<version>1.0.0-BUILD-SNAPSHOT</version>
+
+	<properties>
+		<java-version>1.8</java-version>
+		<org.springframework-version>4.2.5.RELEASE</org.springframework-version>
+		<org.slf4j-version>1.7.21</org.slf4j-version>
+		<shiro-version>1.2.6</shiro-version>
+		<mybatis-version>3.2.8</mybatis-version>
+		<mybatis-spring-version>1.2.2</mybatis-spring-version>
+		<org.aspectj-version>1.6.10</org.aspectj-version>
+		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+	</properties>
+
+	<dependencies>
+		<!-- spring -->
+		<dependency>
+			<groupId>org.springframework</groupId>
+			<artifactId>spring-context</artifactId>
+			<version>${org.springframework-version}</version>
+			<exclusions>
+				<!-- Exclude Commons Logging in favor of SLF4j -->
+				<exclusion>
+					<groupId>commons-logging</groupId>
+					<artifactId>commons-logging</artifactId>
+				</exclusion>
+			</exclusions>
+		</dependency>
+		<dependency>
+			<groupId>org.springframework</groupId>
+			<artifactId>spring-web</artifactId>
+			<version>${org.springframework-version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.springframework</groupId>
+			<artifactId>spring-webmvc</artifactId>
+			<version>${org.springframework-version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.springframework</groupId>
+			<artifactId>spring-jdbc</artifactId>
+			<version>${org.springframework-version}</version>
+		</dependency>
+
+		<dependency>
+			<groupId>org.springframework</groupId>
+			<artifactId>spring-context-support</artifactId>
+			<version>${org.springframework-version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.springframework</groupId>
+			<artifactId>spring-websocket</artifactId>
+			<version>${org.springframework-version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.springframework</groupId>
+			<artifactId>spring-messaging</artifactId>
+			<version>${org.springframework-version}</version>
+		</dependency>
+		<!-- spring end -->
+
+		<!-- redis begin -->
+		<dependency>
+			<groupId>redis.clients</groupId>
+			<artifactId>jedis</artifactId>
+			<version>2.8.0</version>
+		</dependency>
+		<!-- redis end -->
+
+		<!-- mybatis start -->
+		<dependency>
+			<groupId>org.mybatis</groupId>
+			<artifactId>mybatis</artifactId>
+			<version>${mybatis-version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.mybatis</groupId>
+			<artifactId>mybatis-spring</artifactId>
+			<version>${mybatis-spring-version}</version>
+		</dependency>
+		<!-- mybatis end -->
+
+		<!-- JDBC connections pooling -->
+		<dependency>
+			<groupId>com.alibaba</groupId>
+			<artifactId>druid</artifactId>
+			<version>1.0.25</version>
+			<exclusions>
+				<exclusion>
+					<groupId>com.alibaba</groupId>
+					<artifactId>jconsole</artifactId>
+				</exclusion>
+				<exclusion>
+					<groupId>com.alibaba</groupId>
+					<artifactId>tools</artifactId>
+				</exclusion>
+			</exclusions>
+		</dependency>
+		<!-- JDBC connections pooling end -->
+
+		<!-- jackson start -->
+		<dependency>
+			<groupId>com.fasterxml.jackson.core</groupId>
+			<artifactId>jackson-databind</artifactId>
+			<version>2.8.2</version>
+		</dependency>
+		<!-- jackson end -->
+
+		<!-- mysql -->
+		<dependency>
+			<groupId>mysql</groupId>
+			<artifactId>mysql-connector-java</artifactId>
+			<version>5.1.39</version>
+		</dependency>
+
+		<!-- Logging -->
+		<dependency>
+			<groupId>org.slf4j</groupId>
+			<artifactId>slf4j-api</artifactId>
+			<version>${org.slf4j-version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.slf4j</groupId>
+			<artifactId>jcl-over-slf4j</artifactId>
+			<version>${org.slf4j-version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.slf4j</groupId>
+			<artifactId>slf4j-log4j12</artifactId>
+			<version>${org.slf4j-version}</version>
+		</dependency>
+		<dependency>
+			<groupId>log4j</groupId>
+			<artifactId>log4j</artifactId>
+			<version>1.2.17</version>
+			<exclusions>
+				<exclusion>
+					<groupId>javax.mail</groupId>
+					<artifactId>mail</artifactId>
+				</exclusion>
+				<exclusion>
+					<groupId>javax.jms</groupId>
+					<artifactId>jms</artifactId>
+				</exclusion>
+				<exclusion>
+					<groupId>com.sun.jdmk</groupId>
+					<artifactId>jmxtools</artifactId>
+				</exclusion>
+				<exclusion>
+					<groupId>com.sun.jmx</groupId>
+					<artifactId>jmxri</artifactId>
+				</exclusion>
+			</exclusions>
+		</dependency>
+
+		<!-- common lang -->
+		<dependency>
+			<groupId>org.apache.commons</groupId>
+			<artifactId>commons-lang3</artifactId>
+			<version>3.4</version>
+		</dependency>
+		<!-- common codec -->
+		<dependency>
+			<groupId>commons-codec</groupId>
+			<artifactId>commons-codec</artifactId>
+			<version>1.10</version>
+		</dependency>
+
+		<!-- common fileupload -->
+		<dependency>
+			<groupId>commons-fileupload</groupId>
+			<artifactId>commons-fileupload</artifactId>
+			<version>1.3.1</version>
+		</dependency>
+
+		<!-- quartz -->
+		<dependency>
+			<groupId>org.quartz-scheduler</groupId>
+			<artifactId>quartz</artifactId>
+			<version>1.8.5</version>
+		</dependency>
+		<!-- Servlet -->
+		<dependency>
+			<groupId>javax.servlet</groupId>
+			<artifactId>javax.servlet-api</artifactId>
+			<version>3.1.0</version>
+			<scope>provided</scope>
+		</dependency>
+		<dependency>
+			<groupId>javax.servlet.jsp</groupId>
+			<artifactId>jsp-api</artifactId>
+			<version>2.2</version>
+		</dependency>
+		<dependency>
+			<groupId>javax.servlet</groupId>
+			<artifactId>jstl</artifactId>
+			<version>1.2</version>
+		</dependency>
+		<!-- shiro -->
+		<dependency>
+			<groupId>org.apache.shiro</groupId>
+			<artifactId>shiro-web</artifactId>
+			<version>${shiro-version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.shiro</groupId>
+			<artifactId>shiro-quartz</artifactId>
+			<version>${shiro-version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.shiro</groupId>
+			<artifactId>shiro-spring</artifactId>
+			<version>${shiro-version}</version>
+		</dependency>
+		<!-- Test -->
+		<dependency>
+			<groupId>junit</groupId>
+			<artifactId>junit</artifactId>
+			<version>4.12</version>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.springframework</groupId>
+			<artifactId>spring-test</artifactId>
+			<version>${org.springframework-version}</version>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>com.alibaba</groupId>
+			<artifactId>fastjson</artifactId>
+			<version>1.2.16</version>
+		</dependency>
+
+		<!-- AspectJ -->
+		<dependency>
+			<groupId>org.aspectj</groupId>
+			<artifactId>aspectjrt</artifactId>
+			<version>${org.aspectj-version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.aspectj</groupId>
+			<artifactId>aspectjweaver</artifactId>
+			<version>${org.aspectj-version}</version>
+		</dependency>
+		<dependency>
+			<groupId>cglib</groupId>
+			<artifactId>cglib</artifactId>
+			<version>2.2.2</version>
+		</dependency>
+		<!-- JSR-303 bean validator -->
+		<!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-validator -->
+		<dependency>
+			<groupId>org.hibernate</groupId>
+			<artifactId>hibernate-validator</artifactId>
+			<version>5.2.4.Final</version>
+		</dependency>
+		<dependency>
+			<groupId>javax.validation</groupId>
+			<artifactId>validation-api</artifactId>
+			<version>1.1.0.Final</version>
+		</dependency>
+		<dependency>
+			<groupId>org.jboss.logging</groupId>
+			<artifactId>jboss-logging</artifactId>
+			<version>3.2.1.Final</version>
+		</dependency>
+		<dependency>
+			<groupId>com.fasterxml</groupId>
+			<artifactId>classmate</artifactId>
+			<version>1.1.0</version>
+		</dependency>
+	    <dependency>
+		    <groupId>com.googlecode.json-simple</groupId>
+		    <artifactId>json-simple</artifactId>
+		    <version>1.1.1</version>
+		</dependency>
+		<dependency>
+		    <groupId>org.apache.poi</groupId>
+		    <artifactId>poi</artifactId>
+		    <version>3.15</version>
+		</dependency>
+	</dependencies>
+	<build>
+		<resources>
+			<resource>
+				<directory>src/resources</directory>
+				<includes>
+					<include>**/*.properties</include>
+					<include>**/*.xml</include>
+					<include>**/*.ini</include>
+				</includes>
+				<filtering>false</filtering>
+			</resource>
+			<resource>
+				<directory>src/main/java</directory>
+				<includes>
+					<include>**/*.xml</include>
+				</includes>
+				<filtering>false</filtering>
+			</resource>
+		</resources>
+		<plugins>
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-compiler-plugin</artifactId>
+				<version>3.5.1</version>
+				<configuration>
+					<source>1.8</source>
+					<target>1.8</target>
+					<encoding>utf8</encoding>
+					<compilerArgument>-Xlint:all</compilerArgument>
+					<showWarnings>true</showWarnings>
+					<showDeprecation>true</showDeprecation>
+				</configuration>
+			</plugin>
+		</plugins>
+	</build>
+</project>

+ 91 - 0
src/main/java/com/goafanti/common/bo/Error.java

@@ -0,0 +1,91 @@
+package com.goafanti.common.bo;
+
+import java.io.Serializable;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+
+public class Error implements Serializable {
+
+	private static final long serialVersionUID = 1723573714163714211L;
+
+	private transient String code;
+
+	private String field;
+
+	private String message;
+
+	public Error() {
+		this("", "", "");
+	}
+
+	public Error(String message) {
+		this("", "", message);
+	}
+
+	public Error(String code, String message) {
+		this(code, "", message);
+	}
+
+	public Error(String code, String field, String message) {
+		this.code = code;
+		this.field = field;
+		this.message = message;
+	}
+
+	/**
+	 * @return the code
+	 */
+	@JsonIgnore
+	public String getCode() {
+		return code;
+	}
+
+	/**
+	 * @param code
+	 *            the code to set
+	 */
+	public void setCode(String code) {
+		this.code = code;
+	}
+
+	/**
+	 * @return the field
+	 */
+	public String getField() {
+		return field;
+	}
+
+	/**
+	 * @param field
+	 *            the field to set
+	 */
+	public void setField(String field) {
+		this.field = field;
+	}
+
+	/**
+	 * @return the message
+	 */
+	public String getMessage() {
+		return message;
+	}
+
+	/**
+	 * @param message
+	 *            the message to set
+	 */
+	public void setMessage(String message) {
+		this.message = message;
+	}
+
+	public Error buildField(String field) {
+		this.field = field;
+		return this;
+	}
+
+	public Error buildMessage(String message) {
+		this.message = message;
+		return this;
+	}
+
+}

+ 53 - 0
src/main/java/com/goafanti/common/bo/Result.java

@@ -0,0 +1,53 @@
+package com.goafanti.common.bo;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.goafanti.common.model.BaseModel;
+
+public class Result extends BaseModel {
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = 1L;
+	private Object data;
+	private List<Error> error = new ArrayList<Error>();
+
+	public Result() {
+	}
+
+	public Result(Object data) {
+		this.data = data;
+	}
+
+	/**
+	 * @return the data
+	 */
+	public Object getData() {
+		return data;
+	}
+
+	/**
+	 * @param data
+	 *            the data to set
+	 */
+	public void setData(Object data) {
+		this.data = data;
+	}
+
+	/**
+	 * @return the error
+	 */
+	public List<Error> getError() {
+		return error;
+	}
+
+	/**
+	 * @param error
+	 *            the error to set
+	 */
+	public void setError(List<Error> error) {
+		this.error = error;
+	}
+
+}

+ 27 - 0
src/main/java/com/goafanti/common/constant/ErrorConstants.java

@@ -0,0 +1,27 @@
+package com.goafanti.common.constant;
+
+public class ErrorConstants {
+
+	public static final String NON_LOGIN = "NON_LOGIN";
+
+	public static final String VCODE_ERROR = "VCODE_ERROR";
+
+	public static final String EMAIL_SIZE_ERROR = "EMAIL_SIZE_ERROR";
+
+	public static final String EMAIL_PATTERN_ERROR = "EMAIL_PATTERN_ERROR";
+
+	public static final String DUNPLICATE_KAY_ERROR = "DUNPLICATE_KAY_ERROR";
+
+	public static final String PARAM_INTEGER_ERROR = "PARAM_INTEGER_ERROR";
+
+	public static final String PARAM_EMPTY_ERROR = "PARAM_EMPTY_ERROR";
+
+	public static final String PARAM_SIZE_ERROR = "PARAM_SIZE_ERROR";
+
+	public static final String PARAM_PATTERN_ERROR = "PARAM_PATTERN_ERROR";
+
+	public static final String DATA_EMPTY_ERROR = "DATA_EMPTY_ERROR";
+
+	public static final String PWD_NOT_MATCH_ERROR = "PWD_NOT_MATCH_ERROR";
+
+}

+ 16 - 0
src/main/java/com/goafanti/common/constant/ShiroConstants.java

@@ -0,0 +1,16 @@
+package com.goafanti.common.constant;
+
+public class ShiroConstants {
+	public static final String LOGIN_URL = "/login";
+	public static final String UNAUTHORIZED_URL = "/login";
+
+	public static final String SESSION_STATUS = "jfgj-online-status";
+
+	public static final String REDIS_SHIRO_SESSION = "szrobot-jfgj-session:";
+	public static final String REDIS_SHIRO_ALL = "*szrobot-jfgj-session:*";
+
+	// 0:禁止登录
+	public static final Integer USER_DISABLE = new Integer(0);
+	// 1:有效
+	public static final Integer USER_ENABLE = new Integer(1);
+}

+ 31 - 0
src/main/java/com/goafanti/common/controller/BaseApiController.java

@@ -0,0 +1,31 @@
+package com.goafanti.common.controller;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.commons.fileupload.servlet.ServletFileUpload;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.multipart.MultipartFile;
+import org.springframework.web.multipart.MultipartRequest;
+
+@RestController
+@RequestMapping(value = "/api")
+public class BaseApiController extends BaseController {
+
+	protected List<MultipartFile> getFiles(HttpServletRequest request) {
+		if (ServletFileUpload.isMultipartContent(request)) {
+			Iterator<String> itr = ((MultipartRequest) request).getFileNames();
+			List<MultipartFile> files = new ArrayList<MultipartFile>();
+			if (itr.hasNext()) {
+				files.add(((MultipartRequest) request).getFile(itr.next()));
+			}
+			return files;
+		}
+		return new ArrayList<MultipartFile>();
+	}
+
+}

+ 75 - 0
src/main/java/com/goafanti/common/controller/BaseController.java

@@ -0,0 +1,75 @@
+package com.goafanti.common.controller;
+
+import java.text.DateFormat;
+import java.text.MessageFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.beans.propertyeditors.CustomDateEditor;
+import org.springframework.core.io.support.ResourcePropertySource;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.WebDataBinder;
+import org.springframework.web.bind.annotation.InitBinder;
+import org.springframework.web.bind.annotation.ModelAttribute;
+
+import com.goafanti.common.bo.Error;
+
+@Controller
+public class BaseController {
+	@Resource(name = "errorResource")
+	private ResourcePropertySource errorResource;
+
+	@Value(value = "${static.host}")
+	private String staticHost = null;
+
+	protected Error buildError(String key) {
+		return buildError(key, null);
+	}
+
+	protected Error buildError(String key, String message) {
+		String msg = (String) errorResource.getProperty(key);
+		if (StringUtils.isEmpty(msg)) {
+			msg = message;
+		}
+		return new Error(key, msg);
+	}
+
+	/**
+	 * 带参数的error message
+	 * 
+	 * @param key
+	 * @param message
+	 * @param object
+	 * @return
+	 */
+	protected Error buildError(String key, String message, Object... object) {
+		String msg = (String) errorResource.getProperty(key);
+		if (StringUtils.isEmpty(msg)) {
+			msg = message;
+		}
+		msg = MessageFormat.format(msg, object);
+		return new Error(key, msg);
+	}
+
+	protected Error buildErrorByMsg(String msg, Object... params) {
+		return new Error(MessageFormat.format(msg, params));
+	}
+
+	@InitBinder
+	public void initBinder(WebDataBinder binder) {
+		DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+		dateFormat.setLenient(true);
+		binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, true));
+	}
+
+	@ModelAttribute
+	public void prepareModel(HttpServletRequest request, Model model) {
+		model.addAttribute("staticHost", staticHost);
+	}
+}

+ 18 - 0
src/main/java/com/goafanti/common/controller/WebpageController.java

@@ -0,0 +1,18 @@
+package com.goafanti.common.controller;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.servlet.ModelAndView;
+
+@Controller
+public class WebpageController extends BaseController {
+
+	@RequestMapping(value = "/", method = RequestMethod.GET)
+	public String home(HttpServletRequest request, ModelAndView modelview) {
+		return "/index";
+	}
+
+}

+ 17 - 0
src/main/java/com/goafanti/common/dao/PermissionMapper.java

@@ -0,0 +1,17 @@
+package com.goafanti.common.dao;
+
+import com.goafanti.common.model.Permission;
+
+public interface PermissionMapper {
+    int deleteByPrimaryKey(String id);
+
+    int insert(Permission record);
+
+    int insertSelective(Permission record);
+
+    Permission selectByPrimaryKey(String id);
+
+    int updateByPrimaryKeySelective(Permission record);
+
+    int updateByPrimaryKey(Permission record);
+}

+ 17 - 0
src/main/java/com/goafanti/common/dao/RoleMapper.java

@@ -0,0 +1,17 @@
+package com.goafanti.common.dao;
+
+import com.goafanti.common.model.Role;
+
+public interface RoleMapper {
+    int deleteByPrimaryKey(String id);
+
+    int insert(Role record);
+
+    int insertSelective(Role record);
+
+    Role selectByPrimaryKey(String id);
+
+    int updateByPrimaryKeySelective(Role record);
+
+    int updateByPrimaryKey(Role record);
+}

+ 17 - 0
src/main/java/com/goafanti/common/dao/UserMapper.java

@@ -0,0 +1,17 @@
+package com.goafanti.common.dao;
+
+import com.goafanti.common.model.User;
+
+public interface UserMapper {
+    int deleteByPrimaryKey(String id);
+
+    int insert(User record);
+
+    int insertSelective(User record);
+
+    User selectByPrimaryKey(String id);
+
+    int updateByPrimaryKeySelective(User record);
+
+    int updateByPrimaryKey(User record);
+}

+ 87 - 0
src/main/java/com/goafanti/common/error/BusinessException.java

@@ -0,0 +1,87 @@
+package com.goafanti.common.error;
+
+import com.goafanti.common.bo.Error;
+
+public class BusinessException extends RuntimeException {
+
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = 1889327507120174994L;
+
+	private Error error;
+
+	/**
+	 * Creates a new BusinessException.
+	 */
+	public BusinessException() {
+		super();
+	}
+
+	/**
+	 * Constructs a new BusinessException.
+	 * 
+	 * @param message
+	 *            the reason for the exception
+	 */
+	public BusinessException(String message) {
+		super(message);
+	}
+
+	/**
+	 * Constructs a new BusinessException.
+	 * 
+	 * @param cause
+	 *            the underlying Throwable that caused this exception to be
+	 *            thrown.
+	 */
+	public BusinessException(Throwable cause) {
+		super(cause);
+	}
+
+	/**
+	 * Constructs a new BusinessException.
+	 * 
+	 * @param message
+	 *            the reason for the exception
+	 * @param cause
+	 *            the underlying Throwable that caused this exception to be
+	 *            thrown.
+	 */
+	public BusinessException(String message, Throwable cause) {
+		super(message, cause);
+	}
+
+	/**
+	 * Constructs a new BusinessException.
+	 * 
+	 * @param error
+	 *            the error for the exception
+	 */
+	public BusinessException(Error error) {
+		super(error.getMessage());
+		this.error = error;
+	}
+
+	/**
+	 * Constructs a new BusinessException.
+	 * 
+	 * @param error
+	 *            the error for the exception
+	 * @param cause
+	 *            the underlying Throwable that caused this exception to be
+	 *            thrown.
+	 */
+	public BusinessException(Error error, Throwable cause) {
+		super(error.getMessage(), cause);
+		this.error = error;
+	}
+
+	public String getCode() {
+		return error == null ? "" : error.getCode();
+	}
+
+	public String getField() {
+		return error == null ? "" : error.getField();
+	}
+}

+ 74 - 0
src/main/java/com/goafanti/common/error/SystemExceptionResolver.java

@@ -0,0 +1,74 @@
+package com.goafanti.common.error;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.io.support.ResourcePropertySource;
+import org.springframework.web.servlet.ModelAndView;
+import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver;
+
+import com.goafanti.common.bo.Result;
+import com.goafanti.common.bo.Error;
+import com.goafanti.common.utils.LoggerUtils;
+import com.goafanti.common.utils.StringUtils;
+import com.goafanti.core.shiro.filter.ShiroFilterUtils;
+
+public class SystemExceptionResolver extends SimpleMappingExceptionResolver {
+	@Resource(name = "errorResource")
+	private ResourcePropertySource errorResource;
+
+	@Autowired
+	private ShiroFilterUtils shiroFilterUtils;
+
+	@Override
+	protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response, Object handler,
+			Exception ex) {
+		// Expose ModelAndView for chosen error view.
+		String viewName = determineViewName(ex, request);
+		LoggerUtils.fmtError(getClass(), ex, "系统异常。");
+		ex = handleErrorMessage(ex);
+
+		if (viewName != null) {
+			if (!shiroFilterUtils.isAjax(request)) {// JSP
+				// Apply HTTP status code for error views, if specified.
+				// Only apply it if we're processing a top-level request.
+				Integer statusCode = determineStatusCode(request, viewName);
+				if (statusCode != null) {
+					applyStatusCodeIfPossible(request, response, statusCode);
+				}
+				return getModelAndView(viewName, ex, request);
+			} else {// JSON
+				Result res = new Result();
+				res.getError().add(new Error(viewName, ex.getMessage()));
+				shiroFilterUtils.out(response, res);
+				return null;
+			}
+		} else {
+			return null;
+		}
+	}
+
+	private Exception handleErrorMessage(Exception ex) {
+		Error error = null;
+		if (ex instanceof BusinessException) {
+			BusinessException bex = (BusinessException) ex;
+			error = new Error(bex.getCode(), bex.getField(), (String) errorResource.getProperty(bex.getCode()));
+		} else {
+			error = new Error((String) errorResource.getProperty(ex.getClass().getName()));
+		}
+		if (StringUtils.isEmpty(error.getMessage())) {
+			error.setMessage("系统异常,请联系管理员。");
+		}
+		return new BusinessException(error, ex.getCause());
+	}
+
+	/**
+	 * @param errorResource
+	 *            the errorResource to set
+	 */
+	public void setErrorResource(ResourcePropertySource errorResource) {
+		this.errorResource = errorResource;
+	}
+}

+ 71 - 0
src/main/java/com/goafanti/common/mapper/PermissionMapper.xml

@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.goafanti.common.dao.PermissionMapper">
+  <resultMap id="BaseResultMap" type="com.goafanti.common.model.Permission">
+    <id column="id" jdbcType="VARCHAR" property="id" />
+    <result column="url" jdbcType="VARCHAR" property="url" />
+    <result column="name" jdbcType="VARCHAR" property="name" />
+  </resultMap>
+  <sql id="Base_Column_List">
+    id, url, name
+  </sql>
+  <select id="selectByPrimaryKey" parameterType="java.lang.String" resultMap="BaseResultMap">
+    select 
+    <include refid="Base_Column_List" />
+    from permission
+    where id = #{id,jdbcType=VARCHAR}
+  </select>
+  <delete id="deleteByPrimaryKey" parameterType="java.lang.String">
+    delete from permission
+    where id = #{id,jdbcType=VARCHAR}
+  </delete>
+  <insert id="insert" parameterType="com.goafanti.common.model.Permission">
+    insert into permission (id, url, name
+      )
+    values (#{id,jdbcType=VARCHAR}, #{url,jdbcType=VARCHAR}, #{name,jdbcType=VARCHAR}
+      )
+  </insert>
+  <insert id="insertSelective" parameterType="com.goafanti.common.model.Permission">
+    insert into permission
+    <trim prefix="(" suffix=")" suffixOverrides=",">
+      <if test="id != null">
+        id,
+      </if>
+      <if test="url != null">
+        url,
+      </if>
+      <if test="name != null">
+        name,
+      </if>
+    </trim>
+    <trim prefix="values (" suffix=")" suffixOverrides=",">
+      <if test="id != null">
+        #{id,jdbcType=VARCHAR},
+      </if>
+      <if test="url != null">
+        #{url,jdbcType=VARCHAR},
+      </if>
+      <if test="name != null">
+        #{name,jdbcType=VARCHAR},
+      </if>
+    </trim>
+  </insert>
+  <update id="updateByPrimaryKeySelective" parameterType="com.goafanti.common.model.Permission">
+    update permission
+    <set>
+      <if test="url != null">
+        url = #{url,jdbcType=VARCHAR},
+      </if>
+      <if test="name != null">
+        name = #{name,jdbcType=VARCHAR},
+      </if>
+    </set>
+    where id = #{id,jdbcType=VARCHAR}
+  </update>
+  <update id="updateByPrimaryKey" parameterType="com.goafanti.common.model.Permission">
+    update permission
+    set url = #{url,jdbcType=VARCHAR},
+      name = #{name,jdbcType=VARCHAR}
+    where id = #{id,jdbcType=VARCHAR}
+  </update>
+</mapper>

+ 71 - 0
src/main/java/com/goafanti/common/mapper/RoleMapper.xml

@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.goafanti.common.dao.RoleMapper">
+  <resultMap id="BaseResultMap" type="com.goafanti.common.model.Role">
+    <id column="id" jdbcType="VARCHAR" property="id" />
+    <result column="role_name" jdbcType="VARCHAR" property="roleName" />
+    <result column="role_type" jdbcType="INTEGER" property="roleType" />
+  </resultMap>
+  <sql id="Base_Column_List">
+    id, role_name, role_type
+  </sql>
+  <select id="selectByPrimaryKey" parameterType="java.lang.String" resultMap="BaseResultMap">
+    select 
+    <include refid="Base_Column_List" />
+    from role
+    where id = #{id,jdbcType=VARCHAR}
+  </select>
+  <delete id="deleteByPrimaryKey" parameterType="java.lang.String">
+    delete from role
+    where id = #{id,jdbcType=VARCHAR}
+  </delete>
+  <insert id="insert" parameterType="com.goafanti.common.model.Role">
+    insert into role (id, role_name, role_type
+      )
+    values (#{id,jdbcType=VARCHAR}, #{roleName,jdbcType=VARCHAR}, #{roleType,jdbcType=INTEGER}
+      )
+  </insert>
+  <insert id="insertSelective" parameterType="com.goafanti.common.model.Role">
+    insert into role
+    <trim prefix="(" suffix=")" suffixOverrides=",">
+      <if test="id != null">
+        id,
+      </if>
+      <if test="roleName != null">
+        role_name,
+      </if>
+      <if test="roleType != null">
+        role_type,
+      </if>
+    </trim>
+    <trim prefix="values (" suffix=")" suffixOverrides=",">
+      <if test="id != null">
+        #{id,jdbcType=VARCHAR},
+      </if>
+      <if test="roleName != null">
+        #{roleName,jdbcType=VARCHAR},
+      </if>
+      <if test="roleType != null">
+        #{roleType,jdbcType=INTEGER},
+      </if>
+    </trim>
+  </insert>
+  <update id="updateByPrimaryKeySelective" parameterType="com.goafanti.common.model.Role">
+    update role
+    <set>
+      <if test="roleName != null">
+        role_name = #{roleName,jdbcType=VARCHAR},
+      </if>
+      <if test="roleType != null">
+        role_type = #{roleType,jdbcType=INTEGER},
+      </if>
+    </set>
+    where id = #{id,jdbcType=VARCHAR}
+  </update>
+  <update id="updateByPrimaryKey" parameterType="com.goafanti.common.model.Role">
+    update role
+    set role_name = #{roleName,jdbcType=VARCHAR},
+      role_type = #{roleType,jdbcType=INTEGER}
+    where id = #{id,jdbcType=VARCHAR}
+  </update>
+</mapper>

+ 128 - 0
src/main/java/com/goafanti/common/mapper/UserMapper.xml

@@ -0,0 +1,128 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.goafanti.common.dao.UserMapper">
+  <resultMap id="BaseResultMap" type="com.goafanti.common.model.User">
+    <id column="id" jdbcType="VARCHAR" property="id" />
+    <result column="mobile" jdbcType="VARCHAR" property="mobile" />
+    <result column="password" jdbcType="VARCHAR" property="password" />
+    <result column="nickname" jdbcType="VARCHAR" property="nickname" />
+    <result column="email" jdbcType="VARCHAR" property="email" />
+    <result column="create_time" jdbcType="TIMESTAMP" property="createTime" />
+    <result column="number" jdbcType="BIGINT" property="number" />
+    <result column="type" jdbcType="INTEGER" property="type" />
+  </resultMap>
+  <sql id="Base_Column_List">
+    id, mobile, password, nickname, email, create_time, number, type
+  </sql>
+  <select id="selectByPrimaryKey" parameterType="java.lang.String" resultMap="BaseResultMap">
+    select 
+    <include refid="Base_Column_List" />
+    from user
+    where id = #{id,jdbcType=VARCHAR}
+  </select>
+  <delete id="deleteByPrimaryKey" parameterType="java.lang.String">
+    delete from user
+    where id = #{id,jdbcType=VARCHAR}
+  </delete>
+  <insert id="insert" parameterType="com.goafanti.common.model.User">
+    insert into user (id, mobile, password, 
+      nickname, email, create_time, 
+      number, type)
+    values (#{id,jdbcType=VARCHAR}, #{mobile,jdbcType=VARCHAR}, #{password,jdbcType=VARCHAR}, 
+      #{nickname,jdbcType=VARCHAR}, #{email,jdbcType=VARCHAR}, #{createTime,jdbcType=TIMESTAMP}, 
+      #{number,jdbcType=BIGINT}, #{type,jdbcType=INTEGER})
+  </insert>
+  <insert id="insertSelective" parameterType="com.goafanti.common.model.User">
+    insert into user
+    <trim prefix="(" suffix=")" suffixOverrides=",">
+      <if test="id != null">
+        id,
+      </if>
+      <if test="mobile != null">
+        mobile,
+      </if>
+      <if test="password != null">
+        password,
+      </if>
+      <if test="nickname != null">
+        nickname,
+      </if>
+      <if test="email != null">
+        email,
+      </if>
+      <if test="createTime != null">
+        create_time,
+      </if>
+      <if test="number != null">
+        number,
+      </if>
+      <if test="type != null">
+        type,
+      </if>
+    </trim>
+    <trim prefix="values (" suffix=")" suffixOverrides=",">
+      <if test="id != null">
+        #{id,jdbcType=VARCHAR},
+      </if>
+      <if test="mobile != null">
+        #{mobile,jdbcType=VARCHAR},
+      </if>
+      <if test="password != null">
+        #{password,jdbcType=VARCHAR},
+      </if>
+      <if test="nickname != null">
+        #{nickname,jdbcType=VARCHAR},
+      </if>
+      <if test="email != null">
+        #{email,jdbcType=VARCHAR},
+      </if>
+      <if test="createTime != null">
+        #{createTime,jdbcType=TIMESTAMP},
+      </if>
+      <if test="number != null">
+        #{number,jdbcType=BIGINT},
+      </if>
+      <if test="type != null">
+        #{type,jdbcType=INTEGER},
+      </if>
+    </trim>
+  </insert>
+  <update id="updateByPrimaryKeySelective" parameterType="com.goafanti.common.model.User">
+    update user
+    <set>
+      <if test="mobile != null">
+        mobile = #{mobile,jdbcType=VARCHAR},
+      </if>
+      <if test="password != null">
+        password = #{password,jdbcType=VARCHAR},
+      </if>
+      <if test="nickname != null">
+        nickname = #{nickname,jdbcType=VARCHAR},
+      </if>
+      <if test="email != null">
+        email = #{email,jdbcType=VARCHAR},
+      </if>
+      <if test="createTime != null">
+        create_time = #{createTime,jdbcType=TIMESTAMP},
+      </if>
+      <if test="number != null">
+        number = #{number,jdbcType=BIGINT},
+      </if>
+      <if test="type != null">
+        type = #{type,jdbcType=INTEGER},
+      </if>
+    </set>
+    where id = #{id,jdbcType=VARCHAR}
+  </update>
+  <update id="updateByPrimaryKey" parameterType="com.goafanti.common.model.User">
+    update user
+    set mobile = #{mobile,jdbcType=VARCHAR},
+      password = #{password,jdbcType=VARCHAR},
+      nickname = #{nickname,jdbcType=VARCHAR},
+      email = #{email,jdbcType=VARCHAR},
+      create_time = #{createTime,jdbcType=TIMESTAMP},
+      number = #{number,jdbcType=BIGINT},
+      type = #{type,jdbcType=INTEGER}
+    where id = #{id,jdbcType=VARCHAR}
+  </update>
+</mapper>

+ 29 - 0
src/main/java/com/goafanti/common/model/BaseModel.java

@@ -0,0 +1,29 @@
+package com.goafanti.common.model;
+
+import java.io.Serializable;
+import java.util.UUID;
+
+import com.alibaba.fastjson.JSON;
+
+public class BaseModel implements Serializable {
+
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = 1L;
+
+	private String id;
+
+	@Override
+	public String toString() {
+		return JSON.toJSONString(this);
+	}
+
+	public String getId() {
+		return id == null ? UUID.randomUUID().toString().replace("-", "") : "";
+	}
+
+	public void setId(String id) {
+		this.id = id;
+	}
+}

+ 39 - 0
src/main/java/com/goafanti/common/model/Permission.java

@@ -0,0 +1,39 @@
+package com.goafanti.common.model;
+
+public class Permission {
+    private String id;
+
+    /**
+    * 权限对应的url path
+    */
+    private String url;
+
+    /**
+    * 权限名字
+    */
+    private String name;
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getUrl() {
+        return url;
+    }
+
+    public void setUrl(String url) {
+        this.url = url;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+}

+ 33 - 0
src/main/java/com/goafanti/common/model/Role.java

@@ -0,0 +1,33 @@
+package com.goafanti.common.model;
+
+public class Role {
+    private String id;
+
+    private String roleName;
+
+    private Integer roleType;
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getRoleName() {
+        return roleName;
+    }
+
+    public void setRoleName(String roleName) {
+        this.roleName = roleName;
+    }
+
+    public Integer getRoleType() {
+        return roleType;
+    }
+
+    public void setRoleType(Integer roleType) {
+        this.roleType = roleType;
+    }
+}

+ 107 - 0
src/main/java/com/goafanti/common/model/User.java

@@ -0,0 +1,107 @@
+package com.goafanti.common.model;
+
+import java.util.Date;
+
+public class User {
+    private String id;
+
+    /**
+    * 用户注册手机号,作为登录依据
+    */
+    private String mobile;
+
+    /**
+    * 密码,md5+salt
+    */
+    private String password;
+
+    /**
+    * 用户昵称
+    */
+    private String nickname;
+
+    /**
+    * 用户邮箱
+    */
+    private String email;
+
+    /**
+    * 注册时间
+    */
+    private Date createTime;
+
+    /**
+    * 用户编号
+    */
+    private Long number;
+
+    /**
+    * 个人账号还是组织账号
+默认0为个人账号,1为组织账号
+    */
+    private Integer type;
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getMobile() {
+        return mobile;
+    }
+
+    public void setMobile(String mobile) {
+        this.mobile = mobile;
+    }
+
+    public String getPassword() {
+        return password;
+    }
+
+    public void setPassword(String password) {
+        this.password = password;
+    }
+
+    public String getNickname() {
+        return nickname;
+    }
+
+    public void setNickname(String nickname) {
+        this.nickname = nickname;
+    }
+
+    public String getEmail() {
+        return email;
+    }
+
+    public void setEmail(String email) {
+        this.email = email;
+    }
+
+    public Date getCreateTime() {
+        return createTime;
+    }
+
+    public void setCreateTime(Date createTime) {
+        this.createTime = createTime;
+    }
+
+    public Long getNumber() {
+        return number;
+    }
+
+    public void setNumber(Long number) {
+        this.number = number;
+    }
+
+    public Integer getType() {
+        return type;
+    }
+
+    public void setType(Integer type) {
+        this.type = type;
+    }
+}

+ 43 - 0
src/main/java/com/goafanti/common/utils/ArrayUtils.java

@@ -0,0 +1,43 @@
+package com.goafanti.common.utils;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.TreeSet;
+
+import org.apache.commons.lang3.StringUtils;
+
+public class ArrayUtils extends org.apache.commons.lang3.ArrayUtils {
+
+	/**
+	 * 把数组的空数据去掉,并返回一个list
+	 * 
+	 * @param array
+	 * @return ArrayList
+	 */
+	public static List<String> removeBlank(String[] array) {
+		List<String> list = new ArrayList<String>();
+		for (String string : array) {
+			if (StringUtils.isNotBlank(string)) {
+				list.add(string);
+			}
+		}
+		return list;
+	}
+
+	/**
+	 * 把数组转换成set
+	 * 
+	 * @param array
+	 * @return
+	 */
+	public static Set<?> array2Set(Object[] array) {
+		Set<Object> set = new TreeSet<Object>();
+		for (Object id : array) {
+			if (null != id) {
+				set.add(id);
+			}
+		}
+		return set;
+	}
+}

+ 53 - 0
src/main/java/com/goafanti/common/utils/DateUtils.java

@@ -0,0 +1,53 @@
+package com.goafanti.common.utils;
+
+import java.util.Calendar;
+import java.util.Date;
+
+public class DateUtils extends org.apache.commons.lang3.time.DateUtils {
+
+	/**
+	 * Determines how two dates compare up to no more than the specified most
+	 * significant field.
+	 * 
+	 * @param date1
+	 *            the first date, not <code>null</code>
+	 * @param date2
+	 *            the second date, not <code>null</code>
+	 * @param field
+	 *            the field from <code>Calendar</code>
+	 * @return diff millis
+	 * @throws IllegalArgumentException
+	 *             if any argument is <code>null</code>
+	 * @see #truncate(Calendar, int)
+	 * @see #truncatedCompareTo(Date, Date, int)
+	 * @since 3.0
+	 */
+	public static long truncatedDiffTo(final Date date1, final Date date2, final int field) {
+		final Date truncatedDate1 = truncate(date1, field);
+		final Date truncatedDate2 = truncate(date2, field);
+		long thisTime = truncatedDate1.getTime();
+		long anotherTime = truncatedDate2.getTime();
+		return Math.abs(thisTime - anotherTime);
+	}
+
+	/**
+	 * Determines how two dates compare up to no more than the specified most
+	 * significant field.
+	 * 
+	 * @param date1
+	 *            the first date, not <code>null</code>
+	 * @param date2
+	 *            the second date, not <code>null</code>
+	 * @param field
+	 *            the field from <code>Calendar</code>
+	 * @return diff millis
+	 * @throws IllegalArgumentException
+	 *             if any argument is <code>null</code>
+	 * @see #truncate(Calendar, int)
+	 * @see #truncatedCompareTo(Date, Date, int)
+	 * @since 3.0
+	 */
+	public static long truncatedHourDiffTo(final Date date1, final Date date2) {
+		return truncatedDiffTo(date1, date2, Calendar.HOUR) / 3600000;
+	}
+}

+ 121 - 0
src/main/java/com/goafanti/common/utils/LoggerUtils.java

@@ -0,0 +1,121 @@
+package com.goafanti.common.utils;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.log4j.Logger;
+
+public class LoggerUtils {
+	/**
+	 * 是否开启Debug
+	 */
+	public static boolean isDebug = Logger.getLogger(LoggerUtils.class).isDebugEnabled();
+
+	/**
+	 * Debug 输出
+	 * 
+	 * @param clazz
+	 *            目标.Class
+	 * @param message
+	 *            输出信息
+	 */
+	public static void debug(Class<? extends Object> clazz, String message) {
+		if (!isDebug)
+			return;
+		Logger logger = Logger.getLogger(clazz);
+		logger.debug(message);
+	}
+
+	/**
+	 * Debug 输出
+	 * 
+	 * @param clazz
+	 *            目标.Class
+	 * @param fmtString
+	 *            输出信息key
+	 * @param value
+	 *            输出信息value
+	 */
+	public static void fmtDebug(Class<? extends Object> clazz, String fmtString, Object... value) {
+		if (!isDebug)
+			return;
+		if (StringUtils.isBlank(fmtString)) {
+			return;
+		}
+		if (null != value && value.length != 0) {
+			fmtString = String.format(fmtString, value);
+		}
+		debug(clazz, fmtString);
+	}
+
+	/**
+	 * Error 输出
+	 * 
+	 * @param clazz
+	 *            目标.Class
+	 * @param message
+	 *            输出信息
+	 * @param e
+	 *            异常类
+	 */
+	public static void error(Class<? extends Object> clazz, String message, Exception e) {
+		Logger logger = Logger.getLogger(clazz);
+		if (null == e) {
+			logger.error(message);
+			return;
+		}
+		logger.error(message, e);
+	}
+
+	/**
+	 * Error 输出
+	 * 
+	 * @param clazz
+	 *            目标.Class
+	 * @param message
+	 *            输出信息
+	 */
+	public static void error(Class<? extends Object> clazz, String message) {
+		error(clazz, message, null);
+	}
+
+	/**
+	 * 异常填充值输出
+	 * 
+	 * @param clazz
+	 *            目标.Class
+	 * @param fmtString
+	 *            输出信息key
+	 * @param e
+	 *            异常类
+	 * @param value
+	 *            输出信息value
+	 */
+	public static void fmtError(Class<? extends Object> clazz, Exception e, String fmtString, Object... value) {
+		if (StringUtils.isBlank(fmtString)) {
+			return;
+		}
+		if (null != value && value.length != 0) {
+			fmtString = String.format(fmtString, value);
+		}
+		error(clazz, fmtString, e);
+	}
+
+	/**
+	 * 异常填充值输出
+	 * 
+	 * @param clazz
+	 *            目标.Class
+	 * @param fmtString
+	 *            输出信息key
+	 * @param value
+	 *            输出信息value
+	 */
+	public static void fmtError(Class<? extends Object> clazz, String fmtString, Object... value) {
+		if (StringUtils.isBlank(fmtString)) {
+			return;
+		}
+		if (null != value && value.length != 0) {
+			fmtString = String.format(fmtString, value);
+		}
+		error(clazz, fmtString);
+	}
+}

+ 41 - 0
src/main/java/com/goafanti/common/utils/PasswordUtil.java

@@ -0,0 +1,41 @@
+package com.goafanti.common.utils;
+
+import java.util.Date;
+
+import org.apache.shiro.crypto.hash.SimpleHash;
+import org.apache.shiro.util.ByteSource;
+
+import com.goafanti.common.model.User;
+
+public class PasswordUtil {
+	private String algorithmName = "md5";
+	private int hashIterations = 2;
+
+	public void setAlgorithmName(String algorithmName) {
+		this.algorithmName = algorithmName;
+	}
+
+	public void setHashIterations(int hashIterations) {
+		this.hashIterations = hashIterations;
+	}
+
+	public void encryptPassword(User user) {
+		user.setPassword(getEncryptPwd(user));
+	}
+
+	public String getEncryptPwd(User user) {
+		return new SimpleHash(algorithmName, user.getPassword(), getSalt(user), hashIterations).toHex();
+	}
+
+	public String getEncryptPwd(String pwd, Date date) {
+		return new SimpleHash(algorithmName, pwd, getSalt(date), hashIterations).toHex();
+	}
+
+	public ByteSource getSalt(User user) {
+		return ByteSource.Util.bytes(String.valueOf(user.getCreateTime().getTime()));
+	}
+
+	public ByteSource getSalt(Date date) {
+		return ByteSource.Util.bytes(String.valueOf(date.getTime()));
+	}
+}

+ 71 - 0
src/main/java/com/goafanti/common/utils/SerializeUtil.java

@@ -0,0 +1,71 @@
+package com.goafanti.common.utils;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+
+@SuppressWarnings("unchecked")
+public class SerializeUtil {
+
+	public static byte[] serialize(Object value) {
+		if (value == null) {
+			throw new NullPointerException("Can't serialize null");
+		}
+		byte[] rv = null;
+		ByteArrayOutputStream bos = null;
+		ObjectOutputStream os = null;
+		try {
+			bos = new ByteArrayOutputStream();
+			os = new ObjectOutputStream(bos);
+			os.writeObject(value);
+			os.close();
+			bos.close();
+			rv = bos.toByteArray();
+		} catch (Exception e) {
+			e.printStackTrace();
+			System.out.println("serialize error");
+		} finally {
+			close(os);
+			close(bos);
+		}
+		return rv;
+	}
+
+	public static Object deserialize(byte[] in) {
+		return deserialize(in, Object.class);
+	}
+
+	public static <T> T deserialize(byte[] in, Class<T>... requiredType) {
+		Object rv = null;
+		ByteArrayInputStream bis = null;
+		ObjectInputStream is = null;
+		try {
+			if (in != null) {
+				bis = new ByteArrayInputStream(in);
+				is = new ObjectInputStream(bis);
+				rv = is.readObject();
+			}
+		} catch (Exception e) {
+			e.printStackTrace();
+			System.out.println("deserialize error");
+		} finally {
+			close(is);
+			close(bis);
+		}
+		return (T) rv;
+	}
+
+	private static void close(Closeable closeable) {
+		if (closeable != null)
+			try {
+				closeable.close();
+			} catch (IOException e) {
+				e.printStackTrace();
+				System.out.println("close stream error");
+			}
+	}
+
+}

+ 49 - 0
src/main/java/com/goafanti/common/utils/SpringContextUtils.java

@@ -0,0 +1,49 @@
+package com.goafanti.common.utils;
+
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.NoSuchBeanDefinitionException;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+
+public class SpringContextUtils implements ApplicationContextAware {
+	private static ApplicationContext applicationContext;
+
+	// 实现
+	@Override
+	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
+		SpringContextUtils.applicationContext = applicationContext;
+	}
+
+	public static ApplicationContext getApplicationContext() {
+		return applicationContext;
+	}
+
+	public static Object getBean(String name) throws BeansException {
+		try {
+			return applicationContext.getBean(name);
+		} catch (Exception e) {
+			throw new RuntimeException("获取的Bean不存在!");
+		}
+	}
+
+	public static <T> T getBean(String name, Class<T> requiredType) throws BeansException {
+		return applicationContext.getBean(name, requiredType);
+	}
+
+	public static boolean containsBean(String name) {
+		return applicationContext.containsBean(name);
+	}
+
+	public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException {
+		return applicationContext.isSingleton(name);
+	}
+
+	public static Class<? extends Object> getType(String name) throws NoSuchBeanDefinitionException {
+		return applicationContext.getType(name);
+	}
+
+	public static String[] getAliases(String name) throws NoSuchBeanDefinitionException {
+		return applicationContext.getAliases(name);
+	}
+
+}

+ 11 - 0
src/main/java/com/goafanti/common/utils/StringUtils.java

@@ -0,0 +1,11 @@
+package com.goafanti.common.utils;
+
+import java.util.regex.Pattern;
+
+public class StringUtils extends org.apache.commons.lang3.StringUtils {
+	private static Pattern FRACTIONAL_NUMERIC = Pattern.compile("^[\\-+]?\\d+(\\.\\d+)?$");
+
+	public static boolean isFractionalNumeric(String val) {
+		return FRACTIONAL_NUMERIC.matcher(val).matches();
+	}
+}

+ 327 - 0
src/main/java/com/goafanti/common/utils/VerifyCodeUtils.java

@@ -0,0 +1,327 @@
+package com.goafanti.common.utils;
+
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.RenderingHints;
+import java.awt.geom.AffineTransform;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Arrays;
+import java.util.Random;
+
+import javax.imageio.ImageIO;
+
+import com.goafanti.core.shiro.token.TokenManager;
+
+public class VerifyCodeUtils {
+
+	// 使用到Algerian字体,系统里没有的话需要安装字体,字体只显示大写,去掉了1,0,i,o几个容易混淆的字符
+	public static final String VERIFY_CODES = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ";
+	// 验证码的Key
+	public static final String V_CODE = "_CODE";
+	private static Random random = new Random();
+
+	/**
+	 * 验证码对象
+	 * 
+	 */
+	public static class Verify {
+
+		private String code;// 如 1 + 2
+
+		private Integer value;// 如 3
+
+		public String getCode() {
+			return code;
+		}
+
+		public void setCode(String code) {
+			this.code = code;
+		}
+
+		public Integer getValue() {
+			return value;
+		}
+
+		public void setValue(Integer value) {
+			this.value = value;
+		}
+	}
+
+	/**
+	 * 使用系统默认字符源生成验证码
+	 * 
+	 * @param verifySize
+	 *            验证码长度
+	 * @return
+	 */
+	public static Verify generateVerify() {
+		int number1 = new Random().nextInt(10) + 1;
+		int number2 = new Random().nextInt(10) + 1;
+		Verify entity = new Verify();
+		entity.setCode(number1 + " x " + number2);
+		entity.setValue(number1 + number2);
+		return entity;
+	}
+
+	/**
+	 * 使用系统默认字符源生成验证码
+	 * 
+	 * @param verifySize
+	 *            验证码长度
+	 * @return
+	 */
+	public static String generateVerifyCode(int verifySize) {
+		return generateVerifyCode(verifySize, VERIFY_CODES);
+	}
+
+	/**
+	 * 清除验证码
+	 */
+	public static void clearVerifyCode() {
+		TokenManager.getSession().removeAttribute(V_CODE);
+	}
+
+	/**
+	 * 对比验证码
+	 */
+	public static boolean verifyCode(String code) {
+		String v = (String) TokenManager.getVal2Session(V_CODE);
+		return StringUtils.equals(v, StringUtils.lowerCase(code));
+	}
+
+	/**
+	 * 使用指定源生成验证码
+	 * 
+	 * @param verifySize
+	 *            验证码长度
+	 * @param sources
+	 *            验证码字符源
+	 * @return
+	 */
+	public static String generateVerifyCode(int verifySize, String sources) {
+		if (sources == null || sources.length() == 0) {
+			sources = VERIFY_CODES;
+		}
+		int codesLen = sources.length();
+		Random rand = new Random(System.currentTimeMillis());
+		StringBuilder verifyCode = new StringBuilder(verifySize);
+		for (int i = 0; i < verifySize; i++) {
+			verifyCode.append(sources.charAt(rand.nextInt(codesLen - 1)));
+		}
+		return verifyCode.toString();
+	}
+
+	/**
+	 * 生成随机验证码文件,并返回验证码值
+	 * 
+	 * @param w
+	 * @param h
+	 * @param outputFile
+	 * @param verifySize
+	 * @return
+	 * @throws IOException
+	 */
+	public static String outputVerifyImage(int w, int h, File outputFile, int verifySize) throws IOException {
+		String verifyCode = generateVerifyCode(verifySize);
+		outputImage(w, h, outputFile, verifyCode);
+		return verifyCode;
+	}
+
+	/**
+	 * 输出随机验证码图片流,并返回验证码值
+	 * 
+	 * @param w
+	 * @param h
+	 * @param os
+	 * @param verifySize
+	 * @return
+	 * @throws IOException
+	 */
+	public static String outputVerifyImage(int w, int h, OutputStream os, int verifySize) throws IOException {
+		String verifyCode = generateVerifyCode(verifySize);
+		outputImage(w, h, os, verifyCode);
+		return verifyCode;
+	}
+
+	/**
+	 * 生成指定验证码图像文件
+	 * 
+	 * @param w
+	 * @param h
+	 * @param outputFile
+	 * @param code
+	 * @throws IOException
+	 */
+	public static void outputImage(int w, int h, File outputFile, String code) throws IOException {
+		if (outputFile == null) {
+			return;
+		}
+		File dir = outputFile.getParentFile();
+		if (!dir.exists()) {
+			dir.mkdirs();
+		}
+		try {
+			outputFile.createNewFile();
+			FileOutputStream fos = new FileOutputStream(outputFile);
+			outputImage(w, h, fos, code);
+			fos.close();
+		} catch (IOException e) {
+			throw e;
+		}
+	}
+
+	/**
+	 * 输出指定验证码图片流
+	 * 
+	 * @param w
+	 * @param h
+	 * @param os
+	 * @param code
+	 * @throws IOException
+	 */
+	public static void outputImage(int w, int h, OutputStream os, String code) throws IOException {
+		int verifySize = code.length();
+		BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
+		Random rand = new Random();
+		Graphics2D g2 = image.createGraphics();
+		g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+		Color[] colors = new Color[5];
+		Color[] colorSpaces = new Color[] { Color.WHITE, Color.CYAN, Color.GRAY, Color.LIGHT_GRAY, Color.MAGENTA,
+				Color.ORANGE, Color.PINK, Color.YELLOW };
+		float[] fractions = new float[colors.length];
+		for (int i = 0; i < colors.length; i++) {
+			colors[i] = colorSpaces[rand.nextInt(colorSpaces.length)];
+			fractions[i] = rand.nextFloat();
+		}
+		Arrays.sort(fractions);
+
+		g2.setColor(Color.GRAY);// 设置边框色
+		g2.fillRect(0, 0, w, h);
+
+		Color c = getRandColor(200, 250);
+		g2.setColor(c);// 设置背景色
+		g2.fillRect(0, 2, w, h - 4);
+
+		// 绘制干扰线
+		Random random = new Random();
+		g2.setColor(getRandColor(160, 200));// 设置线条的颜色
+		for (int i = 0; i < 20; i++) {
+			int x = random.nextInt(w - 1);
+			int y = random.nextInt(h - 1);
+			int xl = random.nextInt(6) + 1;
+			int yl = random.nextInt(12) + 1;
+			g2.drawLine(x, y, x + xl + 40, y + yl + 20);
+		}
+
+		// 添加噪点
+		float yawpRate = 0.05f;// 噪声率
+		int area = (int) (yawpRate * w * h);
+		for (int i = 0; i < area; i++) {
+			int x = random.nextInt(w);
+			int y = random.nextInt(h);
+			int rgb = getRandomIntColor();
+			image.setRGB(x, y, rgb);
+		}
+
+		shear(g2, w, h, c);// 使图片扭曲
+
+		g2.setColor(getRandColor(100, 160));
+		int fontSize = h - 4;
+		Font font = new Font("Algerian", Font.ITALIC, fontSize);
+		g2.setFont(font);
+		char[] chars = code.toCharArray();
+		for (int i = 0; i < verifySize; i++) {
+			AffineTransform affine = new AffineTransform();
+			affine.setToRotation(Math.PI / 4 * rand.nextDouble() * (rand.nextBoolean() ? 1 : -1),
+					(w / verifySize) * i + fontSize / 2, h / 2);
+			g2.setTransform(affine);
+			g2.drawChars(chars, i, 1, ((w - 10) / verifySize) * i + 5, h / 2 + fontSize / 2 - 10);
+		}
+
+		g2.dispose();
+		ImageIO.write(image, "jpg", os);
+	}
+
+	private static Color getRandColor(int fc, int bc) {
+		if (fc > 255)
+			fc = 255;
+		if (bc > 255)
+			bc = 255;
+		int r = fc + random.nextInt(bc - fc);
+		int g = fc + random.nextInt(bc - fc);
+		int b = fc + random.nextInt(bc - fc);
+		return new Color(r, g, b);
+	}
+
+	private static int getRandomIntColor() {
+		int[] rgb = getRandomRgb();
+		int color = 0;
+		for (int c : rgb) {
+			color = color << 8;
+			color = color | c;
+		}
+		return color;
+	}
+
+	private static int[] getRandomRgb() {
+		int[] rgb = new int[3];
+		for (int i = 0; i < 3; i++) {
+			rgb[i] = random.nextInt(255);
+		}
+		return rgb;
+	}
+
+	private static void shear(Graphics g, int w1, int h1, Color color) {
+		shearX(g, w1, h1, color);
+		shearY(g, w1, h1, color);
+	}
+
+	private static void shearX(Graphics g, int w1, int h1, Color color) {
+
+		int period = random.nextInt(2);
+
+		boolean borderGap = true;
+		int frames = 1;
+		int phase = random.nextInt(2);
+
+		for (int i = 0; i < h1; i++) {
+			double d = (double) (period >> 1)
+					* Math.sin((double) i / (double) period + (6.2831853071795862D * (double) phase) / (double) frames);
+			g.copyArea(0, i, w1, 1, (int) d, 0);
+			if (borderGap) {
+				g.setColor(color);
+				g.drawLine((int) d, i, 0, i);
+				g.drawLine((int) d + w1, i, w1, i);
+			}
+		}
+
+	}
+
+	private static void shearY(Graphics g, int w1, int h1, Color color) {
+
+		int period = random.nextInt(40) + 10; // 50;
+
+		boolean borderGap = true;
+		int frames = 20;
+		int phase = 7;
+		for (int i = 0; i < w1; i++) {
+			double d = (double) (period >> 1)
+					* Math.sin((double) i / (double) period + (6.2831853071795862D * (double) phase) / (double) frames);
+			g.copyArea(i, 0, 1, h1, 0, (int) d);
+			if (borderGap) {
+				g.setColor(color);
+				g.drawLine(i, (int) d, i, 0);
+				g.drawLine(i, (int) d + h1, i, h1);
+			}
+
+		}
+
+	}
+
+}

+ 165 - 0
src/main/java/com/goafanti/core/config/INI4j.java

@@ -0,0 +1,165 @@
+package com.goafanti.core.config;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.springframework.core.io.ClassPathResource;
+
+public class INI4j {
+
+	/**
+	 * 用linked hash map 来保持有序的读取
+	 * 
+	 */
+	final LinkedHashMap<String, LinkedHashMap<String, String>> coreMap = new LinkedHashMap<String, LinkedHashMap<String, String>>();
+	/**
+	 * 当前Section的引用
+	 */
+	String currentSection = null;
+
+	/**
+	 * 读取
+	 * 
+	 * @param file
+	 *            文件
+	 * @throws FileNotFoundException
+	 */
+	public INI4j(File file) throws FileNotFoundException {
+		this.init(new BufferedReader(new FileReader(file)));
+	}
+
+	/***
+	 * 重载读取
+	 * 
+	 * @param path
+	 *            给文件路径
+	 * @throws FileNotFoundException
+	 */
+	public INI4j(String path) throws FileNotFoundException {
+		this.init(new BufferedReader(new FileReader(path)));
+	}
+
+	/***
+	 * 重载读取
+	 * 
+	 * @param source
+	 *            ClassPathResource 文件,如果文件在resource 里,那么直接 new
+	 *            ClassPathResource("file name");
+	 * @throws IOException
+	 */
+	public INI4j(ClassPathResource source) throws IOException {
+		this(source.getFile());
+	}
+
+	void init(BufferedReader bufferedReader) {
+		try {
+			read(bufferedReader);
+		} catch (IOException e) {
+			e.printStackTrace();
+			throw new RuntimeException("IO Exception:" + e);
+		}
+	}
+
+	/**
+	 * 读取文件
+	 * 
+	 * @param reader
+	 * @throws IOException
+	 */
+	void read(BufferedReader reader) throws IOException {
+		String line = null;
+		while ((line = reader.readLine()) != null) {
+			parseLine(line);
+		}
+	}
+
+	/**
+	 * 转换
+	 * 
+	 * @param line
+	 */
+	void parseLine(String line) {
+		line = line.trim();
+		// 此部分为注释
+		if (line.matches("^\\#.*$")) {
+			return;
+		} else if (line.matches("^\\[\\S+\\]$")) {
+			// section
+			String section = line.replaceFirst("^\\[(\\S+)\\]$", "$1");
+			addSection(section);
+		} else if (line.matches("^\\S+=.*$")) {
+			// key ,value
+			int i = line.indexOf("=");
+			String key = line.substring(0, i).trim();
+			String value = line.substring(i + 1).trim();
+			addKeyValue(currentSection, key, value);
+		}
+	}
+
+	/**
+	 * 增加新的Key和Value
+	 * 
+	 * @param currentSection
+	 * @param key
+	 * @param value
+	 */
+	void addKeyValue(String currentSection, String key, String value) {
+		if (!coreMap.containsKey(currentSection)) {
+			return;
+		}
+		Map<String, String> childMap = coreMap.get(currentSection);
+		childMap.put(key, value);
+	}
+
+	/**
+	 * 增加Section
+	 * 
+	 * @param section
+	 */
+	void addSection(String section) {
+		if (!coreMap.containsKey(section)) {
+			currentSection = section;
+			LinkedHashMap<String, String> childMap = new LinkedHashMap<String, String>();
+			coreMap.put(section, childMap);
+		}
+	}
+
+	/**
+	 * 获取配置文件指定Section和指定子键的值
+	 * 
+	 * @param section
+	 * @param key
+	 * @return
+	 */
+	public String get(String section, String key) {
+		if (coreMap.containsKey(section)) {
+			return get(section).containsKey(key) ? get(section).get(key) : null;
+		}
+		return null;
+	}
+
+	/**
+	 * 获取配置文件指定Section的子键和值
+	 * 
+	 * @param section
+	 * @return
+	 */
+	public Map<String, String> get(String section) {
+		return coreMap.containsKey(section) ? coreMap.get(section) : null;
+	}
+
+	/**
+	 * 获取这个配置文件的节点和值
+	 * 
+	 * @return
+	 */
+	public LinkedHashMap<String, LinkedHashMap<String, String>> get() {
+		return coreMap;
+	}
+
+}

+ 244 - 0
src/main/java/com/goafanti/core/mybatis/BaseMybatisDao.java

@@ -0,0 +1,244 @@
+package com.goafanti.core.mybatis;
+
+import java.lang.reflect.ParameterizedType;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.ibatis.mapping.BoundSql;
+import org.apache.ibatis.mapping.ParameterMapping;
+import org.apache.ibatis.session.Configuration;
+import org.mybatis.spring.SqlSessionTemplate;
+import org.mybatis.spring.support.SqlSessionDaoSupport;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import com.goafanti.common.utils.LoggerUtils;
+import com.goafanti.core.mybatis.page.MysqlDialect;
+import com.goafanti.core.mybatis.page.Pagination;
+
+@SuppressWarnings({ "unchecked" })
+public class BaseMybatisDao<T> extends SqlSessionDaoSupport {
+
+	private String NAMESPACE;
+	final static Class<? extends Object> SELF = BaseMybatisDao.class;
+	protected final Log logger = LogFactory.getLog(BaseMybatisDao.class);
+	/** 默认的查询Sql id */
+	final static String DEFAULT_SQL_ID = "findAll";
+	/** 默认的查询Count sql id **/
+	final static String DEFAULT_COUNT_SQL_ID = "findCount";
+
+	public BaseMybatisDao() {
+		try {
+			Object genericClz = getClass().getGenericSuperclass();
+			if (genericClz instanceof ParameterizedType) {
+				Class<T> entityClass = (Class<T>) ((ParameterizedType) genericClz).getActualTypeArguments()[0];
+				NAMESPACE = entityClass.getPackage().getName() + "." + entityClass.getSimpleName();
+			}
+		} catch (RuntimeException e) {
+			LoggerUtils.error(SELF, "初始化失败,继承BaseMybatisDao,没有泛型!");
+		}
+	}
+
+	@Autowired
+	public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
+		super.setSqlSessionTemplate(sqlSessionTemplate);
+	}
+
+	/**
+	 * 根据Sql id 去查询 分页对象
+	 * 
+	 * @param sqlId
+	 *            对应mapper.xml 里的Sql Id
+	 * @param params
+	 *            参数<String,Object>
+	 * @param pageNo
+	 *            number
+	 * @param pageSize
+	 *            size
+	 * @return
+	 */
+	public Pagination<?> findByPageBySqlId(String sqlId, Map<String, Object> params, Integer pageNo, Integer pageSize) {
+
+		pageNo = null == pageNo ? 1 : pageNo;
+		pageSize = null == pageSize ? 10 : pageSize;
+
+		sqlId = String.format("%s.%s", NAMESPACE, sqlId);
+
+		Pagination<?> page = new Pagination<Object>();
+		page.setPageNo(pageNo);
+		page.setPageSize(pageSize);
+		Configuration c = this.getSqlSession().getConfiguration();
+		int offset = (page.getPageNo() - 1) * page.getPageSize();
+		String page_sql = String.format(" limit %s , %s", offset, pageSize);
+		params.put("page_sql", page_sql);
+
+		BoundSql boundSql = c.getMappedStatement(sqlId).getBoundSql(params);
+		String sqlcode = boundSql.getSql();
+
+		LoggerUtils.fmtDebug(SELF, "findByPageBySqlId sql : %s", sqlcode);
+		String countCode = "", countId = "";
+		BoundSql countSql = null;
+		/**
+		 * sql id 和 count id 用同一个
+		 */
+		if (StringUtils.isBlank(sqlId)) {
+			countCode = sqlcode;
+			countSql = boundSql;
+		} else {
+			countId = sqlId;
+
+			Map<String, Object> countMap = new HashMap<String, Object>();
+			countMap.putAll(params);
+			countMap.remove("page_sql");// 去掉,分页的参数。
+			countSql = c.getMappedStatement(countId).getBoundSql(countMap);
+			countCode = countSql.getSql();
+		}
+		try {
+			Connection conn = this.getSqlSession().getConnection();
+
+			List<?> resultList = this.getSqlSession().selectList(sqlId, params);
+			page.setList(resultList);
+			PreparedStatement ps = getPreparedStatement(countCode, countSql.getParameterMappings(), params, conn);
+			ps.execute();
+			ResultSet set = ps.getResultSet();
+
+			while (set.next()) {
+				page.setTotalCount(set.getInt(1));
+			}
+		} catch (Exception e) {
+			LoggerUtils.error(SELF, "jdbc.error.code.findByPageBySqlId", e);
+		}
+		return page;
+	}
+
+	/**
+	 * 根据Sql ID 查询List,而不要查询分页的页码
+	 * 
+	 * @param sqlId
+	 *            对应mapper.xml 里的Sql Id[主语句]
+	 * @param params
+	 *            参数<String,Object>
+	 * @param pageNo
+	 *            number
+	 * @param pageSize
+	 *            size
+	 * @return
+	 */
+	public List<?> findList(String sqlId, Map<String, Object> params, Integer pageNo, Integer pageSize) {
+		pageNo = null == pageNo ? 1 : pageNo;
+		pageSize = null == pageSize ? 10 : pageSize;
+
+		int offset = (pageNo - 1) * pageSize;
+		String page_sql = String.format(" limit %s , %s", offset, pageSize);
+		params.put("page_sql", page_sql);
+		sqlId = String.format("%s.%s", NAMESPACE, sqlId);
+
+		List<?> resultList = this.getSqlSession().selectList(sqlId, params);
+		return resultList;
+	}
+
+	/**
+	 * 当Sql ID 是 default findAll的情况下。
+	 * 
+	 * @param params
+	 * @param pageNo
+	 * @param pageSize
+	 * @param requiredType
+	 *            返回的类型[可以不传参]
+	 * @return
+	 */
+	public List<?> findList(Map<String, Object> params, Integer pageNo, Integer pageSize) {
+		return findList(DEFAULT_SQL_ID, params, pageNo, pageSize);
+	}
+
+	/**
+	 * 分页
+	 * 
+	 * @param sqlId
+	 *            主语句
+	 * @param countId
+	 *            Count 语句
+	 * @param params
+	 *            参数
+	 * @param pageNo
+	 *            第几页
+	 * @param pageSize每页显示多少条
+	 * @param requiredType
+	 *            返回的类型[可以不传参]
+	 * @return
+	 */
+	public Pagination<?> findPage(String sqlId, String countId, Map<String, Object> params, Integer pageNo,
+			Integer pageSize) {
+		pageNo = null == pageNo ? 1 : pageNo;
+		pageSize = null == pageSize ? 10 : pageSize;
+		Pagination<?> page = new Pagination<Object>();
+		page.setPageNo(pageNo);
+		page.setPageSize(pageSize);
+		int offset = (page.getPageNo() - 1) * page.getPageSize();
+		String page_sql = String.format(" limit  %s , %s ", offset, pageSize);
+		params.put("page_sql", page_sql);
+
+		sqlId = String.format("%s.%s", NAMESPACE, sqlId);
+
+		countId = String.format("%s.%s", NAMESPACE, countId);
+		
+		try {
+			List<?> resultList = this.getSqlSession().selectList(sqlId, params);
+
+			page.setList(resultList);
+
+			page.setTotalCount(this.getSqlSession().selectOne(countId, params));
+
+		} catch (Exception e) {
+			LoggerUtils.error(SELF, "jdbc.error.code.findByPageBySqlId", e);
+		}
+		return page;
+
+	}
+
+	/**
+	 * 重载减少参数DEFAULT_SQL_ID, "findCount"
+	 * 
+	 * @param params
+	 * @param pageNo
+	 * @param pageSize
+	 * @return
+	 */
+	public Pagination<?> findPage(Map<String, Object> params, Integer pageNo, Integer pageSize) {
+
+		return findPage(DEFAULT_SQL_ID, DEFAULT_COUNT_SQL_ID, params, pageNo, pageSize);
+	}
+
+	/**
+	 * 组装
+	 * 
+	 * @param sql
+	 * @param parameterMappingList
+	 * @param params
+	 * @param conn
+	 * @return
+	 * @throws SQLException
+	 */
+	private PreparedStatement getPreparedStatement(String sql, List<ParameterMapping> parameterMappingList,
+			Map<String, Object> params, Connection conn) throws SQLException {
+		/**
+		 * 分页根据数据库分页
+		 */
+		MysqlDialect o = new MysqlDialect();
+
+		PreparedStatement ps = conn.prepareStatement(o.getCountSqlString(sql));
+		int index = 1;
+		for (int i = 0; i < parameterMappingList.size(); i++) {
+			ps.setObject(index++, params.get(parameterMappingList.get(i).getProperty()));
+		}
+		return ps;
+	}
+
+}

+ 25 - 0
src/main/java/com/goafanti/core/mybatis/page/Dialect.java

@@ -0,0 +1,25 @@
+package com.goafanti.core.mybatis.page;
+
+public interface Dialect {
+	
+	public static final String RS_COLUMN = "totalCount";
+    
+    public boolean supportsLimit();
+
+    /**
+     * 以传入SQL为基础组装分页查询的SQL语句,传递给myBatis调用
+     * @param sql 原始SQL
+     * @param offset 分页查询的记录的偏移量
+     * @param limit 每页限定记录数
+     * @return 拼装好的SQL
+     */
+    public String getLimitSqlString(String sql, int offset, int limit);
+    
+    /**
+     * 以传入SQL为基础组装总记录数查询的SQL语句
+     * @param sql 原始SQL
+     * @return 拼装好的SQL
+     */
+    public String getCountSqlString(String sql);
+}
+

+ 49 - 0
src/main/java/com/goafanti/core/mybatis/page/MysqlDialect.java

@@ -0,0 +1,49 @@
+package com.goafanti.core.mybatis.page;
+
+public class MysqlDialect implements Dialect {
+	protected static final String SQL_END_DELIMITER = ";";
+
+	public String getLimitSqlString(String sql, int offset, int limit) {
+		sql = sql.trim();
+		boolean isForUpdate = false;
+		if (sql.toLowerCase().endsWith(" for update")) {
+			sql = sql.substring(0, sql.length() - 11);
+			isForUpdate = true;
+		}
+
+		if (offset < 0) {
+			offset = 0;
+		}
+
+		StringBuffer pagingSelect = new StringBuffer();
+
+		pagingSelect.append(sql + " limit " + offset + "," + limit);
+
+		if (isForUpdate) {
+			pagingSelect.append(" for update");
+		}
+
+		return pagingSelect.toString();
+	}
+
+	public String getCountSqlString(String sql) {
+		sql = trim(sql);
+		StringBuffer sb = new StringBuffer(sql.length() + 10);
+		sb.append("SELECT COUNT(1) AS " + RS_COLUMN + " FROM  ( ");
+		sb.append(sql);
+		sb.append(")a");
+		return sb.toString();
+	}
+
+	public boolean supportsLimit() {
+		return true;
+	}
+
+	private static String trim(String sql) {
+		sql = sql.trim();
+		if (sql.endsWith(SQL_END_DELIMITER)) {
+			sql = sql.substring(0, sql.length() - 1 - SQL_END_DELIMITER.length());
+		}
+		return sql;
+	}
+}

+ 56 - 0
src/main/java/com/goafanti/core/mybatis/page/Paginable.java

@@ -0,0 +1,56 @@
+package com.goafanti.core.mybatis.page;
+
+public interface Paginable {
+
+		/**
+		 * 总记录数
+		 * 
+		 * @returnÏ
+		 */
+		public int getTotalCount();
+
+		/**
+		 * 总页数
+		 * 
+		 * @return
+		 */
+		public int getTotalPage();
+
+		/**
+		 * 每页记录数
+		 * 
+		 * @return
+		 */
+		public int getPageSize();
+
+		/**
+		 * 当前页号
+		 * 
+		 * @return
+		 */
+		public int getPageNo();
+
+		/**
+		 * 是否第一页
+		 *
+		 * @return
+		 */
+		public boolean isFirstPage();
+
+		/**
+		 * 是否最后一页
+		 * 
+		 * @return
+		 */
+		public boolean isLastPage();
+
+		/**
+		 * 返回下页的页号
+		 */
+		public int getNextPage();
+
+		/**
+		 * 返回上页的页号
+		 */
+		public int getPrePage();
+	}

+ 38 - 0
src/main/java/com/goafanti/core/mybatis/page/Pagination.java

@@ -0,0 +1,38 @@
+package com.goafanti.core.mybatis.page;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class Pagination<T> extends SimplePage implements java.io.Serializable, Paginable {
+
+	private static final long serialVersionUID = 1L;
+	/**
+	 * 当前页的数据
+	 */
+	private List<?> list = new ArrayList<>();
+
+	public Pagination() {
+	}
+
+	public Pagination(int pageNo, int pageSize, int totalCount) {
+		super(pageNo, pageSize, totalCount);
+	}
+
+	public Pagination(int pageNo, int pageSize, int totalCount, List<?> list) {
+		super(pageNo, pageSize, totalCount);
+		this.list = list;
+	}
+
+	public int getFirstResult() {
+		return (pageNo - 1) * pageSize;
+	}
+
+	public List<?> getList() {
+		return list;
+	}
+
+	public void setList(List<?> list) {
+		this.list = list;
+	}
+
+}

+ 122 - 0
src/main/java/com/goafanti/core/mybatis/page/SimplePage.java

@@ -0,0 +1,122 @@
+package com.goafanti.core.mybatis.page;
+
+public class SimplePage implements Paginable {
+
+	public static final int DEF_COUNT = 20;
+
+	protected int filterNo;
+
+	protected int pageNo = 1;
+
+	protected int pageSize = 20;
+
+	protected int totalCount = 0;
+
+	public SimplePage() {
+	}
+
+	public SimplePage(int pageNo, int pageSize, int totalCount) {
+		if (totalCount <= 0) {
+			this.totalCount = 0;
+		} else {
+			this.totalCount = totalCount;
+		}
+		if (pageSize <= 0) {
+			this.pageSize = DEF_COUNT;
+		} else {
+			this.pageSize = pageSize;
+		}
+		if (pageNo <= 0) {
+			this.pageNo = 1;
+		} else {
+			this.pageNo = pageNo;
+		}
+		if ((this.pageNo - 1) * this.pageSize >= totalCount) {
+			this.pageNo = totalCount / pageSize;
+			if (this.pageNo == 0) {
+				this.pageNo = 1;
+			}
+		}
+	}
+
+	/**
+	 * 调整分页参数,使合理化
+	 */
+	public void adjustPage() {
+		if (totalCount <= 0) {
+			totalCount = 0;
+		}
+		if (pageSize <= 0) {
+			pageSize = DEF_COUNT;
+		}
+		if (pageNo <= 0) {
+			pageNo = 1;
+		}
+		if ((pageNo - 1) * pageSize >= totalCount) {
+			pageNo = totalCount / pageSize;
+		}
+	}
+
+	public int getFilterNo() {
+		return filterNo;
+	}
+
+	public int getNextPage() {
+		if (isLastPage()) {
+			return pageNo;
+		} else {
+			return pageNo + 1;
+		}
+	}
+
+	public int getPageNo() {
+		return pageNo;
+	}
+
+	public int getPageSize() {
+		return pageSize;
+	}
+
+	public int getPrePage() {
+		if (isFirstPage()) {
+			return pageNo;
+		} else {
+			return pageNo - 1;
+		}
+	}
+	public int getTotalCount() {
+		return totalCount;
+	}
+	public int getTotalPage() {
+		int totalPage = totalCount / pageSize;
+		if (totalCount % pageSize != 0 || totalPage == 0) {
+			totalPage++;
+		}
+		return totalPage;
+	}
+
+	public boolean isFirstPage() {
+		return pageNo <= 1;
+	}
+
+	public boolean isLastPage() {
+		return pageNo >= getTotalPage();
+	}
+
+	public void setFilterNo(int filterNo) {
+		this.filterNo = filterNo;
+	}
+
+	public void setPageNo(int pageNo) {
+		this.pageNo = pageNo;
+	}
+
+	public void setPageSize(int pageSize) {
+		this.pageSize = pageSize;
+	}
+
+	public void setTotalCount(int totalCount) {
+		this.totalCount = totalCount;
+	}
+
+}

+ 58 - 0
src/main/java/com/goafanti/core/shiro/CustomShiroSessionDAO.java

@@ -0,0 +1,58 @@
+package com.goafanti.core.shiro;
+
+import java.io.Serializable;
+import java.util.Collection;
+
+import org.apache.shiro.session.Session;
+import org.apache.shiro.session.UnknownSessionException;
+import org.apache.shiro.session.mgt.eis.AbstractSessionDAO;
+
+import com.goafanti.common.utils.LoggerUtils;
+import com.goafanti.core.shiro.session.ShiroSessionRepository;
+
+public class CustomShiroSessionDAO extends AbstractSessionDAO {
+
+	private ShiroSessionRepository shiroSessionRepository;
+
+	public ShiroSessionRepository getShiroSessionRepository() {
+		return shiroSessionRepository;
+	}
+
+	public void setShiroSessionRepository(ShiroSessionRepository shiroSessionRepository) {
+		this.shiroSessionRepository = shiroSessionRepository;
+	}
+
+	@Override
+	public void update(Session session) throws UnknownSessionException {
+		getShiroSessionRepository().saveSession(session);
+	}
+
+	@Override
+	public void delete(Session session) {
+		if (session == null) {
+			LoggerUtils.error(getClass(), "Session 不能为null");
+			return;
+		}
+		Serializable id = session.getId();
+		if (id != null)
+			getShiroSessionRepository().deleteSession(id);
+	}
+
+	@Override
+	public Collection<Session> getActiveSessions() {
+		return getShiroSessionRepository().getAllSessions();
+	}
+
+	@Override
+	protected Serializable doCreate(Session session) {
+		Serializable sessionId = this.generateSessionId(session);
+		this.assignSessionId(session, sessionId);
+		getShiroSessionRepository().saveSession(session);
+		return sessionId;
+	}
+
+	@Override
+	protected Session doReadSession(Serializable sessionId) {
+		return getShiroSessionRepository().getSession(sessionId);
+	}
+}

+ 135 - 0
src/main/java/com/goafanti/core/shiro/cache/JedisManager.java

@@ -0,0 +1,135 @@
+package com.goafanti.core.shiro.cache;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.shiro.session.Session;
+
+import com.goafanti.common.constant.ShiroConstants;
+import com.goafanti.common.utils.LoggerUtils;
+import com.goafanti.common.utils.SerializeUtil;
+import redis.clients.jedis.Jedis;
+import redis.clients.jedis.JedisPool;
+import redis.clients.jedis.exceptions.JedisConnectionException;
+
+public class JedisManager {
+
+	private JedisPool jedisPool;
+
+	public Jedis getJedis() {
+		Jedis jedis = null;
+		try {
+			jedis = getJedisPool().getResource();
+		} catch (Exception e) {
+			throw new JedisConnectionException(e);
+		}
+		return jedis;
+	}
+
+	public void returnResource(Jedis jedis, boolean isBroken) {
+		if (jedis == null)
+			return;
+		/**
+		 * @deprecated starting from Jedis 3.0 this method will not be exposed.
+		 *             Resource cleanup should be done using @see
+		 *             {@link redis.clients.jedis.Jedis#close()} if (isBroken){
+		 *             getJedisPool().returnBrokenResource(jedis); }else{
+		 *             getJedisPool().returnResource(jedis); }
+		 */
+		jedis.close();
+	}
+
+	public byte[] getValueByKey(int dbIndex, byte[] key) throws Exception {
+		Jedis jedis = null;
+		byte[] result = null;
+		boolean isBroken = false;
+		try {
+			jedis = getJedis();
+			jedis.select(dbIndex);
+			result = jedis.get(key);
+		} catch (Exception e) {
+			isBroken = true;
+			throw e;
+		} finally {
+			returnResource(jedis, isBroken);
+		}
+		return result;
+	}
+
+	public void deleteByKey(int dbIndex, byte[] key) throws Exception {
+		Jedis jedis = null;
+		boolean isBroken = false;
+		try {
+			jedis = getJedis();
+			jedis.select(dbIndex);
+			Long result = jedis.del(key);
+			LoggerUtils.fmtDebug(getClass(), "删除Session结果:%s", result);
+		} catch (Exception e) {
+			isBroken = true;
+			throw e;
+		} finally {
+			returnResource(jedis, isBroken);
+		}
+	}
+
+	public void saveValueByKey(int dbIndex, byte[] key, byte[] value, int expireTime) throws Exception {
+		Jedis jedis = null;
+		boolean isBroken = false;
+		try {
+			jedis = getJedis();
+			jedis.select(dbIndex);
+			jedis.set(key, value);
+			if (expireTime > 0)
+				jedis.expire(key, expireTime);
+		} catch (Exception e) {
+			isBroken = true;
+			throw e;
+		} finally {
+			returnResource(jedis, isBroken);
+		}
+	}
+
+	public JedisPool getJedisPool() {
+		return jedisPool;
+	}
+
+	public void setJedisPool(JedisPool jedisPool) {
+		this.jedisPool = jedisPool;
+	}
+
+	/**
+	 * 获取所有Session
+	 * 
+	 * @param dbIndex
+	 * @param redisShiroSession
+	 * @return
+	 * @throws Exception
+	 */
+	@SuppressWarnings("unchecked")
+	public Collection<Session> AllSession(int dbIndex, String redisShiroSession) throws Exception {
+		Jedis jedis = null;
+		boolean isBroken = false;
+		Set<Session> sessions = new HashSet<Session>();
+		try {
+			jedis = getJedis();
+			jedis.select(dbIndex);
+
+			Set<byte[]> byteKeys = jedis.keys((ShiroConstants.REDIS_SHIRO_ALL).getBytes());
+			if (byteKeys != null && byteKeys.size() > 0) {
+				for (byte[] bs : byteKeys) {
+					Session obj = SerializeUtil.deserialize(jedis.get(bs), Session.class);
+					if (obj instanceof Session) {
+						sessions.add(obj);
+					}
+				}
+			}
+		} catch (Exception e) {
+			isBroken = true;
+			throw e;
+		} finally {
+			returnResource(jedis, isBroken);
+		}
+		return sessions;
+	}
+}

+ 109 - 0
src/main/java/com/goafanti/core/shiro/cache/JedisShiroCache.java

@@ -0,0 +1,109 @@
+package com.goafanti.core.shiro.cache;
+
+import java.util.Collection;
+import java.util.Set;
+
+import org.apache.shiro.cache.Cache;
+import org.apache.shiro.cache.CacheException;
+
+import com.goafanti.common.utils.LoggerUtils;
+import com.goafanti.common.utils.SerializeUtil;
+
+@SuppressWarnings("unchecked")
+public class JedisShiroCache<K, V> implements Cache<K, V> {
+
+	/**
+	 * 为了不和其他的缓存混淆,采用追加前缀方式以作区分
+	 */
+	private static final String REDIS_SHIRO_CACHE = "szrobot-jfgj-cache:";
+	/**
+	 * Redis 分片(分区),也可以在配置文件中配置
+	 */
+	private static final int DB_INDEX = 1;
+
+	private JedisManager jedisManager;
+
+	private String name;
+
+	public JedisShiroCache(String name, JedisManager jedisManager) {
+		this.name = name;
+		this.jedisManager = jedisManager;
+	}
+
+	/**
+	 * 自定义realm中的授权/认证的类名加上授权/认证英文名字
+	 */
+	public String getName() {
+		if (name == null)
+			return "";
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	@Override
+	public V get(K key) throws CacheException {
+		byte[] byteKey = SerializeUtil.serialize(buildCacheKey(key));
+		byte[] byteValue = new byte[0];
+		try {
+			byteValue = jedisManager.getValueByKey(DB_INDEX, byteKey);
+		} catch (Exception e) {
+			LoggerUtils.error(getClass(), "get value by cache throw exception", e);
+		}
+		return (V) SerializeUtil.deserialize(byteValue);
+	}
+
+	@Override
+	public V put(K key, V value) throws CacheException {
+		V previos = get(key);
+		try {
+			jedisManager.saveValueByKey(DB_INDEX, SerializeUtil.serialize(buildCacheKey(key)),
+					SerializeUtil.serialize(value), -1);
+		} catch (Exception e) {
+			LoggerUtils.error(getClass(), "put cache throw exception", e);
+		}
+		return previos;
+	}
+
+	@Override
+	public V remove(K key) throws CacheException {
+		V previos = get(key);
+		try {
+			jedisManager.deleteByKey(DB_INDEX, SerializeUtil.serialize(buildCacheKey(key)));
+		} catch (Exception e) {
+			LoggerUtils.error(getClass(), "remove cache  throw exception", e);
+		}
+		return previos;
+	}
+
+	@Override
+	public void clear() throws CacheException {
+		// TODO--
+	}
+
+	@Override
+	public int size() {
+		if (keys() == null)
+			return 0;
+		return keys().size();
+	}
+
+	@Override
+	public Set<K> keys() {
+		// TODO
+		return null;
+	}
+
+	@Override
+	public Collection<V> values() {
+		// TODO
+		return null;
+	}
+
+	private String buildCacheKey(Object key) {
+		return REDIS_SHIRO_CACHE + getName() + ":" + key;
+	}
+
+}

+ 100 - 0
src/main/java/com/goafanti/core/shiro/cache/JedisShiroSessionRepository.java

@@ -0,0 +1,100 @@
+package com.goafanti.core.shiro.cache;
+
+import java.io.Serializable;
+import java.util.Collection;
+
+import org.apache.shiro.session.Session;
+
+import com.goafanti.common.constant.ShiroConstants;
+import com.goafanti.common.utils.LoggerUtils;
+import com.goafanti.common.utils.SerializeUtil;
+import com.goafanti.core.shiro.session.SessionStatus;
+import com.goafanti.core.shiro.session.ShiroSessionRepository;
+
+/**
+ * Session 管理
+ * 
+ * @author sojson.com
+ *
+ */
+@SuppressWarnings("unchecked")
+public class JedisShiroSessionRepository implements ShiroSessionRepository {
+
+	private static final int SESSION_VAL_TIME_SPAN = 18000;
+	private static final int DB_INDEX = 1;
+
+	private JedisManager jedisManager;
+
+	@Override
+	public void saveSession(Session session) {
+		if (session == null || session.getId() == null)
+			throw new NullPointerException("session is empty");
+		try {
+			byte[] key = SerializeUtil.serialize(buildRedisSessionKey(session.getId()));
+
+			// 不存在才添加。
+			if (null == session.getAttribute(ShiroConstants.SESSION_STATUS)) {
+				// Session 踢出自存存储。
+				SessionStatus sessionStatus = new SessionStatus();
+				session.setAttribute(ShiroConstants.SESSION_STATUS, sessionStatus);
+			}
+
+			byte[] value = SerializeUtil.serialize(session);
+			long sessionTimeOut = session.getTimeout() / 1000;
+			Long expireTime = sessionTimeOut + SESSION_VAL_TIME_SPAN + (5 * 60);
+			getJedisManager().saveValueByKey(DB_INDEX, key, value, expireTime.intValue());
+		} catch (Exception e) {
+			LoggerUtils.fmtError(getClass(), e, "save session error,id:[%s]", session.getId());
+		}
+	}
+
+	@Override
+	public void deleteSession(Serializable id) {
+		if (id == null) {
+			throw new NullPointerException("session id is empty");
+		}
+		try {
+			getJedisManager().deleteByKey(DB_INDEX, SerializeUtil.serialize(buildRedisSessionKey(id)));
+		} catch (Exception e) {
+			LoggerUtils.fmtError(getClass(), e, "删除session出现异常,id:[%s]", id);
+		}
+	}
+
+	@Override
+	public Session getSession(Serializable id) {
+		if (id == null)
+			throw new NullPointerException("session id is empty");
+		Session session = null;
+		try {
+			byte[] value = getJedisManager().getValueByKey(DB_INDEX, SerializeUtil.serialize(buildRedisSessionKey(id)));
+			session = SerializeUtil.deserialize(value, Session.class);
+		} catch (Exception e) {
+			LoggerUtils.fmtError(getClass(), e, "获取session异常,id:[%s]", id);
+		}
+		return session;
+	}
+
+	@Override
+	public Collection<Session> getAllSessions() {
+		Collection<Session> sessions = null;
+		try {
+			sessions = getJedisManager().AllSession(DB_INDEX, ShiroConstants.REDIS_SHIRO_SESSION);
+		} catch (Exception e) {
+			LoggerUtils.fmtError(getClass(), e, "获取全部session异常");
+		}
+
+		return sessions;
+	}
+
+	private String buildRedisSessionKey(Serializable sessionId) {
+		return ShiroConstants.REDIS_SHIRO_SESSION + sessionId;
+	}
+
+	public JedisManager getJedisManager() {
+		return jedisManager;
+	}
+
+	public void setJedisManager(JedisManager jedisManager) {
+		this.jedisManager = jedisManager;
+	}
+}

+ 10 - 0
src/main/java/com/goafanti/core/shiro/cache/ShiroCacheManager.java

@@ -0,0 +1,10 @@
+package com.goafanti.core.shiro.cache;
+
+import org.apache.shiro.cache.Cache;
+
+public interface ShiroCacheManager {
+
+	<K, V> Cache<K, V> getCache(String name);
+
+	void destroy();
+}

+ 32 - 0
src/main/java/com/goafanti/core/shiro/cache/impl/CustomShiroCacheManager.java

@@ -0,0 +1,32 @@
+package com.goafanti.core.shiro.cache.impl;
+
+import org.apache.shiro.cache.Cache;
+import org.apache.shiro.cache.CacheException;
+import org.apache.shiro.cache.CacheManager;
+import org.apache.shiro.util.Destroyable;
+
+import com.goafanti.core.shiro.cache.ShiroCacheManager;
+
+public class CustomShiroCacheManager implements CacheManager, Destroyable {
+
+	private ShiroCacheManager shiroCacheManager;
+
+	@Override
+	public <K, V> Cache<K, V> getCache(String name) throws CacheException {
+		return getShiroCacheManager().getCache(name);
+	}
+
+	@Override
+	public void destroy() throws Exception {
+		shiroCacheManager.destroy();
+	}
+
+	public ShiroCacheManager getShiroCacheManager() {
+		return shiroCacheManager;
+	}
+
+	public void setShiroCacheManager(ShiroCacheManager shiroCacheManager) {
+		this.shiroCacheManager = shiroCacheManager;
+	}
+
+}

+ 31 - 0
src/main/java/com/goafanti/core/shiro/cache/impl/JedisShiroCacheManager.java

@@ -0,0 +1,31 @@
+package com.goafanti.core.shiro.cache.impl;
+
+import org.apache.shiro.cache.Cache;
+
+import com.goafanti.core.shiro.cache.JedisManager;
+import com.goafanti.core.shiro.cache.JedisShiroCache;
+import com.goafanti.core.shiro.cache.ShiroCacheManager;
+
+public class JedisShiroCacheManager implements ShiroCacheManager {
+
+	private JedisManager jedisManager;
+
+	@Override
+	public <K, V> Cache<K, V> getCache(String name) {
+		return new JedisShiroCache<K, V>(name, getJedisManager());
+	}
+
+	@Override
+	public void destroy() {
+		// 如果和其他系统,或者应用在一起就不能关闭
+		// getJedisManager().getJedis().shutdown();
+	}
+
+	public JedisManager getJedisManager() {
+		return jedisManager;
+	}
+
+	public void setJedisManager(JedisManager jedisManager) {
+		this.jedisManager = jedisManager;
+	}
+}

+ 51 - 0
src/main/java/com/goafanti/core/shiro/filter/LoginFilter.java

@@ -0,0 +1,51 @@
+package com.goafanti.core.shiro.filter;
+
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.shiro.web.filter.AccessControlFilter;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import com.goafanti.common.bo.Error;
+import com.goafanti.common.bo.Result;
+import com.goafanti.common.model.User;
+import com.goafanti.common.utils.LoggerUtils;
+import com.goafanti.core.shiro.token.TokenManager;
+
+public class LoginFilter extends AccessControlFilter {
+	final static Class<LoginFilter> CLASS = LoginFilter.class;
+
+	@Autowired
+	private ShiroFilterUtils shiroFilterUtils;
+	@Override
+	protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue)
+			throws Exception {
+
+		User token = TokenManager.getToken();
+
+		if (null != token || isLoginRequest(request, response)) {// &&
+																	// isEnabled()
+			return Boolean.TRUE;
+		}
+		if (shiroFilterUtils.isAjax((HttpServletRequest) request)) {// ajax请求
+			LoggerUtils.debug(getClass(), "当前用户没有登录,并且是Ajax请求!");
+			Result res = new Result();
+			res.getError().add(new Error("", "403", "当前用户没有登录!"));// 当前用户没有登录!
+			shiroFilterUtils.out((HttpServletResponse) response, res);
+		}
+		return Boolean.FALSE;
+
+	}
+
+	@Override
+	protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
+		// 保存Request和Response 到登录后的链接
+		if (!shiroFilterUtils.isAjax((HttpServletRequest) request)) {
+			saveRequestAndRedirectToLogin(request, response);			
+		}
+		return Boolean.FALSE;
+	}
+
+}

+ 74 - 0
src/main/java/com/goafanti/core/shiro/filter/PermissionFilter.java

@@ -0,0 +1,74 @@
+package com.goafanti.core.shiro.filter;
+
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.shiro.subject.Subject;
+import org.apache.shiro.util.StringUtils;
+import org.apache.shiro.web.filter.AccessControlFilter;
+import org.apache.shiro.web.util.WebUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import com.goafanti.common.bo.Error;
+import com.goafanti.common.bo.Result;
+import com.goafanti.common.constant.ShiroConstants;
+import com.goafanti.common.utils.LoggerUtils;
+
+public class PermissionFilter extends AccessControlFilter {
+
+	@Autowired
+	private ShiroFilterUtils shiroFilterUtils;
+
+	@Override
+	protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue)
+			throws Exception {
+
+		// 先判断带参数的权限判断
+		Subject subject = getSubject(request, response);
+		if (null != mappedValue) {
+			String[] arra = (String[]) mappedValue;
+			for (String permission : arra) {
+				if (subject.isPermitted(permission)) {
+					return Boolean.TRUE;
+				}
+			}
+		}
+		HttpServletRequest httpRequest = ((HttpServletRequest) request);
+
+		String uri = httpRequest.getRequestURI();// 获取URI
+		String basePath = httpRequest.getContextPath();// 获取basePath
+		if (null != uri && uri.startsWith(basePath)) {
+			uri = uri.replaceAll("^\\" + basePath + "\\/+", "").replaceAll("\\.html$", "");
+		}
+		if (subject.isPermitted(uri) || subject.isPermitted(uri) || subject.hasRole("999999")) {
+			return Boolean.TRUE;
+		}
+		if (shiroFilterUtils.isAjax(httpRequest)) {
+			LoggerUtils.debug(getClass(), "当前用户权限不够,并且是Ajax请求!");
+			Result res = new Result();
+			res.getError().add(new Error("", "403", "当前用户权限不够!"));// 当前用户没有登录!
+			shiroFilterUtils.out((HttpServletResponse) response, res);
+		}
+		return Boolean.FALSE;
+	}
+
+	@Override
+	protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
+
+		Subject subject = getSubject(request, response);
+		if (null == subject.getPrincipal()) {// 表示没有登录,重定向到登录页面
+			saveRequest(request);
+			WebUtils.issueRedirect(request, response, ShiroConstants.LOGIN_URL);
+		} else {
+			if (StringUtils.hasText(ShiroConstants.UNAUTHORIZED_URL)) {// 如果有未授权页面跳转过去
+				WebUtils.issueRedirect(request, response, ShiroConstants.UNAUTHORIZED_URL);
+			} else {// 否则返回401未授权状态码
+				WebUtils.toHttp(response).sendError(HttpServletResponse.SC_UNAUTHORIZED);
+			}
+		}
+		return Boolean.FALSE;
+	}
+
+}

+ 47 - 0
src/main/java/com/goafanti/core/shiro/filter/RoleFilter.java

@@ -0,0 +1,47 @@
+package com.goafanti.core.shiro.filter;
+
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.shiro.subject.Subject;
+import org.apache.shiro.util.StringUtils;
+import org.apache.shiro.web.filter.AccessControlFilter;
+import org.apache.shiro.web.util.WebUtils;
+
+import com.goafanti.common.constant.ShiroConstants;
+
+public class RoleFilter extends AccessControlFilter {
+
+	@Override
+	protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue)
+			throws Exception {
+		String[] arra = (String[]) mappedValue;
+
+		Subject subject = getSubject(request, response);
+		for (String role : arra) {
+			if (subject.hasRole("role:" + role)) {
+				return true;
+			}
+		}
+		return false;
+	}
+
+	@Override
+	protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
+
+		Subject subject = getSubject(request, response);
+		if (subject.getPrincipal() == null) {// 表示没有登录,重定向到登录页面
+			saveRequest(request);
+			WebUtils.issueRedirect(request, response, ShiroConstants.LOGIN_URL);
+		} else {
+			if (StringUtils.hasText(ShiroConstants.UNAUTHORIZED_URL)) {// 如果有未授权页面跳转过去
+				WebUtils.issueRedirect(request, response, ShiroConstants.UNAUTHORIZED_URL);
+			} else {// 否则返回401未授权状态码
+				WebUtils.toHttp(response).sendError(HttpServletResponse.SC_UNAUTHORIZED);
+			}
+		}
+		return false;
+	}
+
+}

+ 71 - 0
src/main/java/com/goafanti/core/shiro/filter/ShiroFilterUtils.java

@@ -0,0 +1,71 @@
+package com.goafanti.core.shiro.filter;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.springframework.beans.factory.annotation.Value;
+
+import com.alibaba.fastjson.JSON;
+
+import com.goafanti.common.bo.Result;
+import com.goafanti.common.utils.LoggerUtils;
+
+public class ShiroFilterUtils {
+	final static Class<? extends ShiroFilterUtils> CLAZZ = ShiroFilterUtils.class;
+
+	@Value(value = "${app.name}")
+	private String appName;
+
+	/**
+	 * 是否是Ajax请求
+	 * 
+	 * @param request
+	 * @return
+	 */
+	public boolean isAjax(HttpServletRequest request) {
+		return (request.getHeader("accept") != null && request.getHeader("accept").indexOf("application/json") > -1)
+				|| (request.getHeader("X-Requested-With") != null
+						&& request.getHeader("X-Requested-With").indexOf("XMLHttpRequest") > -1)
+				|| isFromAPP(request);
+	}
+
+	/**
+	 * 
+	 * @param request
+	 * @return true if App request
+	 */
+	public boolean isFromAPP(HttpServletRequest request) {
+		return request.getHeader("User-Agent") != null && request.getHeader("User-Agent").indexOf(appName) > -1;
+	}
+
+	/**
+	 * response 输出JSON
+	 * 
+	 * @param hresponse
+	 * @param resultMap
+	 * @throws IOException
+	 */
+	public void out(HttpServletResponse response, Result res) {
+		PrintWriter out = null;
+		try {
+			response.setHeader("Content-Type", "application/json");
+			response.setCharacterEncoding("UTF-8");
+			out = response.getWriter();
+			out.println(JSON.toJSONString(res));
+		} catch (Exception e) {
+			LoggerUtils.fmtError(CLAZZ, e, "输出JSON报错。");
+		} finally {
+			if (null != out) {
+				out.flush();
+				out.close();
+			}
+		}
+	}
+
+	public void setAppName(String appName) {
+		this.appName = appName;
+	}
+}

+ 65 - 0
src/main/java/com/goafanti/core/shiro/filter/SimpleAuthFilter.java

@@ -0,0 +1,65 @@
+package com.goafanti.core.shiro.filter;
+
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.shiro.session.Session;
+import org.apache.shiro.subject.Subject;
+import org.apache.shiro.web.filter.AccessControlFilter;
+import org.apache.shiro.web.util.WebUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import com.goafanti.common.bo.Error;
+import com.goafanti.common.bo.Result;
+import com.goafanti.common.constant.ShiroConstants;
+import com.goafanti.common.utils.LoggerUtils;
+import com.goafanti.core.shiro.session.SessionStatus;
+
+public class SimpleAuthFilter extends AccessControlFilter {
+
+	@Autowired
+	private ShiroFilterUtils shiroFilterUtils;
+	@Override
+	protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue)
+			throws Exception {
+
+		HttpServletRequest httpRequest = ((HttpServletRequest) request);
+		String url = httpRequest.getRequestURI();
+		String basePath = httpRequest.getContextPath();// 获取basePath
+		if (null != url && url.startsWith(basePath)) {
+			url = url.replace(basePath, "");
+		}
+		if (url.startsWith("/open/")) {
+			return Boolean.TRUE;
+		}
+		Subject subject = getSubject(request, response);
+		Session session = subject.getSession();
+		SessionStatus sessionStatus = (SessionStatus) session.getAttribute(ShiroConstants.SESSION_STATUS);
+		if (null != sessionStatus && !sessionStatus.isOnlineStatus()) {
+			// 判断是不是Ajax请求
+			if (shiroFilterUtils.isAjax((HttpServletRequest) request)) {
+				LoggerUtils.debug(getClass(), "当前用户已经被踢出,并且是Ajax请求!");
+				Result res = new Result();
+				res.getError().add(new Error("", "403", "您的登录已失效,请重新登录!"));
+				shiroFilterUtils.out((HttpServletResponse) response, res);
+			}
+			return Boolean.FALSE;
+		}
+		return Boolean.TRUE;
+	}
+
+	@Override
+	protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
+
+		// 先退出
+		Subject subject = getSubject(request, response);
+		subject.logout();
+		WebUtils.saveRequest(request);
+		// 再重定向
+		WebUtils.issueRedirect(request, response, ShiroConstants.LOGIN_URL);
+		return false;
+	}
+
+}

+ 41 - 0
src/main/java/com/goafanti/core/shiro/listener/CustomSessionListener.java

@@ -0,0 +1,41 @@
+package com.goafanti.core.shiro.listener;
+
+import org.apache.shiro.session.Session;
+import org.apache.shiro.session.SessionListener;
+
+import com.goafanti.core.shiro.session.ShiroSessionRepository;
+
+public class CustomSessionListener implements SessionListener {
+
+	private ShiroSessionRepository shiroSessionRepository;
+
+	/**
+	 * 一个回话的生命周期开始
+	 */
+	@Override
+	public void onStart(Session session) {
+
+	}
+
+	/**
+	 * 一个回话的生命周期结束
+	 */
+	@Override
+	public void onStop(Session session) {
+
+	}
+
+	@Override
+	public void onExpiration(Session session) {
+		shiroSessionRepository.deleteSession(session.getId());
+	}
+
+	public ShiroSessionRepository getShiroSessionRepository() {
+		return shiroSessionRepository;
+	}
+
+	public void setShiroSessionRepository(ShiroSessionRepository shiroSessionRepository) {
+		this.shiroSessionRepository = shiroSessionRepository;
+	}
+
+}

+ 16 - 0
src/main/java/com/goafanti/core/shiro/service/ShiroManager.java

@@ -0,0 +1,16 @@
+package com.goafanti.core.shiro.service;
+
+public interface ShiroManager {
+
+	 /**
+    * 加载过滤配置信息
+    * @return
+    */
+   public String loadFilterChainDefinitions();
+   
+   /**
+    * 重新构建权限过滤器
+    * 一般在修改了用户角色、用户等信息时,需要再次调用该方法
+    */
+   public void reCreateFilterChains();
+}

+ 96 - 0
src/main/java/com/goafanti/core/shiro/service/impl/ShiroManagerImpl.java

@@ -0,0 +1,96 @@
+package com.goafanti.core.shiro.service.impl;
+
+import java.io.IOException;
+import java.util.Map;
+import java.util.Set;
+
+import javax.annotation.Resource;
+
+import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
+import org.apache.shiro.web.filter.mgt.DefaultFilterChainManager;
+import org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver;
+import org.apache.shiro.web.servlet.AbstractShiroFilter;
+import org.springframework.core.io.ClassPathResource;
+
+import com.goafanti.common.utils.LoggerUtils;
+import com.goafanti.core.config.INI4j;
+import com.goafanti.core.shiro.service.ShiroManager;
+
+public class ShiroManagerImpl implements ShiroManager {
+
+	// 注意/r/n前不能有空格
+	private static final String CRLF = "\r\n";
+
+	@Resource
+	private ShiroFilterFactoryBean shiroFilterFactoryBean;
+
+	@Override
+	public String loadFilterChainDefinitions() {
+
+		StringBuffer sb = new StringBuffer();
+		sb.append(getFixedAuthRule());// 固定权限,采用读取配置文件
+		return sb.toString();
+	}
+
+	/**
+	 * 从配额文件获取固定权限验证规则串
+	 */
+	private String getFixedAuthRule() {
+		String fileName = "shiro_base_auth.ini";
+		ClassPathResource cp = new ClassPathResource(fileName);
+		INI4j ini = null;
+		try {
+			ini = new INI4j(cp.getFile());
+		} catch (IOException e) {
+			LoggerUtils.fmtError(getClass(), e, "加载文件出错。file:[%s]", fileName);
+		}
+		String section = "base_auth";
+		Set<String> keys = ini.get(section).keySet();
+		StringBuffer sb = new StringBuffer();
+		for (String key : keys) {
+			String value = ini.get(section, key);
+			sb.append(key).append(" = ").append(value).append(CRLF);
+		}
+
+		return sb.toString();
+
+	}
+
+	// 此方法加同步锁
+	@Override
+	public synchronized void reCreateFilterChains() {
+		// ShiroFilterFactoryBean shiroFilterFactoryBean =
+		// (ShiroFilterFactoryBean)
+		// SpringContextUtil.getBean("shiroFilterFactoryBean");
+		AbstractShiroFilter shiroFilter = null;
+		try {
+			shiroFilter = (AbstractShiroFilter) shiroFilterFactoryBean.getObject();
+		} catch (Exception e) {
+			LoggerUtils.error(getClass(), "getShiroFilter from shiroFilterFactoryBean error!", e);
+			throw new RuntimeException("get ShiroFilter from shiroFilterFactoryBean error!");
+		}
+
+		PathMatchingFilterChainResolver filterChainResolver = (PathMatchingFilterChainResolver) shiroFilter
+				.getFilterChainResolver();
+		DefaultFilterChainManager manager = (DefaultFilterChainManager) filterChainResolver.getFilterChainManager();
+
+		// 清空老的权限控制
+		manager.getFilterChains().clear();
+
+		shiroFilterFactoryBean.getFilterChainDefinitionMap().clear();
+		shiroFilterFactoryBean.setFilterChainDefinitions(loadFilterChainDefinitions());
+		// 重新构建生成
+		Map<String, String> chains = shiroFilterFactoryBean.getFilterChainDefinitionMap();
+		for (Map.Entry<String, String> entry : chains.entrySet()) {
+			String url = entry.getKey();
+			String chainDefinition = entry.getValue().trim().replace(" ", "");
+			manager.createChain(url, chainDefinition);
+		}
+
+	}
+
+	public void setShiroFilterFactoryBean(ShiroFilterFactoryBean shiroFilterFactoryBean) {
+		this.shiroFilterFactoryBean = shiroFilterFactoryBean;
+	}
+
+}

+ 208 - 0
src/main/java/com/goafanti/core/shiro/session/CustomSessionManager.java

@@ -0,0 +1,208 @@
+package com.goafanti.core.shiro.session;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.shiro.session.Session;
+import org.apache.shiro.subject.SimplePrincipalCollection;
+import org.apache.shiro.subject.support.DefaultSubjectContext;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import com.goafanti.common.constant.ShiroConstants;
+import com.goafanti.common.model.User;
+import com.goafanti.common.utils.ArrayUtils;
+import com.goafanti.common.utils.LoggerUtils;
+import com.goafanti.core.shiro.CustomShiroSessionDAO;
+
+public class CustomSessionManager {
+
+	/**
+	 * session status
+	 */
+	@Autowired
+	ShiroSessionRepository shiroSessionRepository;
+
+	@Autowired
+	CustomShiroSessionDAO customShiroSessionDAO;
+
+//	/**
+//	 * 获取所有的有效Session用户
+//	 * 
+//	 * @return
+//	 */
+//	public List<UserOnlineBo> getAllUser() {
+//		// 获取所有session
+//		Collection<Session> sessions = customShiroSessionDAO.getActiveSessions();
+//		List<UserOnlineBo> list = new ArrayList<UserOnlineBo>();
+//
+//		for (Session session : sessions) {
+//			UserOnlineBo bo = getSessionBo(session);
+//			if (null != bo) {
+//				list.add(bo);
+//			}
+//		}
+//		return list;
+//	}
+
+	/**
+	 * 根据ID查询 SimplePrincipalCollection
+	 * 
+	 * @param userIds
+	 *            用户ID
+	 * @return
+	 */
+	@SuppressWarnings("unchecked")
+	public List<SimplePrincipalCollection> getSimplePrincipalCollectionByUserId(Long... userIds) {
+		// 把userIds 转成Set,好判断
+		Set<Long> idset = (Set<Long>) ArrayUtils.array2Set(userIds);
+		// 获取所有session
+		Collection<Session> sessions = customShiroSessionDAO.getActiveSessions();
+		// 定义返回
+		List<SimplePrincipalCollection> list = new ArrayList<SimplePrincipalCollection>();
+		for (Session session : sessions) {
+			// 获取SimplePrincipalCollection
+			Object obj = session.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY);
+			if (null != obj && obj instanceof SimplePrincipalCollection) {
+				// 强转
+				SimplePrincipalCollection spc = (SimplePrincipalCollection) obj;
+				// 判断用户,匹配用户ID。
+				obj = spc.getPrimaryPrincipal();
+				if (null != obj && obj instanceof User) {
+					User user = (User) obj;
+					// 比较用户ID,符合即加入集合
+					if (null != user && idset.contains(user.getId())) {
+						list.add(spc);
+					}
+				}
+			}
+		}
+		return list;
+	}
+
+//	/**
+//	 * 获取单个Session
+//	 * 
+//	 * @param sessionId
+//	 * @return
+//	 */
+//	public UserOnlineBo getSession(String sessionId) {
+//		Session session = shiroSessionRepository.getSession(sessionId);
+//		UserOnlineBo bo = getSessionBo(session);
+//		return bo;
+//	}
+
+//	private UserOnlineBo getSessionBo(Session session) {
+//		// 获取session登录信息。
+//		Object obj = session.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY);
+//		if (null == obj) {
+//			return null;
+//		}
+//		// 确保是 SimplePrincipalCollection对象。
+//		if (obj instanceof SimplePrincipalCollection) {
+//			SimplePrincipalCollection spc = (SimplePrincipalCollection) obj;
+//			/**
+//			 * 获取用户登录的,@link UserRealm.doGetAuthenticationInfo(...)方法中 return
+//			 * new SimpleAuthenticationInfo(user,user.getPwd(),
+//			 * getName());的user 对象。
+//			 */
+//			obj = spc.getPrimaryPrincipal();
+//			if (null != obj && obj instanceof User) {
+//				// 存储session + user 综合信息
+//				UserOnlineBo userBo = new UserOnlineBo((User) obj);
+//				// 最后一次和系统交互的时间
+//				userBo.setLastAccess(session.getLastAccessTime());
+//				// 主机的ip地址
+//				userBo.setHost(session.getHost());
+//				// session ID
+//				userBo.setSessionId(session.getId().toString());
+//				// 回话到期 ttl(ms)
+//				userBo.setTimeout(session.getTimeout());
+//				// session创建时间
+//				userBo.setStartTime(session.getStartTimestamp());
+//				// 是否踢出
+//				SessionStatus sessionStatus = (SessionStatus) session.getAttribute(ShiroConstants.SESSION_STATUS);
+//				boolean status = Boolean.TRUE;
+//				if (null != sessionStatus) {
+//					status = sessionStatus.getOnlineStatus();
+//				}
+//				userBo.setSessionStatus(status);
+//				return userBo;
+//			}
+//		}
+//		return null;
+//	}
+
+	/**
+	 * 改变Session状态
+	 * 
+	 * @param status
+	 *            {true:踢出,false:激活}
+	 * @param sessionId
+	 * @return
+	 */
+	public Map<String, Object> changeSessionStatus(Boolean status, String sessionIds) {
+		Map<String, Object> map = new HashMap<String, Object>();
+		try {
+			String[] sessionIdArray = null;
+			if (sessionIds.indexOf(",") == -1) {
+				sessionIdArray = new String[] { sessionIds };
+			} else {
+				sessionIdArray = sessionIds.split(",");
+			}
+			for (String id : sessionIdArray) {
+				Session session = shiroSessionRepository.getSession(id);
+				SessionStatus sessionStatus = new SessionStatus();
+				sessionStatus.setOnlineStatus(status);
+				session.setAttribute(ShiroConstants.SESSION_STATUS, sessionStatus);
+				customShiroSessionDAO.update(session);
+			}
+			map.put("status", 200);
+			map.put("sessionStatus", status ? 1 : 0);
+			map.put("sessionStatusText", status ? "踢出" : "激活");
+			map.put("sessionStatusTextTd", status ? "有效" : "已踢出");
+		} catch (Exception e) {
+			LoggerUtils.fmtError(getClass(), e, "改变Session状态错误,sessionId[%s]", sessionIds);
+			map.put("status", 500);
+			map.put("message", "改变失败,有可能Session不存在,请刷新再试!");
+		}
+		return map;
+	}
+
+//	/**
+//	 * 查询要禁用的用户是否在线。
+//	 * 
+//	 * @param id
+//	 *            用户ID
+//	 * @param status
+//	 *            用户状态
+//	 */
+//	public void forbidUserById(Long id, Integer status) {
+//		// 获取所有在线用户
+//		for (UserOnlineBo bo : getAllUser()) {
+//			Long userId = bo.getId();
+//			// 匹配用户ID
+//			if (userId.equals(id)) {
+//				// 获取用户Session
+//				Session session = shiroSessionRepository.getSession(bo.getSessionId());
+//				// 标记用户Session
+//				SessionStatus sessionStatus = (SessionStatus) session.getAttribute(ShiroConstants.SESSION_STATUS);
+//				// 是否踢出 true:有效,false:踢出。
+//				sessionStatus.setOnlineStatus(status == 1);
+//				// 更新Session
+//				customShiroSessionDAO.update(session);
+//			}
+//		}
+//	}
+
+	public void setShiroSessionRepository(ShiroSessionRepository shiroSessionRepository) {
+		this.shiroSessionRepository = shiroSessionRepository;
+	}
+
+	public void setCustomShiroSessionDAO(CustomShiroSessionDAO customShiroSessionDAO) {
+		this.customShiroSessionDAO = customShiroSessionDAO;
+	}
+}

+ 23 - 0
src/main/java/com/goafanti/core/shiro/session/SessionStatus.java

@@ -0,0 +1,23 @@
+package com.goafanti.core.shiro.session;
+
+import java.io.Serializable;
+
+public class SessionStatus implements Serializable {
+	private static final long serialVersionUID = 1L;
+
+	// 是否踢出 true:有效,false:踢出。
+	private Boolean onlineStatus = Boolean.TRUE;
+
+	public Boolean isOnlineStatus() {
+		return onlineStatus;
+	}
+
+	public Boolean getOnlineStatus() {
+		return onlineStatus;
+	}
+
+	public void setOnlineStatus(Boolean onlineStatus) {
+		this.onlineStatus = onlineStatus;
+	}
+
+}

+ 31 - 0
src/main/java/com/goafanti/core/shiro/session/ShiroSessionRepository.java

@@ -0,0 +1,31 @@
+package com.goafanti.core.shiro.session;
+
+import org.apache.shiro.session.Session;
+
+import java.io.Serializable;
+import java.util.Collection;
+
+public interface ShiroSessionRepository {
+
+	/**
+	 * 存储Session
+	 * @param session
+	 */
+    void saveSession(Session session);
+    /**
+     * 删除session
+     * @param sessionId
+     */
+    void deleteSession(Serializable sessionId);
+    /**
+     * 获取session
+     * @param sessionId
+     * @return
+     */
+    Session getSession(Serializable sessionId);
+    /**
+     * 获取所有sessoin
+     * @return
+     */
+    Collection<Session> getAllSessions();
+}

+ 27 - 0
src/main/java/com/goafanti/core/shiro/token/ShiroToken.java

@@ -0,0 +1,27 @@
+package com.goafanti.core.shiro.token;
+
+import java.io.Serializable;
+
+import org.apache.shiro.authc.UsernamePasswordToken;
+
+public class ShiroToken extends UsernamePasswordToken implements Serializable {
+
+	private static final long serialVersionUID = 4364371216579943671L;
+
+	public ShiroToken(String username, String pwd) {
+		super(username, pwd);
+		this.pwd = pwd;
+	}
+
+	/** 登录密码[字符串类型] 因为父类是char[] ] **/
+	private String pwd;
+
+	public String getPwd() {
+		return pwd;
+	}
+
+	public void setPwd(String pwd) {
+		this.pwd = pwd;
+	}
+
+}

+ 160 - 0
src/main/java/com/goafanti/core/shiro/token/TokenManager.java

@@ -0,0 +1,160 @@
+package com.goafanti.core.shiro.token;
+
+import java.util.List;
+
+import org.apache.shiro.SecurityUtils;
+import org.apache.shiro.session.Session;
+import org.apache.shiro.subject.SimplePrincipalCollection;
+
+import com.goafanti.common.model.User;
+import com.goafanti.common.utils.SpringContextUtils;
+import com.goafanti.core.shiro.session.CustomSessionManager;
+
+public class TokenManager {
+	// 用户登录管理
+	public static final UserRealm realm = SpringContextUtils.getBean("userRealm", UserRealm.class);
+	// 用户session管理
+	public static final CustomSessionManager customSessionManager = SpringContextUtils.getBean("customSessionManager",
+			CustomSessionManager.class);
+
+	/**
+	 * 获取当前登录的用户User对象
+	 * 
+	 * @return
+	 */
+	public static User getToken() {
+		User token = (User) SecurityUtils.getSubject().getPrincipal();
+		return token;
+	}
+
+	/**
+	 * 获取当前用户的Session
+	 * 
+	 * @return
+	 */
+	public static Session getSession() {
+		return SecurityUtils.getSubject().getSession();
+	}
+
+	/**
+	 * 获取当前用户ID
+	 * 
+	 * @return
+	 */
+	public static String getUserId() {
+		return getToken() == null ? null : getToken().getId();
+	}
+
+	/**
+	 * 获取当前用户WebSocketID
+	 * 
+	 * @return
+	 */
+	public static String getWebSocketKey() {
+		return (String) TokenManager.getSession().getId() + "|" + getUserId();
+	}
+
+	/**
+	 * 把值放入到当前登录用户的Session里
+	 * 
+	 * @param key
+	 * @param value
+	 */
+	public static void setVal2Session(Object key, Object value) {
+		getSession().setAttribute(key, value);
+	}
+
+	/**
+	 * 从当前登录用户的Session里取值
+	 * 
+	 * @param key
+	 * @return
+	 */
+	public static Object getVal2Session(Object key) {
+		return getSession().getAttribute(key);
+	}
+
+	/**
+	 * 获取验证码,获取一次后删除
+	 * 
+	 * @return
+	 */
+	public static String getYZM() {
+		String code = (String) getSession().getAttribute("CODE");
+		getSession().removeAttribute("CODE");
+		return code;
+	}
+
+	/**
+	 * 登录
+	 * 
+	 * @param user
+	 * @param rememberMe
+	 * @return
+	 */
+	public static User login(User user, Boolean rememberMe) {
+		ShiroToken token = new ShiroToken(user.getMobile(), user.getPassword());
+		token.setRememberMe(null == rememberMe ? false : rememberMe);
+		SecurityUtils.getSubject().login(token);
+		return getToken();
+	}
+
+	/**
+	 * 判断是否登录
+	 * 
+	 * @return
+	 */
+	public static boolean isLogin() {
+		return null != SecurityUtils.getSubject().getPrincipal();
+	}
+
+	/**
+	 * 退出登录
+	 */
+	public static void logout() {
+		SecurityUtils.getSubject().logout();
+	}
+
+	/**
+	 * 清空当前用户权限信息。 目的:为了在判断权限的时候,再次会再次
+	 * <code>doGetAuthorizationInfo(...)  </code>方法。 ps: 当然你可以手动调用
+	 * <code> doGetAuthorizationInfo(...)  </code>方法。
+	 * 这里只是说明下这个逻辑,当你清空了权限,<code> doGetAuthorizationInfo(...)  </code>就会被再次调用。
+	 */
+	public static void clearNowUserAuth() {
+		realm.clearCachedAuthorizationInfo();
+	}
+
+	/**
+	 * 根据UserIds 清空权限信息。
+	 * 
+	 * @param id
+	 *            用户ID
+	 */
+	public static void clearUserAuthByUserId(Long... userIds) {
+
+		if (null == userIds || userIds.length == 0)
+			return;
+		List<SimplePrincipalCollection> result = customSessionManager.getSimplePrincipalCollectionByUserId(userIds);
+
+		for (SimplePrincipalCollection simplePrincipalCollection : result) {
+			realm.clearCachedAuthorizationInfo(simplePrincipalCollection);
+		}
+	}
+
+	/**
+	 * 方法重载
+	 * 
+	 * @param userIds
+	 */
+	public static void clearUserAuthByUserId(List<Long> userIds) {
+		if (null == userIds || userIds.size() == 0) {
+			return;
+		}
+		clearUserAuthByUserId(userIds.toArray(new Long[0]));
+	}
+
+	// public static void forbidUser(Long userId) {
+	// customSessionManager.forbidUserById(userId, ShiroConstants.USER_DISABLE);
+	// }
+}

+ 76 - 0
src/main/java/com/goafanti/core/shiro/token/UserRealm.java

@@ -0,0 +1,76 @@
+package com.goafanti.core.shiro.token;
+
+import org.apache.shiro.SecurityUtils;
+import org.apache.shiro.authc.AuthenticationException;
+import org.apache.shiro.authc.AuthenticationInfo;
+import org.apache.shiro.authc.AuthenticationToken;
+import org.apache.shiro.authz.AuthorizationInfo;
+import org.apache.shiro.realm.AuthorizingRealm;
+import org.apache.shiro.subject.PrincipalCollection;
+import org.apache.shiro.subject.SimplePrincipalCollection;
+
+public class UserRealm extends AuthorizingRealm {
+
+	public UserRealm() {
+		super();
+	}
+
+	/**
+	 * 认证信息,主要针对用户登录,
+	 */
+	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken)
+			throws AuthenticationException {
+
+//		ShiroToken token = (ShiroToken) authcToken;
+//		User user = userService.findUserByEmail(token.getUsername());
+//		if (null == user) {
+//			throw new UnknownAccountException();
+//		} else if (ShiroConstants.USER_DISABLE.equals(user.getStatus())) {
+//			/**
+//			 * 如果用户的status为禁用。那么就抛出<code>DisabledAccountException</code>
+//			 */
+//			throw new DisabledAccountException("帐号已经禁止登录!");
+//		} else {
+//			user.setLastLoginTime(new Date());
+//			userService.updateByPrimaryKeySelective(user);
+//			user.setRoomids(userService.selectRoomIdByUserId(user.getId()));
+//		}
+//		return new SimpleAuthenticationInfo(user, user.getPwd(), passwordUtil.getSalt(user), getName());
+		return null;
+	}
+
+	/**
+	 * 授权
+	 */
+	@Override
+	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
+
+//		Long userId = TokenManager.getUserId();
+//		SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
+//		// 根据用户ID查询角色(role),放入到Authorization里。
+//		Set<String> roles = roleService.findRoleByUserId(userId);
+//		info.setRoles(roles);
+//		// 根据用户ID查询权限(permission),放入到Authorization里。
+//		Set<String> permissions = permissionService.findPermissionByUserId(userId);
+//		info.setStringPermissions(permissions);
+//		return info;
+		return null;
+	}
+
+	/**
+	 * 清空当前用户权限信息
+	 */
+	public void clearCachedAuthorizationInfo() {
+		PrincipalCollection principalCollection = SecurityUtils.getSubject().getPrincipals();
+		SimplePrincipalCollection principals = new SimplePrincipalCollection(principalCollection, getName());
+		super.clearCachedAuthorizationInfo(principals);
+	}
+
+	/**
+	 * 指定principalCollection 清除
+	 */
+	public void clearCachedAuthorizationInfo(PrincipalCollection principalCollection) {
+		SimplePrincipalCollection principals = new SimplePrincipalCollection(principalCollection, getName());
+		super.clearCachedAuthorizationInfo(principals);
+	}
+}

+ 6 - 0
src/main/java/com/goafanti/core/websocket/Constants.java

@@ -0,0 +1,6 @@
+package com.goafanti.core.websocket;
+
+public class Constants {
+
+	public static final String WEBSOCKET_USERNAME = "WEBSOCKET_SESSION";
+}

+ 87 - 0
src/main/java/com/goafanti/core/websocket/SystemWebSocketHandler.java

@@ -0,0 +1,87 @@
+package com.goafanti.core.websocket;
+
+import java.io.IOException;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.springframework.web.socket.CloseStatus;
+import org.springframework.web.socket.TextMessage;
+import org.springframework.web.socket.WebSocketHandler;
+import org.springframework.web.socket.WebSocketMessage;
+import org.springframework.web.socket.WebSocketSession;
+
+import com.goafanti.common.utils.LoggerUtils;
+
+public class SystemWebSocketHandler implements WebSocketHandler {
+
+	private static final Map<Long, WebSocketSession> users = new ConcurrentHashMap<>();
+
+	@Override
+	public void afterConnectionEstablished(WebSocketSession session) throws Exception {
+		Long userId = (Long) session.getAttributes().get(Constants.WEBSOCKET_USERNAME);
+		if (userId != null) {
+			users.put(userId, session);
+			LoggerUtils.debug(getClass(), "添加用户websocket:" + userId);
+		}
+	}
+
+	@Override
+	public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {
+		LoggerUtils.debug(getClass(), message.getPayload().toString());
+	}
+
+	@Override
+	public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
+		if (session.isOpen()) {
+			session.close();
+		}
+		LoggerUtils.debug(getClass(), "websocket: " + users.remove(session) + "关闭");
+	}
+
+	@Override
+	public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {
+		LoggerUtils.debug(getClass(), "websocket: " + users.remove(session) + "关闭");
+	}
+
+	@Override
+	public boolean supportsPartialMessages() {
+		return false;
+	}
+
+	/**
+	 * 给所有在线用户发送消息
+	 *
+	 * @param message
+	 */
+	public void sendMessageToUsers(TextMessage message) {
+		for (Long userId : users.keySet()) {
+			WebSocketSession user = users.get(userId);
+			try {
+				if (user.isOpen()) {
+					user.sendMessage(message);
+				}
+			} catch (IOException e) {
+				LoggerUtils.error(getClass(), "用户:" + userId + "websocket通知异常", e);
+			}
+		}
+	}
+
+	/**
+	 * 给某个用户发送消息
+	 *
+	 * @param userId
+	 * @param message
+	 */
+	public void sendMessageToUser(Long userId, TextMessage message) {
+		WebSocketSession user = users.get(userId);
+		if (user != null) {
+			try {
+				if (user.isOpen()) {
+					user.sendMessage(message);
+				}
+			} catch (IOException e) {
+				LoggerUtils.error(getClass(), "用户" + userId + "websocket通知异常", e);
+			}
+		}
+	}
+}

+ 27 - 0
src/main/java/com/goafanti/core/websocket/WebSocketConfig.java

@@ -0,0 +1,27 @@
+package com.goafanti.core.websocket;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.EnableWebMvc;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
+import org.springframework.web.socket.config.annotation.EnableWebSocket;
+import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
+import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
+
+@Configuration
+@EnableWebMvc
+@EnableWebSocket
+public class WebSocketConfig extends WebMvcConfigurerAdapter implements WebSocketConfigurer {
+	@Autowired
+	SystemWebSocketHandler systemWebSocketHandler;
+
+	@Override
+	public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
+		registry.addHandler(systemWebSocketHandler, "/webSocketServer")
+				.addInterceptors(new WebSocketHandshakeInterceptor());
+
+		registry.addHandler(systemWebSocketHandler, "/sockjs/webSocketServer").setAllowedOrigins("*")
+				.addInterceptors(new WebSocketHandshakeInterceptor()).withSockJS();
+	}
+
+}

+ 37 - 0
src/main/java/com/goafanti/core/websocket/WebSocketHandshakeInterceptor.java

@@ -0,0 +1,37 @@
+package com.goafanti.core.websocket;
+
+import java.util.Map;
+
+import javax.servlet.http.HttpSession;
+
+import org.springframework.http.server.ServerHttpRequest;
+import org.springframework.http.server.ServerHttpResponse;
+import org.springframework.http.server.ServletServerHttpRequest;
+import org.springframework.web.socket.WebSocketHandler;
+import org.springframework.web.socket.server.HandshakeInterceptor;
+
+import com.goafanti.core.shiro.token.TokenManager;
+
+public class WebSocketHandshakeInterceptor implements HandshakeInterceptor {
+
+	@Override
+	public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,
+			Map<String, Object> attributes) throws Exception {
+		if (request instanceof ServletServerHttpRequest) {
+			ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) request;
+			HttpSession session = servletRequest.getServletRequest().getSession(false);
+			if (session != null && TokenManager.isLogin()) {
+				// 使用TokenManager.getUserId区分WebSocketHandler,以便定向发送消息
+				attributes.put(Constants.WEBSOCKET_USERNAME, TokenManager.getUserId());
+				return true;
+			}
+		}
+		return false;
+	}
+
+	@Override
+	public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,
+			Exception exception) {
+
+	}
+}

+ 21 - 0
src/main/java/com/goafanti/user/controller/UserApiController.java

@@ -0,0 +1,21 @@
+package com.goafanti.user.controller;
+
+import javax.annotation.Resource;
+
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+import com.goafanti.common.controller.BaseApiController;
+import com.goafanti.common.utils.PasswordUtil;
+import com.goafanti.user.service.UserService;
+
+@Controller
+@RequestMapping(value = "/api/user")
+public class UserApiController extends BaseApiController {
+	@Resource
+	private UserService userService;
+	@Resource(name = "passwordUtil")
+	private PasswordUtil passwordUtil;
+	
+	
+}

+ 5 - 0
src/main/java/com/goafanti/user/service/UserService.java

@@ -0,0 +1,5 @@
+package com.goafanti.user.service;
+
+public interface UserService {
+
+}

+ 10 - 0
src/main/java/com/goafanti/user/service/impl/UserServiceImpl.java

@@ -0,0 +1,10 @@
+package com.goafanti.user.service.impl;
+
+import org.springframework.stereotype.Service;
+
+import com.goafanti.user.service.UserService;
+
+@Service
+public class UserServiceImpl implements UserService {
+
+}

+ 24 - 0
src/main/resources/applicationContext.xml

@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util"
+	xmlns:context="http://www.springframework.org/schema/context"
+	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+	 http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
+	 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
+
+	<context:property-placeholder
+		location="classpath:props/config_${spring.profiles.active}.properties" />
+
+	<!-- Resources -->
+	<bean id="errorResource"
+		class="org.springframework.core.io.support.ResourcePropertySource">
+		<constructor-arg value="errors" />
+		<constructor-arg value="classpath:props/error.properties" />
+	</bean>
+
+	<import resource="classpath:spring/spring-mybatis.xml" />
+	<import resource="classpath:spring/spring-shiro.xml" />
+	<import resource="classpath:spring/spring-mvc.xml" />
+	<import resource="classpath:spring/spring-task.xml" />
+
+</beans>

+ 24 - 0
src/main/resources/log4j.properties

@@ -0,0 +1,24 @@
+log4j.rootLogger=ERROR,Console
+log4j.appender.Console=org.apache.log4j.ConsoleAppender
+log4j.appender.Console.Target=System.out
+log4j.appender.Console.layout=org.apache.log4j.PatternLayout
+log4j.appender.Console.layout.ConversionPattern=[%p][%d{yyyy-MM-dd HH\:mm\:ss,SSS}][%c]%m%n
+
+log4j.logger.com.springframework=ERROR
+log4j.logger.com.ibatis=ERROR  
+log4j.logger.com.ibatis.common.jdbc.SimpleDataSource=ERROR  
+log4j.logger.com.ibatis.common.jdbc.ScriptRunner=ERROR  
+log4j.logger.com.ibatis.sqlmap.engine.impl.SqlMapClientDelegate=ERROR  
+log4j.logger.java.sql.Connection=ERROR  
+log4j.logger.java.sql.Statement=ERROR  
+log4j.logger.java.sql.PreparedStatement=ERROR  
+log4j.logger.java.sql.ResultSet=ERROR
+
+# Druid
+log4j.logger.druid.sql=ERROR
+log4j.logger.druid.sql.DataSource=ERROR
+log4j.logger.druid.sql.Connection=ERROR
+log4j.logger.druid.sql.Statement=ERROR
+log4j.logger.druid.sql.ResultSet=ERROR
+
+log4j.logger.com.goafanti=DEBUG

+ 17 - 0
src/main/resources/mybatis-config.xml

@@ -0,0 +1,17 @@
+<?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>
+  	<properties>
+		<property name="dialect" value="mysql" />
+	</properties>  
+  <settings>		
+    <setting name="defaultExecutorType" value="REUSE" />
+  </settings>
+ 
+  <typeAliases>
+  	
+  </typeAliases>
+
+
+</configuration>

+ 53 - 0
src/main/resources/props/config_dev.properties

@@ -0,0 +1,53 @@
+#Driver
+jdbc.driverClassName=com.mysql.jdbc.Driver
+#\u6570\u636E\u5E93\u94FE\u63A5\uFF0C
+jdbc.url=jdbc:mysql://123.57.207.214:3306/pro_aft_test?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true
+#\u5E10\u53F7
+jdbc.username=xiaolong
+#\u5BC6\u7801
+jdbc.password=!qaz2wsx
+#\u68C0\u6D4B\u6570\u636E\u5E93\u94FE\u63A5\u662F\u5426\u6709\u6548\uFF0C\u5FC5\u987B\u914D\u7F6E
+jdbc.validationQuery=SELECT 'x'
+#\u521D\u59CB\u8FDE\u63A5\u6570
+jdbc.initialSize=3
+#\u6700\u5927\u8FDE\u63A5\u6C60\u6570\u91CF
+jdbc.maxActive=20
+#\u53BB\u6389\uFF0C\u914D\u7F6E\u6587\u4EF6\u5BF9\u5E94\u53BB\u6389
+#jdbc.maxIdle=20
+#\u914D\u7F6E0,\u5F53\u7EBF\u7A0B\u6C60\u6570\u91CF\u4E0D\u8DB3\uFF0C\u81EA\u52A8\u8865\u5145\u3002
+jdbc.minIdle=0
+#\u83B7\u53D6\u94FE\u63A5\u8D85\u65F6\u65F6\u95F4\u4E3A1\u5206\u949F\uFF0C\u5355\u4F4D\u4E3A\u6BEB\u79D2\u3002
+jdbc.maxWait=120000
+#\u83B7\u53D6\u94FE\u63A5\u7684\u65F6\u5019\uFF0C\u4E0D\u6821\u9A8C\u662F\u5426\u53EF\u7528\uFF0C\u5F00\u542F\u4F1A\u6709\u635F\u6027\u80FD\u3002
+jdbc.testOnBorrow=false
+#\u5F52\u8FD8\u94FE\u63A5\u5230\u8FDE\u63A5\u6C60\u7684\u65F6\u5019\u6821\u9A8C\u94FE\u63A5\u662F\u5426\u53EF\u7528\u3002
+jdbc.testOnReturn=false
+#\u6B64\u9879\u914D\u7F6E\u4E3Atrue\u5373\u53EF\uFF0C\u4E0D\u5F71\u54CD\u6027\u80FD\uFF0C\u5E76\u4E14\u4FDD\u8BC1\u5B89\u5168\u6027\u3002\u610F\u4E49\u4E3A\uFF1A\u7533\u8BF7\u8FDE\u63A5\u7684\u65F6\u5019\u68C0\u6D4B\uFF0C\u5982\u679C\u7A7A\u95F2\u65F6\u95F4\u5927\u4E8EtimeBetweenEvictionRunsMillis\uFF0C\u6267\u884CvalidationQuery\u68C0\u6D4B\u8FDE\u63A5\u662F\u5426\u6709\u6548\u3002
+jdbc.testWhileIdle=true
+#1.Destroy\u7EBF\u7A0B\u4F1A\u68C0\u6D4B\u8FDE\u63A5\u7684\u95F4\u9694\u65F6\u95F4
+#2.testWhileIdle\u7684\u5224\u65AD\u4F9D\u636E
+jdbc.timeBetweenEvictionRunsMillis=60000
+#\u4E00\u4E2A\u94FE\u63A5\u751F\u5B58\u7684\u65F6\u95F4\uFF08\u4E4B\u524D\u7684\u503C\uFF1A25200000\uFF0C\u8FD9\u4E2A\u65F6\u95F4\u6709\u70B9BT\uFF0C\u8FD9\u4E2A\u7ED3\u679C\u4E0D\u77E5\u9053\u662F\u600E\u4E48\u6765\u7684\uFF0C\u6362\u7B97\u540E\u7684\u7ED3\u679C\u662F\uFF1A25200000/1000/60/60 = 7\u4E2A\u5C0F\u65F6\uFF09
+jdbc.minEvictableIdleTimeMillis=300000
+#\u94FE\u63A5\u4F7F\u7528\u8D85\u8FC7\u65F6\u95F4\u9650\u5236\u662F\u5426\u56DE\u6536
+jdbc.removeAbandoned=true
+#\u8D85\u8FC7\u65F6\u95F4\u9650\u5236\u65F6\u95F4\uFF08\u5355\u4F4D\u79D2\uFF09\uFF0C\u76EE\u524D\u4E3A5\u5206\u949F\uFF0C\u5982\u679C\u6709\u4E1A\u52A1\u5904\u7406\u65F6\u95F4\u8D85\u8FC75\u5206\u949F\uFF0C\u53EF\u4EE5\u9002\u5F53\u8C03\u6574\u3002
+jdbc.removeAbandonedTimeout=300
+#\u94FE\u63A5\u56DE\u6536\u7684\u65F6\u5019\u63A7\u5236\u53F0\u6253\u5370\u4FE1\u606F\uFF0C\u6D4B\u8BD5\u73AF\u5883\u53EF\u4EE5\u52A0\u4E0Atrue\uFF0C\u7EBF\u4E0A\u73AF\u5883false\u3002\u4F1A\u5F71\u54CD\u6027\u80FD\u3002
+jdbc.logAbandoned=true
+#\u7EDF\u8BA1\u76D1\u63A7
+jdbc.filters=stat
+
+jedis.host=123.57.207.214
+jedis.port=6379
+jedis.timeout=5000
+jedis.password=aft@2016!redis$
+
+pwd.hash_algorithm_name=md5
+pwd.hash_iterations=2
+
+session.timeout=12000000
+session.validate.timespan=18000000
+
+app.name=AFT
+static.host=//afts.hnzhiming.com

+ 93 - 0
src/main/resources/props/config_local.properties

@@ -0,0 +1,93 @@
+#Driver
+jdbc.driverClassName=com.mysql.jdbc.Driver
+#\u6570\u636E\u5E93\u94FE\u63A5\uFF0C
+jdbc.url=jdbc:mysql://localhost:3306/aft_dev?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true
+#\u5E10\u53F7
+jdbc.username=root
+#\u5BC6\u7801
+jdbc.password=123456
+#\u68C0\u6D4B\u6570\u636E\u5E93\u94FE\u63A5\u662F\u5426\u6709\u6548\uFF0C\u5FC5\u987B\u914D\u7F6E
+jdbc.validationQuery=SELECT 'x'
+#\u521D\u59CB\u8FDE\u63A5\u6570
+jdbc.initialSize=3
+#\u6700\u5927\u8FDE\u63A5\u6C60\u6570\u91CF
+jdbc.maxActive=20
+#\u53BB\u6389\uFF0C\u914D\u7F6E\u6587\u4EF6\u5BF9\u5E94\u53BB\u6389
+#jdbc.maxIdle=20
+#\u914D\u7F6E0,\u5F53\u7EBF\u7A0B\u6C60\u6570\u91CF\u4E0D\u8DB3\uFF0C\u81EA\u52A8\u8865\u5145\u3002
+jdbc.minIdle=0
+#\u83B7\u53D6\u94FE\u63A5\u8D85\u65F6\u65F6\u95F4\u4E3A1\u5206\u949F\uFF0C\u5355\u4F4D\u4E3A\u6BEB\u79D2\u3002
+jdbc.maxWait=120000
+#\u83B7\u53D6\u94FE\u63A5\u7684\u65F6\u5019\uFF0C\u4E0D\u6821\u9A8C\u662F\u5426\u53EF\u7528\uFF0C\u5F00\u542F\u4F1A\u6709\u635F\u6027\u80FD\u3002
+jdbc.testOnBorrow=false
+#\u5F52\u8FD8\u94FE\u63A5\u5230\u8FDE\u63A5\u6C60\u7684\u65F6\u5019\u6821\u9A8C\u94FE\u63A5\u662F\u5426\u53EF\u7528\u3002
+jdbc.testOnReturn=false
+#\u6B64\u9879\u914D\u7F6E\u4E3Atrue\u5373\u53EF\uFF0C\u4E0D\u5F71\u54CD\u6027\u80FD\uFF0C\u5E76\u4E14\u4FDD\u8BC1\u5B89\u5168\u6027\u3002\u610F\u4E49\u4E3A\uFF1A\u7533\u8BF7\u8FDE\u63A5\u7684\u65F6\u5019\u68C0\u6D4B\uFF0C\u5982\u679C\u7A7A\u95F2\u65F6\u95F4\u5927\u4E8EtimeBetweenEvictionRunsMillis\uFF0C\u6267\u884CvalidationQuery\u68C0\u6D4B\u8FDE\u63A5\u662F\u5426\u6709\u6548\u3002
+jdbc.testWhileIdle=true
+#1.Destroy\u7EBF\u7A0B\u4F1A\u68C0\u6D4B\u8FDE\u63A5\u7684\u95F4\u9694\u65F6\u95F4
+#2.testWhileIdle\u7684\u5224\u65AD\u4F9D\u636E
+jdbc.timeBetweenEvictionRunsMillis=60000
+#\u4E00\u4E2A\u94FE\u63A5\u751F\u5B58\u7684\u65F6\u95F4\uFF08\u4E4B\u524D\u7684\u503C\uFF1A25200000\uFF0C\u8FD9\u4E2A\u65F6\u95F4\u6709\u70B9BT\uFF0C\u8FD9\u4E2A\u7ED3\u679C\u4E0D\u77E5\u9053\u662F\u600E\u4E48\u6765\u7684\uFF0C\u6362\u7B97\u540E\u7684\u7ED3\u679C\u662F\uFF1A25200000/1000/60/60 = 7\u4E2A\u5C0F\u65F6\uFF09
+jdbc.minEvictableIdleTimeMillis=300000
+#\u94FE\u63A5\u4F7F\u7528\u8D85\u8FC7\u65F6\u95F4\u9650\u5236\u662F\u5426\u56DE\u6536
+jdbc.removeAbandoned=true
+#\u8D85\u8FC7\u65F6\u95F4\u9650\u5236\u65F6\u95F4\uFF08\u5355\u4F4D\u79D2\uFF09\uFF0C\u76EE\u524D\u4E3A5\u5206\u949F\uFF0C\u5982\u679C\u6709\u4E1A\u52A1\u5904\u7406\u65F6\u95F4\u8D85\u8FC75\u5206\u949F\uFF0C\u53EF\u4EE5\u9002\u5F53\u8C03\u6574\u3002
+jdbc.removeAbandonedTimeout=300
+#\u94FE\u63A5\u56DE\u6536\u7684\u65F6\u5019\u63A7\u5236\u53F0\u6253\u5370\u4FE1\u606F\uFF0C\u6D4B\u8BD5\u73AF\u5883\u53EF\u4EE5\u52A0\u4E0Atrue\uFF0C\u7EBF\u4E0A\u73AF\u5883false\u3002\u4F1A\u5F71\u54CD\u6027\u80FD\u3002
+jdbc.logAbandoned=true
+#\u7EDF\u8BA1\u76D1\u63A7
+jdbc.filters=stat
+
+jedis.host=#Driver
+jdbc.driverClassName=com.mysql.jdbc.Driver
+#\u6570\u636E\u5E93\u94FE\u63A5\uFF0C
+jdbc.url=jdbc:mysql://123.57.207.214:3306/pro_aft_test?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true
+#\u5E10\u53F7
+jdbc.username=xiaolong
+#\u5BC6\u7801
+jdbc.password=!qaz2wsx
+#\u68C0\u6D4B\u6570\u636E\u5E93\u94FE\u63A5\u662F\u5426\u6709\u6548\uFF0C\u5FC5\u987B\u914D\u7F6E
+jdbc.validationQuery=SELECT 'x'
+#\u521D\u59CB\u8FDE\u63A5\u6570
+jdbc.initialSize=3
+#\u6700\u5927\u8FDE\u63A5\u6C60\u6570\u91CF
+jdbc.maxActive=20
+#\u53BB\u6389\uFF0C\u914D\u7F6E\u6587\u4EF6\u5BF9\u5E94\u53BB\u6389
+#jdbc.maxIdle=20
+#\u914D\u7F6E0,\u5F53\u7EBF\u7A0B\u6C60\u6570\u91CF\u4E0D\u8DB3\uFF0C\u81EA\u52A8\u8865\u5145\u3002
+jdbc.minIdle=0
+#\u83B7\u53D6\u94FE\u63A5\u8D85\u65F6\u65F6\u95F4\u4E3A1\u5206\u949F\uFF0C\u5355\u4F4D\u4E3A\u6BEB\u79D2\u3002
+jdbc.maxWait=120000
+#\u83B7\u53D6\u94FE\u63A5\u7684\u65F6\u5019\uFF0C\u4E0D\u6821\u9A8C\u662F\u5426\u53EF\u7528\uFF0C\u5F00\u542F\u4F1A\u6709\u635F\u6027\u80FD\u3002
+jdbc.testOnBorrow=false
+#\u5F52\u8FD8\u94FE\u63A5\u5230\u8FDE\u63A5\u6C60\u7684\u65F6\u5019\u6821\u9A8C\u94FE\u63A5\u662F\u5426\u53EF\u7528\u3002
+jdbc.testOnReturn=false
+#\u6B64\u9879\u914D\u7F6E\u4E3Atrue\u5373\u53EF\uFF0C\u4E0D\u5F71\u54CD\u6027\u80FD\uFF0C\u5E76\u4E14\u4FDD\u8BC1\u5B89\u5168\u6027\u3002\u610F\u4E49\u4E3A\uFF1A\u7533\u8BF7\u8FDE\u63A5\u7684\u65F6\u5019\u68C0\u6D4B\uFF0C\u5982\u679C\u7A7A\u95F2\u65F6\u95F4\u5927\u4E8EtimeBetweenEvictionRunsMillis\uFF0C\u6267\u884CvalidationQuery\u68C0\u6D4B\u8FDE\u63A5\u662F\u5426\u6709\u6548\u3002
+jdbc.testWhileIdle=true
+#1.Destroy\u7EBF\u7A0B\u4F1A\u68C0\u6D4B\u8FDE\u63A5\u7684\u95F4\u9694\u65F6\u95F4
+#2.testWhileIdle\u7684\u5224\u65AD\u4F9D\u636E
+jdbc.timeBetweenEvictionRunsMillis=60000
+#\u4E00\u4E2A\u94FE\u63A5\u751F\u5B58\u7684\u65F6\u95F4\uFF08\u4E4B\u524D\u7684\u503C\uFF1A25200000\uFF0C\u8FD9\u4E2A\u65F6\u95F4\u6709\u70B9BT\uFF0C\u8FD9\u4E2A\u7ED3\u679C\u4E0D\u77E5\u9053\u662F\u600E\u4E48\u6765\u7684\uFF0C\u6362\u7B97\u540E\u7684\u7ED3\u679C\u662F\uFF1A25200000/1000/60/60 = 7\u4E2A\u5C0F\u65F6\uFF09
+jdbc.minEvictableIdleTimeMillis=300000
+#\u94FE\u63A5\u4F7F\u7528\u8D85\u8FC7\u65F6\u95F4\u9650\u5236\u662F\u5426\u56DE\u6536
+jdbc.removeAbandoned=true
+#\u8D85\u8FC7\u65F6\u95F4\u9650\u5236\u65F6\u95F4\uFF08\u5355\u4F4D\u79D2\uFF09\uFF0C\u76EE\u524D\u4E3A5\u5206\u949F\uFF0C\u5982\u679C\u6709\u4E1A\u52A1\u5904\u7406\u65F6\u95F4\u8D85\u8FC75\u5206\u949F\uFF0C\u53EF\u4EE5\u9002\u5F53\u8C03\u6574\u3002
+jdbc.removeAbandonedTimeout=300
+#\u94FE\u63A5\u56DE\u6536\u7684\u65F6\u5019\u63A7\u5236\u53F0\u6253\u5370\u4FE1\u606F\uFF0C\u6D4B\u8BD5\u73AF\u5883\u53EF\u4EE5\u52A0\u4E0Atrue\uFF0C\u7EBF\u4E0A\u73AF\u5883false\u3002\u4F1A\u5F71\u54CD\u6027\u80FD\u3002
+jdbc.logAbandoned=true
+#\u7EDF\u8BA1\u76D1\u63A7
+jdbc.filters=stat
+
+jedis.host=localhost
+jedis.port=6379
+jedis.timeout=5000
+#jedis.password=aft@2016!redis$
+
+pwd.hash_algorithm_name=md5
+pwd.hash_iterations=2
+
+session.timeout=12000000
+session.validate.timespan=18000000
+
+app.name=AFT
+static.host=//afts.hnzhiming.com

+ 53 - 0
src/main/resources/props/config_prod.properties

@@ -0,0 +1,53 @@
+#Driver
+jdbc.driverClassName=com.mysql.jdbc.Driver
+#\u6570\u636E\u5E93\u94FE\u63A5\uFF0C
+jdbc.url=jdbc:mysql://123.57.207.214:3306/pro_aft_test?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true
+#\u5E10\u53F7
+jdbc.username=xiaolong
+#\u5BC6\u7801
+jdbc.password=!qaz2wsx
+#\u68C0\u6D4B\u6570\u636E\u5E93\u94FE\u63A5\u662F\u5426\u6709\u6548\uFF0C\u5FC5\u987B\u914D\u7F6E
+jdbc.validationQuery=SELECT 'x'
+#\u521D\u59CB\u8FDE\u63A5\u6570
+jdbc.initialSize=3
+#\u6700\u5927\u8FDE\u63A5\u6C60\u6570\u91CF
+jdbc.maxActive=20
+#\u53BB\u6389\uFF0C\u914D\u7F6E\u6587\u4EF6\u5BF9\u5E94\u53BB\u6389
+#jdbc.maxIdle=20
+#\u914D\u7F6E0,\u5F53\u7EBF\u7A0B\u6C60\u6570\u91CF\u4E0D\u8DB3\uFF0C\u81EA\u52A8\u8865\u5145\u3002
+jdbc.minIdle=0
+#\u83B7\u53D6\u94FE\u63A5\u8D85\u65F6\u65F6\u95F4\u4E3A1\u5206\u949F\uFF0C\u5355\u4F4D\u4E3A\u6BEB\u79D2\u3002
+jdbc.maxWait=120000
+#\u83B7\u53D6\u94FE\u63A5\u7684\u65F6\u5019\uFF0C\u4E0D\u6821\u9A8C\u662F\u5426\u53EF\u7528\uFF0C\u5F00\u542F\u4F1A\u6709\u635F\u6027\u80FD\u3002
+jdbc.testOnBorrow=false
+#\u5F52\u8FD8\u94FE\u63A5\u5230\u8FDE\u63A5\u6C60\u7684\u65F6\u5019\u6821\u9A8C\u94FE\u63A5\u662F\u5426\u53EF\u7528\u3002
+jdbc.testOnReturn=false
+#\u6B64\u9879\u914D\u7F6E\u4E3Atrue\u5373\u53EF\uFF0C\u4E0D\u5F71\u54CD\u6027\u80FD\uFF0C\u5E76\u4E14\u4FDD\u8BC1\u5B89\u5168\u6027\u3002\u610F\u4E49\u4E3A\uFF1A\u7533\u8BF7\u8FDE\u63A5\u7684\u65F6\u5019\u68C0\u6D4B\uFF0C\u5982\u679C\u7A7A\u95F2\u65F6\u95F4\u5927\u4E8EtimeBetweenEvictionRunsMillis\uFF0C\u6267\u884CvalidationQuery\u68C0\u6D4B\u8FDE\u63A5\u662F\u5426\u6709\u6548\u3002
+jdbc.testWhileIdle=true
+#1.Destroy\u7EBF\u7A0B\u4F1A\u68C0\u6D4B\u8FDE\u63A5\u7684\u95F4\u9694\u65F6\u95F4
+#2.testWhileIdle\u7684\u5224\u65AD\u4F9D\u636E
+jdbc.timeBetweenEvictionRunsMillis=60000
+#\u4E00\u4E2A\u94FE\u63A5\u751F\u5B58\u7684\u65F6\u95F4\uFF08\u4E4B\u524D\u7684\u503C\uFF1A25200000\uFF0C\u8FD9\u4E2A\u65F6\u95F4\u6709\u70B9BT\uFF0C\u8FD9\u4E2A\u7ED3\u679C\u4E0D\u77E5\u9053\u662F\u600E\u4E48\u6765\u7684\uFF0C\u6362\u7B97\u540E\u7684\u7ED3\u679C\u662F\uFF1A25200000/1000/60/60 = 7\u4E2A\u5C0F\u65F6\uFF09
+jdbc.minEvictableIdleTimeMillis=300000
+#\u94FE\u63A5\u4F7F\u7528\u8D85\u8FC7\u65F6\u95F4\u9650\u5236\u662F\u5426\u56DE\u6536
+jdbc.removeAbandoned=true
+#\u8D85\u8FC7\u65F6\u95F4\u9650\u5236\u65F6\u95F4\uFF08\u5355\u4F4D\u79D2\uFF09\uFF0C\u76EE\u524D\u4E3A5\u5206\u949F\uFF0C\u5982\u679C\u6709\u4E1A\u52A1\u5904\u7406\u65F6\u95F4\u8D85\u8FC75\u5206\u949F\uFF0C\u53EF\u4EE5\u9002\u5F53\u8C03\u6574\u3002
+jdbc.removeAbandonedTimeout=300
+#\u94FE\u63A5\u56DE\u6536\u7684\u65F6\u5019\u63A7\u5236\u53F0\u6253\u5370\u4FE1\u606F\uFF0C\u6D4B\u8BD5\u73AF\u5883\u53EF\u4EE5\u52A0\u4E0Atrue\uFF0C\u7EBF\u4E0A\u73AF\u5883false\u3002\u4F1A\u5F71\u54CD\u6027\u80FD\u3002
+jdbc.logAbandoned=true
+#\u7EDF\u8BA1\u76D1\u63A7
+jdbc.filters=stat
+
+jedis.host=123.57.207.214
+jedis.port=6379
+jedis.timeout=5000
+jedis.password=aft@2016!redis$
+
+pwd.hash_algorithm_name=md5
+pwd.hash_iterations=2
+
+session.timeout=12000000
+session.validate.timespan=18000000
+
+app.name=AFT
+static.host=//afts.hnzhiming.com

+ 30 - 0
src/main/resources/props/error.properties

@@ -0,0 +1,30 @@
+org.apache.shiro.authc.UnknownAccountException=\u8D26\u53F7\u6216\u5BC6\u7801\u4E0D\u5339\u914D\u3002
+org.apache.shiro.authc.IncorrectCredentialsException=\u8D26\u53F7\u6216\u5BC6\u7801\u4E0D\u5339\u914D\u3002
+org.apache.shiro.authc.LockedAccountException=\u8BE5\u8D26\u53F7\u5DF2\u51BB\u7ED3\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u3002
+org.apache.shiro.authc.DisabledAccountException=\u8BE5\u8D26\u53F7\u5DF2\u51BB\u7ED3\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u3002
+org.apache.shiro.authc.ExcessiveAttemptsException=\u5C1D\u8BD5\u6B21\u6570\u8FC7\u591A\uFF0C\u8D26\u53F7\u6682\u65F6\u88AB\u9501\u5B9A\uFF0C\u8BF7\u7A0D\u540E\u518D\u8BD5\u3002
+org.apache.shiro.session.ExpiredSessionException=\u767B\u5F55\u4FE1\u606F\u5DF2\u8FC7\u671F\uFF0C\u8BF7\u91CD\u65B0\u767B\u5F55\u3002
+org.springframework.dao.DataIntegrityViolationException=\u670D\u52A1\u5668\u6253\u4E86\u4E2A\u76F9\u513F\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u6216\u7A0D\u540E\u518D\u8BD5\u3002
+org.springframework.jdbc.BadSqlGrammarException=\u6570\u636E\u5F02\u5E38\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u6216\u7A0D\u540E\u518D\u8BD5\u3002
+
+NON_LOGIN=\u7528\u6237\u672A\u767B\u5F55\u3002
+
+VCODE_ERROR=\u9A8C\u8BC1\u7801\u8F93\u5165\u9519\u8BEF\uFF01
+
+EMAIL_SIZE_ERROR=\u90AE\u7BB1\u5730\u5740\u957F\u5EA6\u9519\u8BEF\uFF01
+
+EMAIL_PATTERN_ERROR=\u90AE\u7BB1\u5730\u5740\u683C\u5F0F\u9519\u8BEF\uFF01
+
+DUNPLICATE_KAY_ERROR={0}\u5DF2\u88AB\u4ED6\u4EBA\u6CE8\u518C\uFF01
+
+PARAM_INTEGER_ERROR={0}\u53C2\u6570\u5FC5\u987B\u4E3A\u6570\u5B57\u3002
+
+PARAM_EMPTY_ERROR={0}\u5FC5\u987B\u6307\u5B9A\u3002
+
+PARAM_SIZE_ERROR={0}\u53C2\u6570\u957F\u5EA6\u4E0D\u6B63\u786E\uFF0C\u5FC5\u987B\u5728{min}\u548C{max}\u4E4B\u95F4\u3002
+
+PARAM_PATTERN_ERROR={0}\u53C2\u6570\u683C\u5F0F\u987B\u6B63\u786E\u6307\u5B9A{1}\u3002
+
+DATA_EMPTY_ERROR=\u5F53\u524D{0}\u6CA1\u6709\u6570\u636E\u3002
+
+PWD_NOT_MATCH_ERROR=\u539F\u5BC6\u7801\u8F93\u5165\u4E0D\u5339\u914D\u3002

+ 9 - 0
src/main/resources/shiro_base_auth.ini

@@ -0,0 +1,9 @@
+[base_auth]
+/login=anon
+/signin=anon
+/register=anon
+/open/**=anon
+/favicon.ico=anon
+/static/**=anon
+/api/**=simple,permission
+/**=anon

+ 107 - 0
src/main/resources/spring/spring-mvc.xml

@@ -0,0 +1,107 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+	xmlns:task="http://www.springframework.org/schema/task" xmlns:mvc="http://www.springframework.org/schema/mvc"
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
+	xmlns:context="http://www.springframework.org/schema/context"
+	xsi:schemaLocation="http://www.springframework.org/schema/beans 
+	http://www.springframework.org/schema/beans/spring-beans.xsd 
+	http://www.springframework.org/schema/context 
+	http://www.springframework.org/schema/context/spring-context-4.0.xsd 
+	http://www.springframework.org/schema/task
+	http://www.springframework.org/schema/task/spring-task-4.0.xsd
+	http://www.springframework.org/schema/mvc 
+	http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">
+
+	<!-- Scan controller -->
+	<context:component-scan
+		base-package="com.goafanti.*.controller;com.goafanti.*.*.controller;com.goafanti.common.timer;com.goafanti.core.websocket" />
+
+	<bean name="springContextUtils" class="com.goafanti.common.utils.SpringContextUtils" scope="singleton"></bean>
+	
+	<bean id="passwordUtil" class="com.goafanti.common.utils.PasswordUtil">
+		<property name="algorithmName" value="${pwd.hash_algorithm_name}"/>  
+	    <property name="hashIterations" value="${pwd.hash_iterations}"/> 
+	</bean>
+	
+	<!-- spring bean validator -->
+	<bean id="validator"
+		class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
+		<property name="providerClass" value="org.hibernate.validator.HibernateValidator" />
+		<property name="validationMessageSource" ref="messageSource" />
+	</bean>
+
+	<mvc:annotation-driven validator="validator" />
+
+	<bean
+		class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
+		<property name="securityManager" ref="securityManager" />
+	</bean>
+
+
+
+	<bean id="messageSource"
+		class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
+		<property name="defaultEncoding" value="UTF-8"></property>
+		<property name="basenames">
+			<list>
+				<value>classpath:props/error</value>
+			</list>
+		</property>
+	</bean>
+
+
+	<!-- Handles HTTP GET requests for /assets/** by efficiently serving up 
+		static resources in the ${webappRoot}/assets directory -->
+	<mvc:resources mapping="/static/images/**" location="/WEB-INF/images/" />
+	<mvc:resources mapping="/static/html/**" location="/WEB-INF/html/" />
+	<mvc:resources mapping="/static/**" location="/WEB-INF/build/" />
+	<mvc:resources mapping="/favicon.ico" location="/WEB-INF/assets/favicon.ico" />
+	<mvc:resources mapping="/robots.txt" location="/WEB-INF/assets/robots.txt" />
+
+	<!-- Resolves views selected for rendering by @Controllers to .jsp resources 
+		in the /WEB-INF/views directory -->
+	<bean
+		class="org.springframework.web.servlet.view.InternalResourceViewResolver">
+		<property name="viewClass"
+			value="org.springframework.web.servlet.view.JstlView" />
+		<property name="prefix" value="/WEB-INF/views/" />
+		<property name="suffix" value=".jsp" />
+	</bean>
+
+	<bean id="multipartResolver"
+		class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
+		<property name="defaultEncoding" value="UTF-8" />
+		<property name="maxUploadSize" value="10485760" />
+	</bean>
+
+	<bean
+		class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
+	</bean>
+
+	<bean
+		class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
+		<property name="messageConverters">
+			<list>
+				<bean
+					class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter" />
+			</list>
+		</property>
+	</bean>
+
+	<bean id="exceptionResolver" class="com.goafanti.common.error.SystemExceptionResolver">
+		<property name="exceptionMappings">
+			<props>
+				<prop key="org.apache.shiro.authc.UnknownAccountException">login</prop>
+				<prop key="org.apache.shiro.authc.LockedAccountException">login</prop>
+				<prop key="org.apache.shiro.authc.ExcessiveAttemptsException">login</prop>
+				<prop key="org.apache.shiro.authc.IncorrectCredentialsException">login</prop>
+				<prop key="org.apache.shiro.session.ExpiredSessionException">login</prop>
+				<prop key="java.lang.Exception">error</prop>
+			</props>
+		</property>
+	</bean>
+	<bean id="shiroFilterUtils" class="com.goafanti.core.shiro.filter.ShiroFilterUtils" scope="singleton">
+		<property name="appName" value="${app.name}" />
+	</bean>
+	<bean id="systemWebSocketHandler" class="com.goafanti.core.websocket.SystemWebSocketHandler" scope="singleton"></bean>
+</beans>

+ 90 - 0
src/main/resources/spring/spring-mybatis.xml

@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="utf-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xmlns:aop="http://www.springframework.org/schema/aop"
+	xmlns:tx="http://www.springframework.org/schema/tx"
+	xmlns:context="http://www.springframework.org/schema/context"
+	xsi:schemaLocation="
+    		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
+			http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
+			http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
+			http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.0.xsd
+			http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd">
+	<!-- 自动扫描(自动注入) -->
+	<context:component-scan base-package="com.goafanti.*.service;com.goafanti.*.*.service" />
+	
+	<bean id="log-filter" class="com.alibaba.druid.filter.logging.Log4jFilter">
+    	<property name="resultSetLogEnabled" value="true" />
+	</bean>
+	<!-- 配置数据源 -->
+	<bean name="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
+		<property name="url" value="${jdbc.url}" /> 
+		<property name="username" value="${jdbc.username}" /> 
+		<property name="password" value="${jdbc.password}" /> 
+		<property name="initialSize" value="${jdbc.initialSize}" /> 
+	    <property name="minIdle" value="${jdbc.minIdle}" /> 
+	    <property name="maxActive" value="${jdbc.maxActive}" /> 
+	    <property name="maxWait" value="${jdbc.maxWait}" /> 
+	    <property name="timeBetweenEvictionRunsMillis" value="${jdbc.timeBetweenEvictionRunsMillis}" /> 
+	    <property name="minEvictableIdleTimeMillis" value="${jdbc.minEvictableIdleTimeMillis}" /> 
+	    <property name="validationQuery" value="${jdbc.validationQuery}" /> 
+	    <property name="testWhileIdle" value="${jdbc.testWhileIdle}" /> 
+	    <property name="testOnBorrow" value="${jdbc.testOnBorrow}" /> 
+	    <property name="testOnReturn" value="${jdbc.testOnReturn}" /> 
+	    <property name="removeAbandoned" value="${jdbc.removeAbandoned}" /> 
+	    <property name="removeAbandonedTimeout" value="${jdbc.removeAbandonedTimeout}" /> 
+		<property name="logAbandoned" value="${jdbc.logAbandoned}" />
+	    <property name="filters" value="${jdbc.filters}" />
+		<property name="proxyFilters">
+	        <list>
+	            <ref bean="log-filter"/>
+	        </list>
+	    </property>
+	</bean>
+
+	<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
+		<property name="dataSource" ref="dataSource" />
+		<property name="configLocation" value="classpath:mybatis-config.xml"/>
+		<property name="mapperLocations"  >
+			<list>
+				<value>classpath:com/goafanti/common/mapper/*.xml</value>
+			</list>
+		</property>
+	</bean>
+	
+	<bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
+		<constructor-arg index="0" ref="sqlSessionFactory" />
+	</bean>
+	<bean id="baseMybatisDao" class="com.goafanti.core.mybatis.BaseMybatisDao" >
+		<property name="sqlSessionFactory" ref="sqlSessionFactory" />
+	</bean>	
+	<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
+		<property name="basePackage" value="com.goafanti.common.dao" />
+		<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
+	</bean>
+	 <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
+	     <property name="dataSource" ref="dataSource" />
+	 </bean>
+	<tx:advice id="txAdvice" transaction-manager="transactionManager">
+		<tx:attributes>
+			<tx:method name="publish*" />
+			<tx:method name="save*" />
+			<tx:method name="push*" />
+			<tx:method name="add*" />
+			<tx:method name="update*" />
+			<tx:method name="insert*" />
+			<tx:method name="create*" />
+			<tx:method name="del*" />
+			<tx:method name="load*" />
+			<tx:method name="init*" />
+			<tx:method name="bind*" />
+			<tx:method name="*"  read-only="true"/>
+		</tx:attributes>
+	</tx:advice>
+ 	<!-- AOP配置--> 
+	<aop:config proxy-target-class="true">
+		<aop:pointcut id="myPointcut"
+			expression="execution(public * com.goafanti.*.service.impl.*.*(..))" />
+		<aop:advisor advice-ref="txAdvice" pointcut-ref="myPointcut" />
+	</aop:config>
+</beans>

+ 157 - 0
src/main/resources/spring/spring-shiro.xml

@@ -0,0 +1,157 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
+	xmlns:tx="http://www.springframework.org/schema/tx" xmlns:util="http://www.springframework.org/schema/util"
+	xmlns:context="http://www.springframework.org/schema/context"
+	xsi:schemaLocation="
+       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+       http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
+       http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
+       http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
+       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
+    
+     <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
+        <property name="maxIdle" value="100"/><!-- 最大闲置 -->
+        <property name="minIdle" value="10"/><!-- 最小闲置 -->
+        <property name="testOnBorrow" value="true"/><!-- 可以获取 -->
+    </bean>
+    
+	<bean id="jedisPool" class="redis.clients.jedis.JedisPool">
+		<constructor-arg index="0" ref="jedisPoolConfig" />
+	    <constructor-arg index="2" value="${jedis.port}"  name="port" type="int"/>
+	    <constructor-arg index="3" value="${jedis.timeout}"  name="timeout" type="int"/>
+	    <constructor-arg index="1" value="${jedis.host}" name="host" type="java.lang.String"/>
+	</bean>
+	
+	<!-- 会话Session ID生成器 -->
+	<bean id="sessionIdGenerator" class="org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator"/>
+
+	<!-- 会话Cookie模板 -->
+	<bean id="sessionIdCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
+	    <constructor-arg value="AFT_SID"/>
+	    <property name="httpOnly" value="true"/>
+	    <!--cookie的有效时间 -->
+	    <property name="maxAge" value="1296000"/>
+	    <!-- 配置存储Session Cookie的domain为 一级域名
+	    <property name="domain" value=""/>
+	     -->
+	</bean>
+	<!-- custom shiro session listener -->
+	<bean id="customSessionListener" class="com.goafanti.core.shiro.listener.CustomSessionListener">
+	    <property name="shiroSessionRepository" ref="jedisShiroSessionRepository"/>
+	</bean>
+ 	
+	<!-- custom shiro session listener -->
+	<bean id="customShiroSessionDAO" class="com.goafanti.core.shiro.CustomShiroSessionDAO">
+	    <property name="shiroSessionRepository" ref="jedisShiroSessionRepository"/>
+	    <property name="sessionIdGenerator" ref="sessionIdGenerator"/>
+	</bean>
+	<!-- 手动操作Session,管理Session -->
+	<bean id="customSessionManager" class="com.goafanti.core.shiro.session.CustomSessionManager">
+		<property name="shiroSessionRepository" ref="jedisShiroSessionRepository"/>
+		 <property name="customShiroSessionDAO" ref="customShiroSessionDAO"/>
+	</bean>
+ 
+	<!-- 会话验证调度器 -->
+	<bean id="sessionValidationScheduler" class="org.apache.shiro.session.mgt.ExecutorServiceSessionValidationScheduler">
+		 <!-- 间隔多少时间检查,不配置是60分钟 -->
+	     <property name="interval" value="${session.validate.timespan}"/>
+	     <property name="sessionManager" ref="sessionManager"/>
+	</bean>
+	<!-- 安全管理器 -->
+    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
+        <property name="realm" ref="userRealm"/>
+        <property name="sessionManager" ref="sessionManager"/>
+        <property name="cacheManager" ref="customShiroCacheManager"/>
+    </bean>
+	<!-- 用户缓存 -->
+	<bean id="customShiroCacheManager" class="com.goafanti.core.shiro.cache.impl.CustomShiroCacheManager">
+	    <property name="shiroCacheManager" ref="jedisShiroCacheManager"/>
+	</bean>
+	
+	<!-- shiro 缓存实现,对ShiroCacheManager,我是采用redis的实现 -->
+	<bean id="jedisShiroCacheManager" class="com.goafanti.core.shiro.cache.impl.JedisShiroCacheManager">
+	    <property name="jedisManager" ref="jedisManager"/>
+	</bean>
+	<!-- redis 的缓存 -->
+	<bean id="jedisManager" class="com.goafanti.core.shiro.cache.JedisManager">
+	    <property name="jedisPool" ref="jedisPool"/>
+	</bean>
+	<!-- 静态注入,相当于调用SecurityUtils.setSecurityManager(securityManager) -->
+	<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
+	    <property name="staticMethod" value="org.apache.shiro.SecurityUtils.setSecurityManager"/>
+	    <property name="arguments" ref="securityManager"/>
+	</bean>
+
+	<!-- credential matcher -->  
+	<bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
+	    <property name="hashAlgorithmName" value="${pwd.hash_algorithm_name}"/>  
+	    <property name="hashIterations" value="${pwd.hash_iterations}"/>  
+	    <property name="storedCredentialsHexEncoded" value="true"/>  
+	</bean>  
+	<!-- 授权 认证 -->
+	<bean id="userRealm" class="com.goafanti.core.shiro.token.UserRealm" >
+		<property name="credentialsMatcher" ref="credentialsMatcher"/>  
+	</bean>
+	
+	<!-- Session Manager -->
+	<bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
+		<!-- 相隔多久检查一次session的有效性   -->
+	 	<property name="sessionValidationInterval" value="1800000"/>  
+	 	 <!-- session 有效时间为半小时 (毫秒单位)-->  
+	<property name="globalSessionTimeout" value="1800000"/>
+	   <property name="sessionDAO" ref="customShiroSessionDAO"/>
+	   <!-- session 监听,可以多个。 -->
+	   <property name="sessionListeners">
+	       <list>
+	           <ref bean="customSessionListener"/>
+	       </list>
+	   </property>
+	   <!-- 间隔多少时间检查,不配置是60分钟 -->	
+	  <property name="sessionValidationScheduler" ref="sessionValidationScheduler"/>
+	  <!-- 是否开启 检测,默认开启 -->
+	  <property name="sessionValidationSchedulerEnabled" value="true"/>
+	   <!-- 是否删除无效的,默认也是开启 -->
+	  <property name="deleteInvalidSessions" value="true"/>
+		<!-- 会话Cookie模板 -->
+	   <property name="sessionIdCookie" ref="sessionIdCookie"/>
+	</bean>
+	<!-- session 创建、删除、查询 -->
+	<bean id="jedisShiroSessionRepository" class="com.goafanti.core.shiro.cache.JedisShiroSessionRepository" >
+		 <property name="jedisManager" ref="jedisManager"/>
+	</bean>
+
+	<!--
+		自定义角色过滤器 支持多个角色可以访问同一个资源 eg:/home.jsp = authc,roleOR[admin,user]
+		用户有admin或者user角色 就可以访问
+	-->
+	
+	<!-- 认证数据库存储-->
+    <bean id="shiroManager" class="com.goafanti.core.shiro.service.impl.ShiroManagerImpl"/>
+    <bean id="login" class="com.goafanti.core.shiro.filter.LoginFilter"/>
+    <bean id="role" class="com.goafanti.core.shiro.filter.RoleFilter"/>
+    <bean id="permission" class="com.goafanti.core.shiro.filter.PermissionFilter"/>
+    <bean id="simple" class="com.goafanti.core.shiro.filter.SimpleAuthFilter"/>
+	
+	<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
+		<property name="securityManager" ref="securityManager" />
+		<property name="loginUrl" value="/login" />
+		<property name="successUrl" value="/main" />
+		<property name="unauthorizedUrl" value="/login" />
+	
+		<!-- 读取初始自定义权限内容-->
+       <property name="filterChainDefinitions" value="#{shiroManager.loadFilterChainDefinitions()}"/>   
+       <property name="filters">
+           <util:map>
+              <entry key="login" value-ref="login"></entry>
+              <entry key="role" value-ref="role"></entry>
+              <entry key="simple" value-ref="simple"></entry>
+              <entry key="permission" value-ref="permission"></entry>
+           </util:map>
+       </property>
+	</bean>
+	<!-- Shiro生命周期处理器-->
+	<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />
+	
+</beans>
+

+ 19 - 0
src/main/resources/spring/spring-task.xml

@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+ 	xmlns:task="http://www.springframework.org/schema/task"
+	xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context"
+	xsi:schemaLocation="http://www.springframework.org/schema/beans 
+	http://www.springframework.org/schema/beans/spring-beans.xsd 
+	http://www.springframework.org/schema/context 
+	http://www.springframework.org/schema/context/spring-context-4.0.xsd 
+	http://www.springframework.org/schema/task
+	http://www.springframework.org/schema/task/spring-task-4.0.xsd
+	http://www.springframework.org/schema/mvc 
+	http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">
+
+	<task:executor id="executor" pool-size="5" />  
+    <task:scheduler id="scheduler" pool-size="10" />  
+    <task:annotation-driven executor="executor" scheduler="scheduler" />
+    
+</beans>

+ 18 - 0
src/main/webapp/WEB-INF/views/error.jsp

@@ -0,0 +1,18 @@
+<%@ page contentType="text/html;charset=UTF-8" language="java"%>
+<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags"%>
+<!DOCTYPE HTML>
+<html xmlns=http://www.w3.org/1999/xhtml>
+<head>
+	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+	<meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1" />
+	<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no">
+	<title>错误页</title>
+</head>
+<body>
+	<div>
+	找不到该页面,请返回重试<br/>
+	${exception.message}
+	</div>
+	<a target="self" href="javascript:history.back();">返回</a>
+</body>
+</html>

+ 19 - 0
src/main/webapp/WEB-INF/views/index.jsp

@@ -0,0 +1,19 @@
+<%@ page contentType="text/html;charset=UTF-8" language="java"%>
+<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags"%>
+<!DOCTYPE HTML>
+<html xmlns=http://www.w3.org/1999/xhtml>
+<head>
+	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+	<meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1" />
+	<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no">
+	<!--[if lt IE 9]>
+    <script src="//cdn.bootcss.com/html5shiv/r29/html5.min.js"></script>
+	<script src="//cdn.bootcss.com/respond.js/1.4.2/respond.min.js"></script>
+  	<![endif]-->
+	<title>首页</title>
+</head>
+<body>
+	<div id="root">建设中</div>
+	<script type="text/javascript" src="${staticHost}/test.js"></script>
+</body>
+</html>

+ 126 - 0
src/main/webapp/WEB-INF/web.xml

@@ -0,0 +1,126 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee  
+                             http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
+	version="3.1">
+
+	<display-name>AFT</display-name>
+
+	<!-- The definition of the Root Spring Container shared by all Servlets 
+		and Filters -->
+	<context-param>
+		<param-name>spring.profiles.active</param-name>
+		<param-value>local</param-value>
+	</context-param>
+
+	<!-- Creates the Spring Container shared by all Servlets and Filters -->
+	<listener>
+		<listener-class>org.springframework.web.util.IntrospectorCleanupListener</listener-class>
+	</listener>
+	<!-- 开放所有的跨域操作 -->
+	<filter>
+		<filter-name>CorsFilter</filter-name>
+		<filter-class>org.apache.catalina.filters.CorsFilter</filter-class>
+		<async-supported>true</async-supported>
+	</filter>
+	<filter-mapping>
+		<filter-name>CorsFilter</filter-name>
+		<url-pattern>/*</url-pattern>
+	</filter-mapping>
+	<filter>
+		<filter-name>encodingFilter</filter-name>
+		<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
+		<async-supported>true</async-supported>
+		<init-param>
+			<param-name>encoding</param-name>
+			<param-value>UTF-8</param-value>
+		</init-param>
+		<init-param>
+			<param-name>forceEncoding</param-name>
+			<param-value>false</param-value>
+		</init-param>
+	</filter>
+	<filter-mapping>
+		<filter-name>encodingFilter</filter-name>
+		<url-pattern>/*</url-pattern>
+	</filter-mapping>
+	<filter>
+		<filter-name>shiroFilter</filter-name>
+		<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
+		<async-supported>true</async-supported>
+		<init-param>
+			<param-name>targetFilterLifecycle</param-name>
+			<param-value>true</param-value>
+		</init-param>
+	</filter>
+	<filter-mapping>
+		<filter-name>shiroFilter</filter-name>
+		<url-pattern>/*</url-pattern>
+	</filter-mapping>
+	<!-- Resolve PUT Request Parameters -->
+	<filter>
+		<filter-name>HttpMethodFilter</filter-name>
+		<filter-class>org.springframework.web.filter.HttpPutFormContentFilter</filter-class>
+		<async-supported>true</async-supported>
+	</filter>
+	<filter-mapping>
+		<filter-name>HttpMethodFilter</filter-name>
+		<servlet-name>aft</servlet-name>
+	</filter-mapping>
+	<!-- Processes application requests -->
+	<servlet>
+		<servlet-name>aft</servlet-name>
+		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
+		<init-param>
+			<param-name>contextConfigLocation</param-name>
+			<param-value>classpath:applicationContext.xml</param-value>
+		</init-param>
+		<load-on-startup>1</load-on-startup>
+		<async-supported>true</async-supported>
+	</servlet>
+	<!-- 配置 Druid 监控信息显示页面 -->
+	<!-- <servlet>
+		<servlet-name>DruidStatView</servlet-name>
+		<servlet-class>com.alibaba.druid.support.http.StatViewServlet</servlet-class>
+		<init-param>
+			允许清空统计数据
+			<param-name>resetEnable</param-name>
+			<param-value>true</param-value>
+		</init-param>
+		<init-param>
+			用户名
+			<param-name>loginUsername</param-name>
+			<param-value>druid</param-value>
+		</init-param>
+		<init-param>
+			密码
+			<param-name>loginPassword</param-name>
+			<param-value>aft@2016</param-value>
+		</init-param>
+	</servlet>
+	<servlet-mapping>
+		<servlet-name>DruidStatView</servlet-name>
+		<url-pattern>/druid/*</url-pattern>
+	</servlet-mapping> -->
+	<!-- url -->
+	<servlet-mapping>
+		<servlet-name>aft</servlet-name>
+		<url-pattern>/</url-pattern>
+	</servlet-mapping>
+	<error-page>
+		<error-code>400</error-code>
+		<location>/WEB-INF/views/error.jsp</location>
+	</error-page>
+	<error-page>
+		<error-code>403</error-code>
+		<location>/WEB-INF/views/error.jsp</location>
+	</error-page>
+	<error-page>
+		<error-code>404</error-code>
+		<location>/WEB-INF/views/error.jsp</location>
+	</error-page>
+	<error-page>
+		<error-code>500</error-code>
+		<location>/WEB-INF/views/error.jsp</location>
+	</error-page>
+</web-app>