• 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>  
  • 相关阅读:
    CSS边框
    各大网站注册的用处(个人看法)
    20121011 外边距
    20120921碎碎念
    20121011 CSS一
    20120919碎碎念
    CSS 文本装饰属性
    外边距合并
    EverBox开发笔记1
    “Core Data”中的“dynamic implementation”
  • 原文地址:https://www.cnblogs.com/barrywxx/p/6964423.html
Copyright © 2020-2023  润新知