zhangxiaoyu пре 3 година
комит
0bff5015ea
56 измењених фајлова са 3124 додато и 0 уклоњено
  1. 25 0
      .gitignore
  2. 7 0
      Dockerfile
  3. 23 0
      db/mysql.sql
  4. 25 0
      db/oracle.sql
  5. 26 0
      db/postgresql.sql
  6. 26 0
      db/sqlserver.sql
  7. 188 0
      pom.xml
  8. 48 0
      src/main/java/io/common/aspect/RedisAspect.java
  9. 64 0
      src/main/java/io/common/config/RedisConfig.java
  10. 61 0
      src/main/java/io/common/exception/RRException.java
  11. 166 0
      src/main/java/io/common/utils/DateUtils.java
  12. 21 0
      src/main/java/io/common/utils/HttpContextUtils.java
  13. 64 0
      src/main/java/io/common/utils/IPUtils.java
  14. 110 0
      src/main/java/io/common/utils/PageUtils.java
  15. 63 0
      src/main/java/io/common/utils/R.java
  16. 99 0
      src/main/java/io/common/utils/RedisUtils.java
  17. 51 0
      src/main/java/io/common/utils/SpringContextUtils.java
  18. 32 0
      src/main/java/io/common/validator/Assert.java
  19. 47 0
      src/main/java/io/common/validator/ValidatorUtils.java
  20. 17 0
      src/main/java/io/common/validator/group/AddGroup.java
  21. 21 0
      src/main/java/io/common/validator/group/Group.java
  22. 18 0
      src/main/java/io/common/validator/group/UpdateGroup.java
  23. 530 0
      src/main/java/io/common/xss/HTMLFilter.java
  24. 50 0
      src/main/java/io/common/xss/SQLFilter.java
  25. 38 0
      src/main/java/io/common/xss/XssFilter.java
  26. 150 0
      src/main/java/io/common/xss/XssHttpServletRequestWrapper.java
  27. 23 0
      src/main/java/io/renren/ApiApplication.java
  28. 22 0
      src/main/java/io/renren/annotation/Login.java
  29. 25 0
      src/main/java/io/renren/annotation/LoginUser.java
  30. 35 0
      src/main/java/io/renren/config/FilterConfig.java
  31. 37 0
      src/main/java/io/renren/config/MybatisPlusConfig.java
  32. 60 0
      src/main/java/io/renren/config/SwaggerConfig.java
  33. 42 0
      src/main/java/io/renren/config/WebMvcConfig.java
  34. 62 0
      src/main/java/io/renren/controller/ApiLoginController.java
  35. 54 0
      src/main/java/io/renren/controller/ApiRegisterController.java
  36. 53 0
      src/main/java/io/renren/controller/ApiTestController.java
  37. 23 0
      src/main/java/io/renren/dao/TokenDao.java
  38. 23 0
      src/main/java/io/renren/dao/UserDao.java
  39. 46 0
      src/main/java/io/renren/entity/TokenEntity.java
  40. 56 0
      src/main/java/io/renren/entity/UserEntity.java
  41. 33 0
      src/main/java/io/renren/form/LoginForm.java
  42. 34 0
      src/main/java/io/renren/form/RegisterForm.java
  43. 73 0
      src/main/java/io/renren/interceptor/AuthorizationInterceptor.java
  44. 53 0
      src/main/java/io/renren/resolver/LoginUserHandlerMethodArgumentResolver.java
  45. 36 0
      src/main/java/io/renren/service/TokenService.java
  46. 32 0
      src/main/java/io/renren/service/UserService.java
  47. 69 0
      src/main/java/io/renren/service/impl/TokenServiceImpl.java
  48. 59 0
      src/main/java/io/renren/service/impl/UserServiceImpl.java
  49. 34 0
      src/main/resources/application-dev.yml
  50. 34 0
      src/main/resources/application-prod.yml
  51. 34 0
      src/main/resources/application-test.yml
  52. 63 0
      src/main/resources/application.yml
  53. 5 0
      src/main/resources/banner.txt
  54. 21 0
      src/main/resources/logback-spring.xml
  55. 6 0
      src/main/resources/mapper/TokenDao.xml
  56. 7 0
      src/main/resources/mapper/UserDao.xml

+ 25 - 0
.gitignore

@@ -0,0 +1,25 @@
1
+target/
2
+!.mvn/wrapper/maven-wrapper.jar
3
+
4
+### STS ###
5
+.apt_generated
6
+.classpath
7
+.factorypath
8
+.project
9
+.settings
10
+.springBeans
11
+.sts4-cache
12
+
13
+### IntelliJ IDEA ###
14
+.idea
15
+*.iws
16
+*.iml
17
+*.ipr
18
+
19
+### NetBeans ###
20
+nbproject/private/
21
+build/
22
+nbbuild/
23
+dist/
24
+nbdist/
25
+.nb-gradle/

+ 7 - 0
Dockerfile

@@ -0,0 +1,7 @@
1
+FROM java:8
2
+EXPOSE 8081
3
+
4
+VOLUME /tmp
5
+ADD renren-api.jar /app.jar
6
+RUN bash -c 'touch /app.jar'
7
+ENTRYPOINT ["java","-jar","/app.jar"]

+ 23 - 0
db/mysql.sql

@@ -0,0 +1,23 @@
1
+-- 用户表
2
+CREATE TABLE `tb_user` (
3
+  `user_id` bigint NOT NULL AUTO_INCREMENT,
4
+  `username` varchar(50) NOT NULL COMMENT '用户名',
5
+  `mobile` varchar(20) NOT NULL COMMENT '手机号',
6
+  `password` varchar(64) COMMENT '密码',
7
+  `create_time` datetime COMMENT '创建时间',
8
+  PRIMARY KEY (`user_id`),
9
+  UNIQUE INDEX (`username`)
10
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户';
11
+
12
+-- 用户Token表
13
+CREATE TABLE `tb_token` (
14
+  `user_id` bigint NOT NULL,
15
+  `token` varchar(100) NOT NULL COMMENT 'token',
16
+  `expire_time` datetime COMMENT '过期时间',
17
+  `update_time` datetime COMMENT '更新时间',
18
+  PRIMARY KEY (`user_id`),
19
+  UNIQUE INDEX (`token`)
20
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户Token';
21
+
22
+-- 账号:13612345678  密码:admin
23
+INSERT INTO `tb_user` (`username`, `mobile`, `password`, `create_time`) VALUES ('mark', '13612345678', '8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918', '2017-03-23 22:37:41');

+ 25 - 0
db/oracle.sql

@@ -0,0 +1,25 @@
1
+-- 用户表
2
+CREATE TABLE tb_user (
3
+  user_id NUMBER(20, 0) NOT NULL,
4
+  username varchar2(50) NOT NULL,
5
+  mobile varchar2(20) NOT NULL,
6
+  password varchar2(64),
7
+  create_time timestamp,
8
+  PRIMARY KEY (user_id)
9
+);
10
+CREATE UNIQUE INDEX index_username on tb_user(username);
11
+
12
+-- 用户Token表
13
+CREATE TABLE tb_token (
14
+  user_id NUMBER(20, 0) NOT NULL,
15
+  token varchar2(100) NOT NULL,
16
+  expire_time timestamp,
17
+  update_time timestamp,
18
+  PRIMARY KEY (user_id)
19
+);
20
+CREATE UNIQUE INDEX index_token on tb_token(token);
21
+
22
+-- 账号:13612345678  密码:admin
23
+INSERT INTO tb_user (user_id, username, mobile, password, create_time) VALUES (1, 'mark', '13612345678', '8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918', CURRENT_DATE);
24
+
25
+

+ 26 - 0
db/postgresql.sql

@@ -0,0 +1,26 @@
1
+-- 用户表
2
+CREATE TABLE tb_user (
3
+  user_id bigserial,
4
+  username varchar(50) NOT NULL,
5
+  mobile varchar(20) NOT NULL,
6
+  password varchar(64),
7
+  create_time timestamp,
8
+  PRIMARY KEY (user_id),
9
+  UNIQUE (username)
10
+);
11
+
12
+
13
+-- 用户Token表
14
+CREATE TABLE tb_token (
15
+  user_id int8 NOT NULL,
16
+  token varchar(100) NOT NULL,
17
+  expire_time timestamp,
18
+  update_time timestamp,
19
+  PRIMARY KEY (user_id),
20
+  UNIQUE (token)
21
+);
22
+
23
+-- 账号:13612345678  密码:admin
24
+INSERT INTO tb_user (username, mobile, password, create_time) VALUES ('mark', '13612345678', '8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918', '2017-03-23 22:37:41');
25
+
26
+

+ 26 - 0
db/sqlserver.sql

@@ -0,0 +1,26 @@
1
+-- 用户表
2
+CREATE TABLE tb_user (
3
+  user_id bigint NOT NULL IDENTITY(1,1),
4
+  username varchar(50) NOT NULL,
5
+  mobile varchar(20) NOT NULL,
6
+  password varchar(64),
7
+  create_time datetime,
8
+  PRIMARY KEY (user_id),
9
+  UNIQUE (username)
10
+);
11
+
12
+
13
+-- 用户Token表
14
+CREATE TABLE tb_token (
15
+  user_id bigint NOT NULL,
16
+  token varchar(100) NOT NULL,
17
+  expire_time datetime,
18
+  update_time datetime,
19
+  PRIMARY KEY (user_id),
20
+  UNIQUE (token)
21
+);
22
+
23
+-- 账号:13612345678  密码:admin
24
+INSERT INTO tb_user (username, mobile, password, create_time) VALUES ('mark', '13612345678', '8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918', '2017-03-23 22:37:41');
25
+
26
+

+ 188 - 0
pom.xml

@@ -0,0 +1,188 @@
1
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
2
+	<parent>
3
+		<groupId>org.springframework.boot</groupId>
4
+		<artifactId>spring-boot-starter-parent</artifactId>
5
+		<version>2.2.4.RELEASE</version>
6
+	</parent>
7
+	<modelVersion>4.0.0</modelVersion>
8
+	<artifactId>renren-api</artifactId>
9
+	<packaging>jar</packaging>
10
+	<description>renren-api</description>
11
+
12
+  <properties>
13
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
14
+        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
15
+        <java.version>1.8</java.version>
16
+        <junit.version>4.12</junit.version>
17
+        <jedis.version>2.9.0</jedis.version>
18
+        <druid.version>1.1.13</druid.version>
19
+        <mybatisplus.version>3.1.2</mybatisplus.version>
20
+        <mysql.version>8.0.16</mysql.version>
21
+        <mssql.version>4.0</mssql.version>
22
+        <oracle.version>11.2.0.3</oracle.version>
23
+        <commons.lang.version>2.6</commons.lang.version>
24
+        <commons.fileupload.version>1.3.1</commons.fileupload.version>
25
+        <commons.io.version>2.5</commons.io.version>
26
+        <commons.codec.version>1.10</commons.codec.version>
27
+        <fastjson.version>1.2.70</fastjson.version>
28
+        <joda.time.version>2.9.9</joda.time.version>
29
+        <gson.version>2.8.5</gson.version>
30
+        <lombok.version>1.18.4</lombok.version>
31
+        <swagger.version>2.7.0</swagger.version>
32
+    </properties>
33
+
34
+    <dependencies>
35
+        <dependency>
36
+            <groupId>junit</groupId>
37
+            <artifactId>junit</artifactId>
38
+            <version>${junit.version}</version>
39
+            <scope>test</scope>
40
+        </dependency>
41
+        <dependency>
42
+            <groupId>org.springframework.boot</groupId>
43
+            <artifactId>spring-boot-starter-test</artifactId>
44
+            <scope>test</scope>
45
+        </dependency>
46
+        <dependency>
47
+            <groupId>org.springframework.boot</groupId>
48
+            <artifactId>spring-boot-starter-web</artifactId>
49
+        </dependency>
50
+        <dependency>
51
+            <groupId>org.springframework.boot</groupId>
52
+            <artifactId>spring-boot-starter-aop</artifactId>
53
+        </dependency>
54
+        <dependency>
55
+            <groupId>org.springframework</groupId>
56
+            <artifactId>spring-context-support</artifactId>
57
+        </dependency>
58
+        <dependency>
59
+            <groupId>org.springframework.boot</groupId>
60
+            <artifactId>spring-boot-starter-data-redis</artifactId>
61
+        </dependency>
62
+        <dependency>
63
+            <groupId>org.springframework.boot</groupId>
64
+            <artifactId>spring-boot-configuration-processor</artifactId>
65
+            <optional>true</optional>
66
+        </dependency>
67
+        <dependency>
68
+            <groupId>redis.clients</groupId>
69
+            <artifactId>jedis</artifactId>
70
+            <version>${jedis.version}</version>
71
+        </dependency>
72
+        <!-- mysql驱动 -->
73
+        <dependency>
74
+            <groupId>mysql</groupId>
75
+            <artifactId>mysql-connector-java</artifactId>
76
+            <version>${mysql.version}</version>
77
+        </dependency>
78
+        <!-- oracle驱动 -->
79
+        <dependency>
80
+            <groupId>com.oracle</groupId>
81
+            <artifactId>ojdbc6</artifactId>
82
+            <version>${oracle.version}</version>
83
+        </dependency>
84
+        <!-- mssql驱动 -->
85
+        <dependency>
86
+            <groupId>com.microsoft.sqlserver</groupId>
87
+            <artifactId>sqljdbc4</artifactId>
88
+            <version>${mssql.version}</version>
89
+        </dependency>
90
+        <!-- postgresql驱动 -->
91
+        <dependency>
92
+            <groupId>org.postgresql</groupId>
93
+            <artifactId>postgresql</artifactId>
94
+        </dependency>
95
+        <dependency>
96
+            <groupId>com.alibaba</groupId>
97
+            <artifactId>druid-spring-boot-starter</artifactId>
98
+            <version>${druid.version}</version>
99
+        </dependency>
100
+        <dependency>
101
+            <groupId>com.baomidou</groupId>
102
+            <artifactId>mybatis-plus-boot-starter</artifactId>
103
+            <version>${mybatisplus.version}</version>
104
+            <exclusions>
105
+                <exclusion>
106
+                    <groupId>com.baomidou</groupId>
107
+                    <artifactId>mybatis-plus-generator</artifactId>
108
+                </exclusion>
109
+            </exclusions>
110
+        </dependency>
111
+        <dependency>
112
+            <groupId>com.alibaba</groupId>
113
+            <artifactId>fastjson</artifactId>
114
+            <version>${fastjson.version}</version>
115
+        </dependency>
116
+        <dependency>
117
+            <groupId>commons-lang</groupId>
118
+            <artifactId>commons-lang</artifactId>
119
+            <version>${commons.lang.version}</version>
120
+        </dependency>
121
+        <dependency>
122
+            <groupId>commons-fileupload</groupId>
123
+            <artifactId>commons-fileupload</artifactId>
124
+            <version>${commons.fileupload.version}</version>
125
+        </dependency>
126
+        <dependency>
127
+            <groupId>commons-io</groupId>
128
+            <artifactId>commons-io</artifactId>
129
+            <version>${commons.io.version}</version>
130
+        </dependency>
131
+        <dependency>
132
+            <groupId>commons-codec</groupId>
133
+            <artifactId>commons-codec</artifactId>
134
+            <version>${commons.codec.version}</version>
135
+        </dependency>
136
+        <dependency>
137
+            <groupId>joda-time</groupId>
138
+            <artifactId>joda-time</artifactId>
139
+            <version>${joda.time.version}</version>
140
+        </dependency>
141
+        <dependency>
142
+            <groupId>com.google.code.gson</groupId>
143
+            <artifactId>gson</artifactId>
144
+            <version>${gson.version}</version>
145
+        </dependency>
146
+        <dependency>
147
+            <groupId>org.projectlombok</groupId>
148
+            <artifactId>lombok</artifactId>
149
+            <version>${lombok.version}</version>
150
+        </dependency>
151
+        <dependency>
152
+            <groupId>io.springfox</groupId>
153
+            <artifactId>springfox-swagger2</artifactId>
154
+            <version>${swagger.version}</version>
155
+        </dependency>
156
+        <dependency>
157
+            <groupId>io.springfox</groupId>
158
+            <artifactId>springfox-swagger-ui</artifactId>
159
+            <version>${swagger.version}</version>
160
+        </dependency>
161
+    </dependencies>
162
+
163
+    <!-- 阿里云maven仓库 -->
164
+    <repositories>
165
+        <repository>
166
+            <id>public</id>
167
+            <name>aliyun nexus</name>
168
+            <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
169
+            <releases>
170
+                <enabled>true</enabled>
171
+            </releases>
172
+        </repository>
173
+    </repositories>
174
+    <pluginRepositories>
175
+        <pluginRepository>
176
+            <id>public</id>
177
+            <name>aliyun nexus</name>
178
+            <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
179
+            <releases>
180
+                <enabled>true</enabled>
181
+            </releases>
182
+            <snapshots>
183
+                <enabled>false</enabled>
184
+            </snapshots>
185
+        </pluginRepository>
186
+    </pluginRepositories>
187
+
188
+</project>

+ 48 - 0
src/main/java/io/common/aspect/RedisAspect.java

@@ -0,0 +1,48 @@
1
+/**
2
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
3
+ *
4
+ * https://www.renren.io
5
+ *
6
+ * 版权所有,侵权必究!
7
+ */
8
+
9
+package io.common.aspect;
10
+
11
+import io.common.exception.RRException;
12
+import org.aspectj.lang.ProceedingJoinPoint;
13
+import org.aspectj.lang.annotation.Around;
14
+import org.aspectj.lang.annotation.Aspect;
15
+import org.slf4j.Logger;
16
+import org.slf4j.LoggerFactory;
17
+import org.springframework.beans.factory.annotation.Value;
18
+import org.springframework.stereotype.Component;
19
+
20
+/**
21
+ * Redis切面处理类
22
+ *
23
+ * @author Mark sunlightcs@gmail.com
24
+ */
25
+@Aspect
26
+@Component
27
+public class RedisAspect {
28
+    private Logger logger = LoggerFactory.getLogger(getClass());
29
+    /**
30
+     * 是否开启redis缓存  true开启   false关闭
31
+     */
32
+    @Value("${renren.redis.open: false}")
33
+    private boolean open;
34
+
35
+    @Around("execution(* io.common.utils.RedisUtils.*(..))")
36
+    public Object around(ProceedingJoinPoint point) throws Throwable {
37
+        Object result = null;
38
+        if(open){
39
+            try{
40
+                result = point.proceed();
41
+            }catch (Exception e){
42
+                logger.error("redis error", e);
43
+                throw new RRException("Redis服务异常");
44
+            }
45
+        }
46
+        return result;
47
+    }
48
+}

+ 64 - 0
src/main/java/io/common/config/RedisConfig.java

@@ -0,0 +1,64 @@
1
+/**
2
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
3
+ *
4
+ * https://www.renren.io
5
+ *
6
+ * 版权所有,侵权必究!
7
+ */
8
+
9
+package io.common.config;
10
+
11
+import org.springframework.beans.factory.annotation.Autowired;
12
+import org.springframework.context.annotation.Bean;
13
+import org.springframework.context.annotation.Configuration;
14
+import org.springframework.data.redis.connection.RedisConnectionFactory;
15
+import org.springframework.data.redis.core.*;
16
+import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
17
+import org.springframework.data.redis.serializer.StringRedisSerializer;
18
+
19
+/**
20
+ * Redis配置
21
+ *
22
+ * @author Mark sunlightcs@gmail.com
23
+ */
24
+@Configuration
25
+public class RedisConfig {
26
+    @Autowired
27
+    private RedisConnectionFactory factory;
28
+
29
+    @Bean
30
+    public RedisTemplate<String, Object> redisTemplate() {
31
+        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
32
+        redisTemplate.setKeySerializer(new StringRedisSerializer());
33
+        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
34
+        redisTemplate.setHashValueSerializer(new StringRedisSerializer());
35
+        redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer());
36
+        redisTemplate.setConnectionFactory(factory);
37
+        return redisTemplate;
38
+    }
39
+
40
+    @Bean
41
+    public HashOperations<String, String, Object> hashOperations(RedisTemplate<String, Object> redisTemplate) {
42
+        return redisTemplate.opsForHash();
43
+    }
44
+
45
+    @Bean
46
+    public ValueOperations<String, String> valueOperations(RedisTemplate<String, String> redisTemplate) {
47
+        return redisTemplate.opsForValue();
48
+    }
49
+
50
+    @Bean
51
+    public ListOperations<String, Object> listOperations(RedisTemplate<String, Object> redisTemplate) {
52
+        return redisTemplate.opsForList();
53
+    }
54
+
55
+    @Bean
56
+    public SetOperations<String, Object> setOperations(RedisTemplate<String, Object> redisTemplate) {
57
+        return redisTemplate.opsForSet();
58
+    }
59
+
60
+    @Bean
61
+    public ZSetOperations<String, Object> zSetOperations(RedisTemplate<String, Object> redisTemplate) {
62
+        return redisTemplate.opsForZSet();
63
+    }
64
+}

+ 61 - 0
src/main/java/io/common/exception/RRException.java

@@ -0,0 +1,61 @@
1
+/**
2
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
3
+ *
4
+ * https://www.renren.io
5
+ *
6
+ * 版权所有,侵权必究!
7
+ */
8
+
9
+package io.common.exception;
10
+
11
+/**
12
+ * 自定义异常
13
+ *
14
+ * @author Mark sunlightcs@gmail.com
15
+ */
16
+public class RRException extends RuntimeException {
17
+	private static final long serialVersionUID = 1L;
18
+	
19
+    private String msg;
20
+    private int code = 500;
21
+    
22
+    public RRException(String msg) {
23
+		super(msg);
24
+		this.msg = msg;
25
+	}
26
+	
27
+	public RRException(String msg, Throwable e) {
28
+		super(msg, e);
29
+		this.msg = msg;
30
+	}
31
+	
32
+	public RRException(String msg, int code) {
33
+		super(msg);
34
+		this.msg = msg;
35
+		this.code = code;
36
+	}
37
+	
38
+	public RRException(String msg, int code, Throwable e) {
39
+		super(msg, e);
40
+		this.msg = msg;
41
+		this.code = code;
42
+	}
43
+
44
+	public String getMsg() {
45
+		return msg;
46
+	}
47
+
48
+	public void setMsg(String msg) {
49
+		this.msg = msg;
50
+	}
51
+
52
+	public int getCode() {
53
+		return code;
54
+	}
55
+
56
+	public void setCode(int code) {
57
+		this.code = code;
58
+	}
59
+	
60
+	
61
+}

+ 166 - 0
src/main/java/io/common/utils/DateUtils.java

@@ -0,0 +1,166 @@
1
+/**
2
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
3
+ *
4
+ * https://www.renren.io
5
+ *
6
+ * 版权所有,侵权必究!
7
+ */
8
+
9
+package io.common.utils;
10
+
11
+import org.apache.commons.lang.StringUtils;
12
+import org.joda.time.DateTime;
13
+import org.joda.time.LocalDate;
14
+import org.joda.time.format.DateTimeFormat;
15
+import org.joda.time.format.DateTimeFormatter;
16
+
17
+import java.text.SimpleDateFormat;
18
+import java.util.Date;
19
+
20
+/**
21
+ * 日期处理
22
+ *
23
+ * @author Mark sunlightcs@gmail.com
24
+ */
25
+public class DateUtils {
26
+	/** 时间格式(yyyy-MM-dd) */
27
+	public final static String DATE_PATTERN = "yyyy-MM-dd";
28
+	/** 时间格式(yyyy-MM-dd HH:mm:ss) */
29
+	public final static String DATE_TIME_PATTERN = "yyyy-MM-dd HH:mm:ss";
30
+
31
+    /**
32
+     * 日期格式化 日期格式为:yyyy-MM-dd
33
+     * @param date  日期
34
+     * @return  返回yyyy-MM-dd格式日期
35
+     */
36
+	public static String format(Date date) {
37
+        return format(date, DATE_PATTERN);
38
+    }
39
+
40
+    /**
41
+     * 日期格式化 日期格式为:yyyy-MM-dd
42
+     * @param date  日期
43
+     * @param pattern  格式,如:DateUtils.DATE_TIME_PATTERN
44
+     * @return  返回yyyy-MM-dd格式日期
45
+     */
46
+    public static String format(Date date, String pattern) {
47
+        if(date != null){
48
+            SimpleDateFormat df = new SimpleDateFormat(pattern);
49
+            return df.format(date);
50
+        }
51
+        return null;
52
+    }
53
+
54
+    /**
55
+     * 字符串转换成日期
56
+     * @param strDate 日期字符串
57
+     * @param pattern 日期的格式,如:DateUtils.DATE_TIME_PATTERN
58
+     */
59
+    public static Date stringToDate(String strDate, String pattern) {
60
+        if (StringUtils.isBlank(strDate)){
61
+            return null;
62
+        }
63
+
64
+        DateTimeFormatter fmt = DateTimeFormat.forPattern(pattern);
65
+        return fmt.parseLocalDateTime(strDate).toDate();
66
+    }
67
+
68
+    /**
69
+     * 根据周数,获取开始日期、结束日期
70
+     * @param week  周期  0本周,-1上周,-2上上周,1下周,2下下周
71
+     * @return  返回date[0]开始日期、date[1]结束日期
72
+     */
73
+    public static Date[] getWeekStartAndEnd(int week) {
74
+        DateTime dateTime = new DateTime();
75
+        LocalDate date = new LocalDate(dateTime.plusWeeks(week));
76
+
77
+        date = date.dayOfWeek().withMinimumValue();
78
+        Date beginDate = date.toDate();
79
+        Date endDate = date.plusDays(6).toDate();
80
+        return new Date[]{beginDate, endDate};
81
+    }
82
+
83
+    /**
84
+     * 对日期的【秒】进行加/减
85
+     *
86
+     * @param date 日期
87
+     * @param seconds 秒数,负数为减
88
+     * @return 加/减几秒后的日期
89
+     */
90
+    public static Date addDateSeconds(Date date, int seconds) {
91
+        DateTime dateTime = new DateTime(date);
92
+        return dateTime.plusSeconds(seconds).toDate();
93
+    }
94
+
95
+    /**
96
+     * 对日期的【分钟】进行加/减
97
+     *
98
+     * @param date 日期
99
+     * @param minutes 分钟数,负数为减
100
+     * @return 加/减几分钟后的日期
101
+     */
102
+    public static Date addDateMinutes(Date date, int minutes) {
103
+        DateTime dateTime = new DateTime(date);
104
+        return dateTime.plusMinutes(minutes).toDate();
105
+    }
106
+
107
+    /**
108
+     * 对日期的【小时】进行加/减
109
+     *
110
+     * @param date 日期
111
+     * @param hours 小时数,负数为减
112
+     * @return 加/减几小时后的日期
113
+     */
114
+    public static Date addDateHours(Date date, int hours) {
115
+        DateTime dateTime = new DateTime(date);
116
+        return dateTime.plusHours(hours).toDate();
117
+    }
118
+
119
+    /**
120
+     * 对日期的【天】进行加/减
121
+     *
122
+     * @param date 日期
123
+     * @param days 天数,负数为减
124
+     * @return 加/减几天后的日期
125
+     */
126
+    public static Date addDateDays(Date date, int days) {
127
+        DateTime dateTime = new DateTime(date);
128
+        return dateTime.plusDays(days).toDate();
129
+    }
130
+
131
+    /**
132
+     * 对日期的【周】进行加/减
133
+     *
134
+     * @param date 日期
135
+     * @param weeks 周数,负数为减
136
+     * @return 加/减几周后的日期
137
+     */
138
+    public static Date addDateWeeks(Date date, int weeks) {
139
+        DateTime dateTime = new DateTime(date);
140
+        return dateTime.plusWeeks(weeks).toDate();
141
+    }
142
+
143
+    /**
144
+     * 对日期的【月】进行加/减
145
+     *
146
+     * @param date 日期
147
+     * @param months 月数,负数为减
148
+     * @return 加/减几月后的日期
149
+     */
150
+    public static Date addDateMonths(Date date, int months) {
151
+        DateTime dateTime = new DateTime(date);
152
+        return dateTime.plusMonths(months).toDate();
153
+    }
154
+
155
+    /**
156
+     * 对日期的【年】进行加/减
157
+     *
158
+     * @param date 日期
159
+     * @param years 年数,负数为减
160
+     * @return 加/减几年后的日期
161
+     */
162
+    public static Date addDateYears(Date date, int years) {
163
+        DateTime dateTime = new DateTime(date);
164
+        return dateTime.plusYears(years).toDate();
165
+    }
166
+}

+ 21 - 0
src/main/java/io/common/utils/HttpContextUtils.java

@@ -0,0 +1,21 @@
1
+/**
2
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
3
+ *
4
+ * https://www.renren.io
5
+ *
6
+ * 版权所有,侵权必究!
7
+ */
8
+
9
+package io.common.utils;
10
+
11
+import org.springframework.web.context.request.RequestContextHolder;
12
+import org.springframework.web.context.request.ServletRequestAttributes;
13
+
14
+import javax.servlet.http.HttpServletRequest;
15
+
16
+public class HttpContextUtils {
17
+
18
+	public static HttpServletRequest getHttpServletRequest() {
19
+		return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
20
+	}
21
+}

+ 64 - 0
src/main/java/io/common/utils/IPUtils.java

@@ -0,0 +1,64 @@
1
+/**
2
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
3
+ *
4
+ * https://www.renren.io
5
+ *
6
+ * 版权所有,侵权必究!
7
+ */
8
+
9
+package io.common.utils;
10
+
11
+import org.apache.commons.lang.StringUtils;
12
+import org.slf4j.Logger;
13
+import org.slf4j.LoggerFactory;
14
+
15
+import javax.servlet.http.HttpServletRequest;
16
+
17
+/**
18
+ * IP地址
19
+ *
20
+ * @author Mark sunlightcs@gmail.com
21
+ */
22
+public class IPUtils {
23
+	private static Logger logger = LoggerFactory.getLogger(IPUtils.class);
24
+
25
+	/**
26
+	 * 获取IP地址
27
+	 * 
28
+	 * 使用Nginx等反向代理软件, 则不能通过request.getRemoteAddr()获取IP地址
29
+	 * 如果使用了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串IP地址,X-Forwarded-For中第一个非unknown的有效IP字符串,则为真实IP地址
30
+	 */
31
+	public static String getIpAddr(HttpServletRequest request) {
32
+    	String ip = null;
33
+        try {
34
+            ip = request.getHeader("x-forwarded-for");
35
+            if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
36
+                ip = request.getHeader("Proxy-Client-IP");
37
+            }
38
+            if (StringUtils.isEmpty(ip) || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
39
+                ip = request.getHeader("WL-Proxy-Client-IP");
40
+            }
41
+            if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
42
+                ip = request.getHeader("HTTP_CLIENT_IP");
43
+            }
44
+            if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
45
+                ip = request.getHeader("HTTP_X_FORWARDED_FOR");
46
+            }
47
+            if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
48
+                ip = request.getRemoteAddr();
49
+            }
50
+        } catch (Exception e) {
51
+        	logger.error("IPUtils ERROR ", e);
52
+        }
53
+        
54
+//        //使用代理,则获取第一个IP地址
55
+//        if(StringUtils.isEmpty(ip) && ip.length() > 15) {
56
+//			if(ip.indexOf(",") > 0) {
57
+//				ip = ip.substring(0, ip.indexOf(","));
58
+//			}
59
+//		}
60
+        
61
+        return ip;
62
+    }
63
+	
64
+}

+ 110 - 0
src/main/java/io/common/utils/PageUtils.java

@@ -0,0 +1,110 @@
1
+/**
2
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
3
+ *
4
+ * https://www.renren.io
5
+ *
6
+ * 版权所有,侵权必究!
7
+ */
8
+
9
+package io.common.utils;
10
+
11
+import com.baomidou.mybatisplus.core.metadata.IPage;
12
+
13
+import java.io.Serializable;
14
+import java.util.List;
15
+
16
+/**
17
+ * 分页工具类
18
+ *
19
+ * @author Mark sunlightcs@gmail.com
20
+ */
21
+public class PageUtils implements Serializable {
22
+	private static final long serialVersionUID = 1L;
23
+	/**
24
+	 * 总记录数
25
+	 */
26
+	private int totalCount;
27
+	/**
28
+	 * 每页记录数
29
+	 */
30
+	private int pageSize;
31
+	/**
32
+	 * 总页数
33
+	 */
34
+	private int totalPage;
35
+	/**
36
+	 * 当前页数
37
+	 */
38
+	private int currPage;
39
+	/**
40
+	 * 列表数据
41
+	 */
42
+	private List<?> list;
43
+	
44
+	/**
45
+	 * 分页
46
+	 * @param list        列表数据
47
+	 * @param totalCount  总记录数
48
+	 * @param pageSize    每页记录数
49
+	 * @param currPage    当前页数
50
+	 */
51
+	public PageUtils(List<?> list, int totalCount, int pageSize, int currPage) {
52
+		this.list = list;
53
+		this.totalCount = totalCount;
54
+		this.pageSize = pageSize;
55
+		this.currPage = currPage;
56
+		this.totalPage = (int)Math.ceil((double)totalCount/pageSize);
57
+	}
58
+
59
+	/**
60
+	 * 分页
61
+	 */
62
+	public PageUtils(IPage<?> page) {
63
+		this.list = page.getRecords();
64
+		this.totalCount = (int)page.getTotal();
65
+		this.pageSize = (int)page.getSize();
66
+		this.currPage = (int)page.getCurrent();
67
+		this.totalPage = (int)page.getPages();
68
+	}
69
+
70
+	public int getTotalCount() {
71
+		return totalCount;
72
+	}
73
+
74
+	public void setTotalCount(int totalCount) {
75
+		this.totalCount = totalCount;
76
+	}
77
+
78
+	public int getPageSize() {
79
+		return pageSize;
80
+	}
81
+
82
+	public void setPageSize(int pageSize) {
83
+		this.pageSize = pageSize;
84
+	}
85
+
86
+	public int getTotalPage() {
87
+		return totalPage;
88
+	}
89
+
90
+	public void setTotalPage(int totalPage) {
91
+		this.totalPage = totalPage;
92
+	}
93
+
94
+	public int getCurrPage() {
95
+		return currPage;
96
+	}
97
+
98
+	public void setCurrPage(int currPage) {
99
+		this.currPage = currPage;
100
+	}
101
+
102
+	public List<?> getList() {
103
+		return list;
104
+	}
105
+
106
+	public void setList(List<?> list) {
107
+		this.list = list;
108
+	}
109
+	
110
+}

+ 63 - 0
src/main/java/io/common/utils/R.java

@@ -0,0 +1,63 @@
1
+/**
2
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
3
+ *
4
+ * https://www.renren.io
5
+ *
6
+ * 版权所有,侵权必究!
7
+ */
8
+
9
+package io.common.utils;
10
+
11
+import java.util.HashMap;
12
+import java.util.Map;
13
+
14
+/**
15
+ * 返回数据
16
+ *
17
+ * @author Mark sunlightcs@gmail.com
18
+ */
19
+public class R extends HashMap<String, Object> {
20
+	private static final long serialVersionUID = 1L;
21
+	
22
+	public R() {
23
+		put("code", 0);
24
+		put("msg", "success");
25
+	}
26
+	
27
+	public static R error() {
28
+		return error(500, "未知异常,请联系管理员");
29
+	}
30
+	
31
+	public static R error(String msg) {
32
+		return error(500, msg);
33
+	}
34
+	
35
+	public static R error(int code, String msg) {
36
+		R r = new R();
37
+		r.put("code", code);
38
+		r.put("msg", msg);
39
+		return r;
40
+	}
41
+
42
+	public static R ok(String msg) {
43
+		R r = new R();
44
+		r.put("msg", msg);
45
+		return r;
46
+	}
47
+	
48
+	public static R ok(Map<String, Object> map) {
49
+		R r = new R();
50
+		r.putAll(map);
51
+		return r;
52
+	}
53
+	
54
+	public static R ok() {
55
+		return new R();
56
+	}
57
+
58
+	@Override
59
+	public R put(String key, Object value) {
60
+		super.put(key, value);
61
+		return this;
62
+	}
63
+}

+ 99 - 0
src/main/java/io/common/utils/RedisUtils.java

@@ -0,0 +1,99 @@
1
+/**
2
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
3
+ *
4
+ * https://www.renren.io
5
+ *
6
+ * 版权所有,侵权必究!
7
+ */
8
+
9
+package io.common.utils;
10
+
11
+import com.alibaba.fastjson.JSON;
12
+import org.springframework.beans.factory.annotation.Autowired;
13
+import org.springframework.data.redis.core.*;
14
+import org.springframework.stereotype.Component;
15
+
16
+import javax.annotation.Resource;
17
+import java.util.concurrent.TimeUnit;
18
+
19
+/**
20
+ * Redis工具类
21
+ *
22
+ * @author Mark sunlightcs@gmail.com
23
+ */
24
+@Component
25
+public class RedisUtils {
26
+    @Autowired
27
+    private RedisTemplate redisTemplate;
28
+    @Resource(name="redisTemplate")
29
+    private ValueOperations<String, String> valueOperations;
30
+    @Resource(name="redisTemplate")
31
+    private HashOperations<String, String, Object> hashOperations;
32
+    @Resource(name="redisTemplate")
33
+    private ListOperations<String, Object> listOperations;
34
+    @Resource(name="redisTemplate")
35
+    private SetOperations<String, Object> setOperations;
36
+    @Resource(name="redisTemplate")
37
+    private ZSetOperations<String, Object> zSetOperations;
38
+    /**  默认过期时长,单位:秒 */
39
+    public final static long DEFAULT_EXPIRE = 60 * 60 * 24;
40
+    /**  不设置过期时长 */
41
+    public final static long NOT_EXPIRE = -1;
42
+
43
+    public void set(String key, Object value, long expire){
44
+        valueOperations.set(key, toJson(value));
45
+        if(expire != NOT_EXPIRE){
46
+            redisTemplate.expire(key, expire, TimeUnit.SECONDS);
47
+        }
48
+    }
49
+
50
+    public void set(String key, Object value){
51
+        set(key, value, DEFAULT_EXPIRE);
52
+    }
53
+
54
+    public <T> T get(String key, Class<T> clazz, long expire) {
55
+        String value = valueOperations.get(key);
56
+        if(expire != NOT_EXPIRE){
57
+            redisTemplate.expire(key, expire, TimeUnit.SECONDS);
58
+        }
59
+        return value == null ? null : fromJson(value, clazz);
60
+    }
61
+
62
+    public <T> T get(String key, Class<T> clazz) {
63
+        return get(key, clazz, NOT_EXPIRE);
64
+    }
65
+
66
+    public String get(String key, long expire) {
67
+        String value = valueOperations.get(key);
68
+        if(expire != NOT_EXPIRE){
69
+            redisTemplate.expire(key, expire, TimeUnit.SECONDS);
70
+        }
71
+        return value;
72
+    }
73
+
74
+    public String get(String key) {
75
+        return get(key, NOT_EXPIRE);
76
+    }
77
+
78
+    public void delete(String key) {
79
+        redisTemplate.delete(key);
80
+    }
81
+
82
+    /**
83
+     * Object转成JSON数据
84
+     */
85
+    private String toJson(Object object){
86
+        if(object instanceof Integer || object instanceof Long || object instanceof Float ||
87
+                object instanceof Double || object instanceof Boolean || object instanceof String){
88
+            return String.valueOf(object);
89
+        }
90
+        return JSON.toJSONString(object);
91
+    }
92
+
93
+    /**
94
+     * JSON数据,转成Object
95
+     */
96
+    private <T> T fromJson(String json, Class<T> clazz){
97
+        return JSON.parseObject(json, clazz);
98
+    }
99
+}

+ 51 - 0
src/main/java/io/common/utils/SpringContextUtils.java

@@ -0,0 +1,51 @@
1
+/**
2
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
3
+ *
4
+ * https://www.renren.io
5
+ *
6
+ * 版权所有,侵权必究!
7
+ */
8
+
9
+package io.common.utils;
10
+
11
+import org.springframework.beans.BeansException;
12
+import org.springframework.context.ApplicationContext;
13
+import org.springframework.context.ApplicationContextAware;
14
+import org.springframework.stereotype.Component;
15
+
16
+/**
17
+ * Spring Context 工具类
18
+ *
19
+ * @author Mark sunlightcs@gmail.com
20
+ */
21
+@Component
22
+public class SpringContextUtils implements ApplicationContextAware {
23
+	public static ApplicationContext applicationContext; 
24
+
25
+	@Override
26
+	public void setApplicationContext(ApplicationContext applicationContext)
27
+			throws BeansException {
28
+		SpringContextUtils.applicationContext = applicationContext;
29
+	}
30
+
31
+	public static Object getBean(String name) {
32
+		return applicationContext.getBean(name);
33
+	}
34
+
35
+	public static <T> T getBean(String name, Class<T> requiredType) {
36
+		return applicationContext.getBean(name, requiredType);
37
+	}
38
+
39
+	public static boolean containsBean(String name) {
40
+		return applicationContext.containsBean(name);
41
+	}
42
+
43
+	public static boolean isSingleton(String name) {
44
+		return applicationContext.isSingleton(name);
45
+	}
46
+
47
+	public static Class<? extends Object> getType(String name) {
48
+		return applicationContext.getType(name);
49
+	}
50
+
51
+}

+ 32 - 0
src/main/java/io/common/validator/Assert.java

@@ -0,0 +1,32 @@
1
+/**
2
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
3
+ *
4
+ * https://www.renren.io
5
+ *
6
+ * 版权所有,侵权必究!
7
+ */
8
+
9
+package io.common.validator;
10
+
11
+import io.common.exception.RRException;
12
+import org.apache.commons.lang.StringUtils;
13
+
14
+/**
15
+ * 数据校验
16
+ *
17
+ * @author Mark sunlightcs@gmail.com
18
+ */
19
+public abstract class Assert {
20
+
21
+    public static void isBlank(String str, String message) {
22
+        if (StringUtils.isBlank(str)) {
23
+            throw new RRException(message);
24
+        }
25
+    }
26
+
27
+    public static void isNull(Object object, String message) {
28
+        if (object == null) {
29
+            throw new RRException(message);
30
+        }
31
+    }
32
+}

+ 47 - 0
src/main/java/io/common/validator/ValidatorUtils.java

@@ -0,0 +1,47 @@
1
+/**
2
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
3
+ *
4
+ * https://www.renren.io
5
+ *
6
+ * 版权所有,侵权必究!
7
+ */
8
+
9
+package io.common.validator;
10
+
11
+
12
+import io.common.exception.RRException;
13
+
14
+import javax.validation.ConstraintViolation;
15
+import javax.validation.Validation;
16
+import javax.validation.Validator;
17
+import java.util.Set;
18
+
19
+/**
20
+ * hibernate-validator校验工具类
21
+ *
22
+ * 参考文档:http://docs.jboss.org/hibernate/validator/5.4/reference/en-US/html_single/
23
+ *
24
+ * @author Mark sunlightcs@gmail.com
25
+ */
26
+public class ValidatorUtils {
27
+    private static Validator validator;
28
+
29
+    static {
30
+        validator = Validation.buildDefaultValidatorFactory().getValidator();
31
+    }
32
+
33
+    /**
34
+     * 校验对象
35
+     * @param object        待校验对象
36
+     * @param groups        待校验的组
37
+     * @throws RRException  校验不通过,则报RRException异常
38
+     */
39
+    public static void validateEntity(Object object, Class<?>... groups)
40
+            throws RRException {
41
+        Set<ConstraintViolation<Object>> constraintViolations = validator.validate(object, groups);
42
+        if (!constraintViolations.isEmpty()) {
43
+        	ConstraintViolation<Object> constraint = (ConstraintViolation<Object>)constraintViolations.iterator().next();
44
+            throw new RRException(constraint.getMessage());
45
+        }
46
+    }
47
+}

+ 17 - 0
src/main/java/io/common/validator/group/AddGroup.java

@@ -0,0 +1,17 @@
1
+/**
2
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
3
+ *
4
+ * https://www.renren.io
5
+ *
6
+ * 版权所有,侵权必究!
7
+ */
8
+
9
+package io.common.validator.group;
10
+
11
+/**
12
+ * 新增数据 Group
13
+ *
14
+ * @author Mark sunlightcs@gmail.com
15
+ */
16
+public interface AddGroup {
17
+}

+ 21 - 0
src/main/java/io/common/validator/group/Group.java

@@ -0,0 +1,21 @@
1
+/**
2
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
3
+ *
4
+ * https://www.renren.io
5
+ *
6
+ * 版权所有,侵权必究!
7
+ */
8
+
9
+package io.common.validator.group;
10
+
11
+import javax.validation.GroupSequence;
12
+
13
+/**
14
+ * 定义校验顺序,如果AddGroup组失败,则UpdateGroup组不会再校验
15
+ *
16
+ * @author Mark sunlightcs@gmail.com
17
+ */
18
+@GroupSequence({AddGroup.class, UpdateGroup.class})
19
+public interface Group {
20
+
21
+}

+ 18 - 0
src/main/java/io/common/validator/group/UpdateGroup.java

@@ -0,0 +1,18 @@
1
+/**
2
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
3
+ *
4
+ * https://www.renren.io
5
+ *
6
+ * 版权所有,侵权必究!
7
+ */
8
+
9
+package io.common.validator.group;
10
+
11
+/**
12
+ * 更新数据 Group
13
+ *
14
+ * @author Mark sunlightcs@gmail.com
15
+ */
16
+public interface UpdateGroup {
17
+
18
+}

+ 530 - 0
src/main/java/io/common/xss/HTMLFilter.java

@@ -0,0 +1,530 @@
1
+package io.common.xss;
2
+
3
+import java.util.*;
4
+import java.util.concurrent.ConcurrentHashMap;
5
+import java.util.concurrent.ConcurrentMap;
6
+import java.util.logging.Logger;
7
+import java.util.regex.Matcher;
8
+import java.util.regex.Pattern;
9
+
10
+/**
11
+ *
12
+ * HTML filtering utility for protecting against XSS (Cross Site Scripting).
13
+ *
14
+ * This code is licensed LGPLv3
15
+ *
16
+ * This code is a Java port of the original work in PHP by Cal Hendersen.
17
+ * http://code.iamcal.com/php/lib_filter/
18
+ *
19
+ * The trickiest part of the translation was handling the differences in regex handling
20
+ * between PHP and Java.  These resources were helpful in the process:
21
+ *
22
+ * http://java.sun.com/j2se/1.4.2/docs/api/java/util/regex/Pattern.html
23
+ * http://us2.php.net/manual/en/reference.pcre.pattern.modifiers.php
24
+ * http://www.regular-expressions.info/modifiers.html
25
+ *
26
+ * A note on naming conventions: instance variables are prefixed with a "v"; global
27
+ * constants are in all caps.
28
+ *
29
+ * Sample use:
30
+ * String input = ...
31
+ * String clean = new HTMLFilter().filter( input );
32
+ *
33
+ * The class is not thread safe. Create a new instance if in doubt.
34
+ *
35
+ * If you find bugs or have suggestions on improvement (especially regarding
36
+ * performance), please contact us.  The latest version of this
37
+ * source, and our contact details, can be found at http://xss-html-filter.sf.net
38
+ *
39
+ * @author Joseph O'Connell
40
+ * @author Cal Hendersen
41
+ * @author Michael Semb Wever
42
+ */
43
+public final class HTMLFilter {
44
+
45
+    /** regex flag union representing /si modifiers in php **/
46
+    private static final int REGEX_FLAGS_SI = Pattern.CASE_INSENSITIVE | Pattern.DOTALL;
47
+    private static final Pattern P_COMMENTS = Pattern.compile("<!--(.*?)-->", Pattern.DOTALL);
48
+    private static final Pattern P_COMMENT = Pattern.compile("^!--(.*)--$", REGEX_FLAGS_SI);
49
+    private static final Pattern P_TAGS = Pattern.compile("<(.*?)>", Pattern.DOTALL);
50
+    private static final Pattern P_END_TAG = Pattern.compile("^/([a-z0-9]+)", REGEX_FLAGS_SI);
51
+    private static final Pattern P_START_TAG = Pattern.compile("^([a-z0-9]+)(.*?)(/?)$", REGEX_FLAGS_SI);
52
+    private static final Pattern P_QUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)=([\"'])(.*?)\\2", REGEX_FLAGS_SI);
53
+    private static final Pattern P_UNQUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)(=)([^\"\\s']+)", REGEX_FLAGS_SI);
54
+    private static final Pattern P_PROTOCOL = Pattern.compile("^([^:]+):", REGEX_FLAGS_SI);
55
+    private static final Pattern P_ENTITY = Pattern.compile("&#(\\d+);?");
56
+    private static final Pattern P_ENTITY_UNICODE = Pattern.compile("&#x([0-9a-f]+);?");
57
+    private static final Pattern P_ENCODE = Pattern.compile("%([0-9a-f]{2});?");
58
+    private static final Pattern P_VALID_ENTITIES = Pattern.compile("&([^&;]*)(?=(;|&|$))");
59
+    private static final Pattern P_VALID_QUOTES = Pattern.compile("(>|^)([^<]+?)(<|$)", Pattern.DOTALL);
60
+    private static final Pattern P_END_ARROW = Pattern.compile("^>");
61
+    private static final Pattern P_BODY_TO_END = Pattern.compile("<([^>]*?)(?=<|$)");
62
+    private static final Pattern P_XML_CONTENT = Pattern.compile("(^|>)([^<]*?)(?=>)");
63
+    private static final Pattern P_STRAY_LEFT_ARROW = Pattern.compile("<([^>]*?)(?=<|$)");
64
+    private static final Pattern P_STRAY_RIGHT_ARROW = Pattern.compile("(^|>)([^<]*?)(?=>)");
65
+    private static final Pattern P_AMP = Pattern.compile("&");
66
+    private static final Pattern P_QUOTE = Pattern.compile("<");
67
+    private static final Pattern P_LEFT_ARROW = Pattern.compile("<");
68
+    private static final Pattern P_RIGHT_ARROW = Pattern.compile(">");
69
+    private static final Pattern P_BOTH_ARROWS = Pattern.compile("<>");
70
+
71
+    // @xxx could grow large... maybe use sesat's ReferenceMap
72
+    private static final ConcurrentMap<String,Pattern> P_REMOVE_PAIR_BLANKS = new ConcurrentHashMap<String, Pattern>();
73
+    private static final ConcurrentMap<String,Pattern> P_REMOVE_SELF_BLANKS = new ConcurrentHashMap<String, Pattern>();
74
+
75
+    /** set of allowed html elements, along with allowed attributes for each element **/
76
+    private final Map<String, List<String>> vAllowed;
77
+    /** counts of open tags for each (allowable) html element **/
78
+    private final Map<String, Integer> vTagCounts = new HashMap<String, Integer>();
79
+
80
+    /** html elements which must always be self-closing (e.g. "<img />") **/
81
+    private final String[] vSelfClosingTags;
82
+    /** html elements which must always have separate opening and closing tags (e.g. "<b></b>") **/
83
+    private final String[] vNeedClosingTags;
84
+    /** set of disallowed html elements **/
85
+    private final String[] vDisallowed;
86
+    /** attributes which should be checked for valid protocols **/
87
+    private final String[] vProtocolAtts;
88
+    /** allowed protocols **/
89
+    private final String[] vAllowedProtocols;
90
+    /** tags which should be removed if they contain no content (e.g. "<b></b>" or "<b />") **/
91
+    private final String[] vRemoveBlanks;
92
+    /** entities allowed within html markup **/
93
+    private final String[] vAllowedEntities;
94
+    /** flag determining whether comments are allowed in input String. */
95
+    private final boolean stripComment;
96
+    private final boolean encodeQuotes;
97
+    private boolean vDebug = false;
98
+    /**
99
+     * flag determining whether to try to make tags when presented with "unbalanced"
100
+     * angle brackets (e.g. "<b text </b>" becomes "<b> text </b>").  If set to false,
101
+     * unbalanced angle brackets will be html escaped.
102
+     */
103
+    private final boolean alwaysMakeTags;
104
+
105
+    /** Default constructor.
106
+     *
107
+     */
108
+    public HTMLFilter() {
109
+        vAllowed = new HashMap<>();
110
+
111
+        final ArrayList<String> a_atts = new ArrayList<String>();
112
+        a_atts.add("href");
113
+        a_atts.add("target");
114
+        vAllowed.put("a", a_atts);
115
+
116
+        final ArrayList<String> img_atts = new ArrayList<String>();
117
+        img_atts.add("src");
118
+        img_atts.add("width");
119
+        img_atts.add("height");
120
+        img_atts.add("alt");
121
+        vAllowed.put("img", img_atts);
122
+
123
+        final ArrayList<String> no_atts = new ArrayList<String>();
124
+        vAllowed.put("b", no_atts);
125
+        vAllowed.put("strong", no_atts);
126
+        vAllowed.put("i", no_atts);
127
+        vAllowed.put("em", no_atts);
128
+
129
+        vSelfClosingTags = new String[]{"img"};
130
+        vNeedClosingTags = new String[]{"a", "b", "strong", "i", "em"};
131
+        vDisallowed = new String[]{};
132
+        vAllowedProtocols = new String[]{"http", "mailto", "https"}; // no ftp.
133
+        vProtocolAtts = new String[]{"src", "href"};
134
+        vRemoveBlanks = new String[]{"a", "b", "strong", "i", "em"};
135
+        vAllowedEntities = new String[]{"amp", "gt", "lt", "quot"};
136
+        stripComment = true;
137
+        encodeQuotes = true;
138
+        alwaysMakeTags = true;
139
+    }
140
+
141
+    /** Set debug flag to true. Otherwise use default settings. See the default constructor.
142
+     *
143
+     * @param debug turn debug on with a true argument
144
+     */
145
+    public HTMLFilter(final boolean debug) {
146
+        this();
147
+        vDebug = debug;
148
+
149
+    }
150
+
151
+    /** Map-parameter configurable constructor.
152
+     *
153
+     * @param conf map containing configuration. keys match field names.
154
+     */
155
+    public HTMLFilter(final Map<String,Object> conf) {
156
+
157
+        assert conf.containsKey("vAllowed") : "configuration requires vAllowed";
158
+        assert conf.containsKey("vSelfClosingTags") : "configuration requires vSelfClosingTags";
159
+        assert conf.containsKey("vNeedClosingTags") : "configuration requires vNeedClosingTags";
160
+        assert conf.containsKey("vDisallowed") : "configuration requires vDisallowed";
161
+        assert conf.containsKey("vAllowedProtocols") : "configuration requires vAllowedProtocols";
162
+        assert conf.containsKey("vProtocolAtts") : "configuration requires vProtocolAtts";
163
+        assert conf.containsKey("vRemoveBlanks") : "configuration requires vRemoveBlanks";
164
+        assert conf.containsKey("vAllowedEntities") : "configuration requires vAllowedEntities";
165
+
166
+        vAllowed = Collections.unmodifiableMap((HashMap<String, List<String>>) conf.get("vAllowed"));
167
+        vSelfClosingTags = (String[]) conf.get("vSelfClosingTags");
168
+        vNeedClosingTags = (String[]) conf.get("vNeedClosingTags");
169
+        vDisallowed = (String[]) conf.get("vDisallowed");
170
+        vAllowedProtocols = (String[]) conf.get("vAllowedProtocols");
171
+        vProtocolAtts = (String[]) conf.get("vProtocolAtts");
172
+        vRemoveBlanks = (String[]) conf.get("vRemoveBlanks");
173
+        vAllowedEntities = (String[]) conf.get("vAllowedEntities");
174
+        stripComment =  conf.containsKey("stripComment") ? (Boolean) conf.get("stripComment") : true;
175
+        encodeQuotes = conf.containsKey("encodeQuotes") ? (Boolean) conf.get("encodeQuotes") : true;
176
+        alwaysMakeTags = conf.containsKey("alwaysMakeTags") ? (Boolean) conf.get("alwaysMakeTags") : true;
177
+    }
178
+
179
+    private void reset() {
180
+        vTagCounts.clear();
181
+    }
182
+
183
+    private void debug(final String msg) {
184
+        if (vDebug) {
185
+            Logger.getAnonymousLogger().info(msg);
186
+        }
187
+    }
188
+
189
+    //---------------------------------------------------------------
190
+    // my versions of some PHP library functions
191
+    public static String chr(final int decimal) {
192
+        return String.valueOf((char) decimal);
193
+    }
194
+
195
+    public static String htmlSpecialChars(final String s) {
196
+        String result = s;
197
+        result = regexReplace(P_AMP, "&amp;", result);
198
+        result = regexReplace(P_QUOTE, "&quot;", result);
199
+        result = regexReplace(P_LEFT_ARROW, "&lt;", result);
200
+        result = regexReplace(P_RIGHT_ARROW, "&gt;", result);
201
+        return result;
202
+    }
203
+
204
+    //---------------------------------------------------------------
205
+    /**
206
+     * given a user submitted input String, filter out any invalid or restricted
207
+     * html.
208
+     *
209
+     * @param input text (i.e. submitted by a user) than may contain html
210
+     * @return "clean" version of input, with only valid, whitelisted html elements allowed
211
+     */
212
+    public String filter(final String input) {
213
+        reset();
214
+        String s = input;
215
+
216
+        debug("************************************************");
217
+        debug("              INPUT: " + input);
218
+
219
+        s = escapeComments(s);
220
+        debug("     escapeComments: " + s);
221
+
222
+        s = balanceHTML(s);
223
+        debug("        balanceHTML: " + s);
224
+
225
+        s = checkTags(s);
226
+        debug("          checkTags: " + s);
227
+
228
+        s = processRemoveBlanks(s);
229
+        debug("processRemoveBlanks: " + s);
230
+
231
+        s = validateEntities(s);
232
+        debug("    validateEntites: " + s);
233
+
234
+        debug("************************************************\n\n");
235
+        return s;
236
+    }
237
+
238
+    public boolean isAlwaysMakeTags(){
239
+        return alwaysMakeTags;
240
+    }
241
+
242
+    public boolean isStripComments(){
243
+        return stripComment;
244
+    }
245
+
246
+    private String escapeComments(final String s) {
247
+        final Matcher m = P_COMMENTS.matcher(s);
248
+        final StringBuffer buf = new StringBuffer();
249
+        if (m.find()) {
250
+            final String match = m.group(1); //(.*?)
251
+            m.appendReplacement(buf, Matcher.quoteReplacement("<!--" + htmlSpecialChars(match) + "-->"));
252
+        }
253
+        m.appendTail(buf);
254
+
255
+        return buf.toString();
256
+    }
257
+
258
+    private String balanceHTML(String s) {
259
+        if (alwaysMakeTags) {
260
+            //
261
+            // try and form html
262
+            //
263
+            s = regexReplace(P_END_ARROW, "", s);
264
+            s = regexReplace(P_BODY_TO_END, "<$1>", s);
265
+            s = regexReplace(P_XML_CONTENT, "$1<$2", s);
266
+
267
+        } else {
268
+            //
269
+            // escape stray brackets
270
+            //
271
+            s = regexReplace(P_STRAY_LEFT_ARROW, "&lt;$1", s);
272
+            s = regexReplace(P_STRAY_RIGHT_ARROW, "$1$2&gt;<", s);
273
+
274
+            //
275
+            // the last regexp causes '<>' entities to appear
276
+            // (we need to do a lookahead assertion so that the last bracket can
277
+            // be used in the next pass of the regexp)
278
+            //
279
+            s = regexReplace(P_BOTH_ARROWS, "", s);
280
+        }
281
+
282
+        return s;
283
+    }
284
+
285
+    private String checkTags(String s) {
286
+        Matcher m = P_TAGS.matcher(s);
287
+
288
+        final StringBuffer buf = new StringBuffer();
289
+        while (m.find()) {
290
+            String replaceStr = m.group(1);
291
+            replaceStr = processTag(replaceStr);
292
+            m.appendReplacement(buf, Matcher.quoteReplacement(replaceStr));
293
+        }
294
+        m.appendTail(buf);
295
+
296
+        s = buf.toString();
297
+
298
+        // these get tallied in processTag
299
+        // (remember to reset before subsequent calls to filter method)
300
+        for (String key : vTagCounts.keySet()) {
301
+            for (int ii = 0; ii < vTagCounts.get(key); ii++) {
302
+                s += "</" + key + ">";
303
+            }
304
+        }
305
+
306
+        return s;
307
+    }
308
+
309
+    private String processRemoveBlanks(final String s) {
310
+        String result = s;
311
+        for (String tag : vRemoveBlanks) {
312
+            if(!P_REMOVE_PAIR_BLANKS.containsKey(tag)){
313
+                P_REMOVE_PAIR_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?></" + tag + ">"));
314
+            }
315
+            result = regexReplace(P_REMOVE_PAIR_BLANKS.get(tag), "", result);
316
+            if(!P_REMOVE_SELF_BLANKS.containsKey(tag)){
317
+                P_REMOVE_SELF_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?/>"));
318
+            }
319
+            result = regexReplace(P_REMOVE_SELF_BLANKS.get(tag), "", result);
320
+        }
321
+
322
+        return result;
323
+    }
324
+
325
+    private static String regexReplace(final Pattern regex_pattern, final String replacement, final String s) {
326
+        Matcher m = regex_pattern.matcher(s);
327
+        return m.replaceAll(replacement);
328
+    }
329
+
330
+    private String processTag(final String s) {
331
+        // ending tags
332
+        Matcher m = P_END_TAG.matcher(s);
333
+        if (m.find()) {
334
+            final String name = m.group(1).toLowerCase();
335
+            if (allowed(name)) {
336
+                if (!inArray(name, vSelfClosingTags)) {
337
+                    if (vTagCounts.containsKey(name)) {
338
+                        vTagCounts.put(name, vTagCounts.get(name) - 1);
339
+                        return "</" + name + ">";
340
+                    }
341
+                }
342
+            }
343
+        }
344
+
345
+        // starting tags
346
+        m = P_START_TAG.matcher(s);
347
+        if (m.find()) {
348
+            final String name = m.group(1).toLowerCase();
349
+            final String body = m.group(2);
350
+            String ending = m.group(3);
351
+
352
+            //debug( "in a starting tag, name='" + name + "'; body='" + body + "'; ending='" + ending + "'" );
353
+            if (allowed(name)) {
354
+                String params = "";
355
+
356
+                final Matcher m2 = P_QUOTED_ATTRIBUTES.matcher(body);
357
+                final Matcher m3 = P_UNQUOTED_ATTRIBUTES.matcher(body);
358
+                final List<String> paramNames = new ArrayList<String>();
359
+                final List<String> paramValues = new ArrayList<String>();
360
+                while (m2.find()) {
361
+                    paramNames.add(m2.group(1)); //([a-z0-9]+)
362
+                    paramValues.add(m2.group(3)); //(.*?)
363
+                }
364
+                while (m3.find()) {
365
+                    paramNames.add(m3.group(1)); //([a-z0-9]+)
366
+                    paramValues.add(m3.group(3)); //([^\"\\s']+)
367
+                }
368
+
369
+                String paramName, paramValue;
370
+                for (int ii = 0; ii < paramNames.size(); ii++) {
371
+                    paramName = paramNames.get(ii).toLowerCase();
372
+                    paramValue = paramValues.get(ii);
373
+
374
+//          debug( "paramName='" + paramName + "'" );
375
+//          debug( "paramValue='" + paramValue + "'" );
376
+//          debug( "allowed? " + vAllowed.get( name ).contains( paramName ) );
377
+
378
+                    if (allowedAttribute(name, paramName)) {
379
+                        if (inArray(paramName, vProtocolAtts)) {
380
+                            paramValue = processParamProtocol(paramValue);
381
+                        }
382
+                        params += " " + paramName + "=\"" + paramValue + "\"";
383
+                    }
384
+                }
385
+
386
+                if (inArray(name, vSelfClosingTags)) {
387
+                    ending = " /";
388
+                }
389
+
390
+                if (inArray(name, vNeedClosingTags)) {
391
+                    ending = "";
392
+                }
393
+
394
+                if (ending == null || ending.length() < 1) {
395
+                    if (vTagCounts.containsKey(name)) {
396
+                        vTagCounts.put(name, vTagCounts.get(name) + 1);
397
+                    } else {
398
+                        vTagCounts.put(name, 1);
399
+                    }
400
+                } else {
401
+                    ending = " /";
402
+                }
403
+                return "<" + name + params + ending + ">";
404
+            } else {
405
+                return "";
406
+            }
407
+        }
408
+
409
+        // comments
410
+        m = P_COMMENT.matcher(s);
411
+        if (!stripComment && m.find()) {
412
+            return  "<" + m.group() + ">";
413
+        }
414
+
415
+        return "";
416
+    }
417
+
418
+    private String processParamProtocol(String s) {
419
+        s = decodeEntities(s);
420
+        final Matcher m = P_PROTOCOL.matcher(s);
421
+        if (m.find()) {
422
+            final String protocol = m.group(1);
423
+            if (!inArray(protocol, vAllowedProtocols)) {
424
+                // bad protocol, turn into local anchor link instead
425
+                s = "#" + s.substring(protocol.length() + 1, s.length());
426
+                if (s.startsWith("#//")) {
427
+                    s = "#" + s.substring(3, s.length());
428
+                }
429
+            }
430
+        }
431
+
432
+        return s;
433
+    }
434
+
435
+    private String decodeEntities(String s) {
436
+        StringBuffer buf = new StringBuffer();
437
+
438
+        Matcher m = P_ENTITY.matcher(s);
439
+        while (m.find()) {
440
+            final String match = m.group(1);
441
+            final int decimal = Integer.decode(match).intValue();
442
+            m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal)));
443
+        }
444
+        m.appendTail(buf);
445
+        s = buf.toString();
446
+
447
+        buf = new StringBuffer();
448
+        m = P_ENTITY_UNICODE.matcher(s);
449
+        while (m.find()) {
450
+            final String match = m.group(1);
451
+            final int decimal = Integer.valueOf(match, 16).intValue();
452
+            m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal)));
453
+        }
454
+        m.appendTail(buf);
455
+        s = buf.toString();
456
+
457
+        buf = new StringBuffer();
458
+        m = P_ENCODE.matcher(s);
459
+        while (m.find()) {
460
+            final String match = m.group(1);
461
+            final int decimal = Integer.valueOf(match, 16).intValue();
462
+            m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal)));
463
+        }
464
+        m.appendTail(buf);
465
+        s = buf.toString();
466
+
467
+        s = validateEntities(s);
468
+        return s;
469
+    }
470
+
471
+    private String validateEntities(final String s) {
472
+        StringBuffer buf = new StringBuffer();
473
+
474
+        // validate entities throughout the string
475
+        Matcher m = P_VALID_ENTITIES.matcher(s);
476
+        while (m.find()) {
477
+            final String one = m.group(1); //([^&;]*)
478
+            final String two = m.group(2); //(?=(;|&|$))
479
+            m.appendReplacement(buf, Matcher.quoteReplacement(checkEntity(one, two)));
480
+        }
481
+        m.appendTail(buf);
482
+
483
+        return encodeQuotes(buf.toString());
484
+    }
485
+
486
+    private String encodeQuotes(final String s){
487
+        if(encodeQuotes){
488
+            StringBuffer buf = new StringBuffer();
489
+            Matcher m = P_VALID_QUOTES.matcher(s);
490
+            while (m.find()) {
491
+                final String one = m.group(1); //(>|^)
492
+                final String two = m.group(2); //([^<]+?)
493
+                final String three = m.group(3); //(<|$)
494
+                m.appendReplacement(buf, Matcher.quoteReplacement(one + regexReplace(P_QUOTE, "&quot;", two) + three));
495
+            }
496
+            m.appendTail(buf);
497
+            return buf.toString();
498
+        }else{
499
+            return s;
500
+        }
501
+    }
502
+
503
+    private String checkEntity(final String preamble, final String term) {
504
+
505
+        return ";".equals(term) && isValidEntity(preamble)
506
+                ? '&' + preamble
507
+                : "&amp;" + preamble;
508
+    }
509
+
510
+    private boolean isValidEntity(final String entity) {
511
+        return inArray(entity, vAllowedEntities);
512
+    }
513
+
514
+    private static boolean inArray(final String s, final String[] array) {
515
+        for (String item : array) {
516
+            if (item != null && item.equals(s)) {
517
+                return true;
518
+            }
519
+        }
520
+        return false;
521
+    }
522
+
523
+    private boolean allowed(final String name) {
524
+        return (vAllowed.isEmpty() || vAllowed.containsKey(name)) && !inArray(name, vDisallowed);
525
+    }
526
+
527
+    private boolean allowedAttribute(final String name, final String paramName) {
528
+        return allowed(name) && (vAllowed.isEmpty() || vAllowed.get(name).contains(paramName));
529
+    }
530
+}

+ 50 - 0
src/main/java/io/common/xss/SQLFilter.java

@@ -0,0 +1,50 @@
1
+/**
2
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
3
+ *
4
+ * https://www.renren.io
5
+ *
6
+ * 版权所有,侵权必究!
7
+ */
8
+
9
+package io.common.xss;
10
+
11
+import io.common.exception.RRException;
12
+import org.apache.commons.lang.StringUtils;
13
+
14
+/**
15
+ * SQL过滤
16
+ *
17
+ * @author Mark sunlightcs@gmail.com
18
+ */
19
+public class SQLFilter {
20
+
21
+    /**
22
+     * SQL注入过滤
23
+     * @param str  待验证的字符串
24
+     */
25
+    public static String sqlInject(String str){
26
+        if(StringUtils.isBlank(str)){
27
+            return null;
28
+        }
29
+        //去掉'|"|;|\字符
30
+        str = StringUtils.replace(str, "'", "");
31
+        str = StringUtils.replace(str, "\"", "");
32
+        str = StringUtils.replace(str, ";", "");
33
+        str = StringUtils.replace(str, "\\", "");
34
+
35
+        //转换成小写
36
+        str = str.toLowerCase();
37
+
38
+        //非法字符
39
+        String[] keywords = {"master", "truncate", "insert", "select", "delete", "update", "declare", "alter", "drop"};
40
+
41
+        //判断是否包含非法字符
42
+        for(String keyword : keywords){
43
+            if(str.indexOf(keyword) != -1){
44
+                throw new RRException("包含非法字符");
45
+            }
46
+        }
47
+
48
+        return str;
49
+    }
50
+}

+ 38 - 0
src/main/java/io/common/xss/XssFilter.java

@@ -0,0 +1,38 @@
1
+/**
2
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
3
+ *
4
+ * https://www.renren.io
5
+ *
6
+ * 版权所有,侵权必究!
7
+ */
8
+
9
+package io.common.xss;
10
+
11
+import javax.servlet.*;
12
+import javax.servlet.http.HttpServletRequest;
13
+import java.io.IOException;
14
+
15
+/**
16
+ * XSS过滤
17
+ *
18
+ * @author Mark sunlightcs@gmail.com
19
+ */
20
+public class XssFilter implements Filter {
21
+
22
+	@Override
23
+	public void init(FilterConfig config) throws ServletException {
24
+	}
25
+
26
+	@Override
27
+	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
28
+            throws IOException, ServletException {
29
+		XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper(
30
+				(HttpServletRequest) request);
31
+		chain.doFilter(xssRequest, response);
32
+	}
33
+
34
+	@Override
35
+	public void destroy() {
36
+	}
37
+
38
+}

+ 150 - 0
src/main/java/io/common/xss/XssHttpServletRequestWrapper.java

@@ -0,0 +1,150 @@
1
+/**
2
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
3
+ *
4
+ * https://www.renren.io
5
+ *
6
+ * 版权所有,侵权必究!
7
+ */
8
+
9
+package io.common.xss;
10
+
11
+import org.apache.commons.io.IOUtils;
12
+import org.apache.commons.lang.StringUtils;
13
+import org.springframework.http.HttpHeaders;
14
+import org.springframework.http.MediaType;
15
+
16
+import javax.servlet.ReadListener;
17
+import javax.servlet.ServletInputStream;
18
+import javax.servlet.http.HttpServletRequest;
19
+import javax.servlet.http.HttpServletRequestWrapper;
20
+import java.io.ByteArrayInputStream;
21
+import java.io.IOException;
22
+import java.util.LinkedHashMap;
23
+import java.util.Map;
24
+
25
+/**
26
+ * XSS过滤处理
27
+ *
28
+ * @author Mark sunlightcs@gmail.com
29
+ */
30
+public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
31
+    /**
32
+     * 没被包装过的HttpServletRequest(特殊场景,需要自己过滤)
33
+     */
34
+    HttpServletRequest orgRequest;
35
+    /**
36
+     * html过滤
37
+     */
38
+    private final static HTMLFilter htmlFilter = new HTMLFilter();
39
+
40
+    public XssHttpServletRequestWrapper(HttpServletRequest request) {
41
+        super(request);
42
+        orgRequest = request;
43
+    }
44
+
45
+    @Override
46
+    public ServletInputStream getInputStream() throws IOException {
47
+        //非json类型,直接返回
48
+        if(!MediaType.APPLICATION_JSON_VALUE.equalsIgnoreCase(super.getHeader(HttpHeaders.CONTENT_TYPE))){
49
+            return super.getInputStream();
50
+        }
51
+
52
+        //为空,直接返回
53
+        String json = IOUtils.toString(super.getInputStream(), "utf-8");
54
+        if (StringUtils.isBlank(json)) {
55
+            return super.getInputStream();
56
+        }
57
+
58
+        //xss过滤
59
+        json = xssEncode(json);
60
+        final ByteArrayInputStream bis = new ByteArrayInputStream(json.getBytes("utf-8"));
61
+        return new ServletInputStream() {
62
+            @Override
63
+            public boolean isFinished() {
64
+                return true;
65
+            }
66
+
67
+            @Override
68
+            public boolean isReady() {
69
+                return true;
70
+            }
71
+
72
+            @Override
73
+            public void setReadListener(ReadListener readListener) {
74
+            }
75
+
76
+            @Override
77
+            public int read() throws IOException {
78
+                return bis.read();
79
+            }
80
+        };
81
+    }
82
+
83
+    @Override
84
+    public String getParameter(String name) {
85
+        String value = super.getParameter(xssEncode(name));
86
+        if (StringUtils.isNotBlank(value)) {
87
+            value = xssEncode(value);
88
+        }
89
+        return value;
90
+    }
91
+
92
+    @Override
93
+    public String[] getParameterValues(String name) {
94
+        String[] parameters = super.getParameterValues(name);
95
+        if (parameters == null || parameters.length == 0) {
96
+            return null;
97
+        }
98
+
99
+        for (int i = 0; i < parameters.length; i++) {
100
+            parameters[i] = xssEncode(parameters[i]);
101
+        }
102
+        return parameters;
103
+    }
104
+
105
+    @Override
106
+    public Map<String,String[]> getParameterMap() {
107
+        Map<String,String[]> map = new LinkedHashMap<>();
108
+        Map<String,String[]> parameters = super.getParameterMap();
109
+        for (String key : parameters.keySet()) {
110
+            String[] values = parameters.get(key);
111
+            for (int i = 0; i < values.length; i++) {
112
+                values[i] = xssEncode(values[i]);
113
+            }
114
+            map.put(key, values);
115
+        }
116
+        return map;
117
+    }
118
+
119
+    @Override
120
+    public String getHeader(String name) {
121
+        String value = super.getHeader(xssEncode(name));
122
+        if (StringUtils.isNotBlank(value)) {
123
+            value = xssEncode(value);
124
+        }
125
+        return value;
126
+    }
127
+
128
+    private String xssEncode(String input) {
129
+        return htmlFilter.filter(input);
130
+    }
131
+
132
+    /**
133
+     * 获取最原始的request
134
+     */
135
+    public HttpServletRequest getOrgRequest() {
136
+        return orgRequest;
137
+    }
138
+
139
+    /**
140
+     * 获取最原始的request
141
+     */
142
+    public static HttpServletRequest getOrgRequest(HttpServletRequest request) {
143
+        if (request instanceof XssHttpServletRequestWrapper) {
144
+            return ((XssHttpServletRequestWrapper) request).getOrgRequest();
145
+        }
146
+
147
+        return request;
148
+    }
149
+
150
+}

+ 23 - 0
src/main/java/io/renren/ApiApplication.java

@@ -0,0 +1,23 @@
1
+/**
2
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
3
+ *
4
+ * https://www.renren.io
5
+ *
6
+ * 版权所有,侵权必究!
7
+ */
8
+
9
+package io.renren;
10
+
11
+import org.springframework.boot.SpringApplication;
12
+import org.springframework.boot.autoconfigure.SpringBootApplication;
13
+import org.springframework.context.annotation.ComponentScan;
14
+
15
+@SpringBootApplication
16
+@ComponentScan({"io.common","io.renren"})
17
+public class ApiApplication {
18
+
19
+	public static void main(String[] args) {
20
+		SpringApplication.run(ApiApplication.class, args);
21
+	}
22
+
23
+}

+ 22 - 0
src/main/java/io/renren/annotation/Login.java

@@ -0,0 +1,22 @@
1
+/**
2
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
3
+ *
4
+ * https://www.renren.io
5
+ *
6
+ * 版权所有,侵权必究!
7
+ */
8
+
9
+package io.renren.annotation;
10
+
11
+import java.lang.annotation.*;
12
+
13
+/**
14
+ * 登录效验
15
+ *
16
+ * @author Mark sunlightcs@gmail.com
17
+ */
18
+@Target(ElementType.METHOD)
19
+@Retention(RetentionPolicy.RUNTIME)
20
+@Documented
21
+public @interface Login {
22
+}

+ 25 - 0
src/main/java/io/renren/annotation/LoginUser.java

@@ -0,0 +1,25 @@
1
+/**
2
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
3
+ *
4
+ * https://www.renren.io
5
+ *
6
+ * 版权所有,侵权必究!
7
+ */
8
+
9
+package io.renren.annotation;
10
+
11
+import java.lang.annotation.ElementType;
12
+import java.lang.annotation.Retention;
13
+import java.lang.annotation.RetentionPolicy;
14
+import java.lang.annotation.Target;
15
+
16
+/**
17
+ * 登录用户信息
18
+ *
19
+ * @author Mark sunlightcs@gmail.com
20
+ */
21
+@Target(ElementType.PARAMETER)
22
+@Retention(RetentionPolicy.RUNTIME)
23
+public @interface LoginUser {
24
+
25
+}

+ 35 - 0
src/main/java/io/renren/config/FilterConfig.java

@@ -0,0 +1,35 @@
1
+/**
2
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
3
+ *
4
+ * https://www.renren.io
5
+ *
6
+ * 版权所有,侵权必究!
7
+ */
8
+
9
+package io.renren.config;
10
+
11
+import io.common.xss.XssFilter;
12
+import org.springframework.boot.web.servlet.FilterRegistrationBean;
13
+import org.springframework.context.annotation.Bean;
14
+import org.springframework.context.annotation.Configuration;
15
+
16
+import javax.servlet.DispatcherType;
17
+
18
+/**
19
+ * Filter配置
20
+ *
21
+ * @author Mark sunlightcs@gmail.com
22
+ */
23
+@Configuration
24
+public class FilterConfig {
25
+
26
+    @Bean
27
+    public FilterRegistrationBean xssFilterRegistration() {
28
+        FilterRegistrationBean registration = new FilterRegistrationBean();
29
+        registration.setDispatcherTypes(DispatcherType.REQUEST);
30
+        registration.setFilter(new XssFilter());
31
+        registration.addUrlPatterns("/*");
32
+        registration.setName("xssFilter");
33
+        return registration;
34
+    }
35
+}

+ 37 - 0
src/main/java/io/renren/config/MybatisPlusConfig.java

@@ -0,0 +1,37 @@
1
+/**
2
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
3
+ *
4
+ * https://www.renren.io
5
+ *
6
+ * 版权所有,侵权必究!
7
+ */
8
+
9
+package io.renren.config;
10
+
11
+import com.baomidou.mybatisplus.core.injector.ISqlInjector;
12
+import com.baomidou.mybatisplus.extension.injector.LogicSqlInjector;
13
+import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
14
+import org.springframework.context.annotation.Bean;
15
+import org.springframework.context.annotation.Configuration;
16
+
17
+/**
18
+ * mybatis-plus配置
19
+ *
20
+ * @author Mark sunlightcs@gmail.com
21
+ */
22
+@Configuration
23
+public class MybatisPlusConfig {
24
+
25
+    /**
26
+     * 分页插件
27
+     */
28
+    @Bean
29
+    public PaginationInterceptor paginationInterceptor() {
30
+        return new PaginationInterceptor();
31
+    }
32
+
33
+    @Bean
34
+    public ISqlInjector sqlInjector() {
35
+        return new LogicSqlInjector();
36
+    }
37
+}

+ 60 - 0
src/main/java/io/renren/config/SwaggerConfig.java

@@ -0,0 +1,60 @@
1
+/**
2
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
3
+ *
4
+ * https://www.renren.io
5
+ *
6
+ * 版权所有,侵权必究!
7
+ */
8
+
9
+package io.renren.config;
10
+
11
+
12
+import io.swagger.annotations.ApiOperation;
13
+import org.springframework.context.annotation.Bean;
14
+import org.springframework.context.annotation.Configuration;
15
+import springfox.documentation.builders.ApiInfoBuilder;
16
+import springfox.documentation.builders.PathSelectors;
17
+import springfox.documentation.builders.RequestHandlerSelectors;
18
+import springfox.documentation.service.ApiInfo;
19
+import springfox.documentation.service.ApiKey;
20
+import springfox.documentation.spi.DocumentationType;
21
+import springfox.documentation.spring.web.plugins.Docket;
22
+import springfox.documentation.swagger2.annotations.EnableSwagger2;
23
+
24
+import java.util.List;
25
+
26
+import static com.google.common.collect.Lists.newArrayList;
27
+
28
+@Configuration
29
+@EnableSwagger2
30
+public class SwaggerConfig {
31
+    @Bean
32
+    public Docket createRestApi() {
33
+        return new Docket(DocumentationType.SWAGGER_2)
34
+            .apiInfo(apiInfo())
35
+            .select()
36
+            //加了ApiOperation注解的类,才生成接口文档
37
+            .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
38
+            //包下的类,才生成接口文档
39
+            //.apis(RequestHandlerSelectors.basePackage("io.renren.controller"))
40
+            .paths(PathSelectors.any())
41
+            .build()
42
+            .securitySchemes(security());
43
+    }
44
+
45
+    private ApiInfo apiInfo() {
46
+        return new ApiInfoBuilder()
47
+            .title("人人开源")
48
+            .description("renren-api文档")
49
+            .termsOfServiceUrl("https://www.renren.io")
50
+            .version("4.0.0")
51
+            .build();
52
+    }
53
+
54
+    private List<ApiKey> security() {
55
+        return newArrayList(
56
+            new ApiKey("token", "token", "header")
57
+        );
58
+    }
59
+
60
+}

+ 42 - 0
src/main/java/io/renren/config/WebMvcConfig.java

@@ -0,0 +1,42 @@
1
+/**
2
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
3
+ *
4
+ * https://www.renren.io
5
+ *
6
+ * 版权所有,侵权必究!
7
+ */
8
+
9
+package io.renren.config;
10
+
11
+import io.renren.interceptor.AuthorizationInterceptor;
12
+import io.renren.resolver.LoginUserHandlerMethodArgumentResolver;
13
+import org.springframework.beans.factory.annotation.Autowired;
14
+import org.springframework.context.annotation.Configuration;
15
+import org.springframework.web.method.support.HandlerMethodArgumentResolver;
16
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
17
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
18
+
19
+import java.util.List;
20
+
21
+/**
22
+ * MVC配置
23
+ *
24
+ * @author Mark sunlightcs@gmail.com
25
+ */
26
+@Configuration
27
+public class WebMvcConfig implements WebMvcConfigurer {
28
+    @Autowired
29
+    private AuthorizationInterceptor authorizationInterceptor;
30
+    @Autowired
31
+    private LoginUserHandlerMethodArgumentResolver loginUserHandlerMethodArgumentResolver;
32
+
33
+    @Override
34
+    public void addInterceptors(InterceptorRegistry registry) {
35
+        registry.addInterceptor(authorizationInterceptor).addPathPatterns("/api/**");
36
+    }
37
+
38
+    @Override
39
+    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
40
+        argumentResolvers.add(loginUserHandlerMethodArgumentResolver);
41
+    }
42
+}

+ 62 - 0
src/main/java/io/renren/controller/ApiLoginController.java

@@ -0,0 +1,62 @@
1
+/**
2
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
3
+ *
4
+ * https://www.renren.io
5
+ *
6
+ * 版权所有,侵权必究!
7
+ */
8
+
9
+package io.renren.controller;
10
+
11
+
12
+import io.common.utils.R;
13
+import io.renren.annotation.Login;
14
+
15
+import io.renren.form.LoginForm;
16
+import io.renren.service.TokenService;
17
+import io.renren.service.UserService;
18
+import io.swagger.annotations.Api;
19
+import io.swagger.annotations.ApiOperation;
20
+import org.springframework.beans.factory.annotation.Autowired;
21
+import org.springframework.web.bind.annotation.*;
22
+import springfox.documentation.annotations.ApiIgnore;
23
+
24
+import java.util.Map;
25
+
26
+/**
27
+ * 登录接口
28
+ *
29
+ * @author Mark sunlightcs@gmail.com
30
+ */
31
+@RestController
32
+@RequestMapping("/api")
33
+@Api(tags="登录接口")
34
+public class ApiLoginController {
35
+    @Autowired
36
+    private UserService userService;
37
+    @Autowired
38
+    private TokenService tokenService;
39
+
40
+
41
+    @PostMapping("login")
42
+    @ApiOperation("登录")
43
+    public R login(@RequestBody LoginForm form){
44
+        //表单校验
45
+
46
+//        ValidatorUtils.validateEntity(form);
47
+
48
+        //用户登录
49
+        Map<String, Object> map = userService.login(form);
50
+
51
+        return R.ok(map);
52
+    }
53
+
54
+    @Login
55
+    @PostMapping("logout")
56
+    @ApiOperation("退出")
57
+    public R logout(@ApiIgnore @RequestAttribute("userId") long userId){
58
+        tokenService.expireToken(userId);
59
+        return R.ok();
60
+    }
61
+
62
+}

+ 54 - 0
src/main/java/io/renren/controller/ApiRegisterController.java

@@ -0,0 +1,54 @@
1
+/**
2
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
3
+ *
4
+ * https://www.renren.io
5
+ *
6
+ * 版权所有,侵权必究!
7
+ */
8
+
9
+package io.renren.controller;
10
+
11
+import io.common.utils.R;
12
+import io.common.validator.ValidatorUtils;
13
+import io.renren.entity.UserEntity;
14
+import io.renren.form.RegisterForm;
15
+import io.renren.service.UserService;
16
+import io.swagger.annotations.Api;
17
+import io.swagger.annotations.ApiOperation;
18
+import org.apache.commons.codec.digest.DigestUtils;
19
+import org.springframework.beans.factory.annotation.Autowired;
20
+import org.springframework.web.bind.annotation.PostMapping;
21
+import org.springframework.web.bind.annotation.RequestBody;
22
+import org.springframework.web.bind.annotation.RequestMapping;
23
+import org.springframework.web.bind.annotation.RestController;
24
+
25
+import java.util.Date;
26
+
27
+/**
28
+ * 注册接口
29
+ *
30
+ * @author Mark sunlightcs@gmail.com
31
+ */
32
+@RestController
33
+@RequestMapping("/api")
34
+@Api(tags="注册接口")
35
+public class ApiRegisterController {
36
+    @Autowired
37
+    private UserService userService;
38
+
39
+    @PostMapping("register")
40
+    @ApiOperation("注册")
41
+    public R register(@RequestBody RegisterForm form){
42
+        //表单校验
43
+        ValidatorUtils.validateEntity(form);
44
+
45
+        UserEntity user = new UserEntity();
46
+        user.setMobile(form.getMobile());
47
+        user.setUsername(form.getMobile());
48
+        user.setPassword(DigestUtils.sha256Hex(form.getPassword()));
49
+        user.setCreateTime(new Date());
50
+        userService.save(user);
51
+
52
+        return R.ok();
53
+    }
54
+}

+ 53 - 0
src/main/java/io/renren/controller/ApiTestController.java

@@ -0,0 +1,53 @@
1
+/**
2
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
3
+ *
4
+ * https://www.renren.io
5
+ *
6
+ * 版权所有,侵权必究!
7
+ */
8
+
9
+package io.renren.controller;
10
+
11
+import io.common.utils.R;
12
+import io.renren.annotation.Login;
13
+import io.renren.annotation.LoginUser;
14
+import io.renren.entity.UserEntity;
15
+import io.swagger.annotations.Api;
16
+import io.swagger.annotations.ApiOperation;
17
+import org.springframework.web.bind.annotation.GetMapping;
18
+import org.springframework.web.bind.annotation.RequestAttribute;
19
+import org.springframework.web.bind.annotation.RequestMapping;
20
+import org.springframework.web.bind.annotation.RestController;
21
+import springfox.documentation.annotations.ApiIgnore;
22
+
23
+/**
24
+ * 测试接口
25
+ *
26
+ * @author Mark sunlightcs@gmail.com
27
+ */
28
+@RestController
29
+@RequestMapping("/api")
30
+@Api(tags="测试接口")
31
+public class ApiTestController {
32
+
33
+    @Login
34
+    @GetMapping("userInfo")
35
+    @ApiOperation(value="获取用户信息", response=UserEntity.class)
36
+    public R userInfo(@ApiIgnore @LoginUser UserEntity user){
37
+        return R.ok().put("user", user);
38
+    }
39
+
40
+    @Login
41
+    @GetMapping("userId")
42
+    @ApiOperation("获取用户ID")
43
+    public R userInfo(@ApiIgnore @RequestAttribute("userId") Integer userId){
44
+        return R.ok().put("userId", userId);
45
+    }
46
+
47
+    @GetMapping("notToken")
48
+    @ApiOperation("忽略Token验证测试")
49
+    public R notToken(){
50
+        return R.ok().put("msg", "无需token也能访问。。。");
51
+    }
52
+
53
+}

+ 23 - 0
src/main/java/io/renren/dao/TokenDao.java

@@ -0,0 +1,23 @@
1
+/**
2
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
3
+ *
4
+ * https://www.renren.io
5
+ *
6
+ * 版权所有,侵权必究!
7
+ */
8
+
9
+package io.renren.dao;
10
+
11
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
12
+import io.renren.entity.TokenEntity;
13
+import org.apache.ibatis.annotations.Mapper;
14
+
15
+/**
16
+ * 用户Token
17
+ *
18
+ * @author Mark sunlightcs@gmail.com
19
+ */
20
+@Mapper
21
+public interface TokenDao extends BaseMapper<TokenEntity> {
22
+	
23
+}

+ 23 - 0
src/main/java/io/renren/dao/UserDao.java

@@ -0,0 +1,23 @@
1
+/**
2
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
3
+ *
4
+ * https://www.renren.io
5
+ *
6
+ * 版权所有,侵权必究!
7
+ */
8
+
9
+package io.renren.dao;
10
+
11
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
12
+import io.renren.entity.UserEntity;
13
+import org.apache.ibatis.annotations.Mapper;
14
+
15
+/**
16
+ * 用户
17
+ *
18
+ * @author Mark sunlightcs@gmail.com
19
+ */
20
+@Mapper
21
+public interface UserDao extends BaseMapper<UserEntity> {
22
+
23
+}

+ 46 - 0
src/main/java/io/renren/entity/TokenEntity.java

@@ -0,0 +1,46 @@
1
+/**
2
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
3
+ *
4
+ * https://www.renren.io
5
+ *
6
+ * 版权所有,侵权必究!
7
+ */
8
+
9
+package io.renren.entity;
10
+
11
+import com.baomidou.mybatisplus.annotation.IdType;
12
+import com.baomidou.mybatisplus.annotation.TableId;
13
+import com.baomidou.mybatisplus.annotation.TableName;
14
+import lombok.Data;
15
+
16
+import java.io.Serializable;
17
+import java.util.Date;
18
+
19
+
20
+
21
+/**
22
+ * 用户Token
23
+ *
24
+ * @author Mark sunlightcs@gmail.com
25
+ */
26
+@Data
27
+@TableName("tb_token")
28
+public class TokenEntity implements Serializable {
29
+	private static final long serialVersionUID = 1L;
30
+
31
+	/**
32
+	 * 用户ID
33
+	 */
34
+	@TableId(type= IdType.INPUT)
35
+	private Long userId;
36
+	private String token;
37
+	/**
38
+	 * 过期时间
39
+	 */
40
+	private Date expireTime;
41
+	/**
42
+	 * 更新时间
43
+	 */
44
+	private Date updateTime;
45
+
46
+}

+ 56 - 0
src/main/java/io/renren/entity/UserEntity.java

@@ -0,0 +1,56 @@
1
+/**
2
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
3
+ *
4
+ * https://www.renren.io
5
+ *
6
+ * 版权所有,侵权必究!
7
+ */
8
+
9
+package io.renren.entity;
10
+
11
+import com.baomidou.mybatisplus.annotation.TableId;
12
+import com.baomidou.mybatisplus.annotation.TableName;
13
+import com.fasterxml.jackson.annotation.JsonFormat;
14
+import com.fasterxml.jackson.annotation.JsonProperty;
15
+import lombok.Data;
16
+
17
+import java.io.Serializable;
18
+import java.util.Date;
19
+
20
+
21
+
22
+/**
23
+ * 用户
24
+ *
25
+ * @author Mark sunlightcs@gmail.com
26
+ */
27
+@Data
28
+@TableName("tb_user")
29
+public class UserEntity implements Serializable {
30
+	private static final long serialVersionUID = 1L;
31
+
32
+	/**
33
+	 * 用户ID
34
+	 */
35
+	@TableId
36
+	private Long userId;
37
+	/**
38
+	 * 用户名
39
+	 */
40
+	private String username;
41
+	/**
42
+	 * 手机号
43
+	 */
44
+	private String mobile;
45
+	/**
46
+	 * 密码
47
+	 */
48
+	@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
49
+	private String password;
50
+	/**
51
+	 * 创建时间
52
+	 */
53
+	@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
54
+	private Date createTime;
55
+
56
+}

+ 33 - 0
src/main/java/io/renren/form/LoginForm.java

@@ -0,0 +1,33 @@
1
+/**
2
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
3
+ *
4
+ * https://www.renren.io
5
+ *
6
+ * 版权所有,侵权必究!
7
+ */
8
+
9
+package io.renren.form;
10
+
11
+import io.swagger.annotations.ApiModel;
12
+import io.swagger.annotations.ApiModelProperty;
13
+import lombok.Data;
14
+
15
+import javax.validation.constraints.NotBlank;
16
+
17
+/**
18
+ * 登录表单
19
+ *
20
+ * @author Mark sunlightcs@gmail.com
21
+ */
22
+@Data
23
+@ApiModel(value = "登录表单")
24
+public class LoginForm {
25
+    @ApiModelProperty(value = "手机号")
26
+    @NotBlank(message="手机号不能为空")
27
+    private String mobile;
28
+
29
+    @ApiModelProperty(value = "密码")
30
+    @NotBlank(message="密码不能为空")
31
+    private String password;
32
+
33
+}

+ 34 - 0
src/main/java/io/renren/form/RegisterForm.java

@@ -0,0 +1,34 @@
1
+/**
2
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
3
+ *
4
+ * https://www.renren.io
5
+ *
6
+ * 版权所有,侵权必究!
7
+ */
8
+
9
+package io.renren.form;
10
+
11
+import io.swagger.annotations.ApiModel;
12
+import io.swagger.annotations.ApiModelProperty;
13
+import lombok.Data;
14
+
15
+import javax.validation.constraints.NotBlank;
16
+
17
+
18
+/**
19
+ * 注册表单
20
+ *
21
+ * @author Mark sunlightcs@gmail.com
22
+ */
23
+@Data
24
+@ApiModel(value = "注册表单")
25
+public class RegisterForm {
26
+    @ApiModelProperty(value = "手机号")
27
+    @NotBlank(message="手机号不能为空")
28
+    private String mobile;
29
+
30
+    @ApiModelProperty(value = "密码")
31
+    @NotBlank(message="密码不能为空")
32
+    private String password;
33
+
34
+}

+ 73 - 0
src/main/java/io/renren/interceptor/AuthorizationInterceptor.java

@@ -0,0 +1,73 @@
1
+/**
2
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
3
+ *
4
+ * https://www.renren.io
5
+ *
6
+ * 版权所有,侵权必究!
7
+ */
8
+
9
+package io.renren.interceptor;
10
+
11
+
12
+import io.common.exception.RRException;
13
+import io.renren.annotation.Login;
14
+import io.renren.entity.TokenEntity;
15
+import io.renren.service.TokenService;
16
+import org.apache.commons.lang.StringUtils;
17
+import org.springframework.beans.factory.annotation.Autowired;
18
+import org.springframework.stereotype.Component;
19
+import org.springframework.web.method.HandlerMethod;
20
+import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
21
+
22
+import javax.servlet.http.HttpServletRequest;
23
+import javax.servlet.http.HttpServletResponse;
24
+
25
+/**
26
+ * 权限(Token)验证
27
+ *
28
+ * @author Mark sunlightcs@gmail.com
29
+ */
30
+@Component
31
+public class AuthorizationInterceptor extends HandlerInterceptorAdapter {
32
+    @Autowired
33
+    private TokenService tokenService;
34
+
35
+    public static final String USER_KEY = "userId";
36
+
37
+    @Override
38
+    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
39
+        Login annotation;
40
+        if(handler instanceof HandlerMethod) {
41
+            annotation = ((HandlerMethod) handler).getMethodAnnotation(Login.class);
42
+        }else{
43
+            return true;
44
+        }
45
+
46
+        if(annotation == null){
47
+            return true;
48
+        }
49
+
50
+        //从header中获取token
51
+        String token = request.getHeader("token");
52
+        //如果header中不存在token,则从参数中获取token
53
+        if(StringUtils.isBlank(token)){
54
+            token = request.getParameter("token");
55
+        }
56
+
57
+        //token为空
58
+        if(StringUtils.isBlank(token)){
59
+            throw new RRException("token不能为空");
60
+        }
61
+
62
+        //查询token信息
63
+        TokenEntity tokenEntity = tokenService.queryByToken(token);
64
+        if(tokenEntity == null || tokenEntity.getExpireTime().getTime() < System.currentTimeMillis()){
65
+            throw new RRException("token失效,请重新登录");
66
+        }
67
+
68
+        //设置userId到request里,后续根据userId,获取用户信息
69
+        request.setAttribute(USER_KEY, tokenEntity.getUserId());
70
+
71
+        return true;
72
+    }
73
+}

+ 53 - 0
src/main/java/io/renren/resolver/LoginUserHandlerMethodArgumentResolver.java

@@ -0,0 +1,53 @@
1
+/**
2
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
3
+ *
4
+ * https://www.renren.io
5
+ *
6
+ * 版权所有,侵权必究!
7
+ */
8
+
9
+package io.renren.resolver;
10
+
11
+import io.renren.annotation.LoginUser;
12
+import io.renren.entity.UserEntity;
13
+import io.renren.interceptor.AuthorizationInterceptor;
14
+import io.renren.service.UserService;
15
+import org.springframework.beans.factory.annotation.Autowired;
16
+import org.springframework.core.MethodParameter;
17
+import org.springframework.stereotype.Component;
18
+import org.springframework.web.bind.support.WebDataBinderFactory;
19
+import org.springframework.web.context.request.NativeWebRequest;
20
+import org.springframework.web.context.request.RequestAttributes;
21
+import org.springframework.web.method.support.HandlerMethodArgumentResolver;
22
+import org.springframework.web.method.support.ModelAndViewContainer;
23
+
24
+/**
25
+ * 有@LoginUser注解的方法参数,注入当前登录用户
26
+ *
27
+ * @author Mark sunlightcs@gmail.com
28
+ */
29
+@Component
30
+public class LoginUserHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {
31
+    @Autowired
32
+    private UserService userService;
33
+
34
+    @Override
35
+    public boolean supportsParameter(MethodParameter parameter) {
36
+        return parameter.getParameterType().isAssignableFrom(UserEntity.class) && parameter.hasParameterAnnotation(LoginUser.class);
37
+    }
38
+
39
+    @Override
40
+    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer container,
41
+                                  NativeWebRequest request, WebDataBinderFactory factory) throws Exception {
42
+        //获取用户ID
43
+        Object object = request.getAttribute(AuthorizationInterceptor.USER_KEY, RequestAttributes.SCOPE_REQUEST);
44
+        if(object == null){
45
+            return null;
46
+        }
47
+
48
+        //获取用户信息
49
+        UserEntity user = userService.getById((Long)object);
50
+
51
+        return user;
52
+    }
53
+}

+ 36 - 0
src/main/java/io/renren/service/TokenService.java

@@ -0,0 +1,36 @@
1
+/**
2
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
3
+ *
4
+ * https://www.renren.io
5
+ *
6
+ * 版权所有,侵权必究!
7
+ */
8
+
9
+package io.renren.service;
10
+
11
+import com.baomidou.mybatisplus.extension.service.IService;
12
+import io.renren.entity.TokenEntity;
13
+
14
+/**
15
+ * 用户Token
16
+ *
17
+ * @author Mark sunlightcs@gmail.com
18
+ */
19
+public interface TokenService extends IService<TokenEntity> {
20
+
21
+	TokenEntity queryByToken(String token);
22
+
23
+	/**
24
+	 * 生成token
25
+	 * @param userId  用户ID
26
+	 * @return        返回token信息
27
+	 */
28
+	TokenEntity createToken(long userId);
29
+
30
+	/**
31
+	 * 设置token过期
32
+	 * @param userId 用户ID
33
+	 */
34
+	void expireToken(long userId);
35
+
36
+}

+ 32 - 0
src/main/java/io/renren/service/UserService.java

@@ -0,0 +1,32 @@
1
+/**
2
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
3
+ *
4
+ * https://www.renren.io
5
+ *
6
+ * 版权所有,侵权必究!
7
+ */
8
+
9
+package io.renren.service;
10
+
11
+import com.baomidou.mybatisplus.extension.service.IService;
12
+import io.renren.entity.UserEntity;
13
+import io.renren.form.LoginForm;
14
+
15
+import java.util.Map;
16
+
17
+/**
18
+ * 用户
19
+ *
20
+ * @author Mark sunlightcs@gmail.com
21
+ */
22
+public interface UserService extends IService<UserEntity> {
23
+
24
+	UserEntity queryByMobile(String mobile);
25
+
26
+	/**
27
+	 * 用户登录
28
+	 * @param form    登录表单
29
+	 * @return        返回登录信息
30
+	 */
31
+	Map<String, Object> login(LoginForm form);
32
+}

+ 69 - 0
src/main/java/io/renren/service/impl/TokenServiceImpl.java

@@ -0,0 +1,69 @@
1
+/**
2
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
3
+ *
4
+ * https://www.renren.io
5
+ *
6
+ * 版权所有,侵权必究!
7
+ */
8
+
9
+package io.renren.service.impl;
10
+
11
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
12
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
13
+import io.renren.dao.TokenDao;
14
+import io.renren.entity.TokenEntity;
15
+import io.renren.service.TokenService;
16
+import org.springframework.stereotype.Service;
17
+
18
+import java.util.Date;
19
+import java.util.UUID;
20
+
21
+
22
+@Service("tokenService")
23
+public class TokenServiceImpl extends ServiceImpl<TokenDao, TokenEntity> implements TokenService {
24
+	/**
25
+	 * 12小时后过期
26
+	 */
27
+	private final static int EXPIRE = 3600 * 12;
28
+
29
+	@Override
30
+	public TokenEntity queryByToken(String token) {
31
+		return this.getOne(new QueryWrapper<TokenEntity>().eq("token", token));
32
+	}
33
+
34
+	@Override
35
+	public TokenEntity createToken(long userId) {
36
+		//当前时间
37
+		Date now = new Date();
38
+		//过期时间
39
+		Date expireTime = new Date(now.getTime() + EXPIRE * 1000);
40
+
41
+		//生成token
42
+		String token = generateToken();
43
+
44
+		//保存或更新用户token
45
+		TokenEntity tokenEntity = new TokenEntity();
46
+		tokenEntity.setUserId(userId);
47
+		tokenEntity.setToken(token);
48
+		tokenEntity.setUpdateTime(now);
49
+		tokenEntity.setExpireTime(expireTime);
50
+		this.saveOrUpdate(tokenEntity);
51
+
52
+		return tokenEntity;
53
+	}
54
+
55
+	@Override
56
+	public void expireToken(long userId){
57
+		Date now = new Date();
58
+
59
+		TokenEntity tokenEntity = new TokenEntity();
60
+		tokenEntity.setUserId(userId);
61
+		tokenEntity.setUpdateTime(now);
62
+		tokenEntity.setExpireTime(now);
63
+		this.saveOrUpdate(tokenEntity);
64
+	}
65
+
66
+	private String generateToken(){
67
+		return UUID.randomUUID().toString().replace("-", "");
68
+	}
69
+}

+ 59 - 0
src/main/java/io/renren/service/impl/UserServiceImpl.java

@@ -0,0 +1,59 @@
1
+/**
2
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
3
+ *
4
+ * https://www.renren.io
5
+ *
6
+ * 版权所有,侵权必究!
7
+ */
8
+
9
+package io.renren.service.impl;
10
+
11
+
12
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
13
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
14
+import io.common.exception.RRException;
15
+import io.common.validator.Assert;
16
+import io.renren.dao.UserDao;
17
+import io.renren.entity.TokenEntity;
18
+import io.renren.entity.UserEntity;
19
+import io.renren.form.LoginForm;
20
+import io.renren.service.TokenService;
21
+import io.renren.service.UserService;
22
+import org.apache.commons.codec.digest.DigestUtils;
23
+import org.springframework.beans.factory.annotation.Autowired;
24
+import org.springframework.stereotype.Service;
25
+
26
+import java.util.HashMap;
27
+import java.util.Map;
28
+
29
+@Service("userService")
30
+public class UserServiceImpl extends ServiceImpl<UserDao, UserEntity> implements UserService {
31
+	@Autowired
32
+	private TokenService tokenService;
33
+
34
+	@Override
35
+	public UserEntity queryByMobile(String mobile) {
36
+		return baseMapper.selectOne(new QueryWrapper<UserEntity>().eq("mobile", mobile));
37
+	}
38
+
39
+	@Override
40
+	public Map<String, Object> login(LoginForm form) {
41
+		UserEntity user = queryByMobile(form.getMobile());
42
+		Assert.isNull(user, "手机号或密码错误");
43
+
44
+		//密码错误
45
+		if(!user.getPassword().equals(DigestUtils.sha256Hex(form.getPassword()))){
46
+			throw new RRException("手机号或密码错误");
47
+		}
48
+
49
+		//获取登录token
50
+		TokenEntity tokenEntity = tokenService.createToken(user.getUserId());
51
+
52
+		Map<String, Object> map = new HashMap<>(2);
53
+		map.put("token", tokenEntity.getToken());
54
+		map.put("expire", tokenEntity.getExpireTime().getTime() - System.currentTimeMillis());
55
+
56
+		return map;
57
+	}
58
+
59
+}

+ 34 - 0
src/main/resources/application-dev.yml

@@ -0,0 +1,34 @@
1
+spring:
2
+  datasource:
3
+    type: com.alibaba.druid.pool.DruidDataSource
4
+    druid:
5
+      driver-class-name: com.mysql.cj.jdbc.Driver
6
+      url: jdbc:mysql://localhost:3306/db1?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
7
+      username: root
8
+      password:
9
+      initial-size: 10
10
+      max-active: 100
11
+      min-idle: 10
12
+      max-wait: 60000
13
+      pool-prepared-statements: true
14
+      max-pool-prepared-statement-per-connection-size: 20
15
+      time-between-eviction-runs-millis: 60000
16
+      min-evictable-idle-time-millis: 300000
17
+      #Oracle需要打开注释
18
+      #validation-query: SELECT 1 FROM DUAL
19
+      test-while-idle: true
20
+      test-on-borrow: false
21
+      test-on-return: false
22
+      stat-view-servlet:
23
+        enabled: true
24
+        url-pattern: /druid/*
25
+        #login-username: admin
26
+        #login-password: admin
27
+      filter:
28
+        stat:
29
+          log-slow-sql: true
30
+          slow-sql-millis: 1000
31
+          merge-sql: false
32
+        wall:
33
+          config:
34
+            multi-statement-allow: true

+ 34 - 0
src/main/resources/application-prod.yml

@@ -0,0 +1,34 @@
1
+spring:
2
+  datasource:
3
+    type: com.alibaba.druid.pool.DruidDataSource
4
+    druid:
5
+      driver-class-name: com.mysql.cj.jdbc.Driver
6
+      url: jdbc:mysql://localhost:3306/renren_security?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
7
+      username: renren
8
+      password: 123456
9
+      initial-size: 10
10
+      max-active: 100
11
+      min-idle: 10
12
+      max-wait: 60000
13
+      pool-prepared-statements: true
14
+      max-pool-prepared-statement-per-connection-size: 20
15
+      time-between-eviction-runs-millis: 60000
16
+      min-evictable-idle-time-millis: 300000
17
+      #Oracle需要打开注释
18
+      #validation-query: SELECT 1 FROM DUAL
19
+      test-while-idle: true
20
+      test-on-borrow: false
21
+      test-on-return: false
22
+      stat-view-servlet:
23
+        enabled: true
24
+        url-pattern: /druid/*
25
+        #login-username: admin
26
+        #login-password: admin
27
+      filter:
28
+        stat:
29
+          log-slow-sql: true
30
+          slow-sql-millis: 1000
31
+          merge-sql: false
32
+        wall:
33
+          config:
34
+            multi-statement-allow: true

+ 34 - 0
src/main/resources/application-test.yml

@@ -0,0 +1,34 @@
1
+spring:
2
+  datasource:
3
+    type: com.alibaba.druid.pool.DruidDataSource
4
+    druid:
5
+      driver-class-name: com.mysql.cj.jdbc.Driver
6
+      url: jdbc:mysql://localhost:3306/renren_security?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
7
+      username: renren
8
+      password: 123456
9
+      initial-size: 10
10
+      max-active: 100
11
+      min-idle: 10
12
+      max-wait: 60000
13
+      pool-prepared-statements: true
14
+      max-pool-prepared-statement-per-connection-size: 20
15
+      time-between-eviction-runs-millis: 60000
16
+      min-evictable-idle-time-millis: 300000
17
+      #Oracle需要打开注释
18
+      #validation-query: SELECT 1 FROM DUAL
19
+      test-while-idle: true
20
+      test-on-borrow: false
21
+      test-on-return: false
22
+      stat-view-servlet:
23
+        enabled: true
24
+        url-pattern: /druid/*
25
+        #login-username: admin
26
+        #login-password: admin
27
+      filter:
28
+        stat:
29
+          log-slow-sql: true
30
+          slow-sql-millis: 1000
31
+          merge-sql: false
32
+        wall:
33
+          config:
34
+            multi-statement-allow: true

+ 63 - 0
src/main/resources/application.yml

@@ -0,0 +1,63 @@
1
+# Tomcat
2
+server:
3
+  tomcat:
4
+    uri-encoding: UTF-8
5
+    max-threads: 1000
6
+    min-spare-threads: 30
7
+  port: 8081
8
+#  servlet:
9
+#    context-path: /renren-api
10
+
11
+# mysql
12
+spring:
13
+  # 环境 dev|test|prod
14
+  profiles:
15
+    active: dev
16
+  servlet:
17
+    multipart:
18
+      max-file-size: 100MB
19
+      max-request-size: 100MB
20
+      enabled: true
21
+  redis:
22
+    database: 0
23
+    host: localhost
24
+    port: 6379
25
+    password:      # 密码(默认为空)
26
+    timeout: 6000ms  # 连接超时时长(毫秒)
27
+    jedis:
28
+      pool:
29
+        max-active: 1000  # 连接池最大连接数(使用负值表示没有限制)
30
+        max-wait: -1ms      # 连接池最大阻塞等待时间(使用负值表示没有限制)
31
+        max-idle: 10      # 连接池中的最大空闲连接
32
+        min-idle: 5       # 连接池中的最小空闲连接
33
+
34
+renren:
35
+  redis:
36
+    open: false  # 是否开启redis缓存  true开启   false关闭
37
+
38
+
39
+#mybatis
40
+mybatis-plus:
41
+  mapper-locations: classpath*:/mapper/**/*.xml
42
+  #实体扫描,多个package用逗号或者分号分隔
43
+  typeAliasesPackage: io.renren.entity
44
+  global-config:
45
+    #数据库相关配置
46
+    db-config:
47
+      #主键类型  AUTO:"数据库ID自增", INPUT:"用户输入ID", ID_WORKER:"全局唯一ID (数字类型唯一ID)", UUID:"全局唯一ID UUID";
48
+      id-type: AUTO
49
+      #字段策略 IGNORED:"忽略判断",NOT_NULL:"非 NULL 判断"),NOT_EMPTY:"非空判断"
50
+      field-strategy: NOT_NULL
51
+      #驼峰下划线转换
52
+      column-underline: true
53
+      logic-delete-value: -1
54
+      logic-not-delete-value: 0
55
+    banner: false
56
+  #原生配置
57
+  configuration:
58
+    map-underscore-to-camel-case: true
59
+    cache-enabled: false
60
+    call-setters-on-nulls: true
61
+    jdbc-type-for-null: 'null'
62
+
63
+

+ 5 - 0
src/main/resources/banner.txt

@@ -0,0 +1,5 @@
1
+====================================================================================================================
2
+
3
+                    欢迎使用 renren-api - Powered By https://www.renren.io
4
+
5
+====================================================================================================================

+ 21 - 0
src/main/resources/logback-spring.xml

@@ -0,0 +1,21 @@
1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<configuration>
3
+    <include resource="org/springframework/boot/logging/logback/base.xml" />
4
+    <logger name="org.springframework.web" level="INFO"/>
5
+    <logger name="org.springboot.sample" level="TRACE" />
6
+
7
+    <!-- 开发、测试环境 -->
8
+    <springProfile name="dev,test">
9
+        <logger name="org.springframework.web" level="INFO"/>
10
+        <logger name="org.springboot.sample" level="INFO" />
11
+        <logger name="io.renren" level="DEBUG" />
12
+    </springProfile>
13
+
14
+    <!-- 生产环境 -->
15
+    <springProfile name="prod">
16
+        <logger name="org.springframework.web" level="ERROR"/>
17
+        <logger name="org.springboot.sample" level="ERROR" />
18
+        <logger name="io.renren" level="ERROR" />
19
+    </springProfile>
20
+
21
+</configuration>

+ 6 - 0
src/main/resources/mapper/TokenDao.xml

@@ -0,0 +1,6 @@
1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
3
+
4
+<mapper namespace="io.renren.dao.TokenDao">
5
+
6
+</mapper>

+ 7 - 0
src/main/resources/mapper/UserDao.xml

@@ -0,0 +1,7 @@
1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
3
+
4
+<mapper namespace="io.renren.dao.UserDao">
5
+
6
+
7
+</mapper>