Rumah > Java > javaTutorial > teks badan

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

黄舟
Lepaskan: 2017-10-19 09:29:08
asal
2020 orang telah melayarinya

这篇文章主要介绍了浅谈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();*/ 
}
Salin selepas log masuk

获取页面请求中的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失效"); 
    } 
  } 
}
Salin selepas log masuk

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); 
    } 
  } 
}
Salin selepas log masuk

spring aop配置


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

验证权限方法使用 直接用注解就可以了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; 
    } 
  } 
}
Salin selepas log masuk

方法二: 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 + "]";
  }

}
Salin selepas log masuk

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


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

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);
    }
  }

}
}
Salin selepas log masuk

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>
Salin selepas log masuk

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


@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;
}
Salin selepas log masuk

方法三: 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 { 
     
  } 
}
Salin selepas log masuk

spring-config.xml配置中加入


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

客户端:

拼接请求接口的所有参数,最后拼接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] != &#39;&#39;) 
          addr += &#39;?&#39; + params[0] + "=" + values[0]; 
        for(var i=1; i < valueslength; i++) 
          if(params[i] != undefined && values[i] != undefined && values[i] != &#39;&#39;) 
            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>
Salin selepas log masuk

Atas ialah kandungan terperinci Java实现接口校验的三种方式. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Label berkaitan:
sumber:php.cn
Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
Tutorial Popular
Lagi>
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan