SpringMVC 重寫HttpMessageConverter進行Xss過濾

在工做中,須要對xss攻擊腳本過濾,在此記錄下處理過程。java

因爲只須要對content-type=application/json;charset=UTF-8的json請求進行處理,因此須要重寫MappingJacksonHttpMessageConverter部分方法。spring

注意:若是使用的是MappingJackson2HttpMessageConverter,須要重寫MappingJackson2HttpMessageConverter部分方法  json

  1. 定義接口MessageConverterHandlermvc

    咱們定義一個接口來定義咱們須要實現的方法
app

public interface MessageConverterHandler<T, K> {

	/**
	 * 用於在httpMessageConverter read(..)方法完成以後調用
	 * <p>
	 * 1.能夠對converter映射出的Object進行處理
	 * </p>
	 */
	public Object readAfter( T obj, K type );
}

    2.自定義註解NeedXssxss

    該註解用於須要xss過濾的字段ide

@Target( ElementType.FIELD )
@Retention( RetentionPolicy.RUNTIME )
@Documented
public @interface NeedXss {
}

    3.實現類XssMappingJacksonHttpMessageConverter,該類須要實現MessageConverterHandler,繼承MappingJacksonHttpMessageConverterthis

    代碼以下:
編碼

//默認會過濾全部請求路徑,只會過濾String類型的字段,須要其餘類型過濾的請自行完善
public class XssMappingJacksonHttpMessageConverter extends MappingJacksonHttpMessageConverter implements
		MessageConverterHandler<Object, Type> {

	/**
	 * 不須要xss過濾的路徑
	 */
	protected static List<String> urls;

	static {
		urls = new ArrayList<String>();
		// for example urls.add("/xxx/xxxxx");
	}
        
        //重寫該方法,咱們只須要加上Object tempObj = this.process( obj, type, inputMessage );
        //並返回tempObj,process方法裏面咱們過濾白名單和進行xss處理
	@Override
	public Object read( Type type, Class<?> contextClass, HttpInputMessage inputMessage ) throws IOException,
			HttpMessageNotReadableException {

		JavaType javaType = getJavaType( type, contextClass );

		Object obj = readJavaType( javaType, inputMessage );

		Object tempObj = this.process( obj, type, inputMessage );

		return tempObj;
	}

	//這個就是父類的readJavaType方法,因爲父類該方法是private的,因此咱們copy一個用
	private Object readJavaType( JavaType javaType, HttpInputMessage inputMessage ) {
		try {
			return super.getObjectMapper().readValue( inputMessage.getBody(), javaType );
		} catch ( IOException ex ) {
			throw new HttpMessageNotReadableException( "Could not read JSON: " + ex.getMessage(), ex );
		}
	}
        
        
	protected Object process( Object obj, Type type, HttpInputMessage inputMessage ) {
		if ( this.isNeedProcess( inputMessage ) ) {
			return this.readAfter( obj, type );
		} else {
			return obj;
		}
	}

	//根據白名單,判斷當前請求路徑是否須要xss過濾
	protected boolean isNeedProcess( HttpInputMessage inputMessage ) {

		String url = "";

		try {
		        //通過debug發現inputMessage類型爲ServletServerHttpRequest,因此進行下類型轉換
			ServletServerHttpRequest request = ( ServletServerHttpRequest ) inputMessage;

			url = request.getURI().getPath();

			//根據白名單作下匹配,固然也能夠實現正則什麼的,代碼就不貼了

		} catch ( Exception e ) {
			logger.error( "BACK_ERROR," + this.getClass().getCanonicalName() + ",XSS處理-url處理失敗,url=" + url + ",ERROR=", e );
			return true;
		}

		return true;

	}

	//最重要的一步,進行xss過濾
	@Override
	public Object readAfter( Object obj, Type type ) {
		try {
		        //type實際上就是咱們須要convert的model,咱們經過反射來完成根據NeedXss註解對String
		        //的字段進行xss過濾
			Class clazz = Class.forName( JSON.toJSONString( type ).replace( "\"", "" ) );

			if ( clazz == null ) {
				return obj;
			}

			Field[] fields = clazz.getDeclaredFields();

			if ( fields != null && fields.length > 0 ) {
				// string類型字段名稱列表
				List<String> strList = new ArrayList<String>( fields.length );

				// 1. 將須要xss處理的string類型的字段放入strlist
				for ( int i = 0; i < fields.length; i++ ) {

				        // 1.1該屬性是否有NeedXss.class註解
					NeedXss needXss = fields[ i ].getAnnotation( NeedXss.class );
					// 1.2若是該沒有NeedXss.class註解,則不處理該屬性
					if ( needXss == null || !( needXss instanceof NeedXss ) ) {
						continue;
					}

					String mod = Modifier.toString( fields[ i ].getModifiers() );
					if ( mod.indexOf( "static" ) != -1 )
						continue;
					// 獲得屬性的類名
					String className = fields[ i ].getType().getSimpleName();
					// 獲得屬性字段名
					if ( className.equalsIgnoreCase( "String" ) ) {

						strList.add( fields[ i ].getName() );
					}

				}

				// 2.將strlist中的字段進行xss處理
				if ( strList.size() > 0 ) {

					Object temp = JSON.toJavaObject( ( JSON ) JSON.toJSON( obj ), clazz );

					for ( int i = 0; i < strList.size(); i++ ) {
						Method set = clazz.getMethod( "set" + strList.get( i ).substring( 0, 1 ).toUpperCase()
								+ strList.get( i ).substring( 1 ), String.class );
						Method get = clazz.getMethod( "get" + strList.get( i ).substring( 0, 1 ).toUpperCase()
								+ strList.get( i ).substring( 1 ) );

						Object tempObj = get.invoke( temp );

						if ( tempObj == null ) {
							break;
						}

						String content = tempObj.toString();

						set.invoke( temp, StringUtils.cleanXss( content ) );
					}

					return temp;
				}

			}

		} catch ( Exception e ) {
			logger.error( "BACK_ERROR," + this.getClass().getCanonicalName() + ",XSS處理失敗,obj=" + JSON.toJSONString( obj ) + ",javaType="
					+ JSON.toJSONString( type ) + ",ERROR=", e );
			return obj;
		}

		return obj;
	}
}

    4.配置使用自定義的XssMappingJacksonHttpMessageConverterurl

    在SpringMVC的配置文件裏面加上下面的配置

    <mvc:annotation-driven >
		<mvc:message-converters register-defaults="true">
			<!-- 將StringHttpMessageConverter的默認編碼設爲UTF-8. -->
			<bean class="org.springframework.http.converter.StringHttpMessageConverter">
				<constructor-arg value="UTF-8" />
			</bean>
			
			<bean class="com.zhubajie.seller.common.converter.XssMappingJacksonHttpMessageConverter">
				<property name="prettyPrint" value="false" />
			</bean>
		</mvc:message-converters>
	</mvc:annotation-driven>

   最後,不出意外應該就是能夠啦

相關文章
相關標籤/搜索