• 技术文章 >Java >java教程

    Java实现接口校验的三种方式

    黄舟黄舟2017-10-19 09:29:08原创918
    这篇文章主要介绍了浅谈Java 三种方式实现接口校验,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

    本文介绍了Java 三种方式实现接口校验,主要包括AOP,MVC拦截器,分享给大家,具体如下:

    方法一:AOP

    代码如下定义一个权限注解


    package com.thinkgem.jeesite.common.annotation; 
     
    import java.lang.annotation.ElementType; 
    import java.lang.annotation.Retention; 
    import java.lang.annotation.RetentionPolicy; 
    import java.lang.annotation.Target; 
     
    /** 
     * 权限注解 
     * Created by Hamming on 2016/12/ 
     */ 
    @Target(ElementType.METHOD)//这个注解是应用在方法上 
    @Retention(RetentionPolicy.RUNTIME) 
    public @interface AccessToken { 
    /*  String userId(); 
      String token();*/ 
    }

    获取页面请求中的ID token


    @Aspect 
    @Component 
    public class AccessTokenAspect { 
     
      @Autowired 
      private ApiService apiService; 
     
      @Around("@annotation(com.thinkgem.jeesite.common.annotation.AccessToken)") 
      public Object doAccessCheck(ProceedingJoinPoint pjp) throws Throwable{ 
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); 
        String id = request.getParameter("id"); 
        String token = request.getParameter("token"); 
        boolean verify = apiService.verifyToken(id,token); 
        if(verify){ 
          Object object = pjp.proceed(); //执行连接点方法 
          //获取执行方法的参数 
     
          return object; 
        }else { 
          return ResultApp.error(3,"token失效"); 
        } 
      } 
    }

    token验证类 存储用到redis


    package com.thinkgem.jeesite.common.service; 
     
    import com.thinkgem.jeesite.common.utils.JedisUtils; 
    import io.jsonwebtoken.Jwts; 
    import io.jsonwebtoken.SignatureAlgorithm; 
    import io.jsonwebtoken.impl.crypto.MacProvider; 
    import org.slf4j.Logger; 
    import org.slf4j.LoggerFactory; 
    import org.springframework.beans.factory.annotation.Autowired; 
    import org.springframework.stereotype.Service; 
    import org.springframework.transaction.annotation.Transactional; 
    import redis.clients.jedis.Jedis; 
     
    import java.io.*; 
    import java.security.Key; 
    import java.util.Date; 
     
    /** 
     *token登陆验证 
     * Created by Hamming on 2016/12/ 
     */ 
    @Service 
    public class ApiService { 
      private static final String at="accessToken"; 
     
      public static Key key; 
     
    //  private Logger logger = LoggerFactorygetLogger(getClass()); 
      /** 
       * 生成token 
       * Key以字节流形式存入redis 
       * 
       * @param date 失效时间 
       * @param appId AppId 
       * @return 
       */ 
      public String generateToken(Date date, String appId){ 
        Jedis jedis = null; 
        try { 
          jedis = JedisUtils.getResource(); 
          byte[] buf = jedis.get("api:key".getBytes()); 
          if (buf == null) { // 建新的key 
            key = MacProvider.generateKey(); 
            ByteArrayOutputStream bao = new ByteArrayOutputStream(); 
            ObjectOutputStream oos = new ObjectOutputStream(bao); 
            oos.writeObject(key); 
            buf = bao.toByteArray(); 
            jedis.set("api:key".getBytes(), buf); 
          } else { // 重用老key 
            key = (Key) new ObjectInputStream(new ByteArrayInputStream(buf)).readObject(); 
          } 
     
        }catch (IOException io){ 
    //      System.out.println(io); 
        }catch (ClassNotFoundException c){ 
    //      System.out.println(c); 
        }catch (Exception e) { 
    //      logger.error("ApiService", "generateToken", key, e); 
        } finally { 
          JedisUtils.returnResource(jedis); 
        } 
     
        String token = Jwts.builder() 
            .setSubject(appId) 
            .signWith(SignatureAlgorithm.HS512, key) 
            .setExpiration(date) 
            .compact(); 
        // 计算失效秒,7889400秒三个月 
        Date temp = new Date(); 
        long interval = (date.getTime() - temp.getTime())/1000; 
        JedisUtils.set(at+appId ,token,(int)interval); 
        return token; 
      } 
     
      /** 
       * 验证token 
       * @param appId AppId 
       * @param token token 
       * @return 
       */ 
      public boolean verifyToken(String appId, String token) { 
        if( appId == null|| token == null){ 
          return false; 
        } 
        Jedis jedis = null; 
        try { 
          jedis = JedisUtils.getResource(); 
          if (key == null) { 
            byte[] buf = jedis.get("api:key".getBytes()); 
            if(buf==null){ 
              return false; 
            } 
            key = (Key) new ObjectInputStream(new ByteArrayInputStream(buf))readObject(); 
          } 
          Jwts.parser().setSigningKey(key).parseClaimsJws(token).getBody().getSubject().equals(appId); 
          return true; 
        } catch (Exception e) { 
    //      logger.error("ApiService", "verifyToken", key, e); 
          return false; 
        } finally { 
          JedisUtils.returnResource(jedis); 
        } 
      } 
     
      /** 
       * 获取token 
       * @param appId 
       * @return 
       */ 
      public String getToken(String appId) { 
        Jedis jedis = null; 
        try { 
          jedis = JedisUtils.getResource(); 
          return jedis.get(at+appId); 
        } catch (Exception e) { 
    //      logger.error("ApiService", "getToken", e); 
          return ""; 
        } finally { 
          JedisUtils.returnResource(jedis); 
        } 
      } 
    }

    spring aop配置


    <!--aop --> 
    <!--   扫描注解bean --> 
    <context:component-scan base-package="com.thinkgem.jeesite.common.aspect"/> 
     <aop:aspectj-autoproxy proxy-target-class="true"/>

    验证权限方法使用 直接用注解就可以了AccessToken

    例如


    package com.thinkgem.jeesite.modules.app.web.pay; 
     
    import com.alibaba.fastjson.JSON; 
    import com.thinkgem.jeesite.common.annotation.AccessToken; 
    import com.thinkgem.jeesite.common.base.ResultApp; 
    import com.thinkgem.jeesite.modules.app.service.pay.AppAlipayConfService; 
    import org.springframework.beans.factory.annotation.Autowired; 
    import org.springframework.stereotype.Controller; 
    import org.springframework.web.bind.annotation.RequestMapping; 
    import org.springframework.web.bind.annotation.RequestMethod; 
    import org.springframework.web.bind.annotation.ResponseBody; 
     
    import java.util.HashMap; 
    import java.util.Map; 
     
    /** 
     * 支付接口 
     * Created by Hamming on 2016/12/ 
     */ 
    @Controller 
    @RequestMapping(value = "/app/pay") 
    public class AppPayModule { 
     
      @Autowired 
      private AppAlipayConfService appAlipayConfService; 
     
      @RequestMapping(value = "/alipay", method = RequestMethodPOST, produces="application/json") 
      @AccessToken 
      @ResponseBody 
      public Object alipay(String orderId){ 
        if(orderId ==null){ 
          Map re = new HashMap<>(); 
          re.put("result",3); 
          re.put("msg","参数错误"); 
          String json = JSONtoJSONString(re); 
          return json; 
        }else { 
          return null; 
        } 
      } 
    }

    方法二: AOP方法2

    1.定义一个查询父类,里面包含到authToken跟usedId两个属性,所有需要校验用户的请求的查询参数都继承这个查询父类,之所以会有这个userId,是因为我们校验得到用户之后,需要根据用户Id获取一些用户数据的,所以在AOP层我们就回填了这个参数了,这样也不会影响之前的代码逻辑(这个可能跟我的业务需求有关了)


    public class AuthSearchVO {
      
      public String authToken; //校验字符串
      
      public Integer userId; //APP用户Id
      
      public final String getAuthToken() {
        return authToken;
      }
    
      public final void setAuthToken(String authToken) {
        this.authToken = authToken;
      }
    
      public final Integer getUserId() {
        return userId;
      }
    
      public final void setUserId(Integer userId) {
        this.userId = userId;
      }
    
      @Override
      public String toString() {
        return "SearchVO [authToken=" + authToken + ", userId=" + userId + "]";
      }
    
    }

    2.定义一个方法级的注解,所有需要校验的请求都加上这个注解,用于AOP的拦截(当然你也可以拦截所有控制器的请求)


    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface AuthToken {
    String type();
    }

    3.AOP处理,之所以会将注解作为参数传进来,是因为考虑到可能会有多个APP的校验,可以利用注解的type属性加以区分


    public class AuthTokenAOPInterceptor {
    
    @Resource
    private AppUserService appUserService;
    
    private static final String authFieldName = "authToken";
    private static final String userIdFieldName = "userId";
    
    public void before(JoinPoint joinPoint, AuthToken authToken) throws Throwable{
    
      Object[] args = joinPoint.getArgs(); //获取拦截方法的参数
      boolean isFound = false;
      for(Object arg : args){
        if(arg != null){
          Class<?> clazz = arg.getClass();//利用反射获取属性值
          Field[] fields = clazz.getDeclaredFields();
          int authIndex = -1;
          int userIdIndex = -1;
          for(int i = 0; i < fields.length; i++){
            Field field = fields[i];
            field.setAccessible(true);
            if(authFieldName.equals(field.getName())){//包含校验Token
              authIndex = i;
            }else if(userIdFieldName.equals(field.getName())){//包含用户Id
              userIdIndex = i;
            }
          }
    
          if(authIndex >= 0 & userIdIndex >= 0){
            isFound = true;
            authTokenCheck(fields[authIndex], fields[userIdIndex], arg, authToken);//校验用户
            break;
          }
        }
      }
      if(!isFound){
        throw new BizException(ErrorMessage.CHECK_AUTHTOKEN_FAIL);
      }
    
    }
    
    private void authTokenCheck(Field authField, Field userIdField, Object arg, AuthToken authToken) throws Exception{
      if(String.class == authField.getType()){
        String authTokenStr = (String)authField.get(arg);//获取到校验Token
        AppUser user = appUserService.getUserByAuthToken(authTokenStr);
        if(user != null){
          userIdField.set(arg, user.getId());
        }else{
          throw new BizException(ErrorMessage.CHECK_AUTHTOKEN_FAIL);
        }
      }
    
    }
    }

    4.最后就是在配置文件中配置这个AOP了(因为我们的spring版本跟aspect版本有点出入,导致用不了基于注解的方式)


    <bean id="authTokenAOPInterceptor" class="com.distinct.app.web.common.auth.AuthTokenAOPInterceptor"/>
    <aop:config proxy-target-class="true">
      <aop:pointcut id="authCheckPointcut" expression="@annotation(authToken)"/>
      <aop:aspect ref="authTokenAOPInterceptor" order="1">
        <aop:before method="before" pointcut-ref="authCheckPointcut"/>
      </aop:aspect>
    </aop:config>

    最后给出测试代码,这样的代码就优雅很多了


    @RequestMapping(value = "/appointments", method = { RequestMethod.GET })
    @ResponseBody
    @AuthToken(type="disticntApp")
    public List<AppointmentVo> getAppointments(AppointmentSearchVo appointmentSearchVo) {
      List<AppointmentVo> appointments = appointmentService.getAppointment(appointmentSearchVo.getUserId(), appointmentSearchVo);
      return appointments;
    }

    方法三: MVC拦截器

    服务器:

    拼接token之外所有参数,最后拼接token_key,做MD5,与token参数比对

    如果token比对失败返回状态码 500


    public class APIInterceptor extends HandlerInterceptorAdapter { 
     
      @Override 
      public boolean preHandle(HttpServletRequest request, 
          HttpServletResponse response, Object handler) throws Exception { 
        Log.info(request); 
         
        String token = request.getParameter("token"); 
         
        // token is not needed when debug 
        if(token == null) return true; // !! remember to comment this when deploy on server !! 
         
        Enumeration paraKeys = request.getParameterNames(); 
        String encodeStr = ""; 
        while (paraKeys.hasMoreElements()) { 
          String paraKey = (String) paraKeys.nextElement(); 
          if(paraKey.equals("token"))  
            break; 
          String paraValue = request.getParameter(paraKey); 
          encodeStr += paraValue; 
        } 
        encodeStr += Default.TOKEN_KEY; 
        Log.out(encodeStr); 
         
        if ( ! token.equals(DigestUtils.md5Hex(encodeStr))) { 
          response.setStatus(500); 
          return false; 
        } 
         
        return true; 
      } 
     
      @Override 
      public void postHandle(HttpServletRequest request, 
          HttpServletResponse response, Object handler, 
          ModelAndView modelAndView) throws Exception { 
        Log.info(request); 
      } 
     
      @Override 
      public void afterCompletion(HttpServletRequest request, 
          HttpServletResponse response, Object handler, Exception ex) 
          throws Exception { 
         
      } 
    }

    spring-config.xml配置中加入


    <mvc:interceptors> 
      <mvc:interceptor> 
        <mvc:mapping path="/api/*" /> 
        <bean class="cn.web.interceptor.APIInterceptor" /> 
      </mvc:interceptor> 
    </mvc:interceptors>

    客户端:

    拼接请求接口的所有参数,最后拼接token_key,做MD5,作为token参数

    请求样例:http://127.0.0.1:8080/interface/api?key0=param0&key1=param1&token=md5(concat(param0, param1))

    api测试页面,用到了Bootstrap和AngularJS,还有一个js的hex_md5函数


    <!doctype html> 
    <html ng-app> 
    <head> 
      <meta charset="UTF-8"> 
      <title>API test</title> 
      <link href="../css/bootstrap.min.css" rel="external nofollow" rel="stylesheet"> 
      <script src="../js/md5.min.js"></script> 
      <script src="../js/angular.min.js"></script> 
      <script> 
        function API(url){ 
          this.url = arguments[0]; 
          this.params = Array.prototype.slice.call(arguments, 1, arguments.length); 
          this.request = function(params){ 
            var addr = url; 
            var values = Array.prototype.slice.call(arguments, 1, arguments.length); 
            if(params[0] != undefined && values[0] != undefined && values[0] != '') 
              addr += '?' + params[0] + "=" + values[0]; 
            for(var i=1; i < valueslength; i++) 
              if(params[i] != undefined && values[i] != undefined && values[i] != '') 
                addr += "&" + params[i] + "=" + values[i]; 
            return addr; 
          } 
        } 
         
        function APIListCtrl($scope) { 
          $scope.md5 = hex_md5; 
          $scope.token_key = "9ae5r06fs8"; 
          $scope.concat = function(){ 
            var args = Array.prototype.slice.call(arguments, 0, arguments.length); 
            args.push($scope.token_key); 
            return args.join(""); 
          } 
           
          $scope.apilist = [ 
           
          new API("account/login", "username", "pwd"), 
          new API("account/register", "username", "pwd", "tel", "code"), 
           
          ] ; 
        } 
      </script> 
    </head> 
    <body> 
     
      <p ng-controller="APIListCtrl"> 
        <p> Search: <input type="text" ng-model="search"><hr> 
        token_key <input type="text" ng-model="token_key"> 
        md5 <input type="text" ng-model="str"> {{md5(str)}} 
        </p> 
        <hr> 
        <p ng-repeat="api in apilist | filter:search" > 
          <form action="{{api.url}}" method="post"> 
          <a href="{{api.request(api.params, value0, value1, value2, value3, value4, value5, value6, value7, value8, value9)}}" rel="external nofollow" > 
          {{api.request(api.params, value0, value1, value2, value3, value4, value5, value6, value7, value8, value9)}} 
          </a> 
          <br> 
          {{concat(value0, value1, value2, value3, value4, value5, value6, value7, value8, value9)}} 
          <br> 
          {{api.params[0]}} <input id="{{api.params[0]}}" name="{{api.params[0]}}" ng-model="value0" ng-hide="api.params[0]==undefined"> 
          {{api.params[1]}} <input id="{{api.params[1]}}" name="{{api.params[1]}}" ng-model="value1" ng-hide="api.params[1]==undefined"> 
          {{api.params[2]}} <input id="{{api.params[2]}}" name="{{api.params[2]}}" ng-model="value2" ng-hide="api.params[2]==undefined"> 
          {{api.params[3]}} <input id="{{api.params[3]}}" name="{{api.params[3]}}" ng-model="value3" ng-hide="api.params[3]==undefined"> 
          {{api.params[4]}} <input id="{{api.params[4]}}" name="{{api.params[4]}}" ng-model="value4" ng-hide="api.params[4]==undefined"> 
          {{api.params[5]}} <input id="{{api.params[5]}}" name="{{api.params[5]}}" ng-model="value5" ng-hide="api.params[5]==undefined"> 
          {{api.params[6]}} <input id="{{api.params[6]}}" name="{{api.params[6]}}" ng-model="value6" ng-hide="api.params[6]==undefined"> 
          {{api.params[7]}} <input id="{{api.params[7]}}" name="{{api.params[7]}}" ng-model="value7" ng-hide="api.params[7]==undefined"> 
          {{api.params[8]}} <input id="{{api.params[8]}}" name="{{api.params[8]}}" ng-model="value8" ng-hide="api.params[8]==undefined"> 
          {{api.params[9]}} <input id="{{api.params[9]}}" name="{{api.params[9]}}" ng-model="value9" ng-hide="api.params[9]==undefined"> 
          token <input id="token" name="token" value="{{md5(concat(value0, value1, value2, value3, value4, value5, value6, value7, value8, value9))}}"> 
          <input type="submit" class="btn" ng-hide="api.params[0]==undefined"> 
          </form> 
          <hr> 
        </p> 
      </p> 
     
    </body> 
    </html>

    以上就是Java实现接口校验的三种方式的详细内容,更多请关注php中文网其它相关文章!

    声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn核实处理。
    专题推荐:Java 三种 校验
    上一篇:Java实现扫描包的工具类的实例 下一篇:shell脚本实现运行Java程序jar的方法
    千万级数据并发解决方案

    相关文章推荐

    • Java实例详解之子线程任务异常,主线程事务回滚• 详细整理java枚举的使用总结• 一起聊聊Java常用数据类型的输入输出• 带你搞懂JAVA反射机制(总结分享)• 详细解析Java反射机制原理和几种Class获取方式
    1/1

    PHP中文网