美文网首页程序员
为合作公司提供接口的权限验证问题 拦截器 cookie

为合作公司提供接口的权限验证问题 拦截器 cookie

作者: 刘振宁的博客 | 来源:发表于2019-01-24 15:57 被阅读13次

    巨大的建筑,总是由一木一石叠起来的,我们何妨做做这一木一石呢?我时常做些零碎事,就是为此。
    这是对的,但是我没有说过这句话! —— 鲁迅

    问题描述

    通过Api为第三方公司提供接口服务是一项很common的需求,其中一种实现方式就是利用restful的形式,提供http接口服务.
    提供出接口之后,谁都可以来访问,对系统本身是有一定危险性的,所以我们要做一下验证信息,来确认对方的身份.

    对于自己内部的接口,比如用于提供给mask(前台页面)访问的api,可以做登录验证,只有登录了,才可以访问。
    但是对于第三方公司而言,不是我们的用户,属于合作伙伴,性质不一样.
    我们需要给他们提供10个接口,这10个接口是需要放开权限的,不然直接访问的话,会直接提示:*未登录* 的错误。
    权限放开了,那么权限如何验证呢.

    权限验证

    不增加任何验证,直接可以访问

    安全性差,比如推送数据的接口(一般数据是有格式的,他要是自己猜确实不好猜到),假如第三方公司在正常使用的时候,请求数据推送,
    被坏人截取到了相应的信息。通过分析,他就可以一直推送,知道把你的数据库填满.

    接口的验证

    1. 客户端访问接口,需要佩戴token,假如这个token正好是我们产生的,则验证通过,token有时效限制。
    2. 这样一来,需要提供一个接口,便是产生token的接口,而这个接口是开放的,谁都可以访问。
    3. 那么这时候,假如一个坏人想要恶意访问的话,他首先需要知道这个产生token的接口是什么,其次需要知道自己要访问的接口是什么,然后再访问

    安全性比开始的已经大一些了。此时假如他截取到了某次请求的数据,再请求的话,就只能在实效内管用了.

    对token产生的接口进行验证处理

    上面是对token产生的接口可以随意访问,一旦被人知道了这个接口,则可以随意获取token值了,加一个判断的话,比如需要用户名,和密码,则可以进行一种验证。
    简单的写法,就是对不同的第三方协议公司定一个用户名,密码,直接传过来.

    除了验证身份之外,加上授权处理.

    这种情况就跟咱们的登录差不多了,但是身份不一样。
    一般登录的设计里面,都会有角色的设计,身份不一致的话,可以新增一种角色,这种角色就给服务公司提供一种控制,这样就类似于登录了.
    但是一般对接公司需要的接口都是固定的,假如需要根据功能收费的话,授权是需要考虑的.

    以下主要说一下接口的简单验证.

    接口的验证

    提供产生token接口

    比如直接返回UUID

    @RequestMapping(value = "/", method = RequestMethod.POST)
    @ResponseBody
    public Result<String, State> getToken(HttpServletRequest request, HttpServletResponse response) {
      Result<String, State> result = new Result<>();
      UUID uuid = UUID.randomUUID();
      String token = uuid.toString();
      //放到redis里面,实效为30分钟.
      RedisCacheUtil.setex(BUSSINESSKEY, token, token, 30 * 60);
      result.setCode(State.SUCCESS);
      result.setData(token);
      return result;
    }
    

    这样,就把生成的token返回了,那么接口的写法如下:

    @RequestMapping(value = "/", method = RequestMethod.GET)
    @ResponseBody
    public Result<String, State> getSomeThing(@RequestParam String token,
                                              String param) {
      Result<String, State> result = new Result<>();
      String tokenRedis = RedisCacheUtil.get(BUSSINESSKEY, token, String.class);
      if (token.equals(tokenRedis) {
          result.setCode(State.SUCCESS);
      } else {
          result.setCode(State.FAILED);
      }
      return result;
    }
    

    关于redis里key值的讨论

    开始我打算用请求方的ip,假如是ip的话,那么同一个ip则会用同一个token,不同的ip用不同的token,假如你
    知道了别人的token,不是同一个ip,也照样不能访问。
    现实发现问题,是因为我们的接口是写在api服务里面,而api服务是一个内网服务,第三方(App)是通过请求我们的nginx服务转发到api里面。
    这样的话,每次我获取到的ip,都是我们自己服务器的内网地址,并不能用.

    后来发现用token作为key值也可以,这样每个用户,就拥有自己的token了,但是问题是随便一个人一旦获取到了别人的token,在token相应未失效的时候,也是可以用的.

    用拦截器来处理token的验证。

    按照上面的方式写接口,假如提供10个接口的话,则在每个接口中都需要接收token参数.不好维护。

    配置拦截器

    <mvc:interceptors>
        <!--跨域过滤器-->
        <bean class="com.enn.zhwl.interceptor.CrossInterceptor"/>
    
        <mvc:interceptor>
            <mvc:mapping path="/hdd/out/**" />
            <mvc:exclude-mapping path="/hdd/out/token/**" />
            <bean class="com.enn.zhwl.interceptor.HddInterceptor" />
        </mvc:interceptor>
    </mvc:interceptors>
    
    • springMVC框架中,在servlet.xml中加入拦截器的配置
    • mvc:mapping 是改拦截器对哪些类生效
    • mvc:exclude-mapping 是将上面生效的文件中剔除一些类
    • bean 则是拦截器的实现类.

    拦截器实现

    public class HddInterceptor implements HandlerInterceptor {
      private static final String BUSSINESSKEY = "HDDTOKEN";
    
      @Override
      public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String token = request.getParameter("token");
        String tokenRedis = RedisCacheUtil.get(BUSSINESSKEY, token, String.class);
    
        if (token.equals(tokenRedis) {
           return true;
        } else {
          PrintWriter writer = response.getWriter();
          writer.write("token error");
          writer.close();
           return false;
        }
    
      }
    
      @Override
      public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    
      }
    
      @Override
      public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    
      }
    
    }
    

    token作为参数传递的问题

    这样是让客户端将token值放到请求的参数中,来进行接收。
    现在有一种情况,就是一般接口提供有两种方式。

    • 接收普通变量,比如String 等类型的值
    • 接收对象类型(@RequestBody)。

    接收普通变量和对象类型时,发送的ajax请求的 contentType 是不一样的。
    发送对象数据的时候,contentType='application/json',对于这种情况,直接用 request.getParameter 就获取不到了.

    token放到cookie中进行传递

    一般登录验证的时候,token就是放到cookie中的.放到cookie中可以解决上面的问题。
    而且还方便,客户端完全感知不到token的存在,也不用在调用方法的时候,显示传递.

    修改设置token的方法

    将token放到cookie中而不是放到data中.

    public Result<String, State> getToken(HttpServletRequest request, HttpServletResponse response) {
      Result<String, State> result = new Result<>();
      UUID uuid = UUID.randomUUID();
      String token = uuid.toString();
      //放到redis里面,实效为30分钟.
      RedisCacheUtil.setex(BUSSINESSKEY, token, token, 30 * 60);
      //放到cookie中而不是放到data中.
      Cookie cookie = new Cookie("hddtoken", token);
      cookie.setPath("/");
      response.addCookie(cookie);
      result.setCode(State.SUCCESS);
      return result;
    }
    

    修改拦截器的实现

    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
      //String token = request.getParameter("token");
      Cookie[] cookies = request.getCookies();
      Optional<Cookie> cookieToken = Arrays.stream(cookies).filter(cookie -> cookie.getName().equals("hddtoken")).findFirst();
      //这里需要判断token是否存在,就不写了
      String token = cookieToken.get().getValue();
      String tokenRedis = RedisCacheUtil.get(BUSSINESSKEY, token, String.class);
    
      if (token.equals(tokenRedis) {
          return true;
      } else {
        PrintWriter writer = response.getWriter();
        writer.write("token error");
        writer.close();
        return false;
      }
    }
    

    这样客户端也不用传了,接收端也不用接收了,一切都默默的执行,只需要在请求之前,先调用一下获取token的接口就行了.

    这里是写了一些简单验证是思路与遇到的问题,至于加密啊,密码啊,授权啊等便是另外的事了.

    相关文章

      网友评论

        本文标题:为合作公司提供接口的权限验证问题 拦截器 cookie

        本文链接:https://www.haomeiwen.com/subject/bgxujqtx.html