浅谈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> 

  <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> 

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

(0)

相关推荐

  • java中文及特殊字符的校验方法

    本文实例为大家分享了Android九宫格图片展示的具体代码,供大家参考,具体内容如下 参考链接:Character.UnicodeBlock中cjk的说明详解 1.关于Character.UnicodeBlock的介绍 CJK的意思是"Chinese,Japanese,Korea"的简写 ,实际上就是指中日韩三国的象形文字的Unicode编码 Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS : 4E00-9FBF:Unicode 编码为 U+

  • JAVA 18位身份证号码校验码的算法

    public static char doVerify(String id) { char pszSrc[]=id.toCharArray(); int iS = 0; int iW[]={7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2}; char szVerCode[] = new char[]{'1','0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'}; int i; for(i=0;i

  • java使用计算md5校验码方式比较两个文件是否相同

    复制代码 代码如下: public class MD5Check {/*** 默认的密码字符串组合,用来将字节转换成 16 进制表示的字符,apache校验下载的文件的正确性用的就是默认的这个组合*/    protected char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };    protected  MessageDigest messa

  • java使用正则表达校验手机号码示例(手机号码正则)

    复制代码 代码如下: public static boolean isMobileNumber(String mobiles) {return Pattern.compile("^((13[0-9])|(15[^4,\\D])|(18[^1^4,\\D]))\\d{8}").matcher(mobiles).matches();}

  • java自定义注解实现前后台参数校验的实例

    其实是可以通过@Constraint来限定自定义注解的方法. @Constraint(validatedBy = xxxx.class) 下面是我做的 java自定义注解实现前后台参数校验 的代码示例 对这个感兴趣的,请好好看,好好学: package sonn.sonnannotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.anno

  • Java struts2 validate用户登录校验功能实现

    首先贴一下搭配的环境: 配置: Eclipse4.3.2 jdk1.7_45 Mysql 5.0+ 然后切入正题: 1.login.jsp 主要是使用OGNL 标签 也可使用html form表单,调用LoginAction.action,以post 方式传输. 在LoginaAction 经过判断,然后会有提示信息,需要用到 <s:fielderror/>来显示. <%@ taglib uri="/struts-tags" prefix="s"%

  • 利用Java正则表达式校验邮箱与手机号

    主要是运用java.util.regex类. 复制代码 代码如下: import java.util.regex.Matcher; import java.util.regex.Pattern; public class CheckMobileAndEmail { /** * 验证邮箱地址是否正确 * @param email * @return */ public static boolean checkEmail(String email){ boolean flag = false; tr

  • 浅谈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 j

  • 浅谈java 面对对象(抽象 继承 接口 多态)

    什么是继承? 多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承那个类即可. 多个类可以称为子类,单独这个类称为父类.超类或者基类. 子类可以直接访问父类中的非私有的属性和行为. 通过 extends 关键字让类与类之间产生继承关系. class SubDemo extends Demo{} //SubDemo是子类,Demo是父类 继承有什么好处? •提高代码的复用性. •让类与类之间产生了关系,是多态的前提. 继承的特点 1.Java只支

  • 浅谈Java HttpURLConnection请求方式

    一)URL代理请求 ​ 该方式请求有两种代理方式. 方式一:使用该方式代理之后,之后的所有接口都会使用代理请求 // 对http开启全局代理 System.setProperty("http.proxyHost", "192.168.1.1"); System.setProperty("http.proxyPort", "80"); // 对https开启全局代理 System.setProperty("https.

  • 浅谈Tomcat三种运行模式

    tomcat的运行模式有3种 一.bio(blocking I/O) 即阻塞式I/O操作,表示Tomcat使用的是传统的Java I/O操作(即java.io包及其子包).是基于JAVA的HTTP/1.1连接器,Tomcat7以下版本在默认情况下是以bio模式运行的.一般而言,bio模式是三种运行模式中性能最低的一种.我们可以通过Tomcat Manager来查看服务器的当前状态.(Tomcat7 或以下,在 Linux 系统中默认使用这种方式) 二.nio(new I/O) 是Java SE

  • 浅谈django三种缓存模式的使用及注意点

    django是动态网页,一般来说需要实时的生成访问的页面,展示给访问者,这样,内容可以随时变化,也就说请求到达视图函数之后,然后进行模板渲染,将字符串返回给用户,用户会看到相应的html页面.但是如果每次请求都从数据库中请求并获取数据,并且当用户并发量十分大的时候,这将服务器性能将大大受到影响.因此使用缓存能有效的解决这类问题.如果能将渲染后的结果放到速度更快的缓存中,每次有请求过来,先检查缓存中是否有对应的资源,如果有,直接从缓存中取出来返回响应,节省取数据和渲染的时间,不仅能大大提高系统性能

  • 浅谈java获取UUID与UUID的校验

    背景: 我们在开发的过程中可能需要随机生成一个ID,例如数据库中的某个ID有时候也要对其进行校验. UUID: UUID,是Universally Unique Identifier的缩写,UUID出现的目的,是为了让分布式系统可以不借助中心节点,就可以生成UUID来标识一些唯一的信息. 代码: import java.util.UUID; public class UUIDTest { public static void main(String[] args) { String uuid1

  • 浅谈Java三目运算

    三目条件运算公式为 x?y:z  其中x的运算结果为boolean类型,先计算x的值,若为true,则整个三目运算的结果为表达式y的值,否则整个运算结果为表达式z的值 例:String s=""; String x="默认值"; s=s.isEmpty()?x:s; 这段代码的意思是:先判断s是否为空(结果是空),然后执行s=x,即执行x 再来一个复杂点的 class Dates { int year,month,day; Dates(int x,int y,int

  • 浅谈Java生成唯一标识码的三种方式

    目录 前言 正文 UUID实现唯一标识码 SnowFlake实现唯一标识码 通过时间工具生成带有业务标示的唯一标识码 前言 我们经常会遇到这样的场景,需要生成一个唯一的序列号来表明某一个数据的唯一性,在单节点的应用中我们可以简单地使用一个自增的整型来实现实现,但是在分布式情况下这个方式却存在冲突的可能性,那么有什么办法我们可以生成一个唯一的序列号呢,并且如果想使得这个序列号也能展示一些业务信息呢? 正文 UUID实现唯一标识码 UUID 的目的是让分布式系统中的所有元素,都能有唯一的辨识资讯,而

  • 浅谈Java的两种多线程实现方式

    本文介绍了浅谈Java的两种多线程实现方式,分享给大家.具有如下: 一.创建多线程的两种方式 Java中,有两种方式可以创建多线程: 1 通过继承Thread类,重写Thread的run()方法,将线程运行的逻辑放在其中 2 通过实现Runnable接口,实例化Thread类 在实际应用中,我们经常用到多线程,如车站的售票系统,车站的各个售票口相当于各个线程.当我们做这个系统的时候可能会想到两种方式来实现,继承Thread类或实现Runnable接口,现在看一下这两种方式实现的两种结果. 程序1

  • 浅谈java调用Restful API接口的方式

    摘要:最近有一个需求,为客户提供一些RestfulAPI接口,QA使用postman进行测试,但是postman的测试接口与java调用的相似但并不相同,于是想自己写一个程序去测试RestfulAPI接口,由于使用的是HTTPS,所以还要考虑到对于HTTPS的处理.由于我也是首次使用Java调用restful接口,所以还要研究一番,自然也是查阅了一些资料. 分析:这个问题与模块之间的调用不同,比如我有两个模块frontend和backend,frontend提供前台展示,backend提供数据支

随机推荐