Rest风格下验证POST和GET请求

导读:本篇文章讲解 Rest风格下验证POST和GET请求,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

       HTTP分为两种请求,GET以及POST。现如今,请求中都会带有大量的验证字符。

       我验证的方式一般分为两种:

      1。用当前的时间生成一个时间戳,然后用这个时间戳加盐的方式去用MD5或者其他的加密方式生成一个key。将这些字符传到后台以后后台接收这些参数,然后以特定的格式在后台再以同样的公式生成key,与前台传过来的key进行比对。

       2。前台传的所有值当成一个键值对的map,然后在map中加入一些随机参数,用MD5或者其他的加密方式生成一个key。后台以同样的方式生成key去匹配前台传过来的校验字符串。

     代码一般需要做到简洁,所以在验证方面我们肯定希望在代码的同一个地方去做处理校验,因此我想到了拦截器,去拦截所有请求,获取所有参数,在拦截器中进行校验操作。但是拦截器有一个弊端:

request.getParameterMap()   // 只可以获取url地址上的参数

比如 当前的请求地址为    http://localhost/test?a=1&b=2。通过拦截器是可以截取到当前的参数的。

但是当我们的参数不在url地址上,也就是说如果 前端发来一个POST 请求,参数是以 body 形式传来的,这时候用拦截器是无法截取到请求的参数,上述代码中返回的是一个空的HashMap。

拦截器的具体实现:

@Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String token = request.getHeader("token");
        String url = request.getRequestURI();
        if(request.getParameterMap().size() > 0){
            // 拦截URl参数请求,在切面拦截BODY请求参数
            Map<String,String[]> map = request.getParameterMap();
            Map<String,Object> signMap = new HashMap<>();
            for(Map.Entry<String,String[]> entry:map.entrySet()){
                if(!entry.getKey().equals("sign")){
                    signMap.put(entry.getKey(),entry.getValue());
                }
            }
            MessageDigest md5 = MessageDigest.getInstance("MD5");
            byte[] str = md5.digest((signMap.toString()).getBytes());
            if(map.get("sign") != null && !CommonUtil.byteArrayToHexString(str).equals(Arrays.toString(map.get("sign")))){
                returnJson(response, "校验参数有误");
                return false;
            }
        }
        if(!isWhiteUrl(url)) {
            if (StringUtils.isBlank(token)) {
                returnJson(response, "token不能为空");
                return false;
            }
            if (userMapper.selectByPrimaryToken(null, token) == null) {
                returnJson(response, "未找到用户");
                return false;
            }
        }
        return true;
    }

protected void returnJson(HttpServletResponse response,String json){
        PrintWriter writer = null;
        response.setCharacterEncoding("UTF-8");
        response.setContentType("text/html;charset=utf-8");
        try{
            writer = response.getWriter();
            Map<String,Object> map = new HashMap<>();
            map.put("data",new HashMap<>());
            map.put("err_code",0);
            map.put("reason",json);
            writer.print(JSONUtils.toJSONString(map));
        }catch (IOException e){
            e.printStackTrace();
        }finally {
            if(writer != null ){
                writer.close();
            }
        }
    }

那么,如何在 POST 请求中截取 body 参数呢?

      查阅了大量的资料后,网上有一种方式是新建一个类去重写 HttpServletRequestWrapper 类,重写他的所有方法。作者实验了这种方式,但是一直在代码中会报 流关闭 的异常,一直未能解决该异常,所以我想了第二种方式。

      因为如果参数在URL上我是可以获取的,仅仅是要适配post请求的参数,所以这个是具体到了方法,因此我想到可以通过AOP的方式去拦截参数,而又不是拦截所有的方法,因此自定义一个注解去标记需要拦截的方法

自定义的注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface PostVerify{
}

AOP具体的实现

@Pointcut("execution(public * com.app.stock.controller.*.*(..))")
    public void checkRequest() {
    }

    @Pointcut("@annotation(com.app.stock.annoation.PostVerify)")
    public void annoationPoint(){};

    @Before("checkRequest() && annoationPoint()")
    public void before(JoinPoint joinPoint) throws Exception {
        Map<String, Object> map;
        if(joinPoint.getArgs()[0] instanceof Map){
            map = (Map) joinPoint.getArgs()[0];
        }else{
            map = CommonUtil.beanToMap(joinPoint.getArgs()[0]);
        }
        Map<String,Object> signMap = new LinkedHashMap<>();
        for(Map.Entry<String,Object> entry:map.entrySet()){
            if(!entry.getKey().equals("sign")){
                signMap.put(entry.getKey(),entry.getValue());
            }
        }
        MessageDigest md5 = MessageDigest.getInstance("MD5");
        byte[] str = md5.digest((signMap.toString()).getBytes());
        if(map.get("sign") != null && !CommonUtil.byteArrayToHexString(str).equals(map.get("sign").toString())){
            // 抛出异常,去上一层截取
            throw new VerifyException("");
        }
    }

    @Around("checkRequest()")
    public Response around(ProceedingJoinPoint pjp) throws Throwable {
        Response response = new Response();
        response.setReason("操作成功");
        response.setData(new HashMap<>());
        try{
            response = (Response) pjp.proceed();
        }catch (VerifyException e){
            response.setReason("校验失败");
        }catch (Exception e){
            logger.error("异常信息",e);
            response.setReason("系统异常");
        }
        return response;
    }

因为获取到参数时在@Before中获取的,因此在 @Before中加入限制条件 

@Before("checkRequest() && annoationPoint()")

只有满足这两个条件的才会走 before,否则只会进入 AOP 的 Around,当在@before中作校验时,如果校验的字符串和后台生成的字符串不相同,则向上抛出一个自定义的异常,在 Around 中 去catch 到 before 中抛出的异常,然后返回给前端提示信息 “校验失败” 即可。

如果校验失败的话即返回

{
  "data": "{}",
  "reason": "校验失败",
  "error_code": 0
}

总结:

      1。 用继承的方式也可以很好的解决拦截body请求的问题(可能是要搭配过滤器一起使用)

      2。如果用AOP的方式去实现的话也可以很好的解决问题,在AOP中可以做更好的自适应配置,如果用我的方法的话记得在对应的controller的方法上加上自定义注解

      3。如果不嫌麻烦的话可以写一个共通的方法,去用反射的方式进行验证,然后在每个方法中进行引用即可。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/6481.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!