欢迎来到高博应诺!
0512-62955981
高博应诺

Java的三种接口校验

2019-03-02 482
方法一:AOP
代码如下定义一个权限注解
 
[java] view plain copy 
 
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/26. 
 */  
@Target(ElementType.METHOD)//这个注解是应用在方法上  
@Retention(RetentionPolicy.RUNTIME)  
public @interface AccessToken {  
/*    String userId(); 
    String token();*/  
}  
获取页面请求中的ID token
[java] view plain copy 
 
@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
 
[java] view plain copy 
 
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/23. 
 */  
@Service  
public class ApiService {  
    private static final String at="accessToken";  
  
    public static Key key;  
  
//    private Logger logger = LoggerFactory.getLogger(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配置 
[html] view plain copy 
 
 
 
 
 


验证权限方法使用 直接用注解就可以了AccessToken
例如
 
[java] view plain copy 
 
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/27. 
 */  
@Controller  
@RequestMapping(value = "/app/pay")  
public class AppPayModule {  
  
    @Autowired  
    private AppAlipayConfService appAlipayConfService;  
  
    @RequestMapping(value = "/alipay", method = RequestMethod.POST, 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 = JSON.toJSONString(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版本有点出入,导致用不了基于注解的方式)


   
   
       
   


??最后给出测试代码,这样的代码就优雅很多了
@RequestMapping(value = "/appointments", method = { RequestMethod.GET })
@ResponseBody
@AuthToken(type="disticntApp")
public List getAppointments(AppointmentSearchVo appointmentSearchVo) {
    List appointments = appointmentService.getAppointment(appointmentSearchVo.getUserId(), appointmentSearchVo);
    return appointments;
}
方法三: MVC拦截器
服务器:
拼接token之外所有参数,最后拼接token_key,做MD5,与token参数比对
如果token比对失败返回状态码 500
 
[java] view plain copy 
 
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配置中加入
[html] view plain copy 
 
 
     
         
         
   
 
 
 
 
 
客户端:
拼接请求接口的所有参数,最后拼接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 
 
 
 
 
     
    API test  
     
     
     
     
 
 
  
   
 
       
Search:
 
        token_key  
        md5 {{md5(str)}}  
       
 
       
 
       
 
           
 
             
            {{api.request(api.params, value0, value1, value2, value3, value4, value5, value6, value7, value8, value9)}}  
           
 
           
 
            {{concat(value0, value1, value2, value3, value4, value5, value6, value7, value8, value9)}}  
           
 
            {{api.params[0]}}  
            {{api.params[1]}}  
            {{api.params[2]}}  
            {{api.params[3]}}  
            {{api.params[4]}}  
            {{api.params[5]}}  
            {{api.params[6]}}  
            {{api.params[7]}}  
            {{api.params[8]}}  
            {{api.params[9]}}  
            token  
             
           
 
           
 
       
 
   
 
  
 
 
免费开发基础+项目实战班,火爆抢座中... (前50名享19元住宿)

就业保障

申请优惠

免费试学

立即报名

在线咨询

视频教程