Spring 的优秀工具类盘点,第 2 部分: 特殊字符转义和方法入参检测工具类
特殊字符转义
由于 Web 应用程序需要联合使用到多种语言,每种语言都包含一些特殊的字符,对于动态语言或标签式的语言而言,如果需要动态构造语言的内容时,一个我们经常会碰到的问题就是特殊字符转义的问题。下面是 Web 开发者最常面对需要转义的特殊字符类型:
- HTML 特殊字符;
- JavaScript 特殊字符;
- SQL 特殊字符;
如果不对这些特殊字符进行转义处理,则不但可能破坏文档结构,还可以引发潜在的安全问题。Spring 为 HTML 和 JavaScript 特殊字符提供了转义操作工具类,它们分别是 HtmlUtils 和 JavaScriptUtils。
HTML 特殊字符转义
HTML 中 <,>,& 等字符有特殊含义,它们是 HTML 语言的保留字,因此不能直接使用。使用这些个字符时,应使用它们的转义序列:
- &:&
- " :"
- < :<
- > :>
由于 HTML 网页本身就是一个文本型结构化文档,如果直接将这些包含了 HTML 特殊字符的内容输出到网页中,极有可能破坏整个 HTML 文档的结构。所以,一般情况下需要对动态数据进行转义处理,使用转义序列表示 HTML 特殊字符。下面的 JSP 网页将一些变量动态输出到 HTML 网页中:
清单 1. 未进行 HTML 特殊字符转义处理网页
<%@ page language="java" contentType="text/html; charset=utf-8"%> <%! String userName = "</td><tr></table>"; String address = " \" type=\"button"; %> <table border="1"> <tr> <td>姓名:</td><td><%=userName%></td> ① </tr> <tr> <td>年龄:</td><td>28</td> </tr> </table> <input value="<%=address%>" type="text" /> ②
在 ① 和 ② 处,我们未经任何转义处理就直接将变量输出到 HTML 网页中,由于这些变量可能包含一些特殊的 HTML 的字符,它们将可能破坏整个 HTML 文档的结构。我们可以从以上 JSP 页面的一个具体输出中了解这一问题:
<table border="1"> <tr> <td>姓名:</td><td></td><tr></table></td> ① 破坏了 <table> 的结构 </tr> <tr> <td>年龄:</td><td>28</td> </tr> </table> <input value=" " type="button" type="text" /> ② 将本来是输入框组件偷梁换柱为按钮组件
融合动态数据后的 HTML 网页已经面目全非,首先 ① 处的 <table> 结构被包含 HTML 特殊字符的 userName 变量截断了,造成其后的 <table> 代码变成无效的内容;其次,② 处 <input> 被动态数据改换为按钮类型的组件(type="button")。为了避免这一问题,我们需要事先对可能破坏 HTML 文档结构的动态数据进行转义处理。Spring 为我们提供了一个简单适用的 HTML 特殊字符转义工具类,它就是 HtmlUtils。下面,我们通过一个简单的例子了解 HtmlUtils 的具体用法:
清单 2. HtmpEscapeExample
package com.baobaotao.escape; import org.springframework.web.util.HtmlUtils; public class HtmpEscapeExample { public static void main(String[] args) { String specialStr = "<div id=\"testDiv\">test1;test2</div>"; String str1 = HtmlUtils.htmlEscape(specialStr); ①转换为HTML转义字符表示 System.out.println(str1); String str2 = HtmlUtils.htmlEscapeDecimal(specialStr); ②转换为数据转义表示 System.out.println(str2); String str3 = HtmlUtils.htmlEscapeHex(specialStr); ③转换为十六进制数据转义表示 System.out.println(str3); ④下面对转义后字符串进行反向操作 System.out.println(HtmlUtils.htmlUnescape(str1)); System.out.println(HtmlUtils.htmlUnescape(str2)); System.out.println(HtmlUtils.htmlUnescape(str3)); } }
HTML 不但可以使用通用的转义序列表示 HTML 特殊字符,还可以使用以 # 为前缀的数字序列表示 HTML 特殊字符,它们在最终的显示效果上是一样的。HtmlUtils 提供了三个转义方法:
方法 | 说明 |
---|---|
static String htmlEscape(String input) |
将 HTML 特殊字符转义为 HTML 通用转义序列; |
static String htmlEscapeDecimal(String input) |
将 HTML 特殊字符转义为带 # 的十进制数据转义序列; |
static String htmlEscapeHex(String input) |
将 HTML 特殊字符转义为带 # 的十六进制数据转义序列; |
此外,HtmlUtils 还提供了一个能够将经过转义内容还原的方法:htmlUnescape(String input),它可以还原以上三种转义序列的内容。运行以上代码,您将可以看到以下的输出:
str1:<div id="testDiv">test1;test2</div> str2:<div id="testDiv">test1;test2</div> str3:<div id="testDiv">test1;test2</div> <div id="testDiv">test1;test2</div> <div id="testDiv">test1;test2</div> <div id="testDiv">test1;test2</div>
您只要使用 HtmlUtils 对代码 清单 1 的 userName 和 address 进行转义处理,最终输出的 HTML 页面就不会遭受破坏了。
Weblogic部署报com.ctc.wstx.stax.WstxInputFactory cannot be cast to javax.xml.stream.XMLInputFactory
Weblogic部署Web应用时报com.ctc.wstx.stax.WstxInputFactory cannot be cast to javax.xml.stream.XMLInputFactory,weblogic.xml配置为:
<container-descriptor>
<servlet-reload-check-secs>-1</servlet-reload-check-secs>
<prefer-web-inf-classes>true</prefer-web-inf-classes>
</container-descriptor>
一个httpcomponents-client-4.3 httpclient的例子
Apache的httpclient项目一直在升级,从httpclient3.1 项目到httpcomponents-client 4.0再到4.3都不是太稳定,现在处于4.3版本,终于基本稳定下来。现在httpclient3.1已经中止,官方强烈推荐升级到httpcomponents-client 。
其实开发一个功能完整强大的httpclient客户端还是不容易的。这里只是贴出一个使用httpcomponents-client里CloseableHttpClient进行http请求的例子,但是就这几行代码已经内置对Cookie和SSL的处理。谈不上很安全,但至少对使用Cookie对Https的URL是可以处理取回内容的。
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
public class HttpComponentTest {
public CloseableHttpClient createHttpsClient() {
X509TrustManager x509mgr = new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] xcs, String string) {
}
@Override
public void checkServerTrusted(X509Certificate[] xcs, String string) {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return null;
}
};
SSLContext sslContext = null;
try {
sslContext = SSLContext.getInstance("TLS");
} catch (NoSuchAlgorithmException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
try {
sslContext.init(null, new TrustManager[] { x509mgr }, null);
} catch (KeyManagementException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
sslContext,
SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
return HttpClients.custom().setSSLSocketFactory(sslsf).build();
}
/**
* @param args
*/
public static void main(String[] args) throws Exception{
// CloseableHttpClient httpclient = HttpClients.createDefault();
HttpComponentTest t=new HttpComponentTest();
CloseableHttpClient httpclient = t.createHttpsClient();
// HttpGet httpGet = new HttpGet("http://www.google.com.hk/search?hl=zh&ie=utf-8&num=30&output=rss&q=java+%7C+hibernate+%7C+spring&tbm=blg");
HttpGet httpGet = new HttpGet("https://www.google.com.hk/search?hl=zh&ie=utf-8&num=30&output=rss&q=java+%7C+hibernate+%7C+spring&tbm=blg");
// HttpGet httpGet = new HttpGet("http://itindex.net/");
try {
HttpResponse httpResp = httpclient.execute(httpGet);
int statusCode = httpResp.getStatusLine().getStatusCode();
if (statusCode == 200) {
System.out.println(EntityUtils.toString(httpResp.getEntity()));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
httpGet.releaseConnection();
}
}
}