继上一篇实现的注解式过滤器,对请求进行加解密

本篇的实现方式是基于上一篇: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

相关文章
相关标签/搜索