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 @@ - - - - - - + + + + + + + - - \ No newline at end of file