乱码解决、CharacterEncodingFilter与SpringWebSecurity 编程过程中,时不时就会遇到乱码问题,也就是汉字显⽰不正常。最根本的原因当然是因为现在的计算机不是咱中国⼈发明的,那些歪果仁当初在设计字符编码的时候就只考虑了26个英⽂字母和⼀些英⽂符号,处理不了汉字。其实不仅仅是汉字,就连同样是拼⾳⽂字的欧洲各国语⾔也处理不了,反正只要不是英语的就都处理不了。然后各个国家、各种语⾔都把⾃⼰的语⾔进⾏编码,甚⾄同⼀个国家同⼀种语⾔都会有⼏种不同的编码⽅案,因为⼀开始的时候没有什么标准要求,怎么编都⾏。于是,各种各样的字符集产⽣了,其实编码多并没有太⼤的问题,⼤家各⽤各的,互不相关,互不⼲涉,只是要想交换数据就不太可能,要想同时处理⼏种编码就更不可能。这时就有⼈跳出来统⼀各种编码,把世界上所有语⾔的⽂字、符号统统收编,其实这也是好事,各种⽂字、各种符号,都有不同的编码,想⽤什么⽂字就⽤什么⽂字,只不过是不同的编码⽽已。但是,乱码问题恰恰是这个时候涌现出来的,以前,不管有多少种编码⽅案,但就⼀个程序来说,输⼊的是这种编,输出的也是这种编码,不会有什么乱码问题,⽽⾃从⽤了Unicode之后,要考虑转换问题,转换错了,乱码就出现了。
为什么要转换呢?没有Unicode的时候,各种编码互相转换很⿇烦,也就不转了,即便要转也是专门程序、专业团队的事,⼀般⼈不去管,倒也相安⽆事,有了Unicode的时候,各种编码转成Unicode很容易,⽽且Unicode的⽬标就是要统⼀,所以,当核⼼软件或者说操作系统采⽤Unicode之后,没有采⽤Unicode的程序就只能依靠转换的办法了,因为到最后调⽤操作系统功能进⾏输出的时候只能⽤Unicode。不是所
有的程序都采⽤了Unicode编码的,这其中,很关键的是输⼊法,事实上,⼀个字符或者⼀个字在计算机内如何表⽰,最根本是取决于输⼊法的,当然,⼴义来说,还要包括语⾳输⼊、⽂字识别之类的输⼊,这些是字符编码的根本,要是所有的输⼊都采⽤了Unicode编码,Unicode就能真正实现统⼀编码,转换应该就不需要了。
事实上,决⼤多数程序都不考虑编码转换的问题,⽐⽅说计算⼀个简单的加法,或者是求解⼀个⽅程,或者格式化⼀个磁盘,再或者通过⽹络发送⼀串数据,这些功能和编码没什么关系,是这些程序根本不需要去考虑的。这些程序即便要要输出字符,也只是简单的调⽤操作系统或其他程序的功能,接受输⼊也⼀样,不管输⼊的是什么编码,就是简单的当作⼀串⼆进制数处理就⾏了。本来我们⾃⼰写的Web程序也是可以不⽤管什么编码的,但这取决于接受的输⼊也就是来⾃浏览器请求是不是已经转换成Unicode编码了,显然,浏览器对于输⼊并没有处理编码的转换,⽽IIS、Tomcat这些后台程序也没有,所以,把本地编码转换成Unicode的过程就只能放在Web程序中。另⼀⽅⾯,浏览器可以处理各种编码的输出,但必须通过<meta charset="${encoding}">这个语句告诉它才⾏,同时还要保证提供的确实是指定的编码,否则,浏览器就会转换错误,出现乱码。也就是说,如果是⼀个静态的⽹页,这个html⽂件本⾝的编码就要和meta语句标明的编码⼀致。同样,像Thymeleaf这些经过程序处理的模板其⽂件本⾝采⽤的编码必需正确的告诉这些处理程序或者说设置正确,否则模板引擎在处理的时候就会转换错误出现乱码,处理模板的过程跟meta没有关系,不过,⼀般来说,输⼊的是什么编码输出也是什么编码,
然后输出的到了浏览器⼜必须要跟meta⼀致,所以实际上就要三处⼀致。这些是静态编码,处理起来⽐较简单,就是要设置和实际⼀致。像表单输⼊的编码转换,⼀般的处理办法就是加⼀个filter,Spring⾥⾯就有⼀个现成的CharacterEncodingFilter过滤器,采⽤JavaConfig的情况下,加过滤器的位置通常是在 AbstractAnnotationConfigDispatcherServletInitializer⼦类⾥重载getServletFilters(),代码如下:
import javax.servlet.ServletRegistration.Dynamic;
import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] {RootConfig.class};
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[] {WebConfig.class};
}unicode编码转换二进制
@Override
protected String[] getServletMappings() {
return new String[] {"/"};
}
@Override
protected Filter[] getServletFilters() {
CharacterEncodingFilter characterEncodingFilter=new CharacterEncodingFilter();
characterEncodingFilter.setEncoding("utf-8");
characterEncodingFilter.setForceEncoding(true);
return new Filter[] {characterEncodingFilter};
}
}
这段代码在没有加⼊Spring Security的时候是可以解决乱码问题的,加⼊Spring Security后就失效了,原因是Spring Security把它的Filter Chain⾃动放到了最前⾯,然后其中的CsrfFilter过滤器在验证了csrf token之后,重写了请求中的数据(应该删除了csrf这⼀部分),改变了编码,造成了CharacterEncodingFilter转换失败。虽然想不通为什么删除或者重写会改变编码,但总之问题就是这个,所以解决的办法就是把CharacterEncodingFilter放在CsrfFilter之前,代码可以这样:
@Configuration
@EnableWebMvcSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
CharacterEncodingFilter filter = new CharacterEncodingFilter();
filter.setEncoding("UTF-8");
filter.setForceEncoding(true);
http.addFilterBefore(filter,CsrfFilter.class);
......
......
}
}
或者,仍然是在AbstractAnnotationConfigDispatcherServletInitializer⼦类⾥重载,不过,重载OnStartup。
import javax.servlet.ServletRegistration.Dynamic;
import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
......
......
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
servletContext.addFilter("characterEncodingFilter", new CharacterEncodingFilter("utf-8",true))
.addMappingForUrlPatterns(null,false,"/*");
}
......
......
}
这⾥要注意addMappingForUrlPatterns函数⾥⾯第⼆个参数,API⾥⾯这个参数名称是isMatchAfter,所以为true也达不到放在Spring Security的过滤链之前的⽬的。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论