初始提交

This commit is contained in:
dushitaoyuan
2019-08-30 18:56:39 +08:00
commit 2ab558fc3d
56 changed files with 2270 additions and 0 deletions

31
.gitignore vendored Normal file
View File

@@ -0,0 +1,31 @@
HELP.md
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**
!**/src/test/**
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
### VS Code ###
.vscode/

37
Readme.md Normal file
View File

@@ -0,0 +1,37 @@
#web 安全常见漏洞
1. sql 注入 </br>
解决方案:参数检测,拦截非法入参,后端使用druid 连接池的sql防火墙
参见:com.taoyuanx.securitydemo.web
2.xss攻击 </br>
解决方案:参数检测,拦截非法入参,转义html字符,参见com.taoyuanx.securitydemo.web
</br>
3.跨站请求
解决方案:设置允许跨站的header信息
4.文件,图片等非站内请求
解决方案:防盗链处理(Http Rerfer头部,nginx可设置)
5.接口暴力访问
解决方案:系统限流,全局限流,单个ip限流,黑名单等,参见:com.taoyuanx.securitydemo.security.RateLimitAspect,com.taoyuanx.securitydemo.web.BlackListFilter
7.文件未授权访问
解决方案:上传的文件,展示时后端返回签名的文件,访问时,走一次后端,方便做权限验证
8.文件不安全类型上传
解决方案:校验文件类型,校验流信息,校验文件真实类型,参见/api/upload
9密码泄露风险
密码加密传输,参见login.html

122
pom.xml Normal file
View File

@@ -0,0 +1,122 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.7.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.taoyuanx</groupId>
<artifactId>security-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>security-demo</name>
<description>java security demo</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<skipTests>true</skipTests>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
&lt;!&ndash; <optional>true</optional>&ndash;&gt;
</dependency>-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>20.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.59</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.10</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.8</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.0</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-core</artifactId>
<version>4.0.3</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<dependencies>
<dependency>
<!-- 加入该配置使用maven package依赖jar不会打包在项目最终jar文件内
启动jar 加入参数 -Dthin.root=. -jar
-->
<groupId>org.springframework.boot.experimental</groupId>
<artifactId>spring-boot-thin-layout</artifactId>
<version>1.0.12.RELEASE</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<!-- 加入该配置maven package执行时会在target目录整理好依赖包 -->
<groupId>org.springframework.boot.experimental</groupId>
<artifactId>spring-boot-thin-maven-plugin</artifactId>
<version>1.0.12.RELEASE</version>
<executions>
<execution>
<id>resolve</id>
<goals>
<goal>resolve</goal>
</goals>
<inherited>false</inherited>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,16 @@
package com.taoyuanx.securitydemo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.context.annotation.ComponentScan;
@SpringBootApplication
@ServletComponentScan("com.taoyuanx.securitydemo.web")
class SecurityDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SecurityDemoApplication.class, args);
}
}

View File

@@ -0,0 +1,55 @@
package com.taoyuanx.securitydemo.common;
import lombok.Data;
import java.io.Serializable;
/**
* 统一返回结果
* <p>
* errorCode 错误码
* msg 错误消息
* success 请求成功标识 1成功,0失败
* data 结果
* ext 扩展信息
*/
@Data
public class Result implements Serializable {
private Integer errorCode;
private String msg;
private Integer success;
private Object data;
private Object ext;
public static Result build() {
return new Result();
}
public Result buildCode(Integer code) {
this.setErrorCode(code);
return this;
}
public Result buildMsg(String msg) {
this.setMsg(msg);
return this;
}
public Result buildSuccess(Integer success) {
this.setSuccess(success);
return this;
}
public Result buildData(Object data) {
this.data = data;
return this;
}
public Result buildExt(Object ext) {
this.ext = ext;
return this;
}
}

View File

@@ -0,0 +1,30 @@
package com.taoyuanx.securitydemo.common;
/**
* 结果构造
*/
public class ResultBuilder {
public static Result success(Object data) {
return Result.build().buildData(data).buildSuccess(ResultCode.OK.code);
}
public static Result success() {
return Result.build().buildSuccess(ResultCode.OK.code);
}
public static Result success(String msg) {
return Result.build().buildSuccess(ResultCode.OK.code).buildMsg(msg);
}
public static Result failed(String msg) {
return Result.build().buildSuccess(ResultCode.FAIL.code).buildMsg(msg);
}
public static Result failed(Integer code, String msg) {
return Result.build().buildSuccess(ResultCode.FAIL.code)
.buildCode(code).buildMsg(msg);
}
}

View File

@@ -0,0 +1,22 @@
package com.taoyuanx.securitydemo.common;
/**
* 响应码枚举参考HTTP状态码的语义
*/
public enum ResultCode {
OK(1, "success"),
FAIL(0, "failed"),
UNAUTHORIZED(401, "权限异常"),
NOT_FOUND(404, "service not found"),
UN_SUPPORT_MEDIATYPE(415, "不支持媒体类型"),
PARAM_ERROR(400, "参数异常"),
TOO_MANY_REQUESTS(429, "请求被限制"),
INTERNAL_SERVER_ERROR(500, "服务器内部错误"),
BUSSINESS_ERROR(1001, "业务异常");
public int code;
public String desc;
private ResultCode(int code, String desc) {
this.code = code;
this.desc = desc;
}
}

View File

@@ -0,0 +1,34 @@
package com.taoyuanx.securitydemo.config;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import java.util.List;
/**
* @author dushitaoyuan
* @desc 系统全局配置
* @date 2019/8/29
*/
@Data
@ConfigurationProperties("application.config")
@Configuration
public class GlobalConfig {
@Autowired
Environment environment;
private String systemDomain;
private List<String> refererCheckUrl;
private List<String> refererCheckAllowDomains;
private List<String> blackListIp;
private String allowUploadExt;
private String tokenKey;
private Long expireSeconds;
public String getConfig(String configKey) {
return environment.getProperty(configKey);
}
}

View File

@@ -0,0 +1,150 @@
package com.taoyuanx.securitydemo.config;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.support.config.FastJsonConfig;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import com.taoyuanx.securitydemo.common.Result;
import com.taoyuanx.securitydemo.common.ResultBuilder;
import com.taoyuanx.securitydemo.exception.SystemExceptionHandler;
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 org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.MethodParameter;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.MediaType;
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.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.config.annotation.*;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
/**
* @author dushitaoyuan
* @desc mvc配置
* @date 2019/8/29
*/
@EnableWebMvc
@Configuration
public class MvcConfig implements WebMvcConfigurer {
@Autowired
GlobalConfig globalConfig;
@Override
public void addCorsMappings(CorsRegistry registry) {
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/");
}
/**
* 拦截器
*
* @param registry
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
/**
* 匹配路径按需设定
*/
registry.addInterceptor(refererHandlerIntercepter()).addPathPatterns("/**")
.excludePathPatterns("/static/**").excludePathPatterns("/public/**");
registry.addInterceptor(simpleAuthHandlerIntercepter()).addPathPatterns("/**")
.excludePathPatterns("/static/**").excludePathPatterns("/public/**");
}
/**
* 异常处理
*
* @param resolvers
*/
@Override
public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
resolvers.add(systemExceptionHandler());
}
/**
* 消息转换
*
* @param converters
*/
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
FastJsonHttpMessageConverter fastJsonHttpMessageConverter = new FastJsonHttpMessageConverter();
FastJsonConfig fastJsonConfig = new FastJsonConfig();
fastJsonConfig.setSerializerFeatures(SerializerFeature.PrettyFormat);
List<MediaType> fastMediaTypes = new ArrayList<>();
fastMediaTypes.add(MediaType.APPLICATION_JSON);
fastJsonConfig.setCharset(Charset.forName("UTF-8"));
fastJsonHttpMessageConverter.setSupportedMediaTypes(fastMediaTypes);
fastJsonHttpMessageConverter.setFastJsonConfig(fastJsonConfig);
converters.add(fastJsonHttpMessageConverter);
}
/**
* 统一结果处理
*/
@RestControllerAdvice(basePackages = "com.taoyuanx.securitydemo.controller")
public static class ResponseHandler implements ResponseBodyAdvice<Object> {
@Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
return true;
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
if (body instanceof Result) {
return body;
}
if (body instanceof ResponseEntity) {
return body;
}
return ResultBuilder.success(body);
}
}
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public SystemExceptionHandler systemExceptionHandler() {
return new SystemExceptionHandler();
}
@Bean
public RefererHandlerIntercepter refererHandlerIntercepter() {
RefererHandlerIntercepter refererHandlerIntercepter = new RefererHandlerIntercepter(globalConfig.getRefererCheckUrl(), globalConfig.getRefererCheckAllowDomains());
return refererHandlerIntercepter;
}
@Bean
public SimpleAuthHandlerIntercepter simpleAuthHandlerIntercepter() {
SimpleAuthHandlerIntercepter refererHandlerIntercepter = new SimpleAuthHandlerIntercepter();
return refererHandlerIntercepter;
}
@Bean
public BlackListIpCheck blackListIpCheck() {
BlackListIpCheck blackListIpCheck = new DefaultBlackListIpCheck(globalConfig.getBlackListIp());
return blackListIpCheck;
}
}

View File

@@ -0,0 +1,14 @@
package com.taoyuanx.securitydemo.constant;
/**
* @author dushitaoyuan
* @desc 系统常量
* @date 2019/8/29
*/
public class SystemConstants {
public static final String ACCOUNT_SESSION_KEY = "s";
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";
}

View File

@@ -0,0 +1,158 @@
package com.taoyuanx.securitydemo.controller;
import cn.hutool.core.io.FileTypeUtil;
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.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;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
/**
* @author dushitaoyuan
* @desc 业务api
* @date 2019/8/29
*/
@Controller
@RequestMapping(value = "api")
public class BussinessController {
@Autowired
ToeknHelper toeknHelper;
@Value("${server.servlet.session.timeout}")
Integer sessionTimeOut;
/**
* 管理员访问
* @return
*/
@GetMapping("admin")
@ResponseBody
@RequireRole(role = {Role.ADMIN})
public String admin() {
return "hello admin!";
}
/**
* 普通用户访问
* @return
*/
@GetMapping("commonUser")
@ResponseBody
@RequireRole(role = {Role.COMMONUSER})
public String commonUser() {
return "hello commonUser!";
}
/**
* 公开访问
* @return
*/
@GetMapping("public")
@ResponseBody
public String publicM() {
return "hello anybody!";
}
/**
* 必须携带Rerferer头部
* @return
*/
@GetMapping("refererCheck")
@ResponseBody
public String refererCheck() {
return "hello refererCheck!";
}
/**
* 限流: 单个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")})
public String rateLimit() {
return "hello rateLimit!";
}
/**
* 限流: 每秒10次,
* @return
*/
@GetMapping("rateLimit_key")
@ResponseBody
@RateLimit(type = RateLimitType.SERVICE_KEY,limit = 2,key = "api/rateLimit_key")
public String rateLimitKey() {
return "hello rateLimit!";
}
/**
* 黑名单测试
* @return
*/
@GetMapping("blackList")
@ResponseBody
public String blackList() {
return "hello blackList!";
}
/**
* 登录安全控制
* @return
*/
@PostMapping("login")
@ResponseBody
public String 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();
accountDTO.setAccountId(1L);
accountDTO.setAccountStatus(Role.ADMIN.getAccountStatus());
String token = toeknHelper.create(accountDTO);
Cookie cookie=new Cookie(SystemConstants.TOKEN_COOKIE_KEY,token);
cookie.setPath("/");
cookie.setMaxAge(sessionTimeOut*2);
response.addCookie(cookie);
}
return "hello upload!";
}
/**
* 黑名单测试
* @return
*/
@GetMapping("upload")
@ResponseBody
public 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)){
throw new ServiceException("文件上传失败,类型不支持");
}
ext=FileTypeCheckUtil.getRealType(multipartFile.getInputStream());
if(!FileTypeCheckUtil.allow(ext)){
throw new ServiceException("文件上传失败,类型不支持");
}
return "hello upload!";
}
}

View File

@@ -0,0 +1,47 @@
package com.taoyuanx.securitydemo.controller;
import com.taoyuanx.securitydemo.exception.SystemExceptionHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.error.ErrorAttributes;
import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.context.request.WebRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author dushitaoyuan
* @desc 用途描述
* @date 2019/8/30
**/
@Controller
@RequestMapping("${server.error.path:${error.path:/error}}")
public class SystemErrorController implements ErrorController {
private static final String PATH = "/error";
@Autowired
ErrorAttributes errorAttributes;
@Autowired
SystemExceptionHandler systemExceptionHandler;
@Override
public String getErrorPath() {
return PATH;
}
@RequestMapping
public void doHandleError(HttpServletRequest request, HttpServletResponse response) {
WebRequest webRequest = new ServletWebRequest(request, response);
Throwable e = errorAttributes.getError(webRequest);
systemExceptionHandler.doHandleException(request, response, null, e);
}
}

View File

@@ -0,0 +1,17 @@
package com.taoyuanx.securitydemo.dto;
import lombok.Data;
import java.io.Serializable;
/**
* @author dushitaoyuan
* @desc 账户实体
* @date 2019/8/29
*/
@Data
public class AccountDTO implements Serializable {
private Long accountId;
private Integer accountStatus;
}

View File

@@ -0,0 +1,12 @@
package com.taoyuanx.securitydemo.exception;
/**
* @author dushitaoyuan
* @desc 限制访问异常 http状态码 429
* @date 2019/8/26
*/
public class RadioLimitException extends RuntimeException {
public RadioLimitException(String message) {
super(message);
}
}

View File

@@ -0,0 +1,22 @@
package com.taoyuanx.securitydemo.exception;
import com.taoyuanx.securitydemo.common.ResultCode;
/**
* 业务异常
*/
public class ServiceException extends RuntimeException {
private static final long serialVersionUID = 8793672380339632040L;
private Integer errorCode;
public ServiceException(String msg) {
super(msg);
this.errorCode= ResultCode.BUSSINESS_ERROR.code;
}
public ServiceException(Integer errorCode,String msg) {
super(msg);
this.errorCode=errorCode;
}
}

View File

@@ -0,0 +1,91 @@
package com.taoyuanx.securitydemo.exception;
import com.alibaba.fastjson.JSONException;
import com.taoyuanx.securitydemo.common.Result;
import com.taoyuanx.securitydemo.common.ResultBuilder;
import com.taoyuanx.securitydemo.common.ResultCode;
import com.taoyuanx.securitydemo.utils.ResponseUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.NoHandlerFoundException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 全局异常处理
*/
public class SystemExceptionHandler implements HandlerExceptionResolver {
public static final Logger LOG = LoggerFactory.getLogger(SystemExceptionHandler.class);
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler,
Exception e) {
if (isJson(request)) {
doHandleException(request, response, handler, e);
return new ModelAndView();
} else {
//todo跳转到页面
return new ModelAndView();
}
}
public void doHandleException(HttpServletRequest request, HttpServletResponse response, Object handler,
Throwable e) {
Result result = null;
HttpStatus httpStatus = HttpStatus.OK;
if (e instanceof ValidatorException) {//参数异常
result = ResultBuilder.failed(ResultCode.PARAM_ERROR.code, e.getMessage());
} else if (e instanceof ServiceException) {
result = ResultBuilder.failed(ResultCode.BUSSINESS_ERROR.code, e.getMessage());
} else if (e instanceof UnAuthException) {
httpStatus = HttpStatus.UNAUTHORIZED;
result = ResultBuilder.failed(ResultCode.UNAUTHORIZED.code, e.getMessage());
} else if (e instanceof HttpMediaTypeNotSupportedException) {
HttpMediaTypeNotSupportedException mediaEx = (HttpMediaTypeNotSupportedException) e;
result = ResultBuilder.failed(ResultCode.UN_SUPPORT_MEDIATYPE.code, "不支持该媒体类型:" + mediaEx.getContentType());
} else if (e instanceof RadioLimitException) {
httpStatus = HttpStatus.TOO_MANY_REQUESTS;
result = ResultBuilder.failed(ResultCode.TOO_MANY_REQUESTS.code, e.getMessage());
} else if (e instanceof JSONException) {
result = ResultBuilder.failed(ResultCode.PARAM_ERROR.code, "参数异常,json格式非法:" + e.getMessage());
} else if (e instanceof ServletException) {
result = ResultBuilder.failed(e.getMessage());
} else if (e instanceof NoHandlerFoundException) {
httpStatus = HttpStatus.NOT_FOUND;
result = ResultBuilder.failed(ResultCode.NOT_FOUND.code, "接口 [" + ((NoHandlerFoundException) e).getRequestURL() + "] 不存在");
} else {
result = ResultBuilder.failed(ResultCode.INTERNAL_SERVER_ERROR.code, "接口 [" + request.getRequestURI() + "] 内部错误,请联系管理员");
if (handler != null && handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
LOG.error("接口 [{}] 出现异常,方法:{}.{},异常摘要:{}", request.getRequestURI(),
handlerMethod.getBean().getClass().getName(),
handlerMethod.getMethod().getName(),
e.getMessage());
}
LOG.error("系统未知异常,异常信息:", e);
}
ResponseUtil.responseJson(response, result, httpStatus);
}
public static boolean isJson(HttpServletRequest request) {
String header = request.getHeader("Content-Type");
String accept = request.getHeader("Accept");
if ((header != null && header.contains("application/json")) || (accept != null && accept.contains("application/json"))) {
return true;
} else {
return false;
}
}
}

View File

@@ -0,0 +1,18 @@
package com.taoyuanx.securitydemo.exception;
/**
* 权限异常 http状态码401
*/
public class UnAuthException extends RuntimeException {
/**
*
*/
private static final long serialVersionUID = 1L;
public UnAuthException(String message) {
super(message);
}
}

View File

@@ -0,0 +1,12 @@
package com.taoyuanx.securitydemo.exception;
/**
* @author dushitaoyuan
* @desc 参数异常
* @date 2019/8/29
*/
public class ValidatorException extends RuntimeException {
public ValidatorException(String message) {
super(message);
}
}

View File

@@ -0,0 +1,41 @@
package com.taoyuanx.securitydemo.helper;
import com.taoyuanx.securitydemo.config.GlobalConfig;
import com.taoyuanx.securitydemo.constant.SystemConstants;
import com.taoyuanx.securitydemo.dto.AccountDTO;
import com.taoyuanx.securitydemo.utils.SimpleTokenManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* @author dushitaoyuan
* @desc token签发帮助类
* @date 2019/8/30
*/
@Component
public class ToeknHelper {
@Autowired
GlobalConfig globalConfig;
public String create(AccountDTO accountDTO) {
Map<String, Object> tokenData = new HashMap<>();
tokenData.put(SystemConstants.TOKEN_ACCOUNTID_KEY, String.valueOf(accountDTO.getAccountId()));
tokenData.put(SystemConstants.TOKEN_ACCOUNT_STATUS_KEY, String.valueOf(accountDTO.getAccountStatus()));
return SimpleTokenManager.createToken(globalConfig.getTokenKey(), tokenData, globalConfig.getExpireSeconds(), TimeUnit.SECONDS);
}
public Map<String, Object> vafy(String token) {
return SimpleTokenManager.vafy(globalConfig.getTokenKey(),token);
}
public Long getAccountId(Map<String, Object> tokenData) {
return Long.parseLong(tokenData.get(SystemConstants.TOKEN_ACCOUNTID_KEY).toString());
}
public Integer getAccountStatus(Map<String, Object> tokenData) {
return (Integer) tokenData.get(SystemConstants.TOKEN_ACCOUNT_STATUS_KEY);
}
}

View File

@@ -0,0 +1,63 @@
package com.taoyuanx.securitydemo.interceptor;
import com.taoyuanx.securitydemo.exception.UnAuthException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
/**
* referer校验拦截器
*/
public class RefererHandlerIntercepter implements HandlerInterceptor {
Logger LOG = LoggerFactory.getLogger(RefererHandlerIntercepter.class);
private List<String> refererCheckUrl;
private List<String> allowDomains;
public RefererHandlerIntercepter(List<String> refererCheckUrl, List<String> allowDomains) {
this.refererCheckUrl = refererCheckUrl;
this.allowDomains = allowDomains;
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
if (refererCheckUrl == null) {
return true;
}
String requestUrl = request.getRequestURI();
for (String checkUrl : refererCheckUrl) {
if (requestUrl.contains(checkUrl)) {
String referer = request.getHeader("Referer");
if (referer == null) {
LOG.debug("请求url[{}],未携带有效header:[Referer]", requestUrl);
throw new UnAuthException("操作非法");
}
if (!checkDomain(referer)) {
LOG.debug("请求url[{}],Referer->[{}]不在允许范围内", requestUrl, referer);
throw new UnAuthException("操作非法");
}
return false;
}
}
return true;
}
private boolean checkDomain(String referer) {
for (String domain : allowDomains) {
if (referer.contains(domain)) {
return true;
}
}
return false;
}
}

View File

@@ -0,0 +1,61 @@
package com.taoyuanx.securitydemo.interceptor;
import com.taoyuanx.securitydemo.constant.SystemConstants;
import com.taoyuanx.securitydemo.dto.AccountDTO;
import com.taoyuanx.securitydemo.exception.UnAuthException;
import com.taoyuanx.securitydemo.security.RequireRole;
import com.taoyuanx.securitydemo.security.Role;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 简单权限校验
*/
public class SimpleAuthHandlerIntercepter implements HandlerInterceptor {
Logger LOG = LoggerFactory.getLogger(SimpleAuthHandlerIntercepter.class);
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
AccountDTO account = getAccount(request);
RequireRole requireRole = getAuthority(handlerMethod, RequireRole.class);
Integer accountStatus = account == null ? null : account.getAccountStatus();
if (Role.hasRole(requireRole, accountStatus)) {
return true;
}
LOG.debug("请求url[{}],权限不足,已拦截", request.getRequestURI());
throw new UnAuthException("操作");
}
return true;
}
private AccountDTO getAccount(HttpServletRequest request) {
Object account = request.getSession().getAttribute(SystemConstants.ACCOUNT_SESSION_KEY);
if (account == null) {
return null;
}
return (AccountDTO) account;
}
private <T> T getAuthority(HandlerMethod handlerMethod, Class annotationType) {
if (handlerMethod == null) {
return null;
}
T methodAnno = (T) AnnotationUtils.findAnnotation(handlerMethod.getMethod(), annotationType);
if (methodAnno == null) {
return (T) AnnotationUtils.findAnnotation(handlerMethod.getBean().getClass(), annotationType);
} else {
return methodAnno;
}
}
}

View File

@@ -0,0 +1,89 @@
package com.taoyuanx.securitydemo.interceptor;
import com.google.common.base.Splitter;
import com.google.common.collect.Lists;
import com.taoyuanx.securitydemo.constant.SystemConstants;
import com.taoyuanx.securitydemo.exception.UnAuthException;
import com.taoyuanx.securitydemo.helper.ToeknHelper;
import com.taoyuanx.securitydemo.utils.CookieUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
import java.util.Map;
public class TokenAuthHandlerIntercepter implements HandlerInterceptor {
@Autowired
ToeknHelper toeknHelper;
private List<String> publicUrl;
public TokenAuthHandlerIntercepter(String publicUrl) {
this.publicUrl = Lists.newArrayList();
Splitter.on(",").split(publicUrl).forEach(url -> {
this.publicUrl.add(url.trim());
});
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
if (handler instanceof HandlerMethod) {
Long accountId = getAccountId(request);
if (isPermit(accountId, request)) {
return true;
} else {
throw new UnAuthException("未授权操作");
}
}
return true;
}
public Long getAccountId(HttpServletRequest request) {
Object accountIdObj = request.getSession().getAttribute(SystemConstants.ACCOUNT_SESSION_KEY);
if (accountIdObj == null) {
return null;
}
Long accountId = (Long) accountIdObj;
return accountId;
}
public boolean isPermit(Long accountId, HttpServletRequest request) {
/**
* 校验逻辑:
* 1.登录时,校验操作token中accountId是否等于session中id
* 2.未登录,校验url是否在公开url中
*/
boolean login = accountId != null;
String url = request.getRequestURI();
if (login) {
if (null == accountId) {
return false;
}
String token = CookieUtil.getCookie(request.getCookies(), SystemConstants.TOKEN_COOKIE_KEY);
if (null == token || token.length() == 0) {
return false;
}
Map<String, Object> tokenData = toeknHelper.vafy(token);
Integer tokenAccountId = (Integer) tokenData.get(SystemConstants.TOKEN_ACCOUNTID_KEY);
if (tokenAccountId.longValue() != accountId) {
return false;
}
return true;
}
if (null != publicUrl && publicUrl.size() > 0) {
for (String pUrl : publicUrl) {
if (url.contains(pUrl)) {
return true;
}
}
return false;
}
return false;
}
}

View File

@@ -0,0 +1,14 @@
package com.taoyuanx.securitydemo.security;
import java.lang.annotation.*;
/**
* 限流复合注解
*/
@Inherited
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Rate {
RateLimit[] rate() default {};
}

View File

@@ -0,0 +1,21 @@
package com.taoyuanx.securitydemo.security;
import java.lang.annotation.*;
/**
* 限流注解
*/
@Inherited
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RateLimit {
/**
* 限流并发 和限流的key,类型
*/
double limit() default 100;
String key() default "" ;
RateLimitType type() default RateLimitType.METHOD;
}

View File

@@ -0,0 +1,125 @@
package com.taoyuanx.securitydemo.security;
import com.google.common.collect.Maps;
import com.google.common.util.concurrent.RateLimiter;
import com.taoyuanx.securitydemo.exception.RadioLimitException;
import com.taoyuanx.securitydemo.utils.RequestUtil;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.util.Map;
/**
* aop限流
*/
@Aspect
@Component
public class RateLimitAspect {
private static final Logger LOG = LoggerFactory.getLogger(RateLimitAspect.class);
private Map<String, RateLimiter> rateHolder = Maps.newConcurrentMap();
private static final int MAX_HOLDER_SIZE = 50000;
@Pointcut("execution(* com.taoyuanx.securitydemo.controller..*.*(..))&& (@annotation(RateLimit)||@annotation(Rate))")
public void ratePointCut() {
}
@Around("ratePointCut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
Signature signature = joinPoint.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
Method currentMethod = methodSignature.getMethod();
String className = joinPoint.getTarget().getClass().getName();
String methodName = className + "." + methodSignature.getName();
RateLimit rateLimit = AnnotationUtils.findAnnotation(currentMethod, RateLimit.class);
if (rateLimit != null) {
handleRateLimit(rateLimit, methodName);
} else {
Rate rate = AnnotationUtils.findAnnotation(currentMethod, Rate.class);
if (rate != null) {
RateLimit[] rateLimitArray = rate.rate();
if (rateLimitArray != null) {
for (RateLimit limit : rateLimitArray) {
handleRateLimit(limit, methodName);
}
}
}
}
return joinPoint.proceed();
}
private void handleRateLimit(RateLimit rateLimit, String methodName) throws Throwable {
RateLimiter rateLimiter = doGetRateLimiter(rateLimit, methodName);
if (!rateLimiter.tryAcquire()) {
throw new RadioLimitException("请求过于频繁,请稍后再试");
}
}
private RateLimiter doGetRateLimiter(RateLimit rateLimit, String methodName) {
RateLimitType type = rateLimit.type();
String key = rateLimit.key();
if (type == null) {
key = methodName;
if (LOG.isDebugEnabled()) {
LOG.debug("限流策略未定义,采用[]限流策略", methodName, RateLimitType.METHOD);
}
} else {
switch (type) {
case IP:
String serviceKey = rateLimit.key();
if (serviceKey == null || key.isEmpty()) {
key = RequestUtil.getRemoteIp() + "_" + methodName;
} else {
key = RequestUtil.getRemoteIp() + "_" + serviceKey;
}
if (LOG.isDebugEnabled()) {
LOG.debug("采用[{}]限流策略,限流key:{}", RateLimitType.IP, key);
}
break;
case METHOD:
key = methodName;
if (LOG.isDebugEnabled()) {
LOG.debug("采用[{}]限流策略,限流key:{}", RateLimitType.METHOD, key);
}
break;
case SERVICE_KEY:
if (LOG.isDebugEnabled()) {
LOG.debug("采用[{}]限流策略,限流key:{}", RateLimitType.SERVICE_KEY, key);
}
break;
case GLOBAL:
key = "global";
if (LOG.isDebugEnabled()) {
LOG.debug("采用[{}]限流策略,限流key:{}", RateLimitType.GLOBAL, key);
}
break;
}
}
if (rateHolder.containsKey(key)) {
return rateHolder.get(key);
}
RateLimiter rateLimiter = RateLimiter.create(rateLimit.limit());
rateHolder.put(key, rateLimiter);
//超过固定阈值,清空,重构
if (rateHolder.size() > MAX_HOLDER_SIZE) {
rateHolder.clear();
}
return rateLimiter;
}
}

View File

@@ -0,0 +1,37 @@
package com.taoyuanx.securitydemo.security;
/**
* @author dushitaoyuan
* @desc 限流类型
* @date 2019/8/27
*/
public enum RateLimitType {
IP(0, "IP限流"), METHOD(1, "方法名"),
SERVICE_KEY(3, "业务自定义key"),
GLOBAL(4,"系统全局");
private int code;
private String desc;
private RateLimitType(int code, String desc) {
this.code = code;
this.desc = desc;
}
public static RateLimitType type(Integer code) {
if (code == null) {
return null;
}
switch (code) {
case 0:
return IP;
case 1:
return METHOD;
case 3:
return SERVICE_KEY;
case 4:
return GLOBAL;
}
return null;
}
}

View File

@@ -0,0 +1,10 @@
package com.taoyuanx.securitydemo.security;
import java.lang.annotation.*;
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequireRole {
Role[] role() ;
}

View File

@@ -0,0 +1,64 @@
package com.taoyuanx.securitydemo.security;
/**
* 简单权限控制,将账户账户状态和角色关联
*/
public enum Role {
/**
* 数字越小权限越大
*/
ADMIN(0, "超级管理员"),
COMMONUSER(1, "普通用户"),
USER(2, "任何已登录用户"),
PUBLIC(3, "未登录");
private int accountStatus;
private String desc;
private Role(int accountStatus, String desc) {
this.accountStatus = accountStatus;
this.desc = desc;
}
public Integer getAccountStatus() {
return accountStatus;
}
public static boolean isMatch(Role[] roles, Integer accountStatus) {
for (Role role : roles) {
if (accountStatus <= role.getAccountStatus()) {
return true;
}
}
return false;
}
/**
* 判断账户是否具有某个角色
*
* @param requireRole
* @param accountStatus
* @return
*/
public static boolean hasRole(RequireRole requireRole, Integer accountStatus) {
if (requireRole == null) {
return true;
}
Role[] roles = requireRole.role();
if (roles != null) {
if (accountStatus == null) {
accountStatus = Role.PUBLIC.getAccountStatus();
}
return isMatch(roles, accountStatus);
}
return true;
}
public static void main(String[] args) {
System.out.println(isMatch(new Role[]{Role.ADMIN}, Role.COMMONUSER.accountStatus));
System.out.println(isMatch(new Role[]{Role.COMMONUSER}, Role.ADMIN.accountStatus));
System.out.println(isMatch(new Role[]{Role.PUBLIC}, Role.PUBLIC.accountStatus));
}
}

View File

@@ -0,0 +1,12 @@
package com.taoyuanx.securitydemo.security.blacklist;
/**
* @author dushitaoyuan
* @desc 黑名单判断
* @date 2019/8/30
*/
public interface BlackListIpCheck {
boolean ipInBlackList(String ip);
}

View File

@@ -0,0 +1,31 @@
package com.taoyuanx.securitydemo.security.blacklist;
import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnels;
import java.nio.charset.Charset;
import java.util.List;
/**
* @author dushitaoyuan
* @desc 默认黑名单检测实现类
* @date 2019/8/30
*/
public class DefaultBlackListIpCheck implements BlackListIpCheck {
private BloomFilter<CharSequence> ipBlackList;
public DefaultBlackListIpCheck(List<String> ipBlackList){
if(ipBlackList==null||ipBlackList.isEmpty()){
throw new RuntimeException("黑名单ip不可为空");
}
this.ipBlackList = BloomFilter.create(Funnels.stringFunnel(Charset.defaultCharset()), ipBlackList.size());
for(String blackListIp:ipBlackList){
this.ipBlackList.put(blackListIp);
}
}
@Override
public boolean ipInBlackList(String ip) {
return ipBlackList.mightContain(ip);
}
}

View File

@@ -0,0 +1,21 @@
package com.taoyuanx.securitydemo.utils;
import javax.servlet.http.Cookie;
public class CookieUtil {
//判断cookie是否存在
public static String getCookie(Cookie[] cookies, String cookieName){
if (cookies != null && cookies.length > 0) {
for (Cookie cookie : cookies) {
if (cookie.getName().equals(cookieName)) {
return cookie.getValue();
}
}
}
return null;
}
}

View File

@@ -0,0 +1,44 @@
package com.taoyuanx.securitydemo.utils;
import cn.hutool.core.io.FileTypeUtil;
import com.google.common.base.Splitter;
import com.google.common.collect.Sets;
import com.taoyuanx.securitydemo.config.GlobalConfig;
import java.io.InputStream;
import java.util.Set;
/**
* @author dushitaoyuan
* @desc 文件类型检测工具
* @date 2019/8/30
*/
public class FileTypeCheckUtil {
private static GlobalConfig globalConfig = SpringContextUtil.getBean(GlobalConfig.class);
private static Set<String> ALLOWEXT = null;
static {
ALLOWEXT = Sets.newHashSet();
Splitter.on(",").split(globalConfig.getAllowUploadExt()).forEach(ext -> {
ALLOWEXT.add(ext.trim().toLowerCase());
});
}
public static boolean allow(String fileExt) {
if (ALLOWEXT == null || ALLOWEXT.isEmpty()) {
return true;
}
return ALLOWEXT.contains(fileExt);
}
public static String getType(String fileName) {
return fileName.substring(fileName.lastIndexOf("."));
}
public static String getRealType(InputStream inputStream) {
return FileTypeUtil.getType(inputStream).toLowerCase();
}
}

View File

@@ -0,0 +1,40 @@
package com.taoyuanx.securitydemo.utils;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.digest.DigestUtils;
/**
* @author dushitaoyuan
* @desc 密码工具类
* @date 2019/8/30
*/
public class PasswordUtil {
public static String passwordEncode(String passwordHash) {
/**
* 处理密码hash
*/
return passwordHash;
}
/**
* 明文处理
*
* @param passwordPlain
* @return
*/
public static String passwordHanlePlain(String passwordPlain) {
/**
* 前后端约定好hash算法,涉及密码相关,hash传送
*/
return Base64.encodeBase64String(DigestUtils.getSha256Digest().digest(passwordPlain.getBytes()));
}
public static boolean isPasswordEqual(String passwordHash, String dbPasswordHash) {
if (passwordHash != null) {
return false;
}
return passwordHash.equals(dbPasswordHash);
}
}

View File

@@ -0,0 +1,62 @@
package com.taoyuanx.securitydemo.utils;
import com.google.common.base.Splitter;
import org.springframework.web.util.HtmlUtils;
import java.util.List;
/**
* @author dushitaoyuan
* @desc 请求参数过滤
* @date 2019/8/29
*/
public class RequestParamFilterUtil {
/**
* 处理xss
*
* @param values
* @return
*/
public static String[] xssFilter(String[] values) {
if(values==null){
return null;
}
String[] newValues = new String[values.length];
for (int i = 0; i < values.length; i++) {
newValues[i] = xssFilter(values[i]);
}
return newValues;
}
/**
* 处理xss
*
* @param value
* @return
*/
public static String xssFilter(String value) {
if (value == null) {
return null;
}
return HtmlUtils.htmlEscape(value);
}
/**
* sql注入风险检测
*/
private static List<String> SQL_KEY_WORDS = Splitter.on(",").splitToList("'|and|exec|insert|select|delete|update|count|*|%|chr|mid|master|truncate|char|declare|; |or|-|+|,");
public static boolean isSqlInject(String... params) {
if (null == params) {
return false;
}
for (String param : params) {
for (int i = 0, len = SQL_KEY_WORDS.size(); i < len; i++) {
if (param.contains(" " + SQL_KEY_WORDS.get(i) + " ")) {
return true;
}
}
}
return false;
}
}

View File

@@ -0,0 +1,46 @@
package com.taoyuanx.securitydemo.utils;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
/**
* @author dushitaoyuan
* @desc 请求工具类
* @date 2019/8/27
*/
public class RequestUtil {
/**
* 获取请求ip
*
* @return
*/
public static String getRemoteIp() {
HttpServletRequest request =
((ServletRequestAttributes) RequestContextHolder.
getRequestAttributes()).getRequest();
return request.getRemoteHost();
}
/**
* 判断是否为本站请求
*
* @param referer
* @param systemDomain
* @return
*/
public static boolean isLoalSiteRequest(String referer, String systemDomain) {
if (StringUtils.isEmpty(systemDomain)) {
return true;
}
if (StringUtils.isEmpty(referer)) {
return false;
}
return referer.contains(systemDomain);
}
}

View File

@@ -0,0 +1,27 @@
package com.taoyuanx.securitydemo.utils;
import com.alibaba.fastjson.JSON;
import org.springframework.http.HttpStatus;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author dushitaoyuan
* @desc 自定义结果返回
* @date 2019/8/30
*/
public class ResponseUtil {
public static void responseJson(HttpServletResponse response, Object result, HttpStatus httpStatus) {
response.setCharacterEncoding("UTF-8");
response.setHeader("Content-type", "application/json;charset=UTF-8");
response.setStatus(httpStatus.value());
try {
response.getWriter().write(JSON.toJSONString(result));
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
}

View File

@@ -0,0 +1,64 @@
package com.taoyuanx.securitydemo.utils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.taoyuanx.securitydemo.exception.UnAuthException;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.StringUtils;
import org.apache.commons.codec.digest.HmacUtils;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* @author 都市桃源
* time:2018 下午4:25:55
* usefor: hmac token算法 单向 简单实现
*/
public class SimpleTokenManager {
private static final String END_KEY = "end";
public static String createToken(String hmacKey, Map<String, Object> data, Long expire, TimeUnit timeUnit) {
Long end = System.currentTimeMillis() + timeUnit.toMillis(expire);
if (data == null || data.isEmpty()) {
data = new HashMap<>(1);
}
data.put(END_KEY, end);
byte[] signData = JSON.toJSONBytes(data);
String token = Base64.encodeBase64URLSafeString(HmacUtils.getHmacMd5(hmacKey.getBytes()).doFinal(signData));
token += "." + Base64.encodeBase64URLSafeString(signData);
return token;
}
public static Map<String, Object> vafy(String hmacKey, String token) throws SecurityException {
try {
if (null == token || "".equals(token)) {
return null;
}
String[] split = token.split("\\.");
if (split.length > 2) {
throw new UnAuthException("token格式非法");
}
byte[] signData = Base64.decodeBase64(split[1].getBytes());
JSONObject signObj = JSON.parseObject(StringUtils.newString(signData, "UTF-8"));
Long end = signObj.getLong(END_KEY);
if (end < System.currentTimeMillis()) {
throw new UnAuthException("token过期");
}
String tokenC = Base64.encodeBase64URLSafeString(HmacUtils.getHmacMd5(hmacKey.getBytes()).doFinal(signData));
if (tokenC.equals(split[0])) {
return signObj;
}
throw new UnAuthException("token校验失败");
} catch (Exception e) {
if (e instanceof UnAuthException) {
throw e;
} else {
throw new UnAuthException("token校验失败");
}
}
}
}

View File

@@ -0,0 +1,33 @@
package com.taoyuanx.securitydemo.utils;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
@Component
public class SpringContextUtil implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
public static Object getBean(String name){
return getApplicationContext().getBean(name);
}
public static <T> T getBean(Class<T> clazz){
return getApplicationContext().getBean(clazz);
}
public static <T> T getBean(String name,Class<T> clazz){
return getApplicationContext().getBean(name, clazz);
}
}

View File

@@ -0,0 +1,41 @@
package com.taoyuanx.securitydemo.web;
import com.taoyuanx.securitydemo.exception.UnAuthException;
import com.taoyuanx.securitydemo.security.blacklist.BlackListIpCheck;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
/**
* 黑名单过滤器
*/
/*@WebFilter(urlPatterns = "/api/*")
@Order(Ordered.HIGHEST_PRECEDENCE)*/
public class BlackListFilter implements Filter {
@Autowired
BlackListIpCheck blackListIpCheck;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException, IOException {
String clientIp = servletRequest.getRemoteAddr();
if (blackListIpCheck.ipInBlackList(clientIp)) {
throw new UnAuthException("ip已在黑名单,不可访问");
}
filterChain.doFilter(servletRequest, servletResponse);
}
@Override
public void destroy() {
}
}

View File

@@ -0,0 +1,38 @@
package com.taoyuanx.securitydemo.web;
import com.taoyuanx.securitydemo.exception.UnAuthException;
import com.taoyuanx.securitydemo.utils.RequestParamFilterUtil;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
import java.util.Map;
/**
* xss过滤器,
* 注意测试,不要拦截所有请求,有风险再拦截,容易误杀正常请求
*/
@WebFilter(urlPatterns = "/api/*")
public class SqlFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException, IOException {
Map<String, String[]> parameterMap = servletRequest.getParameterMap();
if (parameterMap != null) {
for (String key : parameterMap.keySet()) {
if (RequestParamFilterUtil.isSqlInject(parameterMap.get(key))) {
throw new UnAuthException("权限异常");
}
}
}
filterChain.doFilter(servletRequest, servletResponse);
}
@Override
public void destroy() {
}
}

View File

@@ -0,0 +1,29 @@
package com.taoyuanx.securitydemo.web;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
* xss过滤器
*/
@WebFilter(urlPatterns = "/api/*")
public class XssFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException, IOException {
//使用包装器
XssRequestWrapper xssRequestWrapper = new XssRequestWrapper((HttpServletRequest) servletRequest);
filterChain.doFilter(xssRequestWrapper, servletResponse);
}
@Override
public void destroy() {
}
}

View File

@@ -0,0 +1,46 @@
package com.taoyuanx.securitydemo.web;
import com.taoyuanx.securitydemo.utils.RequestParamFilterUtil;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
/**
* xss request包装类
*/
public class XssRequestWrapper extends HttpServletRequestWrapper {
public XssRequestWrapper(HttpServletRequest request) {
super(request);
}
/**
* 对数组参数进行特殊字符过滤
*/
@Override
public String[] getParameterValues(String name) {
return RequestParamFilterUtil.xssFilter(super.getParameterValues(name));
}
@Override
public String getParameter(String name) {
return RequestParamFilterUtil.xssFilter(super.getParameter(name));
}
@Override
public Map<String, String[]> getParameterMap() {
Map<String, String[]> parameterMap = super.getParameterMap();
if (parameterMap == null || parameterMap.isEmpty()) {
return Collections.emptyMap();
}
Map<String, String[]> newParameterMap = new HashMap<>(parameterMap.size());
for (String key : parameterMap.keySet()) {
newParameterMap.put(key, RequestParamFilterUtil.xssFilter(parameterMap.get(key)));
}
return newParameterMap;
}
}

View File

@@ -0,0 +1,31 @@
server.port=8080
#数据库连接池
#spring.datasource.url=jdbc:mysql://localhost:3306/demo?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true
#spring.datasource.username:root
#spring.datasource.password:root
#spring.datasource.driver-class-name=com.mysql.jdbc.Driver
#spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
#spring.datasource.initialSize=10
#spring.datasource.minIdle=10
#spring.datasource.maxActive=100
#spring.datasource.maxWait=60000
#spring.datasource.timeBetweenEvictionRunsMillis=60000
#spring.datasource.filters=wall
server.servlet.session.timeout=1800
#自定义配置
#系统域名
application.config.systemDoamin=taoyuanx.com
#referer校验配置
application.config.referer-check-url[0]=refererCheck
application.config.referer-check-allow-domains=taoyuanx.com
#黑名单
application.config.blackListIp[0]=127.0.0.1
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

View File

@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false">
<!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径 -->
<property name="LOG_HOME" value="./logs" />
<!-- 控制台输出 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化输出:%d表示日期%thread表示线程名%-5level级别从左显示5个字符宽度%msg日志消息%n是换行符 -->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} -
%msg%n</pattern>
</encoder>
</appender>
<!-- 按照每天生成日志文件 -->
<appender name="FILE"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--日志文件输出的文件名 -->
<FileNamePattern>${LOG_HOME}/cert.log.%d{yyyy-MM-dd}.log
</FileNamePattern>
<!--日志文件保留天数 -->
<MaxHistory>30</MaxHistory>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化输出:%d表示日期%thread表示线程名%-5level级别从左显示5个字符宽度%msg日志消息%n是换行符 -->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} -
%msg%n</pattern>
</encoder>
<!--日志文件最大的大小 -->
<triggeringPolicy
class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<MaxFileSize>10MB</MaxFileSize>
</triggeringPolicy>
</appender>
<!-- 日志输出级别 -->
<root level="DEBUG">
<appender-ref ref="STDOUT" />
</root>
</configuration>

View File

@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
<h1>首页</h1>
</body>
</html>

View File

@@ -0,0 +1,51 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录测试</title>
</head>
<body>
<form>
<input name="userName" type="text" value="dushitaoyuan" placeholder="请输入账户名">
<input name="password" type="password" value="123456" placeholder="请输入密码">
<input type="button" value="登录" onclick="upload()">
</form>
</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 type="text/javascript">
function upload() {
var password = $("input[name='password']").val();
password = encodePassword(password);
jQuery.support.cors = true;
$.ajax({
type: 'post',
url: getApiUrl("api/login"),
data: {
"userName": $("input[name='userName']").val(),
"password": password
},
dataType: 'json',
traditional: true,
async: false,
xhrFields: {
withCredentials: true
}
}).success(function (result) {
if (result.success == 1) {
alert("登录成功");
window.location.href = getStaticUrl("public/index.html");
} else {
alert(result.msg);
}
});
}
</script>
</html>

View File

@@ -0,0 +1,40 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>文件上传测试</title>
</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"/>
<script src="/static/js/common/system.js"/>
<script type="text/javascript">
function upload() {
var formData = new FormData();
formData.append('file', $('input[name=file]')[0].files[0]);
jQuery.support.cors = true;
$.ajax({
type: 'post',
url: getApiUrl("api/upload"),
data: formData,
xhrFields: {
withCredentials: true
},
cache: false,
processData: false,
contentType: false
}).success(function (result) {
if (result.status == 1) {
alert("上传成功");
} else {
alert(result.msg);
}
});
}
</script>
</body>
</html>

View File

@@ -0,0 +1 @@
!function(t){function n(e){if(r[e])return r[e].exports;var i=r[e]={exports:{},id:e,loaded:!1};return t[e].call(i.exports,i,i.exports,n),i.loaded=!0,i.exports}var r={};return n.m=t,n.c=r,n.p="",n(0)}([function(t,n,r){r(1)(window)},function(t,n){t.exports=function(t){var n="RealXMLHttpRequest";t.hookAjax=function(t){function r(n){return function(){var r=this.hasOwnProperty(n+"_")?this[n+"_"]:this.xhr[n],e=(t[n]||{}).getter;return e&&e(r,this)||r}}function e(n){return function(r){var e=this.xhr,i=this,o=t[n];if("function"==typeof o)e[n]=function(){t[n](i)||r.apply(e,arguments)};else{var u=(o||{}).setter;r=u&&u(r,i)||r;try{e[n]=r}catch(t){this[n+"_"]=r}}}}function i(n){return function(){var r=[].slice.call(arguments);if(!t[n]||!t[n].call(this,r,this.xhr))return this.xhr[n].apply(this.xhr,r)}}return window[n]=window[n]||XMLHttpRequest,XMLHttpRequest=function(){var t=new window[n];for(var o in t){var u="";try{u=typeof t[o]}catch(t){}"function"===u?this[o]=i(o):Object.defineProperty(this,o,{get:r(o),set:e(o),enumerable:!0})}this.xhr=t},window[n]},t.unHookAjax=function(){window[n]&&(XMLHttpRequest=window[n]),window[n]=void 0},t.default=t}}]);

View File

@@ -0,0 +1,43 @@
//全局定义变量和全局函数
var apiBaseUrl = "http://localhost:8080/"
var staticBaseUrl = "http://localhost:8080/"
function getApiUrl(path) {
return apiBaseUrl + path;
}
function getStaticUrl(path) {
return staticBaseUrl + path;
}
$(document).ready(function () {
hookAjax({
//拦截回调
onreadystatechange: function (xhr) {
try {
//权限异常,跳转到登录
if (xhr.status == 401) {
window.location.href = "/";
}
//限流异常
if (xhr.status == 429) {
var result = JSON.parse(xhr.responseText);
alert(result.msg);
}
} catch (e) {
console.log("error")
}
}
});
});
/**
* 加密密码
*/
function encodePassword(password) {
return CryptoJS.SHA256(password).toString(CryptoJS.enc.Base64);
}

View File

@@ -0,0 +1,13 @@
/*
CryptoJS v3.1.2
code.google.com/p/crypto-js
(c) 2009-2013 by Jeff Mott. All rights reserved.
code.google.com/p/crypto-js/wiki/License
*/
var CryptoJS=CryptoJS||function(h,r){var k={},l=k.lib={},n=function(){},f=l.Base={extend:function(a){n.prototype=this;var b=new n;a&&b.mixIn(a);b.hasOwnProperty("init")||(b.init=function(){b.$super.init.apply(this,arguments)});b.init.prototype=b;b.$super=this;return b},create:function(){var a=this.extend();a.init.apply(a,arguments);return a},init:function(){},mixIn:function(a){for(var b in a)a.hasOwnProperty(b)&&(this[b]=a[b]);a.hasOwnProperty("toString")&&(this.toString=a.toString)},clone:function(){return this.init.prototype.extend(this)}},
j=l.WordArray=f.extend({init:function(a,b){a=this.words=a||[];this.sigBytes=b!=r?b:4*a.length},toString:function(a){return(a||s).stringify(this)},concat:function(a){var b=this.words,d=a.words,c=this.sigBytes;a=a.sigBytes;this.clamp();if(c%4)for(var e=0;e<a;e++)b[c+e>>>2]|=(d[e>>>2]>>>24-8*(e%4)&255)<<24-8*((c+e)%4);else if(65535<d.length)for(e=0;e<a;e+=4)b[c+e>>>2]=d[e>>>2];else b.push.apply(b,d);this.sigBytes+=a;return this},clamp:function(){var a=this.words,b=this.sigBytes;a[b>>>2]&=4294967295<<
32-8*(b%4);a.length=h.ceil(b/4)},clone:function(){var a=f.clone.call(this);a.words=this.words.slice(0);return a},random:function(a){for(var b=[],d=0;d<a;d+=4)b.push(4294967296*h.random()|0);return new j.init(b,a)}}),m=k.enc={},s=m.Hex={stringify:function(a){var b=a.words;a=a.sigBytes;for(var d=[],c=0;c<a;c++){var e=b[c>>>2]>>>24-8*(c%4)&255;d.push((e>>>4).toString(16));d.push((e&15).toString(16))}return d.join("")},parse:function(a){for(var b=a.length,d=[],c=0;c<b;c+=2)d[c>>>3]|=parseInt(a.substr(c,
2),16)<<24-4*(c%8);return new j.init(d,b/2)}},p=m.Latin1={stringify:function(a){var b=a.words;a=a.sigBytes;for(var d=[],c=0;c<a;c++)d.push(String.fromCharCode(b[c>>>2]>>>24-8*(c%4)&255));return d.join("")},parse:function(a){for(var b=a.length,d=[],c=0;c<b;c++)d[c>>>2]|=(a.charCodeAt(c)&255)<<24-8*(c%4);return new j.init(d,b)}},t=m.Utf8={stringify:function(a){try{return decodeURIComponent(escape(p.stringify(a)))}catch(b){throw Error("Malformed UTF-8 data");}},parse:function(a){return p.parse(unescape(encodeURIComponent(a)))}},
q=l.BufferedBlockAlgorithm=f.extend({reset:function(){this._data=new j.init;this._nDataBytes=0},_append:function(a){"string"==typeof a&&(a=t.parse(a));this._data.concat(a);this._nDataBytes+=a.sigBytes},_process:function(a){var b=this._data,d=b.words,c=b.sigBytes,e=this.blockSize,f=c/(4*e),f=a?h.ceil(f):h.max((f|0)-this._minBufferSize,0);a=f*e;c=h.min(4*a,c);if(a){for(var g=0;g<a;g+=e)this._doProcessBlock(d,g);g=d.splice(0,a);b.sigBytes-=c}return new j.init(g,c)},clone:function(){var a=f.clone.call(this);
a._data=this._data.clone();return a},_minBufferSize:0});l.Hasher=q.extend({cfg:f.extend(),init:function(a){this.cfg=this.cfg.extend(a);this.reset()},reset:function(){q.reset.call(this);this._doReset()},update:function(a){this._append(a);this._process();return this},finalize:function(a){a&&this._append(a);return this._doFinalize()},blockSize:16,_createHelper:function(a){return function(b,d){return(new a.init(d)).finalize(b)}},_createHmacHelper:function(a){return function(b,d){return(new u.HMAC.init(a,
d)).finalize(b)}}});var u=k.algo={};return k}(Math);

View File

@@ -0,0 +1,8 @@
/*
CryptoJS v3.1.2
code.google.com/p/crypto-js
(c) 2009-2013 by Jeff Mott. All rights reserved.
code.google.com/p/crypto-js/wiki/License
*/
(function(){var h=CryptoJS,j=h.lib.WordArray;h.enc.Base64={stringify:function(b){var e=b.words,f=b.sigBytes,c=this._map;b.clamp();b=[];for(var a=0;a<f;a+=3)for(var d=(e[a>>>2]>>>24-8*(a%4)&255)<<16|(e[a+1>>>2]>>>24-8*((a+1)%4)&255)<<8|e[a+2>>>2]>>>24-8*((a+2)%4)&255,g=0;4>g&&a+0.75*g<f;g++)b.push(c.charAt(d>>>6*(3-g)&63));if(e=c.charAt(64))for(;b.length%4;)b.push(e);return b.join("")},parse:function(b){var e=b.length,f=this._map,c=f.charAt(64);c&&(c=b.indexOf(c),-1!=c&&(e=c));for(var c=[],a=0,d=0;d<
e;d++)if(d%4){var g=f.indexOf(b.charAt(d-1))<<2*(d%4),h=f.indexOf(b.charAt(d))>>>6-2*(d%4);c[a>>>2]|=(g|h)<<24-8*(a%4);a++}return j.create(c,a)},_map:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="}})();

View File

@@ -0,0 +1,19 @@
/*
CryptoJS v3.1.2
code.google.com/p/crypto-js
(c) 2009-2013 by Jeff Mott. All rights reserved.
code.google.com/p/crypto-js/wiki/License
*/
var CryptoJS=CryptoJS||function(s,p){var m={},l=m.lib={},n=function(){},r=l.Base={extend:function(b){n.prototype=this;var h=new n;b&&h.mixIn(b);h.hasOwnProperty("init")||(h.init=function(){h.$super.init.apply(this,arguments)});h.init.prototype=h;h.$super=this;return h},create:function(){var b=this.extend();b.init.apply(b,arguments);return b},init:function(){},mixIn:function(b){for(var h in b)b.hasOwnProperty(h)&&(this[h]=b[h]);b.hasOwnProperty("toString")&&(this.toString=b.toString)},clone:function(){return this.init.prototype.extend(this)}},
q=l.WordArray=r.extend({init:function(b,h){b=this.words=b||[];this.sigBytes=h!=p?h:4*b.length},toString:function(b){return(b||t).stringify(this)},concat:function(b){var h=this.words,a=b.words,j=this.sigBytes;b=b.sigBytes;this.clamp();if(j%4)for(var g=0;g<b;g++)h[j+g>>>2]|=(a[g>>>2]>>>24-8*(g%4)&255)<<24-8*((j+g)%4);else if(65535<a.length)for(g=0;g<b;g+=4)h[j+g>>>2]=a[g>>>2];else h.push.apply(h,a);this.sigBytes+=b;return this},clamp:function(){var b=this.words,h=this.sigBytes;b[h>>>2]&=4294967295<<
32-8*(h%4);b.length=s.ceil(h/4)},clone:function(){var b=r.clone.call(this);b.words=this.words.slice(0);return b},random:function(b){for(var h=[],a=0;a<b;a+=4)h.push(4294967296*s.random()|0);return new q.init(h,b)}}),v=m.enc={},t=v.Hex={stringify:function(b){var a=b.words;b=b.sigBytes;for(var g=[],j=0;j<b;j++){var k=a[j>>>2]>>>24-8*(j%4)&255;g.push((k>>>4).toString(16));g.push((k&15).toString(16))}return g.join("")},parse:function(b){for(var a=b.length,g=[],j=0;j<a;j+=2)g[j>>>3]|=parseInt(b.substr(j,
2),16)<<24-4*(j%8);return new q.init(g,a/2)}},a=v.Latin1={stringify:function(b){var a=b.words;b=b.sigBytes;for(var g=[],j=0;j<b;j++)g.push(String.fromCharCode(a[j>>>2]>>>24-8*(j%4)&255));return g.join("")},parse:function(b){for(var a=b.length,g=[],j=0;j<a;j++)g[j>>>2]|=(b.charCodeAt(j)&255)<<24-8*(j%4);return new q.init(g,a)}},u=v.Utf8={stringify:function(b){try{return decodeURIComponent(escape(a.stringify(b)))}catch(g){throw Error("Malformed UTF-8 data");}},parse:function(b){return a.parse(unescape(encodeURIComponent(b)))}},
g=l.BufferedBlockAlgorithm=r.extend({reset:function(){this._data=new q.init;this._nDataBytes=0},_append:function(b){"string"==typeof b&&(b=u.parse(b));this._data.concat(b);this._nDataBytes+=b.sigBytes},_process:function(b){var a=this._data,g=a.words,j=a.sigBytes,k=this.blockSize,m=j/(4*k),m=b?s.ceil(m):s.max((m|0)-this._minBufferSize,0);b=m*k;j=s.min(4*b,j);if(b){for(var l=0;l<b;l+=k)this._doProcessBlock(g,l);l=g.splice(0,b);a.sigBytes-=j}return new q.init(l,j)},clone:function(){var b=r.clone.call(this);
b._data=this._data.clone();return b},_minBufferSize:0});l.Hasher=g.extend({cfg:r.extend(),init:function(b){this.cfg=this.cfg.extend(b);this.reset()},reset:function(){g.reset.call(this);this._doReset()},update:function(b){this._append(b);this._process();return this},finalize:function(b){b&&this._append(b);return this._doFinalize()},blockSize:16,_createHelper:function(b){return function(a,g){return(new b.init(g)).finalize(a)}},_createHmacHelper:function(b){return function(a,g){return(new k.HMAC.init(b,
g)).finalize(a)}}});var k=m.algo={};return m}(Math);
(function(s){function p(a,k,b,h,l,j,m){a=a+(k&b|~k&h)+l+m;return(a<<j|a>>>32-j)+k}function m(a,k,b,h,l,j,m){a=a+(k&h|b&~h)+l+m;return(a<<j|a>>>32-j)+k}function l(a,k,b,h,l,j,m){a=a+(k^b^h)+l+m;return(a<<j|a>>>32-j)+k}function n(a,k,b,h,l,j,m){a=a+(b^(k|~h))+l+m;return(a<<j|a>>>32-j)+k}for(var r=CryptoJS,q=r.lib,v=q.WordArray,t=q.Hasher,q=r.algo,a=[],u=0;64>u;u++)a[u]=4294967296*s.abs(s.sin(u+1))|0;q=q.MD5=t.extend({_doReset:function(){this._hash=new v.init([1732584193,4023233417,2562383102,271733878])},
_doProcessBlock:function(g,k){for(var b=0;16>b;b++){var h=k+b,w=g[h];g[h]=(w<<8|w>>>24)&16711935|(w<<24|w>>>8)&4278255360}var b=this._hash.words,h=g[k+0],w=g[k+1],j=g[k+2],q=g[k+3],r=g[k+4],s=g[k+5],t=g[k+6],u=g[k+7],v=g[k+8],x=g[k+9],y=g[k+10],z=g[k+11],A=g[k+12],B=g[k+13],C=g[k+14],D=g[k+15],c=b[0],d=b[1],e=b[2],f=b[3],c=p(c,d,e,f,h,7,a[0]),f=p(f,c,d,e,w,12,a[1]),e=p(e,f,c,d,j,17,a[2]),d=p(d,e,f,c,q,22,a[3]),c=p(c,d,e,f,r,7,a[4]),f=p(f,c,d,e,s,12,a[5]),e=p(e,f,c,d,t,17,a[6]),d=p(d,e,f,c,u,22,a[7]),
c=p(c,d,e,f,v,7,a[8]),f=p(f,c,d,e,x,12,a[9]),e=p(e,f,c,d,y,17,a[10]),d=p(d,e,f,c,z,22,a[11]),c=p(c,d,e,f,A,7,a[12]),f=p(f,c,d,e,B,12,a[13]),e=p(e,f,c,d,C,17,a[14]),d=p(d,e,f,c,D,22,a[15]),c=m(c,d,e,f,w,5,a[16]),f=m(f,c,d,e,t,9,a[17]),e=m(e,f,c,d,z,14,a[18]),d=m(d,e,f,c,h,20,a[19]),c=m(c,d,e,f,s,5,a[20]),f=m(f,c,d,e,y,9,a[21]),e=m(e,f,c,d,D,14,a[22]),d=m(d,e,f,c,r,20,a[23]),c=m(c,d,e,f,x,5,a[24]),f=m(f,c,d,e,C,9,a[25]),e=m(e,f,c,d,q,14,a[26]),d=m(d,e,f,c,v,20,a[27]),c=m(c,d,e,f,B,5,a[28]),f=m(f,c,
d,e,j,9,a[29]),e=m(e,f,c,d,u,14,a[30]),d=m(d,e,f,c,A,20,a[31]),c=l(c,d,e,f,s,4,a[32]),f=l(f,c,d,e,v,11,a[33]),e=l(e,f,c,d,z,16,a[34]),d=l(d,e,f,c,C,23,a[35]),c=l(c,d,e,f,w,4,a[36]),f=l(f,c,d,e,r,11,a[37]),e=l(e,f,c,d,u,16,a[38]),d=l(d,e,f,c,y,23,a[39]),c=l(c,d,e,f,B,4,a[40]),f=l(f,c,d,e,h,11,a[41]),e=l(e,f,c,d,q,16,a[42]),d=l(d,e,f,c,t,23,a[43]),c=l(c,d,e,f,x,4,a[44]),f=l(f,c,d,e,A,11,a[45]),e=l(e,f,c,d,D,16,a[46]),d=l(d,e,f,c,j,23,a[47]),c=n(c,d,e,f,h,6,a[48]),f=n(f,c,d,e,u,10,a[49]),e=n(e,f,c,d,
C,15,a[50]),d=n(d,e,f,c,s,21,a[51]),c=n(c,d,e,f,A,6,a[52]),f=n(f,c,d,e,q,10,a[53]),e=n(e,f,c,d,y,15,a[54]),d=n(d,e,f,c,w,21,a[55]),c=n(c,d,e,f,v,6,a[56]),f=n(f,c,d,e,D,10,a[57]),e=n(e,f,c,d,t,15,a[58]),d=n(d,e,f,c,B,21,a[59]),c=n(c,d,e,f,r,6,a[60]),f=n(f,c,d,e,z,10,a[61]),e=n(e,f,c,d,j,15,a[62]),d=n(d,e,f,c,x,21,a[63]);b[0]=b[0]+c|0;b[1]=b[1]+d|0;b[2]=b[2]+e|0;b[3]=b[3]+f|0},_doFinalize:function(){var a=this._data,k=a.words,b=8*this._nDataBytes,h=8*a.sigBytes;k[h>>>5]|=128<<24-h%32;var l=s.floor(b/
4294967296);k[(h+64>>>9<<4)+15]=(l<<8|l>>>24)&16711935|(l<<24|l>>>8)&4278255360;k[(h+64>>>9<<4)+14]=(b<<8|b>>>24)&16711935|(b<<24|b>>>8)&4278255360;a.sigBytes=4*(k.length+1);this._process();a=this._hash;k=a.words;for(b=0;4>b;b++)h=k[b],k[b]=(h<<8|h>>>24)&16711935|(h<<24|h>>>8)&4278255360;return a},clone:function(){var a=t.clone.call(this);a._hash=this._hash.clone();return a}});r.MD5=t._createHelper(q);r.HmacMD5=t._createHmacHelper(q)})(Math);

View File

@@ -0,0 +1,9 @@
/*
CryptoJS v3.1.2
code.google.com/p/crypto-js
(c) 2009-2013 by Jeff Mott. All rights reserved.
code.google.com/p/crypto-js/wiki/License
*/
(function(k){for(var g=CryptoJS,h=g.lib,v=h.WordArray,j=h.Hasher,h=g.algo,s=[],t=[],u=function(q){return 4294967296*(q-(q|0))|0},l=2,b=0;64>b;){var d;a:{d=l;for(var w=k.sqrt(d),r=2;r<=w;r++)if(!(d%r)){d=!1;break a}d=!0}d&&(8>b&&(s[b]=u(k.pow(l,0.5))),t[b]=u(k.pow(l,1/3)),b++);l++}var n=[],h=h.SHA256=j.extend({_doReset:function(){this._hash=new v.init(s.slice(0))},_doProcessBlock:function(q,h){for(var a=this._hash.words,c=a[0],d=a[1],b=a[2],k=a[3],f=a[4],g=a[5],j=a[6],l=a[7],e=0;64>e;e++){if(16>e)n[e]=
q[h+e]|0;else{var m=n[e-15],p=n[e-2];n[e]=((m<<25|m>>>7)^(m<<14|m>>>18)^m>>>3)+n[e-7]+((p<<15|p>>>17)^(p<<13|p>>>19)^p>>>10)+n[e-16]}m=l+((f<<26|f>>>6)^(f<<21|f>>>11)^(f<<7|f>>>25))+(f&g^~f&j)+t[e]+n[e];p=((c<<30|c>>>2)^(c<<19|c>>>13)^(c<<10|c>>>22))+(c&d^c&b^d&b);l=j;j=g;g=f;f=k+m|0;k=b;b=d;d=c;c=m+p|0}a[0]=a[0]+c|0;a[1]=a[1]+d|0;a[2]=a[2]+b|0;a[3]=a[3]+k|0;a[4]=a[4]+f|0;a[5]=a[5]+g|0;a[6]=a[6]+j|0;a[7]=a[7]+l|0},_doFinalize:function(){var d=this._data,b=d.words,a=8*this._nDataBytes,c=8*d.sigBytes;
b[c>>>5]|=128<<24-c%32;b[(c+64>>>9<<4)+14]=k.floor(a/4294967296);b[(c+64>>>9<<4)+15]=a;d.sigBytes=4*b.length;this._process();return this._hash},clone:function(){var b=j.clone.call(this);b._hash=this._hash.clone();return b}});g.SHA256=j._createHelper(h);g.HmacSHA256=j._createHmacHelper(h)})(Math);

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,32 @@
package com.taoyuanx.securitydemo;
import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnels;
import org.junit.Test;
/**
* @author dushitaoyuan
* @desc 布隆过滤测试
* @date 2019/8/30
*/
public class BoomFilterTest {
@Test
public void failedCountTest() {
int batch = 10000000;
BloomFilter bloomFilter = BloomFilter.create(Funnels.integerFunnel(), batch);
for (int i = 0; i < batch; i++) {
bloomFilter.put(i);
}
int count = 0;
for (int i = 0; i < batch; i++) {
if (!bloomFilter.mightContain(i)) {
System.out.println("failed\t" + i);
count++;
}
}
System.out.println("失败次数:" + count);
}
}

View File

@@ -0,0 +1,20 @@
package com.taoyuanx.securitydemo;
import com.taoyuanx.securitydemo.controller.BussinessController;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
public class SecurityDemoApplicationTests {
@Autowired
BussinessController bussinessController;
@Test
public void contextLoads() {
bussinessController.admin();
}
}