Java 三种方式实现接口校验

方法一:AOP

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

 

[java] view plain copy
 
  1. package com.thinkgem.jeesite.common.annotation;  
  2.   
  3. import java.lang.annotation.ElementType;  
  4. import java.lang.annotation.Retention;  
  5. import java.lang.annotation.RetentionPolicy;  
  6. import java.lang.annotation.Target;  
  7.   
  8. /** 
  9.  * 权限注解 
  10.  * Created by Hamming on 2016/12/26. 
  11.  */  
  12. @Target(ElementType.METHOD)//这个注解是应用在方法上  
  13. @Retention(RetentionPolicy.RUNTIME)  
  14. public @interface AccessToken {  
  15. /*    String userId(); 
  16.     String token();*/  
  17. }  

获取页面请求中的ID token

[java] view plain copy
 
  1. @Aspect  
  2. @Component  
  3. public class AccessTokenAspect {  
  4.   
  5.     @Autowired  
  6.     private ApiService apiService;  
  7.   
  8.     @Around("@annotation(com.thinkgem.jeesite.common.annotation.AccessToken)")  
  9.     public Object doAccessCheck(ProceedingJoinPoint pjp) throws Throwable{  
  10.         HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();  
  11.         String id = request.getParameter("id");  
  12.         String token = request.getParameter("token");  
  13.         boolean verify = apiService.verifyToken(id,token);  
  14.         if(verify){  
  15.             Object object = pjp.proceed(); //执行连接点方法  
  16.             //获取执行方法的参数  
  17.   
  18.             return object;  
  19.         }else {  
  20.             return ResultApp.error(3,"token失效");  
  21.         }  
  22.     }  
  23. }  

 

token验证类  存储用到redis

 

[java] view plain copy
 
  1. package com.thinkgem.jeesite.common.service;  
  2.   
  3. import com.thinkgem.jeesite.common.utils.JedisUtils;  
  4. import io.jsonwebtoken.Jwts;  
  5. import io.jsonwebtoken.SignatureAlgorithm;  
  6. import io.jsonwebtoken.impl.crypto.MacProvider;  
  7. import org.slf4j.Logger;  
  8. import org.slf4j.LoggerFactory;  
  9. import org.springframework.beans.factory.annotation.Autowired;  
  10. import org.springframework.stereotype.Service;  
  11. import org.springframework.transaction.annotation.Transactional;  
  12. import redis.clients.jedis.Jedis;  
  13.   
  14. import java.io.*;  
  15. import java.security.Key;  
  16. import java.util.Date;  
  17.   
  18. /** 
  19.  *token登陆验证 
  20.  * Created by Hamming on 2016/12/23. 
  21.  */  
  22. @Service  
  23. public class ApiService {  
  24.     private static final String at="accessToken";  
  25.   
  26.     public static Key key;  
  27.   
  28. //    private Logger logger = LoggerFactory.getLogger(getClass());  
  29.     /** 
  30.      * 生成token 
  31.      * Key以字节流形式存入redis 
  32.      * 
  33.      * @param date  失效时间 
  34.      * @param appId AppId 
  35.      * @return 
  36.      */  
  37.     public String generateToken(Date date, String appId){  
  38.         Jedis jedis = null;  
  39.         try {  
  40.             jedis = JedisUtils.getResource();  
  41.             byte[] buf = jedis.get("api:key".getBytes());  
  42.             if (buf == null) { // 建新的key  
  43.                 key = MacProvider.generateKey();  
  44.                 ByteArrayOutputStream bao = new ByteArrayOutputStream();  
  45.                 ObjectOutputStream oos = new ObjectOutputStream(bao);  
  46.                 oos.writeObject(key);  
  47.                 buf = bao.toByteArray();  
  48.                 jedis.set("api:key".getBytes(), buf);  
  49.             } else { // 重用老key  
  50.                 key = (Key) new ObjectInputStream(new ByteArrayInputStream(buf)).readObject();  
  51.             }  
  52.   
  53.         }catch (IOException io){  
  54. //            System.out.println(io);  
  55.         }catch (ClassNotFoundException c){  
  56. //            System.out.println(c);  
  57.         }catch (Exception e) {  
  58. //            logger.error("ApiService", "generateToken", key, e);  
  59.         } finally {  
  60.             JedisUtils.returnResource(jedis);  
  61.         }  
  62.   
  63.         String token = Jwts.builder()  
  64.                 .setSubject(appId)  
  65.                 .signWith(SignatureAlgorithm.HS512, key)  
  66.                 .setExpiration(date)  
  67.                 .compact();  
  68.         // 计算失效秒,7889400秒三个月  
  69.         Date temp = new Date();  
  70.         long interval = (date.getTime() - temp.getTime())/1000;  
  71.         JedisUtils.set(at+appId ,token,(int)interval);  
  72.         return token;  
  73.     }  
  74.   
  75.     /** 
  76.      * 验证token 
  77.      * @param appId AppId 
  78.      * @param token token 
  79.      * @return 
  80.      */  
  81.     public boolean verifyToken(String appId, String token) {  
  82.         if( appId == null|| token == null){  
  83.             return false;  
  84.         }  
  85.         Jedis jedis = null;  
  86.         try {  
  87.             jedis = JedisUtils.getResource();  
  88.             if (key == null) {  
  89.                 byte[] buf = jedis.get("api:key".getBytes());  
  90.                 if(buf==null){  
  91.                     return false;  
  92.                 }  
  93.                 key = (Key) new ObjectInputStream(new ByteArrayInputStream(buf)).readObject();  
  94.             }  
  95.             Jwts.parser().setSigningKey(key).parseClaimsJws(token).getBody().getSubject().equals(appId);  
  96.             return true;  
  97.         } catch (Exception e) {  
  98. //            logger.error("ApiService", "verifyToken", key, e);  
  99.             return false;  
  100.         } finally {  
  101.             JedisUtils.returnResource(jedis);  
  102.         }  
  103.     }  
  104.   
  105.     /** 
  106.      * 获取token 
  107.      * @param appId 
  108.      * @return 
  109.      */  
  110.     public String getToken(String appId) {  
  111.         Jedis jedis = null;  
  112.         try {  
  113.             jedis = JedisUtils.getResource();  
  114.             return jedis.get(at+appId);  
  115.         } catch (Exception e) {  
  116. //            logger.error("ApiService", "getToken", e);  
  117.             return "";  
  118.         } finally {  
  119.             JedisUtils.returnResource(jedis);  
  120.         }  
  121.     }  
  122. }  


spring aop配置 

[html] view plain copy
 
  1. <!--aop -->  
  2. <!--      扫描注解bean -->  
  3. <context:component-scan base-package="com.thinkgem.jeesite.common.aspect"/>  
  4. <aop:aspectj-autoproxy proxy-target-class="true"/>  



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

例如

 

[java] view plain copy
 
    1. package com.thinkgem.jeesite.modules.app.web.pay;  
    2.   
    3. import com.alibaba.fastjson.JSON;  
    4. import com.thinkgem.jeesite.common.annotation.AccessToken;  
    5. import com.thinkgem.jeesite.common.base.ResultApp;  
    6. import com.thinkgem.jeesite.modules.app.service.pay.AppAlipayConfService;  
    7. import org.springframework.beans.factory.annotation.Autowired;  
    8. import org.springframework.stereotype.Controller;  
    9. import org.springframework.web.bind.annotation.RequestMapping;  
    10. import org.springframework.web.bind.annotation.RequestMethod;  
    11. import org.springframework.web.bind.annotation.ResponseBody;  
    12.   
    13. import java.util.HashMap;  
    14. import java.util.Map;  
    15.   
    16. /** 
    17.  * 支付接口 
    18.  * Created by Hamming on 2016/12/27. 
    19.  */  
    20. @Controller  
    21. @RequestMapping(value = "/app/pay")  
    22. public class AppPayModule {  
    23.   
    24.     @Autowired  
    25.     private AppAlipayConfService appAlipayConfService;  
    26.   
    27.     @RequestMapping(value = "/alipay", method = RequestMethod.POST, produces="application/json")  
    28.     @AccessToken  
    29.     @ResponseBody  
    30.     public Object alipay(String orderId){  
    31.         if(orderId ==null){  
    32.             Map re = new HashMap<>();  
    33.             re.put("result",3);  
    34.             re.put("msg","参数错误");  
    35.             String json = JSON.toJSONString(re);  
    36.             return json;  
    37.         }else {  
    38.             return null;  
    39.         }  
    40.     }  
    41. }  

方法二: 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

 

[java] view plain copy
 
  1. public class APIInterceptor extends HandlerInterceptorAdapter {  
  2.   
  3.     @Override  
  4.     public boolean preHandle(HttpServletRequest request,  
  5.             HttpServletResponse response, Object handler) throws Exception {  
  6.         Log.info(request);  
  7.           
  8.         String token = request.getParameter("token");  
  9.           
  10.         // token is not needed when debug  
  11.         if(token == null) return true;  // !! remember to comment this when deploy on server !!  
  12.           
  13.         Enumeration paraKeys = request.getParameterNames();  
  14.         String encodeStr = "";  
  15.         while (paraKeys.hasMoreElements()) {  
  16.             String paraKey = (String) paraKeys.nextElement();  
  17.             if(paraKey.equals("token"))   
  18.                 break;  
  19.             String paraValue = request.getParameter(paraKey);  
  20.             encodeStr += paraValue;  
  21.         }  
  22.         encodeStr += Default.TOKEN_KEY;  
  23.         Log.out(encodeStr);  
  24.           
  25.         if ( ! token.equals(DigestUtils.md5Hex(encodeStr))) {  
  26.             response.setStatus(500);  
  27.             return false;  
  28.         }  
  29.           
  30.         return true;  
  31.     }  
  32.   
  33.     @Override  
  34.     public void postHandle(HttpServletRequest request,  
  35.             HttpServletResponse response, Object handler,  
  36.             ModelAndView modelAndView) throws Exception {  
  37.         Log.info(request);  
  38.     }  
  39.   
  40.     @Override  
  41.     public void afterCompletion(HttpServletRequest request,  
  42.             HttpServletResponse response, Object handler, Exception ex)  
  43.             throws Exception {  
  44.           
  45.     }  
  46. }  


spring-config.xml配置中加入

[html] view plain copy
 
  1. <mvc:interceptors>  
  2.     <mvc:interceptor>  
  3.         <mvc:mapping path="/api/*" />  
  4.         <bean class="cn.web.interceptor.APIInterceptor" />  
  5.     </mvc:interceptor>  
  6. </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函数
[html] view plain copy
 
  1. <!doctype html>  
  2. <html ng-app>  
  3. <head>  
  4.     <meta charset="UTF-8">  
  5.     <title>API test</title>  
  6.     <link href="../css/bootstrap.min.css" rel="stylesheet">  
  7.     <script src="../js/md5.min.js"></script>  
  8.     <script src="../js/angular.min.js"></script>  
  9.     <script>  
  10.         function API(url){  
  11.             this.url = arguments[0];  
  12.             this.params = Array.prototype.slice.call(arguments, 1, arguments.length);  
  13.             this.request = function(params){  
  14.                 var addr = url;  
  15.                 var values = Array.prototype.slice.call(arguments, 1, arguments.length);  
  16.                 if(params[0] != undefined && values[0] != undefined && values[0] != '')  
  17.                     addr += '?' + params[0] + "=" + values[0];  
  18.                 for(var i=1; i values.length; i++)  
  19.                     if(params[i] != undefined && values[i] != undefined && values[i] != '')  
  20.                         addr += "&" + params[i] + "=" + values[i];  
  21.                 return addr;  
  22.             }  
  23.         }  
  24.           
  25.         function APIListCtrl($scope) {  
  26.             $scope.md5 = hex_md5;  
  27.             $scope.token_key = "9ae5r06fs8";  
  28.             $scope.concat = function(){  
  29.                 var args = Array.prototype.slice.call(arguments, 0, arguments.length);  
  30.                 args.push($scope.token_key);  
  31.                 return args.join("");  
  32.             }  
  33.               
  34.             $scope.apilist = [  
  35.               
  36.             new API("account/login", "username", "pwd"),  
  37.             new API("account/register", "username", "pwd", "tel", "code"),  
  38.               
  39.             ] ;  
  40.         }  
  41.     </script>  
  42. </head>  
  43. <body>  
  44.   
  45.     <div ng-controller="APIListCtrl">  
  46.         <div> Search: <input type="text" ng-model="search"><hr>  
  47.         token_key <input type="text" ng-model="token_key">  
  48.         md5 <input type="text" ng-model="str"> {{md5(str)}}  
  49.         </div>  
  50.         <hr>  
  51.         <div ng-repeat="api in apilist | filter:search" >  
  52.             <form action="{{api.url}}" method="post">  
  53.             <href="{{api.request(api.params, value0, value1, value2, value3, value4, value5, value6, value7, value8, value9)}}">  
  54.             {{api.request(api.params, value0, value1, value2, value3, value4, value5, value6, value7, value8, value9)}}  
  55.             </a>  
  56.             <br>  
  57.             {{concat(value0, value1, value2, value3, value4, value5, value6, value7, value8, value9)}}  
  58.             <br>  
  59.             {{api.params[0]}} <input id="{{api.params[0]}}" name="{{api.params[0]}}" ng-model="value0" ng-hide="api.params[0]==undefined">  
  60.             {{api.params[1]}} <input id="{{api.params[1]}}" name="{{api.params[1]}}" ng-model="value1" ng-hide="api.params[1]==undefined">  
  61.             {{api.params[2]}} <input id="{{api.params[2]}}" name="{{api.params[2]}}" ng-model="value2" ng-hide="api.params[2]==undefined">  
  62.             {{api.params[3]}} <input id="{{api.params[3]}}" name="{{api.params[3]}}" ng-model="value3" ng-hide="api.params[3]==undefined">  
  63.             {{api.params[4]}} <input id="{{api.params[4]}}" name="{{api.params[4]}}" ng-model="value4" ng-hide="api.params[4]==undefined">  
  64.             {{api.params[5]}} <input id="{{api.params[5]}}" name="{{api.params[5]}}" ng-model="value5" ng-hide="api.params[5]==undefined">  
  65.             {{api.params[6]}} <input id="{{api.params[6]}}" name="{{api.params[6]}}" ng-model="value6" ng-hide="api.params[6]==undefined">  
  66.             {{api.params[7]}} <input id="{{api.params[7]}}" name="{{api.params[7]}}" ng-model="value7" ng-hide="api.params[7]==undefined">  
  67.             {{api.params[8]}} <input id="{{api.params[8]}}" name="{{api.params[8]}}" ng-model="value8" ng-hide="api.params[8]==undefined">  
  68.             {{api.params[9]}} <input id="{{api.params[9]}}" name="{{api.params[9]}}" ng-model="value9" ng-hide="api.params[9]==undefined">  
  69.             token <input id="token" name="token" value="{{md5(concat(value0, value1, value2, value3, value4, value5, value6, value7, value8, value9))}}">  
  70.             <input type="submit" class="btn" ng-hide="api.params[0]==undefined">  
  71.             </form>  
  72.             <hr>  
  73.         </div>  
  74.     </div>  
  75.   
  76. </body>  
  77. </html>  
posted @ 2017-06-08 19:21  BarryW  阅读(11006)  评论(0编辑  收藏  举报