公共抽取
import java.util.Date;
import java.util.Map;
import org.pingruan.framework.exception.BException;
import org.pingruan.framework.model.response.BaseEnum;
import org.pingruan.framework.model.response.Code;
import org.pingruan.framework.model.response.RestPage;
import org.pingruan.framework.model.response.RestResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 公共抽取,提供Controller、Service、Dao继承
*
* @author vander
* @date 2018年11月15日
*/
public abstract class Base {
protected Logger logger = LoggerFactory.getLogger(getClass());
protected void throwException(Code code) {
throw new BException(code);
}
protected void throwException(String msg) {
throw new BException(msg);
}
protected void throwException(String msg, int code) {
throw new BException(msg,code);
}
protected void throwException(String msg, Throwable e) {
throw new BException(msg,e);
}
protected RestResult code(Code code) {
return RestResult.builder().codeMsg(code);
}
protected RestResult ok() {
return RestResult.builder().codeMsg(Code.OK);
}
protected RestResult msg(BaseEnum baseEnum) {
return RestResult.builder().codeMessage(baseEnum.getCode(), baseEnum.getMsg());
}
protected RestResult ok(Object data) {
return RestResult.builder().codeMsg(Code.OK).dataResult(data);
}
protected Date date() {
return new Date();
}
protected RestResult okMsg(String msg) {
return RestResult.builder().codeMessage(200, msg);
}
protected RestResult errorMsg(String msg) {
return RestResult.builder().codeMessage(500, msg);
}
protected RestResult error() {
return RestResult.builder().codeMsg(Code.ERROR);
}
protected RestResult paramError() {
return RestResult.builder().codeMsg(Code.PARAM_ERROR);
}
protected RestResult paramErrorMsg(String msg) {
return RestResult.builder().codeMessage(400, msg);
}
protected RestResult notFindError() {
return RestResult.builder().codeMsg(Code.RESOURCE_NO_EXISTS);
}
protected RestResult loginError() {
return RestResult.builder().codeMsg(Code.LOGIN_ERROR);
}
protected RestResult pageResult(RestPage page, Object data) {
return RestResult.builder().codeMsg(Code.OK).pageResult(page, data);
}
protected RestResult page(Map<String, Object> params,long total,Object data) {
RestPage page = new RestPage();
page.setPageNum(Integer.parseInt(params.get("_page").toString()));
page.setPageSize(Integer.parseInt(params.get("_pageSize").toString()));
page.setTotal(total);
return pageResult(page,data);
}
}
import java.util.List;
import java.util.Map;
/**
* 基础Dao(还需在XML文件里,有对应的SQL语句)
*
*
* @author vander
* @date 2018年11月28日
*/
public interface BaseDao<T,ID> {
T selectByPrimaryKey(ID id);
Integer deleteByPrimaryKey(ID id);
Integer insert(T entity);
Integer updateByPrimaryKeySelective(T entity);
Integer updateByPrimaryKey(T entity);
List<T> findList(Map<String,Object> params);
List<T> findListByPage(Map<String,Object> params);
Long findTotal(Map<String,Object> params);
Integer insertBatch(List<T> list);
}
import java.util.List;
import java.util.Map;
/**
* 公共Service
*
* @author vander
* @date 2018年11月15日
*/
public interface BaseService<T,ID> {
T selectByPrimaryKey(ID id);
Integer deleteByPrimaryKey(ID id);
Integer insert(T entity);
Integer updateByPrimaryKeySelective(T entity);
Integer updateByPrimaryKey(T entity);
List<T> findList(Map<String,Object> params);
List<T> findListByPage(Map<String,Object> params);
Long findTotal(Map<String,Object> params);
Integer insertBatch(List<T> list);
}
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.pingruan.framework.constant.Auth;
import org.springframework.web.bind.annotation.ModelAttribute;
import io.jsonwebtoken.Claims;
/**
* 公共Controller
*
* 获取到请求和响应对象
*
* @author vander
* @date 2018年11月15日
*/
public class BaseController extends Base{
protected HttpServletRequest request;
protected HttpServletResponse response;
protected HttpSession session;
protected Claims claims;
@ModelAttribute
public void setReqAndRes(HttpServletRequest request, HttpServletResponse response) {
this.request = request;
this.response = response;
this.session = request.getSession();
Object obj = request.getAttribute(Auth.JWT_USER_CLAIMS);
if(obj!=null) {
this.claims = (Claims) obj;
}
}
protected String getPara(String name) {
return request.getParameter(name);
}
/**
* 文件下载
*
* @param fileName 全文件名
* @return
* @throws IOException
*/
protected HttpServletResponse download(String fileName) throws IOException {
response.setContentType("application/octet-stream");
fileName = response.encodeURL(new String(fileName.getBytes(),"iso8859-1")); //保存的文件名,必须和页面编码一致,否则乱码
response.addHeader("content-disposition","attachment;filename=" + fileName);
return response;
}
}
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
/**
* springboot 公共测试类
*
* @author vander
*
*/
@RunWith(SpringRunner.class)
@SpringBootTest
public class BaseTest {
}
错误页面跳转
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.pingruan.framework.base.BaseController;
import org.pingruan.framework.model.response.RestResult;
import org.pingruan.framework.web.BProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
/**
*
*
* 请求接口不存在处理
*
* @author Vander
* @author 2018年8月22日
* @version 0.8
*/
@Controller
@RequestMapping("${server.error.path:${error.path:/error}}")
public class FundaErrorController extends BaseController implements ErrorController {
@Autowired
private BProperties bProperties;
@Override
public String getErrorPath() {
return "/error";
}
/**
* 页面请求跳转到指定错误页面
*/
@RequestMapping(produces = { "text/html" })
public void doHandleError(HttpServletResponse resp) throws IOException {
resp.sendRedirect(bProperties.getErrorPage());
}
/**
* ajax请求返回
*/
@ResponseStatus(code = HttpStatus.NOT_FOUND)
@RequestMapping
@ResponseBody
public RestResult error(HttpServletRequest request) {
return notFindError();
}
}
全局异常处理
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.text.StringEscapeUtils;
import org.pingruan.framework.model.response.Code;
import org.pingruan.framework.model.response.RestResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.ValidationException;
import java.beans.PropertyEditorSupport;
import java.util.HashSet;
import java.util.Set;
/**
* 防止XSS
*
* 全局异常处理
*
* @author Vander
* @author 2018年5月22日
* @version 0.8
*/
@ControllerAdvice
public class ExceptionHandlerAdvice {
private static Logger LOGGER = LoggerFactory.getLogger(ExceptionHandlerAdvice.class);
public ExceptionHandlerAdvice() {
LOGGER.info("全局异常处理类初始化成功");
}
@InitBinder
protected void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(String.class, new PropertyEditorSupport() {
public void setAsText(String text) {
setValue(StringUtils.isBlank(text) ? null : StringEscapeUtils.escapeHtml4(StringUtils.trim(text.trim())));
}
public String getAsText() {
Object value = getValue();
return value != null ? value.toString() : "";
}
});
}
/**
* 全局非法参数响应
*
* @param exception
* @return
*/
@ExceptionHandler(ValidationException.class)
@ResponseBody
public RestResult handle(Exception exception) {
RestResult result = RestResult.builder().codeMsg(Code.PARAM_ERROR);
if (exception instanceof ConstraintViolationException) {
ConstraintViolationException exs = (ConstraintViolationException) exception;
Set<ConstraintViolation<?>> violations = exs.getConstraintViolations();
Set<String> mStrings = new HashSet<>();
for (ConstraintViolation<?> constraintViolation : violations) {
mStrings.add(constraintViolation.getMessage());
}
result = RestResult.builder().codeMsg(Code.PARAM_ERROR).dataResult(mStrings);
}
return result;
}
/**
* 业务异常处理
*/
@ExceptionHandler(BException.class)
@ResponseBody
public Object customHandler(BException exception){
LOGGER.info("通用异常", exception);
return RestResult.builder().codeMessage(exception.getCode(),exception.getMsg());
}
/**
* 异常处理
*/
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ExceptionHandler(Exception.class)
@ResponseBody
public RestResult handleException(HttpServletRequest request, HttpServletResponse response, Exception e) throws Exception {
LOGGER.error("通用异常", e);
return RestResult.builder().codeMsg(Code.ERROR);
}
}
全局响应
import java.io.Serializable;
/**
* 全局响应
*
* @author Vander
* @author 2018年8月23日
* @version 0.8
*/
@SuppressWarnings("all")
public class RestResult implements Serializable {
private static final long serialVersionUID = 1899954023157050623L;
private Integer code;
private String msg;
private Object data;
private RestPage page;
public String getMsg() {
return msg;
}
public Object getData() {
return data;
}
public RestPage getPage() {
return page;
}
public Integer getCode() {
return code;
}
public static RestResult builder() {
return new RestResult();
}
public RestResult codeMsg(Code code) {
this.code = code.getCode();
this.msg = code.getMsg();
return this;
}
public RestResult codeMessage(int code,String message) {
this.code = code;
this.msg = message;
return this;
}
public RestResult dataResult(Object object) {
if (null == object) {
object = new Object();
}
data = object;
return this;
}
public RestResult pageResult(RestPage page,Object object) {
this.page = page;
this.data = object;
return this;
}
}
import java.io.Serializable;
/**
* 分页实例
*
* @author Vander
* @author 2018年8月23日
* @version 0.8
*/
public class RestPage implements Serializable {
private static final long serialVersionUID = 8153585715122814385L;
private int pageNum = 0;
private int pageSize = 10;
private int pages;
private long total;
public RestPage() {
}
public RestPage(int pageNum,int pageSize,long total){
this.pageNum = pageNum;
this.pageSize = pageSize;
this.total = total;
if (total>0) {
this.pages = (int) (total%pageSize==0?total/pageSize:total/pageSize+1);
}
}
public int getPageSize() {
return this.pageSize;
}
public void setPageSize(int pageSize) {
this.pageSize = pageSize;
}
public long getTotal() {
return this.total;
}
public void setTotal(long total) {
this.total = total;
if (total>0) {
this.pages = (int) (total%pageSize==0?total/pageSize:total/pageSize+1);
}
}
public int getPageNum() {
return this.pageNum;
}
public void setPageNum(int pageNum) {
this.pageNum = pageNum;
}
public int getPages() {
return this.pages;
}
}
业务异常类
import org.pingruan.framework.model.response.Code;
/**
*
* 业务异常
*
* @author Vander
* @author 2018年8月23日
* @version 0.8
*/
public class BException extends RuntimeException {
private static final long serialVersionUID = 9004467195546878166L;
private Integer code;
private String msg;
public BException(Code code) {
super(code.getMsg());
this.msg = code.getMsg();
this.code = code.getCode();
}
public BException(String msg, Throwable e) {
super(msg, e);
this.code = 500;
this.msg = msg;
}
public BException(String msg) {
super(msg);
this.code = 500;
this.msg = msg;
}
public BException(String msg, int code) {
super(msg);
this.msg = msg;
this.code = code;
}
public BException(String msg, int code, Throwable e) {
super(msg, e);
this.msg = msg;
this.code = code;
}
public int getCode() {
return code;
}
public String getMsg() {
return msg;
}
}
响应枚举
/**
* 响应状态数据
*
* @author vander
* @date 2018年11月15日
*/
public enum Code implements BaseEnum{
OK(200, "操作成功"),
PARAM_ERROR(400, "参数错误"),
USER_VALIDATE_REQUIRED(401, "需要用户验证"),
USER_NO_PERMISSION(403, "用户无权限"),
RESOURCE_NO_EXISTS(404, "资源不存在"),
METHOD_NO_SUPPORT(405, "不支持的操作方法"),
ERROR(500, "服务器内部错误"),
APPLICATION_ERROR(502, "应用程序错误"),
MAINTAIN(503, "系统维护中"),
LOGIN_ERROR(401, "用户名或密码错误");
private int code;
private String msg;
private Code (int code, String msg) {
this.code = code;
this.msg = msg;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
}
/**
* base状态封装
*
* 提供模块实现
*
* @author vander
* @date 2018年11月15日
*/
public interface BaseEnum {
int getCode();
String getMsg();
}
/**
*
* Description: 通过code获取对应枚举
* Company: 51zan
* @author Vander
* @author 2018年4月26日
* @version 0.8
*/
public class EnumUtil {
public static <T extends BaseEnum> T getByCode(Integer code, Class<T> enumClass) {
for (T each: enumClass.getEnumConstants()) {
if (code.equals(each.getCode())) {
return each;
}
}
return null;
}
}
Spring上下文容器,HttpServletRequest
调用方式
@Autoward
SpringContextHolder springContextHolder
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
/**
* Spring的ApplicationContext容器 获取对象
*
* @author Vander
* @author 2018年7月26日
* @version 0.8
*/
@Component //配置文件方式: <bean id="springContextHolder" class="org.pingruan.common.SpringContextHolder" />
public class SpringContextHolder implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
SpringContextHolder.applicationContext = applicationContext;
}
public static ApplicationContext getApplicationContext() {
assertApplicationContext();
return applicationContext;
}
@SuppressWarnings("unchecked")
public static <T> T getBean(String beanName) {
assertApplicationContext();
return (T) applicationContext.getBean(beanName);
}
public static <T> T getBean(Class<T> requiredType) {
assertApplicationContext();
return applicationContext.getBean(requiredType);
}
private static void assertApplicationContext() {
if (SpringContextHolder.applicationContext == null) {
throw new RuntimeException("applicaitonContext属性为null,请检查是否注入了SpringContextHolder!");
}
}
public static HttpServletRequest getHttpServletRequest() {
return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
}
}
自定义配置对象
/**
* 项目基本配置
*
* @author vander
* @date 2018年11月15日
*/
@Component
@ConfigurationProperties(prefix="pingruan.base")
@Data
public class BProperties {
//swagger2adoc
private String projectIdPort;//如http://localhost:8083
//shiro相关配置----vander-component-starter-shiro
private boolean enableShiro = false;
private boolean enableShiroRedis = false;//使用redis统一管理回话
private boolean enableShiroOauth2 = false;//开启token认证,需要封装VToken对象
private List<String> shiroFilterList; //权限过滤器集合
private String shiroLoginUrl = "/auth/login";//设置登录页面
private String shiroUnauthorizedUrl = "/auth/autherror";//授权失败跳转 接口
private long shiroSessionExpire = 3600;//Session过期时间 1小时
//redis缓存配置----vander-component-starter-data-redis
private long cacheRedisTtl = 30; //默认缓存30s
//jwt配置
private boolean enableJwt = false; //
private String jwtKey = "vander"; //唯一key 保密
private long jwtTtl = 3600; //过期时间
private String jwtPath = "/**";//拦截匹配
private String[] jwtExclude = {"login","register"};
//cas配置
private String casServerUrl;
private String casServerLoginUrl;
private String casServerLogoutUrl;
private String appServerUrl;
private String appLoginUrl;
private String appLogoutUrl;
private Boolean appSendRenew = false;
//项目根目录
private String rootPath = "";
//是否开启Ribbon负载均衡
private boolean enableRibbon = false;
//fastdfs配置
private boolean enableFastdfs = false;
private Integer fastdfsConnectTimeout = 5; //http连接超时时间
private Integer fastdfsNetworkTimeout = 30;//tracker与storage网络通信超时时间
private String fastdfsCharset = "UTF-8"; //字符编码
private String fastdfsTrackerUrls; //tracker服务器地址,多个地址中间用英文逗号分隔
//mybatis代码生成
private boolean enableCodeGen = false;
private String codeGenFilePath = "D:\\code.zip";
private String codeGenBasePackage;
private String codeGenTables; //多个用逗号隔开
private String codeGenTablePrefix;//表名前缀
private String author = "vander";
//htts配置
private boolean enableSsl = false;
private Integer sslPort = 8083;
//错误页面
private String errorPage = "404.html";
//swagger2相关配置
private String apiPackageScan = "";
private String apiTitle = "Swagger2 api 文档";
private String apiDesc = "Swagger2 api 文档";
//连接池管理
private boolean enableDruid = false;//是否开启druid
private boolean multiStatementAllow = false;//开启多sql执行支持
private String druidUserName = "admin";
private String druidPassword = "qwe123";
private String druidAllowIps = "127.0.0.1";
private String druidDenyIps = "";
//GridFS文件上传、下载
private boolean gridfsEnable = false;//是否启用GridFS
private String gridfsUri;
private String gridfsHost;
private Integer gridfsPort;
private String gridfsUserName;
private String gridfsPassword;
private String gridfsDatabase;
//mongodb配置
private boolean useMongo = false;//是否使用mongodb
//mybatis mapper地址
private boolean useMybaits = false;
private String mapperScan = "classpath:mapper/*.xml";
private String mybatisPageDialect = "mysql"; //默认mysql
private String mybatisPagePattern = "page"; //dao方法名包含的字段 进行分页操作 默认page
//微信
private String wxAppId;
private String wxAppSecret;
//代理上网
private boolean useProxy = false; //是否使用代理
private String proxyHost;
private String proxyPort;
//基于mysql的定时任务
private boolean scheduleEnable = false;//是否启用定时任务 需要在classpath添加quartz.properties配置文件
}
XSS过滤
/**
* XSS过滤
*/
public class XssFilter implements Filter {
@Override
public void init(FilterConfig config) throws ServletException {
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper(
(HttpServletRequest) request);
chain.doFilter(xssRequest, response);
}
@Override
public void destroy() {
}
}
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
*
* HTML filtering utility for protecting against XSS (Cross Site Scripting).
*
* This code is licensed LGPLv3
*
* This code is a Java port of the original work in PHP by Cal Hendersen.
* http://code.iamcal.com/php/lib_filter/
*
* The trickiest part of the translation was handling the differences in regex handling
* between PHP and Java. These resources were helpful in the process:
*
* http://java.sun.com/j2se/1.4.2/docs/api/java/util/regex/Pattern.html
* http://us2.php.net/manual/en/reference.pcre.pattern.modifiers.php
* http://www.regular-expressions.info/modifiers.html
*
* A note on naming conventions: instance variables are prefixed with a "v"; global
* constants are in all caps.
*
* Sample use:
* String input = ...
* String clean = new HTMLFilter().filter( input );
*
* The class is not thread safe. Create a new instance if in doubt.
*
* If you find bugs or have suggestions on improvement (especially regarding
* performance), please contact us. The latest version of this
* source, and our contact details, can be found at http://xss-html-filter.sf.net
*
* @author Joseph O'Connell
* @author Cal Hendersen
* @author Michael Semb Wever
*/
public final class HTMLFilter {
/** regex flag union representing /si modifiers in php **/
private static final int REGEX_FLAGS_SI = Pattern.CASE_INSENSITIVE | Pattern.DOTALL;
private static final Pattern P_COMMENTS = Pattern.compile("<!--(.*?)-->", Pattern.DOTALL);
private static final Pattern P_COMMENT = Pattern.compile("^!--(.*)--$", REGEX_FLAGS_SI);
private static final Pattern P_TAGS = Pattern.compile("<(.*?)>", Pattern.DOTALL);
private static final Pattern P_END_TAG = Pattern.compile("^/([a-z0-9]+)", REGEX_FLAGS_SI);
private static final Pattern P_START_TAG = Pattern.compile("^([a-z0-9]+)(.*?)(/?)$", REGEX_FLAGS_SI);
private static final Pattern P_QUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)=([\"'])(.*?)\\2", REGEX_FLAGS_SI);
private static final Pattern P_UNQUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)(=)([^\"\\s']+)", REGEX_FLAGS_SI);
private static final Pattern P_PROTOCOL = Pattern.compile("^([^:]+):", REGEX_FLAGS_SI);
private static final Pattern P_ENTITY = Pattern.compile("&#(\\d+);?");
private static final Pattern P_ENTITY_UNICODE = Pattern.compile("&#x([0-9a-f]+);?");
private static final Pattern P_ENCODE = Pattern.compile("%([0-9a-f]{2});?");
private static final Pattern P_VALID_ENTITIES = Pattern.compile("&([^&;]*)(?=(;|&|$))");
private static final Pattern P_VALID_QUOTES = Pattern.compile("(>|^)([^<]+?)(<|$)", Pattern.DOTALL);
private static final Pattern P_END_ARROW = Pattern.compile("^>");
private static final Pattern P_BODY_TO_END = Pattern.compile("<([^>]*?)(?=<|$)");
private static final Pattern P_XML_CONTENT = Pattern.compile("(^|>)([^<]*?)(?=>)");
private static final Pattern P_STRAY_LEFT_ARROW = Pattern.compile("<([^>]*?)(?=<|$)");
private static final Pattern P_STRAY_RIGHT_ARROW = Pattern.compile("(^|>)([^<]*?)(?=>)");
private static final Pattern P_AMP = Pattern.compile("&");
private static final Pattern P_QUOTE = Pattern.compile("<");
private static final Pattern P_LEFT_ARROW = Pattern.compile("<");
private static final Pattern P_RIGHT_ARROW = Pattern.compile(">");
private static final Pattern P_BOTH_ARROWS = Pattern.compile("<>");
// @xxx could grow large... maybe use sesat's ReferenceMap
private static final ConcurrentMap<String,Pattern> P_REMOVE_PAIR_BLANKS = new ConcurrentHashMap<String, Pattern>();
private static final ConcurrentMap<String,Pattern> P_REMOVE_SELF_BLANKS = new ConcurrentHashMap<String, Pattern>();
/** set of allowed html elements, along with allowed attributes for each element **/
private final Map<String, List<String>> vAllowed;
/** counts of open tags for each (allowable) html element **/
private final Map<String, Integer> vTagCounts = new HashMap<String, Integer>();
/** html elements which must always be self-closing (e.g. "<img />") **/
private final String[] vSelfClosingTags;
/** html elements which must always have separate opening and closing tags (e.g. "<b></b>") **/
private final String[] vNeedClosingTags;
/** set of disallowed html elements **/
private final String[] vDisallowed;
/** attributes which should be checked for valid protocols **/
private final String[] vProtocolAtts;
/** allowed protocols **/
private final String[] vAllowedProtocols;
/** tags which should be removed if they contain no content (e.g. "<b></b>" or "<b />") **/
private final String[] vRemoveBlanks;
/** entities allowed within html markup **/
private final String[] vAllowedEntities;
/** flag determining whether comments are allowed in input String. */
private final boolean stripComment;
private final boolean encodeQuotes;
private boolean vDebug = false;
/**
* flag determining whether to try to make tags when presented with "unbalanced"
* angle brackets (e.g. "<b text </b>" becomes "<b> text </b>"). If set to false,
* unbalanced angle brackets will be html escaped.
*/
private final boolean alwaysMakeTags;
/** Default constructor.
*
*/
public HTMLFilter() {
vAllowed = new HashMap<>();
final ArrayList<String> a_atts = new ArrayList<String>();
a_atts.add("href");
a_atts.add("target");
vAllowed.put("a", a_atts);
final ArrayList<String> img_atts = new ArrayList<String>();
img_atts.add("src");
img_atts.add("width");
img_atts.add("height");
img_atts.add("alt");
vAllowed.put("img", img_atts);
final ArrayList<String> no_atts = new ArrayList<String>();
vAllowed.put("b", no_atts);
vAllowed.put("strong", no_atts);
vAllowed.put("i", no_atts);
vAllowed.put("em", no_atts);
vSelfClosingTags = new String[]{"img"};
vNeedClosingTags = new String[]{"a", "b", "strong", "i", "em"};
vDisallowed = new String[]{};
vAllowedProtocols = new String[]{"http", "mailto", "https"}; // no ftp.
vProtocolAtts = new String[]{"src", "href"};
vRemoveBlanks = new String[]{"a", "b", "strong", "i", "em"};
vAllowedEntities = new String[]{"amp", "gt", "lt", "quot"};
stripComment = true;
encodeQuotes = true;
alwaysMakeTags = true;
}
/** Set debug flag to true. Otherwise use default settings. See the default constructor.
*
* @param debug turn debug on with a true argument
*/
public HTMLFilter(final boolean debug) {
this();
vDebug = debug;
}
/** Map-parameter configurable constructor.
*
* @param conf map containing configuration. keys match field names.
*/
public HTMLFilter(final Map<String,Object> conf) {
assert conf.containsKey("vAllowed") : "configuration requires vAllowed";
assert conf.containsKey("vSelfClosingTags") : "configuration requires vSelfClosingTags";
assert conf.containsKey("vNeedClosingTags") : "configuration requires vNeedClosingTags";
assert conf.containsKey("vDisallowed") : "configuration requires vDisallowed";
assert conf.containsKey("vAllowedProtocols") : "configuration requires vAllowedProtocols";
assert conf.containsKey("vProtocolAtts") : "configuration requires vProtocolAtts";
assert conf.containsKey("vRemoveBlanks") : "configuration requires vRemoveBlanks";
assert conf.containsKey("vAllowedEntities") : "configuration requires vAllowedEntities";
vAllowed = Collections.unmodifiableMap((HashMap<String, List<String>>) conf.get("vAllowed"));
vSelfClosingTags = (String[]) conf.get("vSelfClosingTags");
vNeedClosingTags = (String[]) conf.get("vNeedClosingTags");
vDisallowed = (String[]) conf.get("vDisallowed");
vAllowedProtocols = (String[]) conf.get("vAllowedProtocols");
vProtocolAtts = (String[]) conf.get("vProtocolAtts");
vRemoveBlanks = (String[]) conf.get("vRemoveBlanks");
vAllowedEntities = (String[]) conf.get("vAllowedEntities");
stripComment = conf.containsKey("stripComment") ? (Boolean) conf.get("stripComment") : true;
encodeQuotes = conf.containsKey("encodeQuotes") ? (Boolean) conf.get("encodeQuotes") : true;
alwaysMakeTags = conf.containsKey("alwaysMakeTags") ? (Boolean) conf.get("alwaysMakeTags") : true;
}
private void reset() {
vTagCounts.clear();
}
private void debug(final String msg) {
if (vDebug) {
Logger.getAnonymousLogger().info(msg);
}
}
//---------------------------------------------------------------
// my versions of some PHP library functions
public static String chr(final int decimal) {
return String.valueOf((char) decimal);
}
public static String htmlSpecialChars(final String s) {
String result = s;
result = regexReplace(P_AMP, "&", result);
result = regexReplace(P_QUOTE, """, result);
result = regexReplace(P_LEFT_ARROW, "<", result);
result = regexReplace(P_RIGHT_ARROW, ">", result);
return result;
}
//---------------------------------------------------------------
/**
* given a user submitted input String, filter out any invalid or restricted
* html.
*
* @param input text (i.e. submitted by a user) than may contain html
* @return "clean" version of input, with only valid, whitelisted html elements allowed
*/
public String filter(final String input) {
reset();
String s = input;
debug("************************************************");
debug(" INPUT: " + input);
s = escapeComments(s);
debug(" escapeComments: " + s);
s = balanceHTML(s);
debug(" balanceHTML: " + s);
s = checkTags(s);
debug(" checkTags: " + s);
s = processRemoveBlanks(s);
debug("processRemoveBlanks: " + s);
s = validateEntities(s);
debug(" validateEntites: " + s);
debug("************************************************\n\n");
return s;
}
public boolean isAlwaysMakeTags(){
return alwaysMakeTags;
}
public boolean isStripComments(){
return stripComment;
}
private String escapeComments(final String s) {
final Matcher m = P_COMMENTS.matcher(s);
final StringBuffer buf = new StringBuffer();
if (m.find()) {
final String match = m.group(1); //(.*?)
m.appendReplacement(buf, Matcher.quoteReplacement("<!--" + htmlSpecialChars(match) + "-->"));
}
m.appendTail(buf);
return buf.toString();
}
private String balanceHTML(String s) {
if (alwaysMakeTags) {
//
// try and form html
//
s = regexReplace(P_END_ARROW, "", s);
s = regexReplace(P_BODY_TO_END, "<$1>", s);
s = regexReplace(P_XML_CONTENT, "$1<$2", s);
} else {
//
// escape stray brackets
//
s = regexReplace(P_STRAY_LEFT_ARROW, "<$1", s);
s = regexReplace(P_STRAY_RIGHT_ARROW, "$1$2><", s);
//
// the last regexp causes '<>' entities to appear
// (we need to do a lookahead assertion so that the last bracket can
// be used in the next pass of the regexp)
//
s = regexReplace(P_BOTH_ARROWS, "", s);
}
return s;
}
private String checkTags(String s) {
Matcher m = P_TAGS.matcher(s);
final StringBuffer buf = new StringBuffer();
while (m.find()) {
String replaceStr = m.group(1);
replaceStr = processTag(replaceStr);
m.appendReplacement(buf, Matcher.quoteReplacement(replaceStr));
}
m.appendTail(buf);
s = buf.toString();
// these get tallied in processTag
// (remember to reset before subsequent calls to filter method)
for (String key : vTagCounts.keySet()) {
for (int ii = 0; ii < vTagCounts.get(key); ii++) {
s += "</" + key + ">";
}
}
return s;
}
private String processRemoveBlanks(final String s) {
String result = s;
for (String tag : vRemoveBlanks) {
if(!P_REMOVE_PAIR_BLANKS.containsKey(tag)){
P_REMOVE_PAIR_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?></" + tag + ">"));
}
result = regexReplace(P_REMOVE_PAIR_BLANKS.get(tag), "", result);
if(!P_REMOVE_SELF_BLANKS.containsKey(tag)){
P_REMOVE_SELF_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?/>"));
}
result = regexReplace(P_REMOVE_SELF_BLANKS.get(tag), "", result);
}
return result;
}
private static String regexReplace(final Pattern regex_pattern, final String replacement, final String s) {
Matcher m = regex_pattern.matcher(s);
return m.replaceAll(replacement);
}
private String processTag(final String s) {
// ending tags
Matcher m = P_END_TAG.matcher(s);
if (m.find()) {
final String name = m.group(1).toLowerCase();
if (allowed(name)) {
if (!inArray(name, vSelfClosingTags)) {
if (vTagCounts.containsKey(name)) {
vTagCounts.put(name, vTagCounts.get(name) - 1);
return "</" + name + ">";
}
}
}
}
// starting tags
m = P_START_TAG.matcher(s);
if (m.find()) {
final String name = m.group(1).toLowerCase();
final String body = m.group(2);
String ending = m.group(3);
//debug( "in a starting tag, name='" + name + "'; body='" + body + "'; ending='" + ending + "'" );
if (allowed(name)) {
String params = "";
final Matcher m2 = P_QUOTED_ATTRIBUTES.matcher(body);
final Matcher m3 = P_UNQUOTED_ATTRIBUTES.matcher(body);
final List<String> paramNames = new ArrayList<String>();
final List<String> paramValues = new ArrayList<String>();
while (m2.find()) {
paramNames.add(m2.group(1)); //([a-z0-9]+)
paramValues.add(m2.group(3)); //(.*?)
}
while (m3.find()) {
paramNames.add(m3.group(1)); //([a-z0-9]+)
paramValues.add(m3.group(3)); //([^\"\\s']+)
}
String paramName, paramValue;
for (int ii = 0; ii < paramNames.size(); ii++) {
paramName = paramNames.get(ii).toLowerCase();
paramValue = paramValues.get(ii);
// debug( "paramName='" + paramName + "'" );
// debug( "paramValue='" + paramValue + "'" );
// debug( "allowed? " + vAllowed.get( name ).contains( paramName ) );
if (allowedAttribute(name, paramName)) {
if (inArray(paramName, vProtocolAtts)) {
paramValue = processParamProtocol(paramValue);
}
params += " " + paramName + "=\"" + paramValue + "\"";
}
}
if (inArray(name, vSelfClosingTags)) {
ending = " /";
}
if (inArray(name, vNeedClosingTags)) {
ending = "";
}
if (ending == null || ending.length() < 1) {
if (vTagCounts.containsKey(name)) {
vTagCounts.put(name, vTagCounts.get(name) + 1);
} else {
vTagCounts.put(name, 1);
}
} else {
ending = " /";
}
return "<" + name + params + ending + ">";
} else {
return "";
}
}
// comments
m = P_COMMENT.matcher(s);
if (!stripComment && m.find()) {
return "<" + m.group() + ">";
}
return "";
}
private String processParamProtocol(String s) {
s = decodeEntities(s);
final Matcher m = P_PROTOCOL.matcher(s);
if (m.find()) {
final String protocol = m.group(1);
if (!inArray(protocol, vAllowedProtocols)) {
// bad protocol, turn into local anchor link instead
s = "#" + s.substring(protocol.length() + 1, s.length());
if (s.startsWith("#//")) {
s = "#" + s.substring(3, s.length());
}
}
}
return s;
}
private String decodeEntities(String s) {
StringBuffer buf = new StringBuffer();
Matcher m = P_ENTITY.matcher(s);
while (m.find()) {
final String match = m.group(1);
final int decimal = Integer.decode(match).intValue();
m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal)));
}
m.appendTail(buf);
s = buf.toString();
buf = new StringBuffer();
m = P_ENTITY_UNICODE.matcher(s);
while (m.find()) {
final String match = m.group(1);
final int decimal = Integer.valueOf(match, 16).intValue();
m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal)));
}
m.appendTail(buf);
s = buf.toString();
buf = new StringBuffer();
m = P_ENCODE.matcher(s);
while (m.find()) {
final String match = m.group(1);
final int decimal = Integer.valueOf(match, 16).intValue();
m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal)));
}
m.appendTail(buf);
s = buf.toString();
s = validateEntities(s);
return s;
}
private String validateEntities(final String s) {
StringBuffer buf = new StringBuffer();
// validate entities throughout the string
Matcher m = P_VALID_ENTITIES.matcher(s);
while (m.find()) {
final String one = m.group(1); //([^&;]*)
final String two = m.group(2); //(?=(;|&|$))
m.appendReplacement(buf, Matcher.quoteReplacement(checkEntity(one, two)));
}
m.appendTail(buf);
return encodeQuotes(buf.toString());
}
private String encodeQuotes(final String s){
if(encodeQuotes){
StringBuffer buf = new StringBuffer();
Matcher m = P_VALID_QUOTES.matcher(s);
while (m.find()) {
final String one = m.group(1); //(>|^)
final String two = m.group(2); //([^<]+?)
final String three = m.group(3); //(<|$)
m.appendReplacement(buf, Matcher.quoteReplacement(one + regexReplace(P_QUOTE, """, two) + three));
}
m.appendTail(buf);
return buf.toString();
}else{
return s;
}
}
private String checkEntity(final String preamble, final String term) {
return ";".equals(term) && isValidEntity(preamble)
? '&' + preamble
: "&" + preamble;
}
private boolean isValidEntity(final String entity) {
return inArray(entity, vAllowedEntities);
}
private static boolean inArray(final String s, final String[] array) {
for (String item : array) {
if (item != null && item.equals(s)) {
return true;
}
}
return false;
}
private boolean allowed(final String name) {
return (vAllowed.isEmpty() || vAllowed.containsKey(name)) && !inArray(name, vDisallowed);
}
private boolean allowedAttribute(final String name, final String paramName) {
return allowed(name) && (vAllowed.isEmpty() || vAllowed.get(name).contains(paramName));
}
}
/**
* SQL过滤
*/
public class SQLFilter {
/**
* SQL注入过滤
* @param str 待验证的字符串
*/
public static String sqlInject(String str){
if(StringUtils.isBlank(str)){
return null;
}
//去掉'|"|;|\字符
str = StringUtils.replace(str, "'", "");
str = StringUtils.replace(str, "\"", "");
str = StringUtils.replace(str, ";", "");
str = StringUtils.replace(str, "\\", "");
//转换成小写
str = str.toLowerCase();
//非法字符
String[] keywords = {"master", "truncate", "insert", "select", "delete", "update", "declare", "alert", "drop"};
//判断是否包含非法字符
for(String keyword : keywords){
if(str.indexOf(keyword) != -1){
throw new RRException("包含非法字符");
}
}
return str;
}
}
操作日志记录
通过AOP方式进行方法调用记录,推荐使用MongoDB存储日志数据
(1)实体类
@Data
@ToString
@Document(collection = "sys_log")
public class SysLogEntity implements Serializable {
private static final long serialVersionUID = 1L;
@Id
private Long id;
//用户名
private String username;
//用户操作
private String operation;
//请求方法
private String method;
//请求参数
private String params;
//执行时长(毫秒)
private Long time;
//IP地址
private String ip;
//创建时间
private Date createDate;
}
(2)注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SysLog {
String value() default "";
}
(3)核心代码
@Aspect
@Component
public class SysLogAspect {
@Autowired
private SysLogService sysLogService;
@Pointcut("@annotation(org.plxc.common.annotation.SysLog)")
public void logPointCut() {
}
@Around("logPointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
long beginTime = System.currentTimeMillis();
//执行方法
Object result = point.proceed();
//执行时长(毫秒)
long time = System.currentTimeMillis() - beginTime;
//保存日志
saveSysLog(point, time);
return result;
}
private void saveSysLog(ProceedingJoinPoint joinPoint, long time) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
SysLogEntity sysLog = new SysLogEntity();
SysLog syslog = method.getAnnotation(SysLog.class);
if(syslog != null){
//注解上的描述
sysLog.setOperation(syslog.value());
}
//请求的方法名
String className = joinPoint.getTarget().getClass().getName();
String methodName = signature.getName();
sysLog.setMethod(className + "." + methodName + "()");
//请求的参数
Object[] args = joinPoint.getArgs();
try{
String params = new Gson().toJson(args);
sysLog.setParams(params);
}catch (Exception e){
}
//获取request
HttpServletRequest request = HttpContextUtils.getHttpServletRequest();
//设置IP地址
sysLog.setIp(IPUtils.getIpAddr(request));
//用户名
String username = ((SysUserEntity) SecurityUtils.getSubject().getPrincipal()).getUsername();
sysLog.setUsername(username);
sysLog.setTime(time);
sysLog.setCreateDate(new Date());
//保存系统日志
sysLogService.save(sysLog);
}
}
(4)一般在Controller方法上添加注解,如@SysLog(“保存配置”)
时间工具类
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.9.9</version>
</dependency>
代码
public class DateUtils {
/** 时间格式(yyyy-MM-dd) */
public final static String DATE_PATTERN = "yyyy-MM-dd";
/** 时间格式(yyyy-MM-dd HH:mm:ss) */
public final static String DATE_TIME_PATTERN = "yyyy-MM-dd HH:mm:ss";
/** 时间格式(HH:mm:ss) */
public final static String TIME_PATTERN = "HH:mm:ss";
/**
* 日期格式化 日期格式为:yyyy-MM-dd
* @param date 日期
* @return 返回yyyy-MM-dd格式日期
*/
public static String formatDate(Date date) {
return format(date, DATE_PATTERN);
}
/**
* 日期格式化 日期格式为:yyyy-MM-dd HH:mm:ss
* @param date
* @return
*/
public static String formatDateTime(Date date) {
return format(date,DATE_TIME_PATTERN);
}
/**
* 日期格式化 日期格式为:HH:mm:ss
* @param date
* @return
*/
public static String formatTime(Date date) {
return format(date,TIME_PATTERN);
}
/**
* 日期格式化 日期格式为:yyyy-MM-dd
* @param date 日期
* @param pattern 格式,如:DateUtils.DATE_TIME_PATTERN
* @return 返回yyyy-MM-dd格式日期
*/
public static String format(Date date, String pattern) {
if(date != null){
SimpleDateFormat df = new SimpleDateFormat(pattern);
return df.format(date);
}
return null;
}
/**
* 字符串转换成日期
* @param strDate 日期字符串
* @param pattern 日期的格式,如:DateUtils.DATE_TIME_PATTERN
*/
public static Date stringToDate(String strDate, String pattern) {
if (StringUtils.isBlank(strDate)){
return null;
}
DateTimeFormatter fmt = DateTimeFormat.forPattern(pattern);
return fmt.parseLocalDateTime(strDate).toDate();
}
/**
* 根据周数,获取开始日期、结束日期
* @param week 周期 0本周,-1上周,-2上上周,1下周,2下下周
* @return 返回date[0]开始日期、date[1]结束日期
*/
public static Date[] getWeekStartAndEnd(int week) {
DateTime dateTime = new DateTime();
LocalDate date = new LocalDate(dateTime.plusWeeks(week));
date = date.dayOfWeek().withMinimumValue();
Date beginDate = date.toDate();
Date endDate = date.plusDays(6).toDate();
return new Date[]{beginDate, endDate};
}
/**
* 对日期的【秒】进行加/减
*
* @param date 日期
* @param seconds 秒数,负数为减
* @return 加/减几秒后的日期
*/
public static Date addDateSeconds(Date date, int seconds) {
DateTime dateTime = new DateTime(date);
return dateTime.plusSeconds(seconds).toDate();
}
/**
* 对日期的【分钟】进行加/减
*
* @param date 日期
* @param minutes 分钟数,负数为减
* @return 加/减几分钟后的日期
*/
public static Date addDateMinutes(Date date, int minutes) {
DateTime dateTime = new DateTime(date);
return dateTime.plusMinutes(minutes).toDate();
}
/**
* 对日期的【小时】进行加/减
*
* @param date 日期
* @param hours 小时数,负数为减
* @return 加/减几小时后的日期
*/
public static Date addDateHours(Date date, int hours) {
DateTime dateTime = new DateTime(date);
return dateTime.plusHours(hours).toDate();
}
/**
* 对日期的【天】进行加/减
*
* @param date 日期
* @param days 天数,负数为减
* @return 加/减几天后的日期
*/
public static Date addDateDays(Date date, int days) {
DateTime dateTime = new DateTime(date);
return dateTime.plusDays(days).toDate();
}
/**
* 对日期的【周】进行加/减
*
* @param date 日期
* @param weeks 周数,负数为减
* @return 加/减几周后的日期
*/
public static Date addDateWeeks(Date date, int weeks) {
DateTime dateTime = new DateTime(date);
return dateTime.plusWeeks(weeks).toDate();
}
/**
* 对日期的【月】进行加/减
*
* @param date 日期
* @param months 月数,负数为减
* @return 加/减几月后的日期
*/
public static Date addDateMonths(Date date, int months) {
DateTime dateTime = new DateTime(date);
return dateTime.plusMonths(months).toDate();
}
/**
* 对日期的【年】进行加/减
*
* @param date 日期
* @param years 年数,负数为减
* @return 加/减几年后的日期
*/
public static Date addDateYears(Date date, int years) {
DateTime dateTime = new DateTime(date);
return dateTime.plusYears(years).toDate();
}
public static void main(String[] args) {
System.err.println(formatTime(new Date()));
}
}