一、前言
关于springframework框架spring-web包(4.1.4)的org.springframework.web.util.HtmlUtils页面工具类,进行对html字符串进行加密htmlEscape伪码、对加密html字符串进行解密htmlUnescape等处理。
二、源码说明
1. HtmlUtils类
package org.springframework.web.util;@b@@b@import org.springframework.util.Assert;@b@@b@public abstract class HtmlUtils@b@{@b@ private static final HtmlCharacterEntityReferences characterEntityReferences = new HtmlCharacterEntityReferences();@b@@b@ public static String htmlEscape(String input)@b@ {@b@ return htmlEscape(input, "ISO-8859-1");@b@ }@b@@b@ public static String htmlEscape(String input, String encoding)@b@ {@b@ Assert.notNull(encoding, "Encoding is required");@b@ if (input == null)@b@ return null;@b@@b@ StringBuilder escaped = new StringBuilder(input.length() * 2);@b@ for (int i = 0; i < input.length(); ++i) {@b@ char character = input.charAt(i);@b@ String reference = characterEntityReferences.convertToReference(character, encoding);@b@ if (reference != null) {@b@ escaped.append(reference);@b@ }@b@ else@b@ escaped.append(character);@b@ }@b@@b@ return escaped.toString();@b@ }@b@@b@ public static String htmlEscapeDecimal(String input)@b@ {@b@ return htmlEscapeDecimal(input, "ISO-8859-1");@b@ }@b@@b@ public static String htmlEscapeDecimal(String input, String encoding)@b@ {@b@ Assert.notNull(encoding, "Encoding is required");@b@ if (input == null)@b@ return null;@b@@b@ StringBuilder escaped = new StringBuilder(input.length() * 2);@b@ for (int i = 0; i < input.length(); ++i) {@b@ char character = input.charAt(i);@b@ if (characterEntityReferences.isMappedToReference(character, encoding)) {@b@ escaped.append("&#");@b@ escaped.append(character);@b@ escaped.append(';');@b@ }@b@ else {@b@ escaped.append(character);@b@ }@b@ }@b@ return escaped.toString();@b@ }@b@@b@ public static String htmlEscapeHex(String input)@b@ {@b@ return htmlEscapeHex(input, "ISO-8859-1");@b@ }@b@@b@ public static String htmlEscapeHex(String input, String encoding)@b@ {@b@ Assert.notNull(encoding, "Encoding is required");@b@ if (input == null)@b@ return null;@b@@b@ StringBuilder escaped = new StringBuilder(input.length() * 2);@b@ for (int i = 0; i < input.length(); ++i) {@b@ char character = input.charAt(i);@b@ if (characterEntityReferences.isMappedToReference(character, encoding)) {@b@ escaped.append("&#x");@b@ escaped.append(Integer.toString(character, 16));@b@ escaped.append(';');@b@ }@b@ else {@b@ escaped.append(character);@b@ }@b@ }@b@ return escaped.toString();@b@ }@b@@b@ public static String htmlUnescape(String input)@b@ {@b@ if (input == null)@b@ return null;@b@@b@ return new HtmlCharacterEntityDecoder(characterEntityReferences, input).decode();@b@ }@b@}
2.HtmlCharacterEntityReferences、HtmlCharacterEntityDecoder类
package org.springframework.web.util;@b@@b@import java.io.IOException;@b@import java.io.InputStream;@b@import java.util.Enumeration;@b@import java.util.HashMap;@b@import java.util.Map;@b@import java.util.Properties;@b@import org.springframework.util.Assert;@b@@b@class HtmlCharacterEntityReferences@b@{@b@ private static final String PROPERTIES_FILE = "HtmlCharacterEntityReferences.properties";@b@ static final char REFERENCE_START = 38;@b@ static final String DECIMAL_REFERENCE_START = "&#";@b@ static final String HEX_REFERENCE_START = "&#x";@b@ static final char REFERENCE_END = 59;@b@ static final char CHAR_NULL = 65535;@b@ private final String[] characterToEntityReferenceMap = new String[3000];@b@ private final Map<String, Character> entityReferenceToCharacterMap = new HashMap(252);@b@@b@ public HtmlCharacterEntityReferences()@b@ {@b@ Properties entityReferences = new Properties();@b@@b@ InputStream is = HtmlCharacterEntityReferences.class.getResourceAsStream("HtmlCharacterEntityReferences.properties");@b@ if (is == null)@b@ throw new IllegalStateException("Cannot find reference definition file [HtmlCharacterEntityReferences.properties] as class path resource");@b@ try@b@ {@b@ try@b@ {@b@ entityReferences.load(is);@b@@b@ is.close(); } finally { is.close();@b@ }@b@ }@b@ catch (IOException ex)@b@ {@b@ throw new IllegalStateException("Failed to parse reference definition file [HtmlCharacterEntityReferences.properties]: " + ex@b@ .getMessage());@b@ }@b@@b@ Enumeration keys = entityReferences.propertyNames();@b@ while (keys.hasMoreElements()) {@b@ String key = (String)keys.nextElement();@b@ int referredChar = Integer.parseInt(key);@b@ Assert.isTrue(((referredChar < 1000) || ((referredChar >= 8000) && (referredChar < 10000))) ? 1 : false, "Invalid reference to special HTML entity: " + referredChar);@b@@b@ int index = (referredChar < 1000) ? referredChar : referredChar - 7000;@b@ String reference = entityReferences.getProperty(key);@b@ this.characterToEntityReferenceMap[index] = '&' + reference + ';';@b@ this.entityReferenceToCharacterMap.put(reference, Character.valueOf((char)referredChar));@b@ }@b@ }@b@@b@ public int getSupportedReferenceCount()@b@ {@b@ return this.entityReferenceToCharacterMap.size();@b@ }@b@@b@ public boolean isMappedToReference(char character)@b@ {@b@ return isMappedToReference(character, "ISO-8859-1");@b@ }@b@@b@ public boolean isMappedToReference(char character, String encoding)@b@ {@b@ return (convertToReference(character, encoding) != null);@b@ }@b@@b@ public String convertToReference(char character)@b@ {@b@ return convertToReference(character, "ISO-8859-1");@b@ }@b@@b@ public String convertToReference(char character, String encoding)@b@ {@b@ if (encoding.startsWith("UTF-")) {@b@ switch (character)@b@ {@b@ case '<':@b@ return "<";@b@ case '>':@b@ return ">";@b@ case '"':@b@ return """;@b@ case '&':@b@ return "&";@b@ case '\'':@b@ return "'";@b@ }@b@ }@b@ else if ((character < 1000) || ((character >= 8000) && (character < 10000))) {@b@ int index = (character < 1000) ? character : character - 7000;@b@ String entityReference = this.characterToEntityReferenceMap[index];@b@ if (entityReference != null)@b@ return entityReference;@b@ }@b@@b@ return null;@b@ }@b@@b@ public char convertToCharacter(String entityReference)@b@ {@b@ Character referredCharacter = (Character)this.entityReferenceToCharacterMap.get(entityReference);@b@ if (referredCharacter != null)@b@ return referredCharacter.charValue();@b@@b@ return 65535;@b@ }@b@}
package org.springframework.web.util;@b@@b@class HtmlCharacterEntityDecoder@b@{@b@ private static final int MAX_REFERENCE_SIZE = 10;@b@ private final HtmlCharacterEntityReferences characterEntityReferences;@b@ private final String originalMessage;@b@ private final StringBuilder decodedMessage;@b@ private int currentPosition = 0;@b@ private int nextPotentialReferencePosition = -1;@b@ private int nextSemicolonPosition = -2;@b@@b@ public HtmlCharacterEntityDecoder(HtmlCharacterEntityReferences characterEntityReferences, String original)@b@ {@b@ this.characterEntityReferences = characterEntityReferences;@b@ this.originalMessage = original;@b@ this.decodedMessage = new StringBuilder(original.length());@b@ }@b@@b@ public String decode()@b@ {@b@ while (this.currentPosition < this.originalMessage.length()) {@b@ findNextPotentialReference(this.currentPosition);@b@ copyCharactersTillPotentialReference();@b@ processPossibleReference();@b@ }@b@ return this.decodedMessage.toString();@b@ }@b@@b@ private void findNextPotentialReference(int startPosition) {@b@ this.nextPotentialReferencePosition = Math.max(startPosition, this.nextSemicolonPosition - 10);@b@ do@b@ {@b@ this.nextPotentialReferencePosition = this.originalMessage@b@ .indexOf(38, this.nextPotentialReferencePosition);@b@@b@ if ((this.nextSemicolonPosition != -1) && (this.nextSemicolonPosition < this.nextPotentialReferencePosition))@b@ {@b@ this.nextSemicolonPosition = this.originalMessage.indexOf(59, this.nextPotentialReferencePosition + 1);@b@ }@b@ boolean isPotentialReference = (this.nextPotentialReferencePosition != -1) && (this.nextSemicolonPosition != -1) && (this.nextPotentialReferencePosition - this.nextSemicolonPosition < 10);@b@@b@ if (isPotentialReference)@b@ return;@b@@b@ if (this.nextPotentialReferencePosition == -1)@b@ return;@b@@b@ if (this.nextSemicolonPosition == -1) {@b@ this.nextPotentialReferencePosition = -1;@b@ return;@b@ }@b@@b@ this.nextPotentialReferencePosition += 1;@b@ }@b@ while (this.nextPotentialReferencePosition != -1);@b@ }@b@@b@ private void copyCharactersTillPotentialReference() {@b@ if (this.nextPotentialReferencePosition != this.currentPosition)@b@ {@b@ int skipUntilIndex = (this.nextPotentialReferencePosition != -1) ? this.nextPotentialReferencePosition : this.originalMessage@b@ .length();@b@ if (skipUntilIndex - this.currentPosition > 3) {@b@ this.decodedMessage.append(this.originalMessage.substring(this.currentPosition, skipUntilIndex));@b@ this.currentPosition = skipUntilIndex;@b@ }@b@ else {@b@ while (this.currentPosition < skipUntilIndex)@b@ this.decodedMessage.append(this.originalMessage.charAt(this.currentPosition++));@b@ }@b@ }@b@ }@b@@b@ private void processPossibleReference() {@b@ if (this.nextPotentialReferencePosition != -1) {@b@ boolean isNumberedReference = this.originalMessage.charAt(this.currentPosition + 1) == '#';@b@ boolean wasProcessable = (isNumberedReference) ? processNumberedReference() : processNamedReference();@b@ if (wasProcessable) {@b@ this.currentPosition = (this.nextSemicolonPosition + 1);@b@ }@b@ else {@b@ char currentChar = this.originalMessage.charAt(this.currentPosition);@b@ this.decodedMessage.append(currentChar);@b@ this.currentPosition += 1;@b@ }@b@ }@b@ }@b@@b@ private boolean processNumberedReference() {@b@ char referenceChar = this.originalMessage.charAt(this.nextPotentialReferencePosition + 2);@b@ boolean isHexNumberedReference = (referenceChar == 'x') || (referenceChar == 'X');@b@ try@b@ {@b@ int value = (!(isHexNumberedReference)) ? @b@ Integer.parseInt(getReferenceSubstring(2))@b@ : @b@ Integer.parseInt(getReferenceSubstring(3), @b@ 16);@b@ this.decodedMessage.append((char)value);@b@ return true;@b@ } catch (NumberFormatException ex) {@b@ }@b@ return false;@b@ }@b@@b@ private boolean processNamedReference()@b@ {@b@ String referenceName = getReferenceSubstring(1);@b@ char mappedCharacter = this.characterEntityReferences.convertToCharacter(referenceName);@b@ if (mappedCharacter != 65535) {@b@ this.decodedMessage.append(mappedCharacter);@b@ return true;@b@ }@b@ return false;@b@ }@b@@b@ private String getReferenceSubstring(int referenceOffset) {@b@ return this.originalMessage.substring(this.nextPotentialReferencePosition + referenceOffset, this.nextSemicolonPosition);@b@ }@b@}