天天看点

接口鉴权

一、问题说明:接口如果是被同一个项目的前端项目调用,一般都是加了各种鉴权的,比如springsercurity+token安全机制,shiro          等框架都可以控制接口访问权限。但是如果接口是提供给外部调用,肯定是不需要登录的,所以需要在自身的权限控制中放开          该接口的token校验,这样就会造成安全问题,我们一般采取拦截器的方式,和第三方做个鉴权;

二、鉴权方法:

   鉴权采用固定参数同样存在安全问题,容易被抓包获取到。所以一般带入动态的时间戳来鉴权,常用的鉴权逻辑是:两边各存一     份appId和appecret,采用post请求,

1、调用处:传入appId参数,header中再传入两个参数,一个是时间戳,一个是appId、appSecret、时间戳三个参数排序后拼接的字符串的MD码;如:

String  url = thirdUrl += "?appId="+appId;
            Map<String, String> header = new HashMap<>();
            long timestamp = new Date().getTime();
            List<String> paramList = new ArrayList<>();
            paramList.add(appKey);
            paramList.add(timestamp);
            paramList.add(appSecret);
            Collections.sort(paramList, new Comparator<String>() {
               @Override
               public int compare(String o1, String o2) {
                  return o1.compareTo(o2);
             }
             });
           StringBuilder builder = new StringBuilder();
           for (String str : paramList) {
              builder.append(str);
           }
            String md5 = EncryptionUtil.getMD5(builder );
            header.put("sign",md5 );
            header.put("timestamp",String.valueOf(timestamp));
            String res = HttpUtil.postWithHeader(url,header,null);
           

附上请求util和md5生成util:

/**
	 * 发送post请求
	 * 
	 * @param url
	 * @param header
	 * @param body
	 * @return
	 */
	public static String postWithHeader(String url, Map<String, String> header, String body) {
		String result = "";
		BufferedReader in = null;
		PrintWriter out = null;
		try {
			// 设置 url
			URL realUrl = new URL(url);
			URLConnection connection = realUrl.openConnection();
			// 设置 header
			for (String key : header.keySet()) {
				connection.setRequestProperty(key, header.get(key));
			}
			// 设置请求 body
			connection.setDoOutput(true);
			connection.setDoInput(true);
			out = new PrintWriter(connection.getOutputStream());
			// 保存body
			out.print(body);
			// 发送body
			out.flush();
			// 获取响应body
			in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
			String line;
			while ((line = in.readLine()) != null) {
				result += line;
			}
		} catch (Exception e) {
			log.error(e.getMessage(), e);
			return null;
		}
		return result;
	}
           
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class EncryptionUtil {

    /***
     * MD5加码 生成32位md5码
     */
    public static String string2MD5(String inStr) {
        MessageDigest md5 = null;
        try {
            md5 = MessageDigest.getInstance("MD5");
        } catch (Exception e) {
            System.out.println(e.toString());
            e.printStackTrace();
            return "";
        }
        char[] charArray = inStr.toCharArray();
        byte[] byteArray = new byte[charArray.length];

        for (int i = 0; i < charArray.length; i++)
            byteArray[i] = (byte) charArray[i];
        byte[] md5Bytes = md5.digest(byteArray);
        StringBuffer hexValue = new StringBuffer();
        for (int i = 0; i < md5Bytes.length; i++) {
            int val = ((int) md5Bytes[i]) & 0xff;
            if (val < 16){
                hexValue.append("0");
            }
            hexValue.append(Integer.toHexString(val));
        }
        return hexValue.toString();

    }


    /**
     * 加密解密算法 执行一次加密,两次解密
     */
    public static String convertMD5(String inStr) {

        char[] a = inStr.toCharArray();
        for (int i = 0; i < a.length; i++) {
            a[i] = (char) (a[i] ^ 't');
        }
        String s = new String(a);
        return s;

    }

    public static String getMD5(String sha){
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            md.reset();
            md.update(sha.getBytes());
            byte[] result = md.digest();
            StringBuffer sb = new StringBuffer();
            int len = result.length;
            for (int i = 0; i < len; i++) {
                int v = result[i] & 0XFF;
                if(v < 16)
                    sb.append("0");
                sb.append(Integer.toString(v, 16).toLowerCase());
            }
            return sb.toString();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return null;
    }


}
           

2、被调用处:判断时间戳是否超过多长时间;再根据传过来的appId查询appSecret,然后这三个参数根据同样的算法生成md5,比较和第三方传过来的sign是否相等即可:

package com.demo.interceptor;

import com.alibaba.druid.util.StringUtils;
import com.demo.service.UserService;
import com.demo.util.EncryptionUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.DigestUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class ApiInterCeptor implements HandlerInterceptor {
    
    @Autowired
    private UserService userService;
    
    /**
     * 原接口post请求:1、header传入timestamp时间戳;
     *         2、header传入sign,值为appId、appSecret、timestamp排序后生成的md5码
     * @param request
     * @param response
     * @param o
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object o) throws Exception {
        //1、校验header中的时间戳,时差不超过5分钟
        String timestamp = "";
        String sign = "";
        try {
            timestamp = request.getHeader("timestamp");
            sign = request.getHeader("sign");
            if ((Math.abs(System.currentTimeMillis() - Long.valueOf(timestamp)) / 1000 / 60) > 5) {
                response.getWriter().print("访问失效");
                return false;
            }
        } catch (Exception e) {
            response.getWriter().print("参数错误");
            return false;
        }
        //2、校验参数
        // (1). 将参数进行字典序排序
        List<String> paramList = new ArrayList<>();
        String appId = request.getParameter("appId");
        paramList.add(appId);
        paramList.add(timestamp);
        String appSercret = userService.getAppSecretById(appId);
        paramList.add(appSecret);
        Collections.sort(paramList, new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                return o1.compareTo(o2);
            }
        });
        // (2). 将参数字符串拼接成一个字符串进行MD5
        StringBuilder builder = new StringBuilder();
        for (String str : paramList) {
            builder.append(str);
        }
        String md5 = EncryptionUtil.getMD5(builder.toString());
        //(3).与带入的sign比较是否相等
        if (!StringUtils.equals(sign, md5)) {
            response.getWriter().print("sign不一致");
            return false;
        }
        return false;
    }

    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {

    }
}
           

继续阅读