首页 > 开发 > Java > 正文

浅谈Java 三种方式实现接口校验

2024-07-13 10:13:14
字体:
来源:转载
供稿:网友

本文介绍了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 {@Resourceprivate 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>    <div ng-controller="APIListCtrl">     <div> 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)}}     </div>     <hr>     <div 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>     </div>   </div>  </body> </html> 

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持VeVb武林网。


注:相关教程知识阅读请移步到JAVA教程频道。
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表