/** * Copyright (c) 2015,TravelSky. * All Rights Reserved. * TravelSky CONFIDENTIAL * * Project Name:ShoppinguiOnlineScript * Package Name:com.travelsky.shoppingui.utils * File Name:JavaScriptUtil.java * Date:2015年6月8日 下午2:35:12 * */ package com.ruoyi.web.controller.tool; import org.apache.commons.lang3.RandomStringUtils; import org.apache.commons.lang3.RandomUtils; import java.util.Formatter; import java.util.HashMap; import java.util.Map; import java.util.Properties; import java.util.StringTokenizer; /** * JavaScript工具类。 * * @author Michael J Chane * @version $Revision: 1.2 $ $Date: 2009/09/10 15:08:30 $ */ public final class JavaScriptUtil { /** * 特殊字符 */ private static final String SPECIAL_CHARACTERS = "\n\r\f\'\"\\"; /** * 特殊字符-转义字符的映射 */ private static final Properties ESCAPE_MAP = new Properties(); static { ESCAPE_MAP.put("\n", "\\n"); ESCAPE_MAP.put("\r", "\\r"); ESCAPE_MAP.put("\f", "\\f"); ESCAPE_MAP.put("\'", "\\\'"); ESCAPE_MAP.put("\"", "\\\""); ESCAPE_MAP.put("\\", "\\\\"); } /** * 不需要实例化 */ private JavaScriptUtil() { } /** * 对JavaScript字符串内部的文本进行转义操作,避免字符串内的特殊字符影响JavaScript代码的执行。 * * @param text * 要转义的字符串文本 * @return 转义后的字符串文本 */ public static String escapeInStringLiterals(CharSequence text) { // 对null返回空白字符串 if (text == null) { return ""; } // 字符串缓冲 StringBuilder buff = new StringBuilder(text.length() + 16); // 以特殊字符为分隔符,将文本分段(含特殊字符) StringTokenizer st = new StringTokenizer(text.toString(), SPECIAL_CHARACTERS, true); while (st.hasMoreTokens()) { // 当前片段 String token = st.nextToken(); // 如果是特殊字符则转义,否则返回本身 buff.append(ESCAPE_MAP.getProperty(token, token)); } return buff.toString(); } /** * 将指定的JavaScript代码进行混淆。 * * @param script * 指定的JavaScript代码 * @return 混淆后的JavaScript代码 */ public static String obfuscateScript(CharSequence script) { if (script == null) { return ""; } // String.fromCharCode中参数最大个数 final int stringFromCharCodeLimit = 100; // 每行的参数个数 final int parametersPerLine = 10; // 使用xor函数的比例 final float xorRate = 0.1f; // 字符缓冲 StringBuilder buff = new StringBuilder(script.length() * 10 + 500); // 格式化输出到字符串缓冲 Formatter formatter = new Formatter(buff); // 输出String.fromCharCode的别名定义,并返回其别名 String stringFromCharCode = stringFromCharCode(formatter); // 输出xor函数,并返回函数名列表及对应的xor阈值 Map xorFunctions = xorFunctions(formatter); // xor函数名称 String[] xorFuncNames = xorFunctions.keySet().toArray(new String[0]); // eval函数开始,其中第一个使用String.fromCharCode(32)即空格 formatter.format("/*%2$s*/\\u0065\\u0076\\u0061\\u006c/*%3$s*/(%1$s(32", formatArguments(3, stringFromCharCode)); // 遍历代码中的所有字符 for (int i = 0; i < script.length(); i++) { // 当前字符 int code = script.charAt(i); if (i % stringFromCharCodeLimit == 0) { // 结束旧的String.fromCharCode, formatter.format(")%n"); // 开始新的String.fromCharCode formatter.format("+/*%2$s*/%1$s(", formatArguments(2, stringFromCharCode)); } else { // 一般的String.fromCharCode参数之间使用逗号分隔 buff.append(","); if (i % parametersPerLine == 0) { // 当前字符结束后需要换行 formatter.format("%n"); } } // 根据xorRate确定的比例,输出当前字符参数 if (RandomUtils.nextFloat() < xorRate) { // 使用xor参数的名称 String xorFunc = xorFuncNames[i % xorFuncNames.length]; // 对应的异或计算阈值 int xor = xorFunctions.get(xorFunc); // 进行过异或计算后的结果 int xorCode = code ^ xor; // 输出函数调用 formatter.format("%1$s(", xorFunc); // 输出函数参数 formatter.format(numberFormat(i), xorCode); // 调用结束 buff.append(")"); } else { // 正常输出 formatter.format(numberFormat(i), code); } } // 最后一个String.fromCharCode和eval函数的结尾 formatter.format("/*%1$s%2$s*/));/*%3$s*/%n", formatArguments(3)); // 返回混淆代码 return buff.toString(); } /** * 输出String.fromCharCode的别名定义,并返回其别名。 * * @param formatter * 格式化输出 * @return String.fromCharCode别名 */ private static String stringFromCharCode(Formatter formatter) { String stringFromCharCode = "__" + randomAlphanumeric(3, 10); formatter .format("/*%2$s*/var/*%3$s*/%1$s/*%4$s*/=\\u0053\\u0074\\u0072\\u0069\\u006e\\u0067%n/*%5$s*/./*%6$s*/\\u0066r\\u006fm\\u0043ha\\u0072C\\u006fde/*%7$s*/;%n", formatArguments(7, stringFromCharCode)); return stringFromCharCode; } /** * 输出xor函数,并返回函数名列表及对应的xor阈值。 * * @param formatter * 格式化输出 * @return xor函数名列表及对应的xor阈值 */ private static Map xorFunctions(Formatter formatter) { int[] xorArray = new int[5]; for (int i = 0; i < xorArray.length; i++) { xorArray[i] = RandomUtils.nextInt(); } String xorArrayName = "_x_" + randomAlphanumeric(3); formatter.format("var/*%2$s*/%1$s = [/*%3$s*/", formatArguments(3, xorArrayName)); for (int i = 0; i < xorArray.length; i++) { formatter.format("%d,", xorArray[i]); } formatter.format("/*%s*/];//%s%n", formatArguments(2)); Map functions = new HashMap(); for (int i = 0; i < xorArray.length; i++) { String func = "_$" + randomAlphanumeric(3, 5); formatter.format("var/*%2$s*/%1$s/*%3$s*/=/*%4$s*/function(/*%5$s*/){%n", formatArguments(5, func)); formatter.format("/*%1$s*/return/*%2$s*/arguments[/*%3$s*/0]^/*%4$s*/%n", formatArguments(4)); formatter.format("/*%3$s*/%1$s[/*%4$s*/%2$d];/*%5$s*/}/*%6$s*/;%n", formatArguments(6, xorArrayName, i)); functions.put(func, xorArray[i]); } return functions; } /** * 获取格式化输出参数。 * * @param count * 参数个数 * @param firstFixedParameters * 最初的固定参数 * @return 根据给定的参数个数,在最初的固定参数后生成随机字符串作为格式化输出参数 */ private static Object[] formatArguments(int count, Object... firstFixedParameters) { if (count < firstFixedParameters.length) { throw new IllegalArgumentException("length < codes.length"); } Object[] args = new Object[count]; System.arraycopy(firstFixedParameters, 0, args, 0, firstFixedParameters.length); for (int i = firstFixedParameters.length; i < args.length; i++) { args[i] = randomAlphanumeric(5, 20); } return args; } /** * 生成由字母及数字组成的随机字符串。 * * @param length * 随机字符串长度 * @return 由字母及数字组成的随机字符串 */ private static String randomAlphanumeric(int length) { return randomAlphanumeric(length, length); } /** * 生成由字母及数字组成的随机字符串。 * * @param minLength * 随机字符串最小长度 * @param maxLength * 随机字符串最大长度 * @return 由字母及数字组成的随机字符串 */ private static String randomAlphanumeric(int minLength, int maxLength) { if (minLength <= 0) { throw new IllegalArgumentException("minLength <= 0"); } if (maxLength <= 0) { throw new IllegalArgumentException("maxLength <= 0"); } if (minLength > maxLength) { throw new IllegalArgumentException("minLength > maxLength"); } else if (minLength == maxLength) { return RandomStringUtils.randomAlphanumeric(minLength); } else { int length = minLength + RandomUtils.nextInt(); return RandomStringUtils.randomAlphanumeric(length); } } /** * 随机的格式化输出格式。 * * @param seed * 随机种子 * @return 随机的格式化输出格式 */ private static String numberFormat(int seed) { int rnd = RandomUtils.nextInt(); switch (rnd % 17) { case 0: return "0x%x"; case 1: return String.format("-1-~/*%s*/(0x%%x^0)", randomAlphanumeric(2, 5)); case 2: return String.format("%%d%d/0xA", RandomUtils.nextInt()); case 3: return "Math.abs(%d)&-1"; case 4: return "0%o"; case 5: return "%d&(-1^0x00)"; case 6: return "0x0|0x%x"; case 7: return String.format("~/*%s*/~/*%s*/%%d", formatArguments(2)); case 8: return String.format("~(0x%%x^/*%s*/-1)", randomAlphanumeric(2, 5)); case 9: return String.format("0x%%x%d%d/0400", RandomUtils.nextInt(), RandomUtils.nextInt()); case 10: return String.format("0x%%x%d%d>>/*%s*/4>>4", formatArguments(3, RandomUtils.nextInt(), RandomUtils.nextInt())); case 11: return String.format("%%d/*%s*/", randomAlphanumeric(2)); default: return "%d"; } } }