签名(Signature)认证实现方式-用于请求鉴权

常用的请求认证方式有两种:

1、Signature认证

一次性的身份校验方式,常见于不同项目间的api通信
一般形式是通过 AppID/AccessKey/AppSecret 及签名算法针对通信数据生成签名
AccessKey作为公钥,AppSecret作为私钥,AppSecret不能放在网络上传输
接口数据推送时,会随带上AppID、AccessKey、Timestamp 及 Signature
在服务端同样留存着一份相同的 AppID/AccessKey/AppSecret 配置
服务端接受到请求后通过AppID对应出匹配的AppSecret,结合相同的签名算法计算签名,并与请求附带的签名做校验
时间戳可以防止恶意用户截取到签名后进行伪装请求,使签名只在一定时间范围内有效,过期后不能再请求
由于每次请求的数据载荷不同,所以一般每次请求都会产生不同的签名.

2、Token认证

状态可维持的身份校验方式,常见于第三方服务或APP接口的OAUTH2认证中
一般形式是通过 账号/密码 或 clientId/clientSecret 向认证服务请求Token
服务端Authenticate通过后返回Token,并在一定时间内对Token及相应账号信息进行缓存
后续的接口通信都将通过Token来进行认证
在一段活跃期内, 连续的请求Token是相同

 

本文主要介绍签名认证的主要实现方式

客户端:

(1)封装一个获取时间戳、生成签名的工具类

package com.solin.tools.utils;

import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.security.MessageDigest;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Formatter;

public class EncryptedUtil {

    /*
     * HMACSHA256加密签名
     * Hash-based Message Authentication Code  SHA256
     * @param sourceStr 加密的源字符串
     * @param secretAccessKey 密钥
     */
    public static String getSign(String sourceStr,String secretAccessKey) throws Exception{
        SecretKey secretKey = new SecretKeySpec(secretAccessKey.getBytes(),"HmacSHA256");
        Mac mac = Mac.getInstance(secretKey.getAlgorithm());
        mac.init(secretKey);
        final byte[] hmac = mac.doFinal(sourceStr.getBytes());
        StringBuilder stringBuilder = new StringBuilder(hmac.length*2);
        Formatter formatter = new Formatter(stringBuilder);
        for (byte b :hmac){
            formatter.format("%02x",b);
        }
        formatter.close();

        return stringBuilder.toString();
    }

    /*
     * MD5加密
     * @param sourceStr 加密的源字符串
     */
    public static String MD5(String sourceStr) {
        String result = "";
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            md.update(sourceStr.getBytes("UTF-8"));
            byte b[] = md.digest();
            int i;
            StringBuffer buf = new StringBuffer("");
            for (int offset = 0; offset < b.length; offset++) {
                i = b[offset];
                if (i < 0)
                    i += 256;
                if (i < 16)
                    buf.append("0");
                buf.append(Integer.toHexString(i));
            }
            result = buf.toString();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /*
     * 时间转换成unix时间戳
     * @param dateStr 时间字符串
     */
    public static Long getUnixTimestamp(String dateStr){
        SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
        try {
            Date date = simpleDateFormat.parse(dateStr);
            return  date.getTime();

        } catch (ParseException e) {
            return null;
        }
    }

(2)封装一个简单的日期工具类

package com.solin.tools.utils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * 日期工具类
 */
public class DateUtils {
    //log
    private final static Logger logger = LoggerFactory.getLogger(DateUtils.class);
    private DateUtils(){}


    //Srting转date
    public static Date changeStringToDate(String dateStr) {
        Date date = new Date();
        try {
            date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(dateStr);
        } catch (ParseException e) {
            logger.error(e.getMessage());
        }
        return date;

    }

    //Srting转date精确到毫秒
    public static Date changeStringToMillisecondDate(String dateStr) {
        Date date = new Date();
        try {
            date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").parse(dateStr);
        } catch (ParseException e) {
            logger.error(e.getMessage());
        }
        return date;

    }

    //date转String精确到毫秒
    public static String changeDateToMillisecondString(Date date) {
        String dateStr = "";
        try {
            dateStr = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(date);
        } catch (Exception e) {
            logger.error(e.getMessage());
        }
        return dateStr;
    }

}

 (3)生成时间戳和签名测试

package com.solin.tools;

import com.solin.tools.utils.SignResult;

/**
 * 生成签名测试
 */
public class SignTest {

    public static void main(String[] args) throws Exception{ 
        String appId = "123456";
        String accessKey = "e16188442d8f460696bddf2b";
        String secretKey = "c0248ad5cced4820835ad54d90ba1fe0";
        String timestamp = DateUtils.changeDateToMillisecondString(new Date());
// 将timestamp转化为unix时间戳
	long timestampLong = EncryptedUtil.getUnixTimestamp(timestamp);
	// 拼接字符串
	String requestStr = timestampLong + appId + accessKey;
	// MD5字符串
	String requestMd5Str = EncryptedUtil.MD5(requestStr);
	// 生成签名
	String sign = EncryptedUtil.getSign(requestMd5Str ,secretKey);

        System.out.println("timestamp ===>>> " + timestamp);
        System.out.println("sign ===>>> " + sign);
    }
}

服务端:

(1)时间戳及签名校验工具类


/**
 * API鉴权工具类
 * 1、校验时间戳
 * 2、校验签名 
 */
public class AppAuthenticationUtils {
    //log日志
    private static final Logger logger = LoggerFactory.getLogger(AppAuthenticationUtils.class);
    //鉴权时间戳范围,单位毫秒
    public static final long scopeTime = 300000L;

    /**
     * API鉴权
     * @param appId 应用ID
     * @param accessKey 访问密钥
     * @param secretKey 签名密钥
     * @param requestTimestamp 时间戳
     * @param signature 签名
     */
    public static ResponseStatus checkAuthentication(String appId , String accessKey , String secretKey,
                                                        String requestTimestamp, String signature)  {
        long requestUnixTimestamp = EncryptedUtil.getUnixTimestamp(requestTimestamp);
        // 校验请求时间戳
        if (!checkRequestTimestamp(requestUnixTimestamp , scopeTime)){
            return new ResponseStatus(false, "Invalid requestTimestamp ...");
        }
        // 校验签名
        if (!checkSignature(appId, accessKey, secretKey, requestTimestamp,signature)){
            return new ResponseStatus(false, "Invalid signature...");
        }

        return new ResponseStatus(true, "鉴权通过");
    }


    /**
     * 校验签名
     * @param appId 应用ID
     * @param accessKey 接口密钥
     * @param requestTimestamp 时间戳
     * @param signature 签名
     */
    private static boolean checkSignature(String appId, String accessKey, String secretKey, String requestTimestamp,
                                          String signature) {
        boolean isAuthPass = false;
        try {
            long requestUnixTimestamp = EncryptedUtil.getUnixTimestamp(requestTimestamp);
            String requestMd5Str = EncryptedUtil.MD5(requestUnixTimestamp + appId + accessKey);
            String sign = EncryptedUtil.getSign(requestMd5Str ,secretKey);
            if (StringUtils.isNotEmpty(sign) && sign.equals(signature)){
                isAuthPass = true;
            }
        } catch (Exception e) {
            logger.error("Interface authentication failed ", e);
        }
        return isAuthPass;
    }

    /**
     * 判断失效时间
     * @param requestUnixTimestamp unix时间戳
     */
    private static boolean checkRequestTimestamp(long requestUnixTimestamp  ,long scopeTime){
        boolean isVaildTimestamp = false;
        long currentTime = System.currentTimeMillis();
        long minVaildTime = currentTime - scopeTime;
        long maxVaildTime = currentTime + scopeTime;
        if (requestUnixTimestamp >= minVaildTime && requestUnixTimestamp <= maxVaildTime){
            isVaildTimestamp = true;
        }
        return isVaildTimestamp;
    }

}

 

你可能感兴趣的:(java)