SpringMVC-05-乱码问题

SpringMVC-05-乱码问题

1. 过滤器解决

测试步骤:

  1. 我们可以在首页编写一个提交的表单
1
2
3
4
<form action="/e/t" method="post">
<input type="text" name="name">
<input type="submit">
</form>
  1. 后台编写对应的处理类
1
2
3
4
5
6
7
8
@Controller
public class Encoding {
@RequestMapping("/e/t")
public String test(Model model,String name){
model.addAttribute("msg",name); //获取表单提交的值
return "test"; //跳转到test页面显示输入的值
}
}
  1. 输入中文测试,发现乱码

  • 不得不说,乱码问题是在我们开发中十分常见的问题,也是让我们程序猿比较头大的问题!

  • 以前乱码问题通过过滤器解决 , 而SpringMVC给我们提供了一个过滤器 , 可以在web.xml中配置 .

  • 修改了xml文件需要重启服务器!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<filter>
<filter-name>encoding</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

// /*的作用是处理所有的jsp中的请求
  • /*的作用是处理包括jsp的请求

2. 自定义过滤器

  • 有些极端情况下.这个过滤器对get的支持不好 .

  • 处理方法 :

  1. 修改tomcat配置文件 : 设置编码!
1
2
3
<Connector URIEncoding="utf-8" port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
  1. 自定义过滤器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Map;

/**
* 解决get和post请求 全部乱码的过滤器
*/
public class GenericEncodingFilter implements Filter {

@Override
public void destroy() {
}

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
// 1. 处理response的字符编码
HttpServletResponse myResponse=(HttpServletResponse) response;
myResponse.setContentType("text/html;charset=UTF-8");

// 2.转型为与协议相关对象
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
// 3. 对request包装增强
HttpServletRequest myrequest = new MyRequest(httpServletRequest);
chain.doFilter(myrequest, response);
}

@Override
public void init(FilterConfig filterConfig) throws ServletException {
}

}

// 自定义request对象,HttpServletRequest的包装类
class MyRequest extends HttpServletRequestWrapper {

private HttpServletRequest request;
//1. 是否编码的标记
private boolean hasEncode;
//2. 定义一个可以传入HttpServletRequest对象的构造函数,以便对其进行装饰
public MyRequest(HttpServletRequest request) {
super(request);// super必须写
this.request = request;
}

// 3. 对需要增强方法 进行覆盖
@Override
public Map getParameterMap() {
// 先获得请求方式
String method = request.getMethod();
if (method.equalsIgnoreCase("post")) {
// post请求
try {
// 处理post乱码
request.setCharacterEncoding("utf-8");
return request.getParameterMap();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
} else if (method.equalsIgnoreCase("get")) {
// get请求
Map<String, String[]> parameterMap = request.getParameterMap();
if (!hasEncode) { // 确保get手动编码逻辑只运行一次
for (String parameterName : parameterMap.keySet()) {
String[] values = parameterMap.get(parameterName);
if (values != null) {
for (int i = 0; i < values.length; i++) {
try {
// 处理get乱码
values[i] = new String(values[i]
.getBytes("ISO-8859-1"), "utf-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
}
hasEncode = true;
}
return parameterMap;
}
return super.getParameterMap();
}

// 取一个值
@Override
public String getParameter(String name) {
Map<String, String[]> parameterMap = getParameterMap();
String[] values = parameterMap.get(name);
if (values == null) {
return null;
}
return values[0]; // 取回参数的第一个值
}

// 取所有值
@Override
public String[] getParameterValues(String name) {
Map<String, String[]> parameterMap = getParameterMap();
String[] values = parameterMap.get(name);
return values;
}
}
  • 这个也是我在网上找的一些大神写的,一般情况下,SpringMVC默认的乱码处理就已经能够很好的解决了!

  • 然后在web.xml中配置这个过滤器即可!

  • 乱码问题,需要平时多注意,在尽可能能设置编码的地方,都设置为统一编码 UTF-8!

3. HttpServletRequestWrapper

1
2
老大给了一个很实际的需求:有段程序,使用Http的方式与合作商交互,而且是明文传输数据。我方的代码已经打包放在服务器上运行了很长时间,这时合作商突然要求修改数据传输的方式,要求加密后再传输,而我方的原有的代码不能改变,以防止引发其它问题。 
问:如何在不修改我方现有的代码的前提下,满足合作商的要求?
  • 可能大家都想到了,只要加上一个过滤器Filter不就可以了吗?事实就是这样的,采用Filter+HttpServletRequestWrapper就可以解决这个问题。
  • 首先:在filter中拦截到加密后的请求,将参数解密,然后组装成一个新的明文请求串。
  • 然后:重写HttpServletRequestWrapper中的getInputStream()方法,让其返回过滤器解析后的明文串即可。

3.1 代码示例

  • 具体代码解释如下。

    首先我写了两个一摸一样的servlet,一个用来直接接收合作商的明文请求并打印;一个用来接收Filter处理后的合作商的请求并打印(Filter中将合作商加密后的参数解密再传给这个Servlet)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
@WebServlet("/SiServlet")  
public class SiServlet extends HttpServlet {
private static final long serialVersionUID = 1L;

/**
* @see HttpServlet#HttpServlet()
*/
public SiServlet() {
super();
}

/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
* response)
*/
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}

/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
* response)
*
*/
protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
String bizBindMsg = IOUtils.toString(request.getInputStream(), "UTF-8");
bizBindMsg = URLDecoder.decode(bizBindMsg.toString(), "UTF-8");
System.out.println("SiServlet接收到请求为: " + bizBindMsg);

response.getWriter().write("==========success=========");
}
}



@WebServlet("/SiServletNormal")
public class SiServletNormal extends HttpServlet {
private static final long serialVersionUID = 1L;

/**
* @see HttpServlet#HttpServlet()
*/
public SiServletNormal() {
super();
}

/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
* response)
*/
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}

/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
* response)
*
*/
protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
String bizBindMsg = IOUtils.toString(request.getInputStream(), "UTF-8");
bizBindMsg = URLDecoder.decode(bizBindMsg.toString(), "UTF-8");
System.out.println("SiServletNormal接收到请求为: " + bizBindMsg);

response.getWriter()
.write("==========SiServletNormal Success=========");
}
}
  • 然后使用HttpClient模拟了一下合作商发送明文和密文请求的过程,加密使用Base64简单模拟一下。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
public class AdcClient {  
private HttpPost httpPost = null;
private HttpClient client = null;
private List<NameValuePair> pairs = null;

public AdcClient() {
httpPost = new HttpPost("http://localhost:8080/filtertest/SiServlet");
client = new DefaultHttpClient();
}

/**
* 发送明文消息
*
*/
public void sendMsg() {

try {
httpPost = new HttpPost(
"http://localhost:8080/filtertest/SiServletNormal");

pairs = new ArrayList<NameValuePair>();
pairs.add(new BasicNameValuePair(("param1"), "obama没加密"));
pairs.add(new BasicNameValuePair(("param2"), "男没加密"));
pairs.add(new BasicNameValuePair(("param3"), "汉没加密"));
pairs.add(new BasicNameValuePair(("param4"), "山东没加密"));

httpPost.setEntity(new UrlEncodedFormEntity(pairs, "UTF-8"));
// httpPost.setHeader("Cookie", "TOKEN=1234567890");
HttpResponse response = client.execute(httpPost);

HttpEntity entity = response.getEntity();
BufferedReader br = new BufferedReader(new InputStreamReader(
entity.getContent()));
String line = null;
StringBuffer result = new StringBuffer();
while ((line = br.readLine()) != null) {
result.append(line);
line = br.readLine();
}

System.out.println("来自SiServletNormal的响应为:" + result.toString());
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}

/**
* 发送加密后的消息
*/
public void sendEncryptMsg() {
try {
pairs = new ArrayList<NameValuePair>();
pairs.add(new BasicNameValuePair(("param1"), Base64EnDecrypt
.base64Encode("obama")));
pairs.add(new BasicNameValuePair(("param2"), Base64EnDecrypt
.base64Encode("男")));
pairs.add(new BasicNameValuePair(("param3"), Base64EnDecrypt
.base64Encode("汉")));
pairs.add(new BasicNameValuePair(("param4"), Base64EnDecrypt
.base64Encode("山东")));

HttpEntity reqEntity = new UrlEncodedFormEntity(pairs, "UTF-8");
httpPost.setEntity(reqEntity);
// httpPost.setHeader("Cookie", "TOKEN=1234567890");
HttpResponse response = client.execute(httpPost);

/**
* 获取响应信息
*/
HttpEntity entity = response.getEntity();
BufferedReader br = new BufferedReader(new InputStreamReader(
entity.getContent()));
String line = null;
StringBuffer result = new StringBuffer();
while ((line = br.readLine()) != null) {
result.append(line);
line = br.readLine();
}

System.out.println("来自SiServlet的响应为:" + result.toString());
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}

/**
* @param args
* @throws UnsupportedEncodingException
*/
public static void main(String[] args) throws UnsupportedEncodingException {
new AdcClient().sendMsg();

new AdcClient().sendEncryptMsg();
}
}
  • 重点是下面的这个HttpServletRequestWrapper,我重写了它的getInputStream()方法,这个方法返回包含明文的ServletInputStream
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
public class MyRequestWrapper extends HttpServletRequestWrapper {  
private HttpServletRequest request;

public MyRequestWrapper(HttpServletRequest request) {
super(request);
this.request = request;
}

/**
* 先解密,获取明文;然后将明文转化为字节数组;然后再去读取字节数组中的内容
*/
@Override
public ServletInputStream getInputStream() {
String bizBindMsg = null;
ServletInputStream stream = null;

try {
stream = request.getInputStream();
bizBindMsg = IOUtils.toString(stream, "UTF-8");
} catch (IOException e) {
e.printStackTrace();
}
try {
bizBindMsg = URLDecoder.decode(bizBindMsg.toString(), "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
System.out.println("MyRequestWrapper接收到的请求为: " + bizBindMsg);

/**
* 获取加密的值进行解密
*/
final StringBuffer reqStr = new StringBuffer();
reqStr.append("param1=").append(
Base64EnDecrypt.base64Decode(bizBindMsg.substring(
bizBindMsg.indexOf("param1=") + 7,
bizBindMsg.indexOf("param2="))));
reqStr.append("&");
reqStr.append("param2=").append(
Base64EnDecrypt.base64Decode(bizBindMsg.substring(
bizBindMsg.indexOf("param2=") + 7,
bizBindMsg.indexOf("param3="))));
reqStr.append("&");
reqStr.append("param3=").append(
Base64EnDecrypt.base64Decode(bizBindMsg.substring(
bizBindMsg.indexOf("param3=") + 7,
bizBindMsg.indexOf("param4="))));
reqStr.append("&");
reqStr.append("param4=").append(
Base64EnDecrypt.base64Decode(bizBindMsg.substring(bizBindMsg
.indexOf("param4=") + 7)));

System.out.println("********MyRequestWrapper接收到的解密后的请求为*********");
System.out.println(reqStr.toString());

/**
* 将解密后的明文串放到buffer数组中
*/
byte[] buffer = null;
try {
buffer = reqStr.toString().getBytes("UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
final ByteArrayInputStream bais = new ByteArrayInputStream(buffer);

ServletInputStream newStream = new ServletInputStream() {

@Override
public int read() throws IOException {
return bais.read();
}
};
return newStream;
}
}
  • 最后是简单的Filter,在这里将加密后的ServletRequest重新包装,交给SiServlet进行处理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class EncryptFilter implements Filter {  

@Override
public void destroy() {
}

@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {

chain.doFilter(new MyRequestWrapper((HttpServletRequest) request),
response);
}

@Override
public void init(FilterConfig arg0) throws ServletException {

}

}
  • web.xml 中的配置
1
2
3
4
5
6
7
8
<filter>  
<filter-name>encryptFilter</filter-name>
<filter-class>com.test.filter.EncryptFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>encryptFilter</filter-name>
<url-pattern>/SiServlet</url-pattern>
</filter-mapping> s

参考文档http://tomcat.apache.org/tomcat-5.5-doc/servletapi/index.html

打赏
  • 版权声明: 本博客所有文章除特别声明外,均采用 Apache License 2.0 许可协议。转载请注明出处!
  • © 2019-2022 Zhuuu
  • PV: UV:

请我喝杯咖啡吧~

支付宝
微信