症状:
项目使用了spring mvc 和 spring security ,直接浏览器地址栏敲击请求,导致control中数据绑定到对象乱码。
使用 request.getParameter(name) 编码正确,request.getParameterValues(name) 乱码
经查org.springframework.web.bind.ServletRequestDataBinder.bind() 方法
MutablePropertyValues mpvs = new ServletRequestParameterPropertyValues(request);
结果已为乱码
org.springframework.web.util.WebUtils.getParametersStartingWith 中进行了request数据的获取封装,但是request已经被spring security封装为org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestWrapper,见代码org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(),水土不服的spring security 已经让我纠结到是不是自己写权限框架的地步了。
对症下药:
import java.io.IOException; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import org.springframework.util.Assert; import org.springframework.web.filter.GenericFilterBean; public class I18NSecurityContextHolderAwareRequestFilter extends GenericFilterBean { //~ Instance fields ================================================================================================ private String rolePrefix; //~ Methods ======================================================================================================== public void setRolePrefix(String rolePrefix) { Assert.notNull(rolePrefix, "Role prefix must not be null"); this.rolePrefix = rolePrefix.trim(); } public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { chain.doFilter(new I18NSecurityContextHolderAwareRequestWrapper((HttpServletRequest) req, rolePrefix), res); } }
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import java.security.Principal; import java.util.Collection; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import org.springframework.security.authentication.AuthenticationTrustResolver; import org.springframework.security.authentication.AuthenticationTrustResolverImpl; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetails; import com.emagsoftware.web.session.SessionHttpServletRequestWrapper; /** * A Spring Security-aware <code>HttpServletRequestWrapper</code>, which uses the * <code>SecurityContext</code>-defined <code>Authentication</code> object to implement the servlet API security * methods {@link SecurityContextHolderAwareRequestWrapper#isUserInRole(String)} and {@link * HttpServletRequestWrapper#getRemoteUser()}. * * @see SecurityContextHolderAwareRequestFilter * * @author Orlando Garcia Carmona * @author Ben Alex * @author Luke Taylor */ public class I18NSecurityContextHolderAwareRequestWrapper extends SessionHttpServletRequestWrapper { //~ Instance fields ================================================================================================ private final AuthenticationTrustResolver authenticationTrustResolver = new AuthenticationTrustResolverImpl(); /** * The prefix passed by the filter. It will be prepended to any supplied role values before * comparing it with the roles obtained from the security context. */ private final String rolePrefix; private static String charset = "utf-8"; //~ Constructors =================================================================================================== public I18NSecurityContextHolderAwareRequestWrapper(HttpServletRequest request, String rolePrefix) { super(request,charset); this.rolePrefix = rolePrefix; } //~ Methods ======================================================================================================== /** * Obtain the current active <code>Authentication</code> * * @return the authentication object or <code>null</code> */ private Authentication getAuthentication() { Authentication auth = SecurityContextHolder.getContext().getAuthentication(); if (!authenticationTrustResolver.isAnonymous(auth)) { return auth; } return null; } /** * Returns the principal's name, as obtained from the <code>SecurityContextHolder</code>. Properly handles * both <code>String</code>-based and <code>UserDetails</code>-based principals. * * @return the username or <code>null</code> if unavailable */ @Override public String getRemoteUser() { Authentication auth = getAuthentication(); if ((auth == null) || (auth.getPrincipal() == null)) { return null; } if (auth.getPrincipal() instanceof UserDetails) { return ((UserDetails) auth.getPrincipal()).getUsername(); } return auth.getPrincipal().toString(); } /** * Returns the <code>Authentication</code> (which is a subclass of <code>Principal</code>), or * <code>null</code> if unavailable. * * @return the <code>Authentication</code>, or <code>null</code> */ @Override public Principal getUserPrincipal() { Authentication auth = getAuthentication(); if ((auth == null) || (auth.getPrincipal() == null)) { return null; } return auth; } private boolean isGranted(String role) { Authentication auth = getAuthentication(); if( rolePrefix != null ) { role = rolePrefix + role; } if ((auth == null) || (auth.getPrincipal() == null)) { return false; } Collection<? extends GrantedAuthority> authorities = auth.getAuthorities(); if (authorities == null) { return false; } for (GrantedAuthority grantedAuthority : authorities) { if (role.equals(grantedAuthority.getAuthority())) { return true; } } return false; } /** * Simple searches for an exactly matching {@link org.springframework.security.core.GrantedAuthority#getAuthority()}. * <p> * Will always return <code>false</code> if the <code>SecurityContextHolder</code> contains an * <code>Authentication</code> with <code>null</code><code>principal</code> and/or <code>GrantedAuthority[]</code> * objects. * * @param role the <code>GrantedAuthority</code><code>String</code> representation to check for * * @return <code>true</code> if an <b>exact</b> (case sensitive) matching granted authority is located, * <code>false</code> otherwise */ @Override public boolean isUserInRole(String role) { return isGranted(role); } @Override public String toString() { return "SecurityContextHolderAwareRequestWrapper[ " + getRequest() + "]"; } }
<custom-filter before="securityContextHolderAwareRequestFilte" ref="securityContextHolderAwareRequestFilte"/> <b:bean id="securityContextHolderAwareRequestFilte" class="com.ff.ffservice.mvc.rule.I18NSecurityContextHolderAwareRequestFilter"/>
public class SessionHttpServletRequestWrapper extends HttpServletRequestWrapper private String charset = "UTF-8"; /** * 实际上就是调用被包装的请求对象的getParameter方法获得参数,然后再进行编码转换 */ public String getParameter(String name) { String value = super.getParameter(name); if ("get".equalsIgnoreCase(super.getMethod())) { value = value == null ? null : convert(value); } return value; } private HttpServletRequest getHttpServletRequest() { return (HttpServletRequest) super.getRequest(); } // 重写获得一组表单数据方法 // @param arg0 请求参数 public String[] getParameterValues(String arg0) { // 请求内容类型字符串 String contentType = getHttpServletRequest().getContentType(); // 上传组件 if (contentType != null && contentType.indexOf("multipart/form-data") != -1) { return getHttpServletRequest().getParameterValues(arg0); } else {// 普通提交 String values[] = getHttpServletRequest().getParameterValues(arg0); if (values != null) { for (int i = 0; i < values.length; i++) { values[i] = convert(values[i]); } } return values; } } public String convert(String target) { try { return new String(target.trim().getBytes("ISO-8859-1"), charset); } catch (UnsupportedEncodingException e) { return target; } } }