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