初步完善完毕
This commit is contained in:
@@ -24,7 +24,7 @@
|
||||
|
||||
7.文件未授权访问
|
||||
|
||||
解决方案:上传的文件,展示时后端返回签名的文件,访问时,走一次后端,方便做权限验证
|
||||
解决方案:上传的文件,展示时后端返回签名的文件,访问时,走一次后端,方便做权限验证,参见,/api/upload,/api/file
|
||||
|
||||
|
||||
8.文件不安全类型上传
|
||||
@@ -35,3 +35,4 @@
|
||||
|
||||
密码加密传输,参见login.html
|
||||
|
||||
|
||||
|
||||
6
pom.xml
6
pom.xml
@@ -81,7 +81,11 @@
|
||||
<artifactId>hutool-core</artifactId>
|
||||
<version>4.0.3</version>
|
||||
</dependency>
|
||||
|
||||
<!-- <dependency>
|
||||
<groupId>commons-fileupload</groupId>
|
||||
<artifactId>commons-fileupload</artifactId>
|
||||
<version>1.4</version>
|
||||
</dependency>-->
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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,16 +47,16 @@ public class MvcConfig implements WebMvcConfigurer {
|
||||
|
||||
@Override
|
||||
public void addCorsMappings(CorsRegistry registry) {
|
||||
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<MediaType> 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
|
||||
}
|
||||
|
||||
@@ -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,6 +94,7 @@ public class BussinessController {
|
||||
/**
|
||||
* 限流: 单个ip每秒一次,
|
||||
* 自定义key 每秒10次
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@GetMapping("rateLimit")
|
||||
@@ -90,6 +107,7 @@ public class BussinessController {
|
||||
|
||||
/**
|
||||
* 限流: 每秒10次,
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@GetMapping("rateLimit_key")
|
||||
@@ -102,6 +120,7 @@ public class BussinessController {
|
||||
|
||||
/**
|
||||
* 黑名单测试
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@GetMapping("blackList")
|
||||
@@ -111,14 +130,14 @@ 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
|
||||
*/
|
||||
@@ -131,17 +150,20 @@ public class BussinessController {
|
||||
cookie.setPath("/");
|
||||
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<String, String> 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)) {
|
||||
@@ -151,8 +173,23 @@ public class BussinessController {
|
||||
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<String, String> 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
if (e != null) {
|
||||
systemExceptionHandler.doHandleException(request, response, null, e);
|
||||
} else {
|
||||
Map<String, Object> 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -29,3 +29,7 @@ application.config.allowUploadExt=bmp,jpg,png,tif,gif,pcx,tga,exif,fpx,svg,psd,c
|
||||
#系统颁发token秘钥hmac算法的key
|
||||
application.config.token-key=123456
|
||||
application.config.expire-seconds=1800
|
||||
#文件存储地址
|
||||
application.config.file-storage-dir=G:/temp
|
||||
#系统文件访问地址,参见fileHandler
|
||||
application.config.systemFileFormat=http://localhost:${server.port}/api/file
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>首页</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>首页</h1>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -13,12 +13,12 @@
|
||||
|
||||
|
||||
</body>
|
||||
<script src="/static/js/crypt/core-min.js"></script>
|
||||
<script src="/static/js/crypt/enc-base64-min.js"></script>
|
||||
<script src="/static/js/crypt/sha256-min.js"></script>
|
||||
<script src="/static/js/jquery-2.1.1.min.js"></script>
|
||||
<script src="/static/js/ajaxhook.min.js"></script>
|
||||
<script src="/static/js/common/system.js"></script>
|
||||
<script src="/js/crypt/core-min.js"></script>
|
||||
<script src="/js/crypt/enc-base64-min.js"></script>
|
||||
<script src="/js/crypt/sha256-min.js"></script>
|
||||
<script src="/js/jquery-2.1.1.min.js"></script>
|
||||
<script src="/js/ajaxhook.min.js"></script>
|
||||
<script src="/js/common/system.js"></script>
|
||||
<script type="text/javascript">
|
||||
function upload() {
|
||||
var password = $("input[name='password']").val();
|
||||
@@ -40,7 +40,7 @@
|
||||
}).success(function (result) {
|
||||
if (result.success == 1) {
|
||||
alert("登录成功");
|
||||
window.location.href = getStaticUrl("public/index.html");
|
||||
window.location.href = getStaticUrl("upload.html");
|
||||
} else {
|
||||
alert(result.msg);
|
||||
}
|
||||
|
||||
@@ -6,11 +6,13 @@
|
||||
</head>
|
||||
<body>
|
||||
<input name="file" type="file" data-allow="jpg,jpeg">
|
||||
<input type="button" value="上传">
|
||||
<script src="/static/js/jquery-2.1.1.min.js"/>
|
||||
<script src="/static/js/ajaxhook.min.js"/>
|
||||
<input type="button" value="上传" onclick="upload()">
|
||||
<img src="" style="display: none" id="showImg">
|
||||
</body>
|
||||
<script src="/js/jquery-2.1.1.min.js"></script>
|
||||
<script src="/js/ajaxhook.min.js"></script>
|
||||
|
||||
<script src="/static/js/common/system.js"/>
|
||||
<script src="/js/common/system.js"></script>
|
||||
<script type="text/javascript">
|
||||
function upload() {
|
||||
var formData = new FormData();
|
||||
@@ -23,12 +25,14 @@
|
||||
xhrFields: {
|
||||
withCredentials: true
|
||||
},
|
||||
cache: false,
|
||||
processData: false,
|
||||
contentType: false
|
||||
contentType: false,
|
||||
headers:{"Accept":"application/json"}
|
||||
}).success(function (result) {
|
||||
if (result.status == 1) {
|
||||
if (result.success == 1) {
|
||||
alert("上传成功");
|
||||
$("#showImg").attr("src",result.data.url);
|
||||
$("#showImg").show();
|
||||
} else {
|
||||
alert(result.msg);
|
||||
}
|
||||
@@ -36,5 +40,4 @@
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user