diff --git a/README.md b/README.md index f6602d96..29f2e05d 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ - 使用renren-security搭建项目,只需编写30%左右代码,其余的代码交给系统自动生成 - 一个月的工作量,一周就能完成,剩余的时间可以陪家人、朋友、撩妹、钓凯子等,从此踏入高富帅、白富美行业 - 也是接私活的利器,能快速完成项目并交付,轻松赚取外快,实现财务自由,走向人生巅峰(接私活赚了钱,可以给作者打赏点辛苦费,让作者更有动力持续优化、完善) - + **具有如下特点** diff --git a/pom.xml b/pom.xml index da93e3da..5b1f0247 100644 --- a/pom.xml +++ b/pom.xml @@ -292,7 +292,7 @@ org.apache.tomcat.maven tomcat7-maven-plugin - 2.1 + 2.2 / UTF-8 diff --git a/renren-api/src/main/java/io/renren/interceptor/AuthorizationInterceptor.java b/renren-api/src/main/java/io/renren/interceptor/AuthorizationInterceptor.java index c8da0904..db9ac144 100644 --- a/renren-api/src/main/java/io/renren/interceptor/AuthorizationInterceptor.java +++ b/renren-api/src/main/java/io/renren/interceptor/AuthorizationInterceptor.java @@ -40,8 +40,12 @@ public boolean preHandle(HttpServletRequest request, HttpServletResponse respons return true; } - //获取token - String token = request.getParameter("token"); + //从header中获取token + String token = request.getHeader("token"); + //如果header中不存在token,则从参数中获取token + if(StringUtils.isBlank(token)){ + token = request.getParameter("token"); + } //token为空 if(StringUtils.isBlank(token)){ diff --git a/renren-api/src/main/resources/renren-api.xml b/renren-api/src/main/resources/renren-api.xml index 19a73862..c081c800 100644 --- a/renren-api/src/main/resources/renren-api.xml +++ b/renren-api/src/main/resources/renren-api.xml @@ -26,6 +26,8 @@ WriteMapNullValue QuoteFieldNames WriteDateUseDateFormat + + DisableCircularReferenceDetect diff --git a/renren-common/pom.xml b/renren-common/pom.xml index 0763aaf3..426961e3 100644 --- a/renren-common/pom.xml +++ b/renren-common/pom.xml @@ -12,7 +12,7 @@ [7.2.0, 7.2.99] 2.5.0 - 3.3 + 4.4 diff --git a/renren-common/src/main/java/io/renren/oss/CloudStorageConfig.java b/renren-common/src/main/java/io/renren/oss/CloudStorageConfig.java index 1ee5870f..1365354e 100644 --- a/renren-common/src/main/java/io/renren/oss/CloudStorageConfig.java +++ b/renren-common/src/main/java/io/renren/oss/CloudStorageConfig.java @@ -76,6 +76,9 @@ public class CloudStorageConfig implements Serializable { //腾讯云BucketName @NotBlank(message="腾讯云BucketName不能为空", groups = QcloudGroup.class) private String qcloudBucketName; + //腾讯云COS所属地区 + @NotBlank(message="所属地区不能为空", groups = QcloudGroup.class) + private String qcloudRegion; public Integer getType() { return type; @@ -220,4 +223,12 @@ public String getQcloudBucketName() { public void setQcloudBucketName(String qcloudBucketName) { this.qcloudBucketName = qcloudBucketName; } + + public String getQcloudRegion() { + return qcloudRegion; + } + + public void setQcloudRegion(String qcloudRegion) { + this.qcloudRegion = qcloudRegion; + } } diff --git a/renren-common/src/main/java/io/renren/oss/QcloudCloudStorageService.java b/renren-common/src/main/java/io/renren/oss/QcloudCloudStorageService.java index e7f24255..5d0d7039 100644 --- a/renren-common/src/main/java/io/renren/oss/QcloudCloudStorageService.java +++ b/renren-common/src/main/java/io/renren/oss/QcloudCloudStorageService.java @@ -1,80 +1,81 @@ -package io.renren.oss; - -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.JSONObject; -import com.qcloud.cos.COSClient; -import com.qcloud.cos.request.UploadFileRequest; -import io.renren.utils.RRException; -import org.apache.commons.io.FileUtils; - -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; - -/** - * 腾讯云存储 - * @author chenshun - * @email sunlightcs@gmail.com - * @date 2017-03-26 20:51 - */ -public class QcloudCloudStorageService extends CloudStorageService{ - private COSClient client; - - public QcloudCloudStorageService(CloudStorageConfig config){ - this.config = config; - - //初始化 - init(); - } - - private void init(){ - client = new COSClient(config.getQcloudAppId(), config.getQcloudSecretId(), - config.getQcloudSecretKey()); - } - - @Override - public String upload(byte[] data, String path) { - return this.upload(new ByteArrayInputStream(data), path); - } - - @Override - public String upload(InputStream inputStream, String path) { - String tmp = System.getProperty("java.io.tmpdir"); - File file = new File(tmp + path); - try { - FileUtils.copyInputStreamToFile(inputStream, file); - } catch (IOException e) { - throw new RRException("上传文件失败", e); - } - - //腾讯云必需要以"/"开头 - if(!path.startsWith("/")) { - path = "/" + path; - } - - //上传到腾讯云 - UploadFileRequest request = new UploadFileRequest(config.getQcloudBucketName(), path, file.getPath()); - String response = client.uploadFile(request); - - //删除临时文件 - file.delete(); - - JSONObject jsonObject = JSON.parseObject(response); - if(jsonObject.getIntValue("code") != 0) { - throw new RRException("文件上传失败," + jsonObject.getString("message")); - } - - return config.getQcloudDomain() + path; - } - - @Override - public String upload(byte[] data) { - return upload(data, getPath(config.getQcloudPrefix())); - } - - @Override - public String upload(InputStream inputStream) { - return upload(inputStream, getPath(config.getQcloudPrefix())); - } -} +package io.renren.oss; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.qcloud.cos.COSClient; +import com.qcloud.cos.ClientConfig; +import com.qcloud.cos.request.UploadFileRequest; +import com.qcloud.cos.sign.Credentials; +import io.renren.utils.RRException; +import org.apache.commons.io.IOUtils; + +import java.io.IOException; +import java.io.InputStream; + +/** + * 腾讯云存储 + * @author chenshun + * @email sunlightcs@gmail.com + * @date 2017-03-26 20:51 + */ +public class QcloudCloudStorageService extends CloudStorageService{ + private COSClient client; + + public QcloudCloudStorageService(CloudStorageConfig config){ + this.config = config; + + //初始化 + init(); + } + + private void init(){ + Credentials credentials = new Credentials(config.getQcloudAppId(), config.getQcloudSecretId(), + config.getQcloudSecretKey()); + + //初始化客户端配置 + ClientConfig clientConfig = new ClientConfig(); + //设置bucket所在的区域,华南:gz 华北:tj 华东:sh + clientConfig.setRegion(config.getQcloudRegion()); + + client = new COSClient(clientConfig, credentials); + } + + @Override + public String upload(byte[] data, String path) { + //腾讯云必需要以"/"开头 + if(!path.startsWith("/")) { + path = "/" + path; + } + + //上传到腾讯云 + UploadFileRequest request = new UploadFileRequest(config.getQcloudBucketName(), path, data); + String response = client.uploadFile(request); + + JSONObject jsonObject = JSON.parseObject(response); + if(jsonObject.getIntValue("code") != 0) { + throw new RRException("文件上传失败," + jsonObject.getString("message")); + } + + return config.getQcloudDomain() + path; + } + + @Override + public String upload(InputStream inputStream, String path) { + try { + byte[] data = IOUtils.toByteArray(inputStream); + return this.upload(data, path); + } catch (IOException e) { + throw new RRException("上传文件失败", e); + } + } + + @Override + public String upload(byte[] data) { + return upload(data, getPath(config.getQcloudPrefix())); + } + + @Override + public String upload(InputStream inputStream) { + return upload(inputStream, getPath(config.getQcloudPrefix())); + } +} diff --git a/renren-common/src/main/java/io/renren/oss/QiniuCloudStorageService.java b/renren-common/src/main/java/io/renren/oss/QiniuCloudStorageService.java index 56f4aa98..2dc3da94 100644 --- a/renren-common/src/main/java/io/renren/oss/QiniuCloudStorageService.java +++ b/renren-common/src/main/java/io/renren/oss/QiniuCloudStorageService.java @@ -29,7 +29,7 @@ public QiniuCloudStorageService(CloudStorageConfig config){ } private void init(){ - uploadManager = new UploadManager(new Configuration(Zone.zone0())); + uploadManager = new UploadManager(new Configuration(Zone.autoZone())); token = Auth.create(config.getQiniuAccessKey(), config.getQiniuSecretKey()). uploadToken(config.getQiniuBucketName()); } diff --git a/renren-common/src/main/java/io/renren/service/impl/SysConfigServiceImpl.java b/renren-common/src/main/java/io/renren/service/impl/SysConfigServiceImpl.java index 9f82b12c..0c72d074 100644 --- a/renren-common/src/main/java/io/renren/service/impl/SysConfigServiceImpl.java +++ b/renren-common/src/main/java/io/renren/service/impl/SysConfigServiceImpl.java @@ -71,7 +71,7 @@ public T getConfigObject(String key, Class clazz) { try { return clazz.newInstance(); } catch (Exception e) { - throw new RRException("获取云存储配置信息失败"); + throw new RRException("获取参数失败"); } } } diff --git a/renren-common/src/main/java/io/renren/utils/Constant.java b/renren-common/src/main/java/io/renren/utils/Constant.java index 55687293..70ad5c35 100644 --- a/renren-common/src/main/java/io/renren/utils/Constant.java +++ b/renren-common/src/main/java/io/renren/utils/Constant.java @@ -76,7 +76,7 @@ public int getValue() { */ public enum CloudService { /** - * 阿里云 + * 七牛云 */ QINIU(1), /** diff --git a/renren-common/src/main/java/io/renren/utils/Query.java b/renren-common/src/main/java/io/renren/utils/Query.java index 7efe1ff0..a600d584 100644 --- a/renren-common/src/main/java/io/renren/utils/Query.java +++ b/renren-common/src/main/java/io/renren/utils/Query.java @@ -1,5 +1,7 @@ package io.renren.utils; +import io.renren.xss.SQLFilter; + import java.util.LinkedHashMap; import java.util.Map; @@ -26,6 +28,12 @@ public Query(Map params){ this.put("offset", (page - 1) * limit); this.put("page", page); this.put("limit", limit); + + //防止SQL注入(因为sidx、order是通过拼接SQL实现排序的,会有SQL注入风险) + String sidx = params.get("sidx").toString(); + String order = params.get("order").toString(); + this.put("sidx", SQLFilter.sqlInject(sidx)); + this.put("order", SQLFilter.sqlInject(order)); } diff --git a/renren-common/src/main/java/io/renren/xss/HTMLFilter.java b/renren-common/src/main/java/io/renren/xss/HTMLFilter.java new file mode 100644 index 00000000..bbdfb2ea --- /dev/null +++ b/renren-common/src/main/java/io/renren/xss/HTMLFilter.java @@ -0,0 +1,534 @@ +package io.renren.xss; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +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 P_REMOVE_PAIR_BLANKS = new ConcurrentHashMap(); + private static final ConcurrentMap P_REMOVE_SELF_BLANKS = new ConcurrentHashMap(); + + /** set of allowed html elements, along with allowed attributes for each element **/ + private final Map> vAllowed; + /** counts of open tags for each (allowable) html element **/ + private final Map vTagCounts = new HashMap(); + + /** html elements which must always be self-closing (e.g. "") **/ + private final String[] vSelfClosingTags; + /** html elements which must always have separate opening and closing tags (e.g. "") **/ + 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. "" or "") **/ + 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. "" becomes " text "). If set to false, + * unbalanced angle brackets will be html escaped. + */ + private final boolean alwaysMakeTags; + + /** Default constructor. + * + */ + public HTMLFilter() { + vAllowed = new HashMap<>(); + + final ArrayList a_atts = new ArrayList(); + a_atts.add("href"); + a_atts.add("target"); + vAllowed.put("a", a_atts); + + final ArrayList img_atts = new ArrayList(); + img_atts.add("src"); + img_atts.add("width"); + img_atts.add("height"); + img_atts.add("alt"); + vAllowed.put("img", img_atts); + + final ArrayList no_atts = new ArrayList(); + 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 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>) 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("")); + } + 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 += ""; + } + } + + 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[^>]*)?>")); + } + 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 ""; + } + } + } + } + + // 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 paramNames = new ArrayList(); + final List paramValues = new ArrayList(); + 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)); + } +} \ No newline at end of file diff --git a/renren-common/src/main/java/io/renren/xss/SQLFilter.java b/renren-common/src/main/java/io/renren/xss/SQLFilter.java new file mode 100644 index 00000000..86972a0f --- /dev/null +++ b/renren-common/src/main/java/io/renren/xss/SQLFilter.java @@ -0,0 +1,43 @@ +package io.renren.xss; + +import io.renren.utils.RRException; +import org.apache.commons.lang.StringUtils; + +/** + * SQL过滤 + * @author chenshun + * @email sunlightcs@gmail.com + * @date 2017-04-01 16:16 + */ +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", "create", "drop"}; + + //判断是否包含非法字符 + for(String keyword : keywords){ + if(str.indexOf(keyword) != -1){ + throw new RRException("包含非法字符"); + } + } + + return str; + } +} diff --git a/renren-common/src/main/java/io/renren/xss/XssFilter.java b/renren-common/src/main/java/io/renren/xss/XssFilter.java new file mode 100644 index 00000000..783ebafa --- /dev/null +++ b/renren-common/src/main/java/io/renren/xss/XssFilter.java @@ -0,0 +1,30 @@ +package io.renren.xss; + +import javax.servlet.*; +import javax.servlet.http.HttpServletRequest; +import java.io.IOException; + +/** + * XSS过滤 + * @author chenshun + * @email sunlightcs@gmail.com + * @date 2017-04-01 10:20 + */ +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() { + } + +} \ No newline at end of file diff --git a/renren-common/src/main/java/io/renren/xss/XssHttpServletRequestWrapper.java b/renren-common/src/main/java/io/renren/xss/XssHttpServletRequestWrapper.java new file mode 100644 index 00000000..0d03e9f5 --- /dev/null +++ b/renren-common/src/main/java/io/renren/xss/XssHttpServletRequestWrapper.java @@ -0,0 +1,94 @@ +package io.renren.xss; + +import org.apache.commons.lang.StringUtils; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletRequestWrapper; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * XSS过滤处理 + * @author chenshun + * @email sunlightcs@gmail.com + * @date 2017-04-01 11:29 + */ +public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper { + //没被包装过的HttpServletRequest(特殊场景,需求自己过滤) + HttpServletRequest orgRequest; + //html过滤 + private final static HTMLFilter htmlFilter = new HTMLFilter(); + + public XssHttpServletRequestWrapper(HttpServletRequest request) { + super(request); + orgRequest = request; + } + + @Override + public String getParameter(String name) { + String value = super.getParameter(xssEncode(name)); + if (StringUtils.isNotBlank(value)) { + value = xssEncode(value); + } + return value; + } + + @Override + public String[] getParameterValues(String name) { + String[] parameters = super.getParameterValues(name); + if (parameters == null || parameters.length == 0) { + return null; + } + + for (int i = 0; i < parameters.length; i++) { + parameters[i] = xssEncode(parameters[i]); + } + return parameters; + } + + @Override + public Map getParameterMap() { + Map map = new LinkedHashMap<>(); + Map parameters = super.getParameterMap(); + for (String key : parameters.keySet()) { + String[] values = parameters.get(key); + for (int i = 0; i < values.length; i++) { + values[i] = xssEncode(values[i]); + } + map.put(key, values); + } + return map; + } + + @Override + public String getHeader(String name) { + String value = super.getHeader(xssEncode(name)); + if (StringUtils.isNotBlank(value)) { + value = xssEncode(value); + } + return value; + } + + private String xssEncode(String input) { + return htmlFilter.filter(input); + } + + /** + * 获取最原始的request + */ + public HttpServletRequest getOrgRequest() { + return orgRequest; + } + + /** + * 获取最原始的request + */ + public static HttpServletRequest getOrgRequest(HttpServletRequest request) { + if (request instanceof XssHttpServletRequestWrapper) { + return ((XssHttpServletRequestWrapper) request).getOrgRequest(); + } + + return request; + } + +} diff --git a/renren-gen/src/main/java/io/renren/controller/SysGeneratorController.java b/renren-gen/src/main/java/io/renren/controller/SysGeneratorController.java index 7327c91a..2a5bf6b0 100644 --- a/renren-gen/src/main/java/io/renren/controller/SysGeneratorController.java +++ b/renren-gen/src/main/java/io/renren/controller/SysGeneratorController.java @@ -1,11 +1,11 @@ package io.renren.controller; import com.alibaba.fastjson.JSON; -import io.renren.annotation.SysLog; import io.renren.service.SysGeneratorService; import io.renren.utils.PageUtils; import io.renren.utils.Query; import io.renren.utils.R; +import io.renren.xss.XssHttpServletRequestWrapper; import org.apache.commons.io.IOUtils; import org.apache.shiro.authz.annotation.RequiresPermissions; import org.springframework.beans.factory.annotation.Autowired; @@ -14,6 +14,7 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; +import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.List; @@ -52,11 +53,13 @@ public R list(@RequestParam Map params){ /** * 生成代码 */ - @SysLog("生成代码") @RequestMapping("/code") @RequiresPermissions("sys:generator:code") - public void code(String tables, HttpServletResponse response) throws IOException{ + public void code(HttpServletRequest request, HttpServletResponse response) throws IOException{ String[] tableNames = new String[]{}; + //获取表名,不进行xss过滤 + HttpServletRequest orgRequest = XssHttpServletRequestWrapper.getOrgRequest(request); + String tables = orgRequest.getParameter("tables"); tableNames = JSON.parseArray(tables).toArray(tableNames); byte[] data = sysGeneratorService.generatorCode(tableNames); diff --git a/renren-gen/src/main/resources/template/Controller.java.vm b/renren-gen/src/main/resources/template/Controller.java.vm index 714fd4f8..0c1b96c3 100644 --- a/renren-gen/src/main/resources/template/Controller.java.vm +++ b/renren-gen/src/main/resources/template/Controller.java.vm @@ -1,6 +1,5 @@ package ${package}.controller; -import java.util.HashMap; import java.util.List; import java.util.Map; @@ -9,13 +8,14 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.ResponseBody; -import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; import ${package}.entity.${className}Entity; import ${package}.service.${className}Service; -import io.renren.utils.PageUtils; -import io.renren.utils.R; +import ${package}.utils.PageUtils; +import ${package}.utils.Query; +import ${package}.utils.R; /** @@ -25,7 +25,7 @@ import io.renren.utils.R; * @email ${email} * @date ${datetime} */ -@Controller +@RestController @RequestMapping("${pathName}") public class ${className}Controller { @Autowired @@ -34,19 +34,16 @@ public class ${className}Controller { /** * 列表 */ - @ResponseBody @RequestMapping("/list") @RequiresPermissions("${pathName}:list") - public R list(Integer page, Integer limit){ - Map map = new HashMap<>(); - map.put("offset", (page - 1) * limit); - map.put("limit", limit); - + public R list(@RequestParam Map params){ //查询列表数据 - List<${className}Entity> ${classname}List = ${classname}Service.queryList(map); - int total = ${classname}Service.queryTotal(map); + Query query = new Query(params); + + List<${className}Entity> ${classname}List = ${classname}Service.queryList(query); + int total = ${classname}Service.queryTotal(query); - PageUtils pageUtil = new PageUtils(${classname}List, total, limit, page); + PageUtils pageUtil = new PageUtils(${classname}List, total, query.getLimit(), query.getPage()); return R.ok().put("page", pageUtil); } @@ -55,7 +52,6 @@ public class ${className}Controller { /** * 信息 */ - @ResponseBody @RequestMapping("/info/{${pk.attrname}}") @RequiresPermissions("${pathName}:info") public R info(@PathVariable("${pk.attrname}") ${pk.attrType} ${pk.attrname}){ @@ -67,7 +63,6 @@ public class ${className}Controller { /** * 保存 */ - @ResponseBody @RequestMapping("/save") @RequiresPermissions("${pathName}:save") public R save(@RequestBody ${className}Entity ${classname}){ @@ -79,7 +74,6 @@ public class ${className}Controller { /** * 修改 */ - @ResponseBody @RequestMapping("/update") @RequiresPermissions("${pathName}:update") public R update(@RequestBody ${className}Entity ${classname}){ @@ -91,7 +85,6 @@ public class ${className}Controller { /** * 删除 */ - @ResponseBody @RequestMapping("/delete") @RequiresPermissions("${pathName}:delete") public R delete(@RequestBody ${pk.attrType}[] ${pk.attrname}s){ diff --git a/renren-gen/src/main/resources/template/list.js.vm b/renren-gen/src/main/resources/template/list.js.vm index 0595247a..07b6574f 100644 --- a/renren-gen/src/main/resources/template/list.js.vm +++ b/renren-gen/src/main/resources/template/list.js.vm @@ -5,9 +5,9 @@ $(function () { colModel: [ #foreach($column in $columns) #if($column.columnName == $pk.columnName) - { label: '${column.attrname}', name: '${column.attrname}', index: '$${column.columnName}', width: 50, key: true }, + { label: '${column.attrname}', name: '${column.attrname}', index: '${column.columnName}', width: 50, key: true }, #else - { label: '${column.comments}', name: '${column.attrname}', index: '$${column.columnName}', width: 80 }#if($velocityCount != $columns.size()), #end + { label: '${column.comments}', name: '${column.attrname}', index: '${column.columnName}', width: 80 }#if($velocityCount != $columns.size()), #end #end #end @@ -66,7 +66,17 @@ var vm = new Vue({ vm.getInfo(${pk.attrname}) }, saveOrUpdate: function (event) { - var url = vm.${classname}.${pk.attrname} == null ? "../${pathName}/save" : "../${pathName}/update"; + if(vm.title == "新增") + { + url = "../${pathName}/save"; + } + else if(vm.title == "修改") + { + url = "../${pathName}/update"; + }else + { + url = ""; + } $.ajax({ type: "POST", url: url, diff --git a/renren-web/src/main/java/io/renren/task/TestTask.java b/renren-web/src/main/java/io/renren/task/TestTask.java new file mode 100644 index 00000000..16cb561a --- /dev/null +++ b/renren-web/src/main/java/io/renren/task/TestTask.java @@ -0,0 +1,46 @@ +package io.renren.task; + +import io.renren.entity.SysUserEntity; +import io.renren.service.SysUserService; + +import org.apache.commons.lang.builder.ToStringBuilder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * 测试定时任务(演示Demo,可删除) + * + * testTask为spring bean的名称 + * + * @author chenshun + * @email sunlightcs@gmail.com + * @date 2016年11月30日 下午1:34:24 + */ +@Component("testTask") +public class TestTask { + private Logger logger = LoggerFactory.getLogger(getClass()); + + @Autowired + private SysUserService sysUserService; + + public void test(String params){ + logger.info("我是带参数的test方法,正在被执行,参数为:" + params); + + try { + Thread.sleep(1000L); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + SysUserEntity user = sysUserService.queryObject(1L); + System.out.println(ToStringBuilder.reflectionToString(user)); + + } + + + public void test2(){ + logger.info("我是不带参数的test2方法,正在被执行"); + } +} diff --git a/renren-web/src/main/resources/spring-mvc.xml b/renren-web/src/main/resources/spring-mvc.xml index 0a1ac84c..cfe1c910 100644 --- a/renren-web/src/main/resources/spring-mvc.xml +++ b/renren-web/src/main/resources/spring-mvc.xml @@ -31,6 +31,8 @@ WriteMapNullValue QuoteFieldNames WriteDateUseDateFormat + + DisableCircularReferenceDetect diff --git a/renren-web/src/main/webapp/WEB-INF/page/sys/oss.html b/renren-web/src/main/webapp/WEB-INF/page/sys/oss.html index d30ce357..4f302ff5 100644 --- a/renren-web/src/main/webapp/WEB-INF/page/sys/oss.html +++ b/renren-web/src/main/webapp/WEB-INF/page/sys/oss.html @@ -35,7 +35,7 @@ +
+
Bucket所属地区
+
+ +
+
diff --git a/renren-web/src/main/webapp/WEB-INF/web.xml b/renren-web/src/main/webapp/WEB-INF/web.xml index 71023d69..ecefdac1 100644 --- a/renren-web/src/main/webapp/WEB-INF/web.xml +++ b/renren-web/src/main/webapp/WEB-INF/web.xml @@ -63,6 +63,16 @@ shiroFilter /* + + + xssFilter + io.renren.xss.XssFilter + + + + xssFilter + /* + dispatcher