diff --git a/Readme.md b/Readme.md
index 3718ea6..fad9fd4 100644
--- a/Readme.md
+++ b/Readme.md
@@ -1,4 +1,4 @@
-#web 安全常见漏洞
+# web 安全常见漏洞
1. sql 注入
@@ -24,7 +24,7 @@
7.文件未授权访问
-解决方案:上传的文件,展示时后端返回签名的文件,访问时,走一次后端,方便做权限验证
+解决方案:上传的文件,展示时后端返回签名的文件,访问时,走一次后端,方便做权限验证,参见,/api/upload,/api/file
8.文件不安全类型上传
@@ -35,3 +35,4 @@
密码加密传输,参见login.html
+
diff --git a/pom.xml b/pom.xml
index 13ad6c3..3920b14 100644
--- a/pom.xml
+++ b/pom.xml
@@ -81,7 +81,11 @@
hutool-core
4.0.3
-
+
diff --git a/src/main/java/com/taoyuanx/securitydemo/config/GlobalConfig.java b/src/main/java/com/taoyuanx/securitydemo/config/GlobalConfig.java
index 0dd5498..8f9cabf 100644
--- a/src/main/java/com/taoyuanx/securitydemo/config/GlobalConfig.java
+++ b/src/main/java/com/taoyuanx/securitydemo/config/GlobalConfig.java
@@ -28,7 +28,12 @@ public class GlobalConfig {
private String allowUploadExt;
private String tokenKey;
private Long expireSeconds;
+ private String fileStorageDir;
+
+ private String systemFileFormat;
public String getConfig(String configKey) {
return environment.getProperty(configKey);
}
+
+
}
diff --git a/src/main/java/com/taoyuanx/securitydemo/config/MvcConfig.java b/src/main/java/com/taoyuanx/securitydemo/config/MvcConfig.java
index dc9cee3..c206593 100644
--- a/src/main/java/com/taoyuanx/securitydemo/config/MvcConfig.java
+++ b/src/main/java/com/taoyuanx/securitydemo/config/MvcConfig.java
@@ -10,6 +10,7 @@ import com.taoyuanx.securitydemo.interceptor.RefererHandlerIntercepter;
import com.taoyuanx.securitydemo.interceptor.SimpleAuthHandlerIntercepter;
import com.taoyuanx.securitydemo.security.blacklist.BlackListIpCheck;
import com.taoyuanx.securitydemo.security.blacklist.DefaultBlackListIpCheck;
+import com.taoyuanx.securitydemo.utils.FileHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -21,9 +22,12 @@ import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
+import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.HandlerExceptionResolver;
-import org.springframework.web.servlet.config.annotation.*;
+import org.springframework.web.servlet.config.annotation.CorsRegistry;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
import java.nio.charset.Charset;
@@ -35,7 +39,6 @@ import java.util.List;
* @desc mvc配置
* @date 2019/8/29
*/
-@EnableWebMvc
@Configuration
public class MvcConfig implements WebMvcConfigurer {
@@ -44,15 +47,15 @@ public class MvcConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
- registry.addMapping("/**").allowedOrigins(globalConfig.getSystemDomain());
+ if (StringUtils.isEmpty(globalConfig.getSystemDomain())) {
+ registry.addMapping("/**");
+ } else {
+ registry.addMapping("/**").allowedOrigins(globalConfig.getSystemDomain());
+ }
+
+
}
- @Override
- public void addResourceHandlers(ResourceHandlerRegistry registry) {
- registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");
- registry.addResourceHandler("/public/**").addResourceLocations("classpath:/public/");
- registry.addResourceHandler("/**").addResourceLocations("classpath:/META-INF/resources/");
- }
/**
* 拦截器
@@ -65,9 +68,10 @@ public class MvcConfig implements WebMvcConfigurer {
* 匹配路径按需设定
*/
registry.addInterceptor(refererHandlerIntercepter()).addPathPatterns("/**")
- .excludePathPatterns("/static/**").excludePathPatterns("/public/**");
+ .excludePathPatterns("/**/*.css", "/**/*.html", "/**/*.js");
registry.addInterceptor(simpleAuthHandlerIntercepter()).addPathPatterns("/**")
- .excludePathPatterns("/static/**").excludePathPatterns("/public/**");
+ .excludePathPatterns("/**/*.css", "/**/*.html", "/**/*.js", "/**/*.png")
+ ;
}
/**
@@ -92,10 +96,12 @@ public class MvcConfig implements WebMvcConfigurer {
fastJsonConfig.setSerializerFeatures(SerializerFeature.PrettyFormat);
List fastMediaTypes = new ArrayList<>();
fastMediaTypes.add(MediaType.APPLICATION_JSON);
+ fastMediaTypes.add(MediaType.APPLICATION_JSON_UTF8);
+ fastMediaTypes.add(MediaType.TEXT_PLAIN);
fastJsonConfig.setCharset(Charset.forName("UTF-8"));
fastJsonHttpMessageConverter.setSupportedMediaTypes(fastMediaTypes);
fastJsonHttpMessageConverter.setFastJsonConfig(fastJsonConfig);
- converters.add(fastJsonHttpMessageConverter);
+ converters.add(0,fastJsonHttpMessageConverter);
}
@@ -147,4 +153,10 @@ public class MvcConfig implements WebMvcConfigurer {
return blackListIpCheck;
}
+ @Bean
+ public FileHandler fileHandler(){
+ FileHandler fileHandler=new FileHandler(globalConfig.getFileStorageDir(),globalConfig.getTokenKey(),false,globalConfig.getSystemFileFormat());
+ return fileHandler;
+ }
+
}
diff --git a/src/main/java/com/taoyuanx/securitydemo/constant/SystemConstants.java b/src/main/java/com/taoyuanx/securitydemo/constant/SystemConstants.java
index 0cec7f6..f8c30bb 100644
--- a/src/main/java/com/taoyuanx/securitydemo/constant/SystemConstants.java
+++ b/src/main/java/com/taoyuanx/securitydemo/constant/SystemConstants.java
@@ -11,4 +11,12 @@ public class SystemConstants {
public static final String TOKEN_ACCOUNTID_KEY = "a";
public static final String TOKEN_ACCOUNT_STATUS_KEY = "as";
public static final String TOKEN_COOKIE_KEY = "_t";
+
+ /**
+ * 文件签名相关
+ */
+ public static final String REQUEST_PARAM_FILE_KEY = "p";
+ public static final String REQUEST_PARAM_TYPE_KEY = "t";
+ public static final String REQUEST_PARAM_TOKEN_KEY = "s";
+
}
diff --git a/src/main/java/com/taoyuanx/securitydemo/controller/BussinessController.java b/src/main/java/com/taoyuanx/securitydemo/controller/BussinessController.java
index cf7766b..5be8695 100644
--- a/src/main/java/com/taoyuanx/securitydemo/controller/BussinessController.java
+++ b/src/main/java/com/taoyuanx/securitydemo/controller/BussinessController.java
@@ -1,15 +1,17 @@
package com.taoyuanx.securitydemo.controller;
-import cn.hutool.core.io.FileTypeUtil;
+import cn.hutool.core.date.DateUtil;
+import com.taoyuanx.securitydemo.common.Result;
+import com.taoyuanx.securitydemo.common.ResultBuilder;
import com.taoyuanx.securitydemo.config.GlobalConfig;
import com.taoyuanx.securitydemo.constant.SystemConstants;
import com.taoyuanx.securitydemo.dto.AccountDTO;
import com.taoyuanx.securitydemo.exception.ServiceException;
import com.taoyuanx.securitydemo.helper.ToeknHelper;
import com.taoyuanx.securitydemo.security.*;
+import com.taoyuanx.securitydemo.utils.FileHandler;
import com.taoyuanx.securitydemo.utils.FileTypeCheckUtil;
import com.taoyuanx.securitydemo.utils.PasswordUtil;
-import com.taoyuanx.securitydemo.utils.SimpleTokenManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
@@ -17,10 +19,13 @@ import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
-import java.io.IOException;
+import java.io.File;
+import java.util.Date;
import java.util.HashMap;
import java.util.Map;
+import java.util.concurrent.TimeUnit;
/**
* @author dushitaoyuan
@@ -34,8 +39,15 @@ public class BussinessController {
ToeknHelper toeknHelper;
@Value("${server.servlet.session.timeout}")
Integer sessionTimeOut;
+
+ @Autowired
+ GlobalConfig globalConfig;
+ @Autowired
+ FileHandler fileHandler;
+
/**
* 管理员访问
+ *
* @return
*/
@GetMapping("admin")
@@ -44,8 +56,10 @@ public class BussinessController {
public String admin() {
return "hello admin!";
}
+
/**
* 普通用户访问
+ *
* @return
*/
@GetMapping("commonUser")
@@ -57,6 +71,7 @@ public class BussinessController {
/**
* 公开访问
+ *
* @return
*/
@GetMapping("public")
@@ -67,6 +82,7 @@ public class BussinessController {
/**
* 必须携带Rerferer头部
+ *
* @return
*/
@GetMapping("refererCheck")
@@ -78,23 +94,25 @@ public class BussinessController {
/**
* 限流: 单个ip每秒一次,
* 自定义key 每秒10次
+ *
* @return
*/
@GetMapping("rateLimit")
@ResponseBody
- @Rate(rate = {@RateLimit(type = RateLimitType.IP,limit = 1),
- @RateLimit(type = RateLimitType.SERVICE_KEY,limit = 10,key = "api/rateLimit")})
+ @Rate(rate = {@RateLimit(type = RateLimitType.IP, limit = 1),
+ @RateLimit(type = RateLimitType.SERVICE_KEY, limit = 10, key = "api/rateLimit")})
public String rateLimit() {
return "hello rateLimit!";
}
/**
* 限流: 每秒10次,
+ *
* @return
*/
@GetMapping("rateLimit_key")
@ResponseBody
- @RateLimit(type = RateLimitType.SERVICE_KEY,limit = 2,key = "api/rateLimit_key")
+ @RateLimit(type = RateLimitType.SERVICE_KEY, limit = 2, key = "api/rateLimit_key")
public String rateLimitKey() {
return "hello rateLimit!";
}
@@ -102,6 +120,7 @@ public class BussinessController {
/**
* 黑名单测试
+ *
* @return
*/
@GetMapping("blackList")
@@ -111,48 +130,66 @@ public class BussinessController {
}
-
/**
* 登录安全控制
+ *
* @return
*/
@PostMapping("login")
@ResponseBody
- public String login(String userName, String password, HttpServletResponse response) throws Exception {
+ public Result login(String userName, String password, HttpServletResponse response) throws Exception {
/**
* select from db
*/
- if(userName.equals("dushitaoyuan")&& PasswordUtil.isPasswordEqual(password,PasswordUtil.passwordHanlePlain("123456"))){
- AccountDTO accountDTO=new AccountDTO();
+ if (userName.equals("dushitaoyuan") && PasswordUtil.isPasswordEqual(password, PasswordUtil.passwordHanlePlain("123456"))) {
+ AccountDTO accountDTO = new AccountDTO();
accountDTO.setAccountId(1L);
accountDTO.setAccountStatus(Role.ADMIN.getAccountStatus());
String token = toeknHelper.create(accountDTO);
- Cookie cookie=new Cookie(SystemConstants.TOKEN_COOKIE_KEY,token);
+ Cookie cookie = new Cookie(SystemConstants.TOKEN_COOKIE_KEY, token);
cookie.setPath("/");
- cookie.setMaxAge(sessionTimeOut*2);
+ cookie.setMaxAge(sessionTimeOut * 2);
response.addCookie(cookie);
+ return ResultBuilder.success();
}
- return "hello upload!";
+ throw new ServiceException("登陆异常");
+
}
/**
* 黑名单测试
+ *
* @return
*/
- @GetMapping("upload")
+ @PostMapping("upload")
@ResponseBody
- public String upload(@RequestParam("file") MultipartFile multipartFile,@CookieValue(name=SystemConstants.TOKEN_COOKIE_KEY) String token) throws Exception {
+ public Map upload(@RequestParam("file") MultipartFile multipartFile, @CookieValue(name = SystemConstants.TOKEN_COOKIE_KEY) String token) throws Exception {
toeknHelper.vafy(token);
- String ext=FileTypeCheckUtil.getType(multipartFile.getOriginalFilename());
- if(!FileTypeCheckUtil.allow(ext)){
+ String ext = FileTypeCheckUtil.getType(multipartFile.getOriginalFilename());
+ if (!FileTypeCheckUtil.allow(ext)) {
throw new ServiceException("文件上传失败,类型不支持");
}
- ext=FileTypeCheckUtil.getRealType(multipartFile.getInputStream());
- if(!FileTypeCheckUtil.allow(ext)){
+ ext = FileTypeCheckUtil.getRealType(multipartFile.getInputStream());
+ if (!FileTypeCheckUtil.allow(ext)) {
throw new ServiceException("文件上传失败,类型不支持");
}
- return "hello upload!";
+ /**
+ * 文件签名后返回前端,5分钟过期
+ */
+ String fileId = DateUtil.format(new Date(), "yyyy-mm-dd") + "/" + multipartFile.getOriginalFilename();
+ File file = new File(globalConfig.getFileStorageDir(), fileId);
+ if(!file.getParentFile().exists()){
+ file.getParentFile().mkdirs();
+ }
+ multipartFile.transferTo(file);
+ Map result = new HashMap<>();
+ result.put("path", fileId);
+ result.put("url", fileHandler.signFileUrl(fileId, FileHandler.LOOK, 5L, TimeUnit.MINUTES));
+ return result;
}
-
+ @GetMapping("file")
+ public void upload(HttpServletRequest request, HttpServletResponse response) throws Exception {
+ fileHandler.handleFile(response, request);
+ }
}
diff --git a/src/main/java/com/taoyuanx/securitydemo/controller/SystemErrorController.java b/src/main/java/com/taoyuanx/securitydemo/controller/SystemErrorController.java
index 28c8c86..d9f228b 100644
--- a/src/main/java/com/taoyuanx/securitydemo/controller/SystemErrorController.java
+++ b/src/main/java/com/taoyuanx/securitydemo/controller/SystemErrorController.java
@@ -1,6 +1,8 @@
package com.taoyuanx.securitydemo.controller;
+import com.taoyuanx.securitydemo.common.ResultBuilder;
import com.taoyuanx.securitydemo.exception.SystemExceptionHandler;
+import com.taoyuanx.securitydemo.utils.ResponseUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.error.ErrorAttributes;
import org.springframework.boot.web.servlet.error.ErrorController;
@@ -11,6 +13,7 @@ import org.springframework.web.context.request.WebRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
+import java.util.Map;
/**
@@ -39,7 +42,15 @@ public class SystemErrorController implements ErrorController {
public void doHandleError(HttpServletRequest request, HttpServletResponse response) {
WebRequest webRequest = new ServletWebRequest(request, response);
Throwable e = errorAttributes.getError(webRequest);
- systemExceptionHandler.doHandleException(request, response, null, e);
+ if (e != null) {
+ systemExceptionHandler.doHandleException(request, response, null, e);
+ } else {
+ Map errorAttributes = this.errorAttributes.getErrorAttributes(webRequest, false);
+ String errorMsg = String.format("%s %s", errorAttributes.get("path"), errorAttributes.get("error"));
+ Integer status = (Integer) errorAttributes.get("status");
+ ResponseUtil.responseJson(response, ResultBuilder.failed(errorMsg), status);
+ }
+
}
diff --git a/src/main/java/com/taoyuanx/securitydemo/exception/SystemExceptionHandler.java b/src/main/java/com/taoyuanx/securitydemo/exception/SystemExceptionHandler.java
index 06b020f..bfdee17 100644
--- a/src/main/java/com/taoyuanx/securitydemo/exception/SystemExceptionHandler.java
+++ b/src/main/java/com/taoyuanx/securitydemo/exception/SystemExceptionHandler.java
@@ -8,6 +8,7 @@ import com.taoyuanx.securitydemo.utils.ResponseUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
+import org.springframework.stereotype.Component;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerExceptionResolver;
@@ -31,8 +32,9 @@ public class SystemExceptionHandler implements HandlerExceptionResolver {
doHandleException(request, response, handler, e);
return new ModelAndView();
} else {
+ ModelAndView modelAndView=new ModelAndView();
//todo跳转到页面
- return new ModelAndView();
+ return modelAndView;
}
}
@@ -78,9 +80,9 @@ public class SystemExceptionHandler implements HandlerExceptionResolver {
public static boolean isJson(HttpServletRequest request) {
- String header = request.getHeader("Content-Type");
+ String contentType = request.getHeader("Content-Type");
String accept = request.getHeader("Accept");
- if ((header != null && header.contains("application/json")) || (accept != null && accept.contains("application/json"))) {
+ if ((accept != null && accept.contains("json"))||(contentType != null && contentType.contains("json"))) {
return true;
} else {
return false;
diff --git a/src/main/java/com/taoyuanx/securitydemo/utils/FileTypeCheckUtil.java b/src/main/java/com/taoyuanx/securitydemo/utils/FileTypeCheckUtil.java
index 6ec1bf3..b2d7c11 100644
--- a/src/main/java/com/taoyuanx/securitydemo/utils/FileTypeCheckUtil.java
+++ b/src/main/java/com/taoyuanx/securitydemo/utils/FileTypeCheckUtil.java
@@ -33,7 +33,7 @@ public class FileTypeCheckUtil {
}
public static String getType(String fileName) {
- return fileName.substring(fileName.lastIndexOf("."));
+ return fileName.substring(fileName.lastIndexOf(".")+1);
}
public static String getRealType(InputStream inputStream) {
diff --git a/src/main/java/com/taoyuanx/securitydemo/utils/PasswordUtil.java b/src/main/java/com/taoyuanx/securitydemo/utils/PasswordUtil.java
index 8ad9c62..8b2a966 100644
--- a/src/main/java/com/taoyuanx/securitydemo/utils/PasswordUtil.java
+++ b/src/main/java/com/taoyuanx/securitydemo/utils/PasswordUtil.java
@@ -30,7 +30,7 @@ public class PasswordUtil {
}
public static boolean isPasswordEqual(String passwordHash, String dbPasswordHash) {
- if (passwordHash != null) {
+ if (passwordHash == null) {
return false;
}
return passwordHash.equals(dbPasswordHash);
diff --git a/src/main/java/com/taoyuanx/securitydemo/utils/ResponseUtil.java b/src/main/java/com/taoyuanx/securitydemo/utils/ResponseUtil.java
index ce42afa..8bda6aa 100644
--- a/src/main/java/com/taoyuanx/securitydemo/utils/ResponseUtil.java
+++ b/src/main/java/com/taoyuanx/securitydemo/utils/ResponseUtil.java
@@ -22,6 +22,15 @@ public class ResponseUtil {
throw new RuntimeException(ex);
}
}
-
+ public static void responseJson(HttpServletResponse response, Object result, Integer httpStatus) {
+ response.setCharacterEncoding("UTF-8");
+ response.setHeader("Content-type", "application/json;charset=UTF-8");
+ response.setStatus(httpStatus);
+ try {
+ response.getWriter().write(JSON.toJSONString(result));
+ } catch (IOException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
}
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index 067d69d..70d9273 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -17,7 +17,7 @@ server.servlet.session.timeout=1800
#自定义配置
#系统域名
-application.config.systemDoamin=taoyuanx.com
+application.config.systemDoamin=
#referer校验配置
application.config.referer-check-url[0]=refererCheck
application.config.referer-check-allow-domains=taoyuanx.com
@@ -28,4 +28,8 @@ application.config.blackListIp[1]=0:0:0:0:0:0:0:1
application.config.allowUploadExt=bmp,jpg,png,tif,gif,pcx,tga,exif,fpx,svg,psd,cdr,pcd,dxf,ufo,eps,ai,raw,WMF,webp
#系统颁发token秘钥hmac算法的key
application.config.token-key=123456
-application.config.expire-seconds=1800
\ No newline at end of file
+application.config.expire-seconds=1800
+#文件存储地址
+application.config.file-storage-dir=G:/temp
+#系统文件访问地址,参见fileHandler
+application.config.systemFileFormat=http://localhost:${server.port}/api/file
diff --git a/src/main/resources/public/index.html b/src/main/resources/public/index.html
deleted file mode 100644
index 8f95957..0000000
--- a/src/main/resources/public/index.html
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
-
- 首页
-
-
-首页
-
-
-
\ No newline at end of file
diff --git a/src/main/resources/public/login.html b/src/main/resources/public/login.html
index 46c03a6..8e86d32 100644
--- a/src/main/resources/public/login.html
+++ b/src/main/resources/public/login.html
@@ -13,12 +13,12 @@