繼上一篇實現的註解式過濾器,對請求進行加解密

本篇的實現方式是基於上一篇:https://my.oschina.net/u/1428688/blog/2252569java

註解定義:web

@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
@Documented
@Inherited
@ParamMapResolver
// 最高優先級
@Order(Ordered.HIGHEST_PRECEDENCE)
public @interface SafeRequest {
	boolean disable() default false;

	Class<? extends BaseDecrypt> decriptClass() default BaseDecrypt.class;

}

 

數據解密過濾器實現app

@Component
@Slf4j
public class SafeRequestInterceptor extends BaseAnnotationInterceptor<SafeRequest> {

	private static final String APPLICATION_FORM_URLENCODED_UTF8_VALUE = "application/x-www-form-urlencoded; charset=UTF-8";

	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod,
			SafeRequest annotation) {
		String contentType = request.getContentType();
		if (!APPLICATION_FORM_URLENCODED_UTF8_VALUE.equalsIgnoreCase(contentType)) {
			log.error("錯誤的contentType:{}", contentType);
			ResponseUtils.writeJson(response, new Resp<>("請求contentType必須是" + APPLICATION_FORM_URLENCODED_UTF8_VALUE));
			return false;
		}
		if (!RequestHold.isAjax(request)) {
			ResponseUtils.writeJson(response, new Resp<>("缺乏請求頭x-requested-with:XMLHttpRequest"));
			return false;
		}
		if (!handlerMethod.getReturnType().getMethod().getReturnType().isAssignableFrom(Resp.class)) {
			log.error("{}請求返回值不規範,必須使用Resp作爲響應值", request.getRequestURI());
			ResponseUtils.writeJson(response, new Resp<>("控制器層響應值必須Resp"));
			return false;
		}
		String key = (String) WebUtils.getSessionAttribute(request, Const.SESSION_TOKEN);
		if (key == null || key.length() < 16) {
			return false;
		}
		key = key.substring(0, 16);
		BaseDecrypt baseDecrypt = null;
		try {
			baseDecrypt = annotation.decriptClass().newInstance();
		} catch (InstantiationException | IllegalAccessException e) {
			log.error("baseDecrypt沒法實例化", annotation.decriptClass());
			return false;
		}
		// 進行參數綁定
		// 將key-value封裝爲map,傳給bind方法進行參數值綁定
		Map<String, String> paramMap = new HashMap<>();
		Map<String, String[]> params = request.getParameterMap();
		for (Map.Entry<String, String[]> entry : params.entrySet()) {
			String value = null;
			for (String val : entry.getValue()) {
				if (StringUtils.isNotBlank(val)) {
					val = baseDecrypt.decrypt(key, val);
					if (val == null) {
						log.error("屬性{}解密失敗", entry.getKey());
						ResponseUtils.writeJson(response, new Resp<>("屬性" + entry.getKey() + "解密失敗"));
						return false;
					}
				}
				if (value == null) {
					value = val;
				} else {
					value = "," + val;
				}
			}
			paramMap.put(entry.getKey(), value);
		}
		request.setAttribute(Const.REQUEST_RESOLVER_PARAM_MAP_NAME, paramMap);
		return true;
	}

}

 

自定義的參數解析器實現邏輯:ide

@SuppressWarnings("unchecked")
	@Override
	public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
		if (binderFactory == null) {
			return null;
		}
		// 解析器中的自定義邏輯
		HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
		Map<String, String> paramMap = (Map<String, String>) request
				.getAttribute(Const.REQUEST_RESOLVER_PARAM_MAP_NAME);
		Object arg = paramMap.get(parameter.getParameterName());
		if (arg != null) {
			// 生成參數綁定器,第一個參數爲request請求對象,第二個參數爲須要綁定的目標對象,第三個參數爲須要綁定的目標對象名
			WebDataBinder binder = binderFactory.createBinder(webRequest, null, parameter.getParameterName());
			arg = binder.convertIfNecessary(arg, parameter.getParameterType(), parameter);
		} else {
			String name = parameter.getParameterName();
			// 查找是否已有名爲name的參數值的映射,若是沒有則建立一個
			arg = mavContainer.containsAttribute(name) ? mavContainer.getModel().get(name)
					: BeanUtils.instantiateClass(parameter.getParameterType());
			WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name);
			if (binder.getTarget() != null) {
				PropertyValues propertyValues = new MutablePropertyValues(paramMap);
				// 將K-V綁定到binder.target屬性上
				binder.bind(propertyValues);
			}
			// 將參數轉到預期類型,第一個參數爲解析後的值,第二個參數爲綁定Controller參數的類型,第三個參數爲綁定的Controller參數
			arg = binder.convertIfNecessary(binder.getTarget(), parameter.getParameterType(), parameter);
		}
		return arg;
	}

控制器層使用方式,示例:加密

@PostMapping(value = "updateMyPassword")
	@SafeRequest
	@Token
	public Resp<?> updateMyPassword(HttpServletRequest request, String username, String oldPassword,
			String newPassword) {
		SysUser current = UserHolder.getCurrentSysUser(request);
		current = getService().load(current.getId());
		if (!current.getUsername().equals(username)) {
			return new Resp<>("用戶名錯誤");
		}
		if (!current.getPassword().equals(PasswordUtil.encode(oldPassword))) {
			return new Resp<>("密碼錯誤");
		}
		PasswordUtil.validPassword(newPassword);
		current.setPassword(PasswordUtil.encode(newPassword));
		current.setPasswordUpdateTime(new Date());
		getService().save(current);
		WebUtils.setSessionAttribute(request, Const.SESSION_SYS_USER, current);
		return new Resp<>();
	}

過程解析:url

頁面對每個參數的值進行加密,參數名不用加密,發送加密過的數據到後臺,後臺統一對帶有SafeRequest註解的請求進行攔截,取出加密過的數據進行解密,並經過自定義參數解析綁定到控制層來.net

相關文章
相關標籤/搜索