【Java认证——mac、MessageDigest(1)】

【前言】

            这两天一直在写Open Api,Api是写好了,但是为了安全,所以必须需要进行对接口进行认证。

【主体】

一、Base64

(1)编码

    public static String encryptBASE64(byte[] key) throws Exception {
        return (new BASE64Encoder()).encodeBuffer(key);
    }

(2)解码

    public static byte[] decryptBASE64(String key) throws Exception {
        return (new BASE64Decoder()).decodeBuffer(key);
    }

二、MessageDigest

(1)简介

MessageDigest类为应用程序提供信息摘要算法的功能,如MD5或SHA算法,信息摘要是安全的单向的哈希函数,它接受任意大小的数据,并输出固定长度的哈希值。

 (2)步骤

(a)对象开始初始化

(b)调用ipdate方法处理数据

 (c) 调用digest方法完成哈希计算

(3)代码

public class MessageDigestTest {
    static String abc = "zhaoxiaodong";
    public static void main(String[] args) throws Exception{
        //MessageDigest 对象开始被初始化
        MessageDigest md5 = MessageDigest.getInstance("MD5");
        //通过调用update方法处理数据
        md5.update(abc.getBytes());
        //调用digest方法完成哈希计算
        System.out.println("md5(abc)="+ Base64Coder.encode(md5.digest()));
        

    }
}

三、mac

(1)简介

考虑到消息摘要的不安全性-拿到原文并知道算法,就可以得到正确的消息摘要。人们又加入了密钥的元素,得到了安全消息摘要——MAC(Message Authentication Code)。MAC的特点是仅通过原文是无法计算出安全消息摘要的。还需要一个双方都知道的消息密钥。如果有人修改了消息本体,有修改了消息摘要,但是因为没有密钥,那么将被发现破坏了数据,计算MAC的方法有很多,但是核心Java API没有相关实现。Java的实现主要是通过JCE提供者来实现的。Mac类对应消息摘要的MessageDigest类。计算消息摘要时需要一个密钥。密钥的管理是另一个话题了。这里主要讲MAC的算法,JCE实现主要基于HmacSHA1和HmacMD5。具体provider提供了哪些MAC相关的算法,它们比较如何,见下面的代码:

(2)步骤

(a)getInstance得到实例

  (b)传入key和算法参数进行初始化

 (c)通过update进行传入数据

 (d)通过doFinal得到结果

(3)代码


public class Md5Test {
    static String appId = "zhaoxiaodong";
    static String appKey ="nizhenbang";
    public static String main(String[] args) throws Exception{
        //1、getInstance得到实例
        Mac mac = Mac.getInstance("HmacSHA1");
        //2、传入key和算法参数进行初始化
        mac.init(new SecretKeySpec(appId.getBytes("utf-8"),"HmacSHA1"));
        //3、通过update进行传入数据
        //4、通过doFinal得到结果
        byte[] signData = mac.doFinal(appKey.getBytes("utf-8"));
        return Base64.encodeBase64String(signData);
    }
}

四、项目实战

(1)发送方

package com.openApi;

import com.alibaba.fastjson.JSONObject;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.digest.DigestUtils;
import org.yaml.snakeyaml.external.biz.base64Coder.Base64Coder;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

import javax.crypto.Cipher;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.security.KeyFactory;
import java.security.MessageDigest;
import java.security.PrivateKey;
import java.security.spec.PKCS8EncodedKeySpec;

/**
 * @Describe: 首先我们要考虑是不是可逆的还是不可逆的。
 * @Author: 赵晓东
 * @Date: 17:03 2021-1-22
 * @param:
 * @return:
 */
public class openAPi {
    //首先声明一个静态的常量appId
    static String appId = "10037e6f66f2d0f901672aa27d690006";
    static String url ="http://192.168.72.15:61043/app/orderApi/orderClose";

    public static void main(String[] args) throws Exception {
        //接下来就是对appId进行加密操作,我们就采用不可逆的吧
//        byte[] appIdByte = appId.getBytes("UTF-8");
//        byte[] bytes = DigestUtils.md5(appIdByte);
        //实例化TenantEntity
        TenantEntity tenantEntity = new TenantEntity();
        tenantEntity.setAccount("15197272363");
        tenantEntity.setCode("1113");
        String tenantJson = JSONObject.toJSONString(tenantEntity);
        //然后将这些发送到getOpenBodySig
        String send = send(url, tenantJson);
        System.out.println(send);
//        String openBodySig = getOpenBodySig(appId, tenantJson);
//        System.out.println(openBodySig);
//      接下来获取到Token之后,就是对发送数据给url
//        request()


    }
    public static String getOpenBodySig(String appId,String entity) throws Exception{
        //MessageDigest对象开始被初始化
        MessageDigest md5 = MessageDigest.getInstance("MD5");
        //通过调用update方法处理数据
        String encodeBody= appId+entity;
        md5.update(encodeBody.getBytes("utf-8"));
        //调用digest方法完成哈希计算
        String appIdEncode = Base64.encodeBase64String(md5.digest());
        //appId已进行加密
//        byte[] bytes = DigestUtils.md5(appId);
        //将appId和entity一起进行加密
        //接下来是需要把商家的信息也封装起来,然后传入到token中,到过滤器进行解析
        System.out.println("------------->"+ appIdEncode);
//        String token = JSONObject.toJSONString(appIdEncode);
        return appIdEncode;
    }
    public static String send(String url,String entity) throws Exception{
        String openBodySig = getOpenBodySig(appId, entity);
        String requestString = request(url, openBodySig, entity);
        return requestString;
    }
    public static String request(String url,String authorization,String entity)throws  Exception{
        String response="";
        PrintWriter out = null;
        BufferedReader in = null;
        try {
            URL realUrl = new URL(url);
            URLConnection conn = realUrl.openConnection();
            HttpURLConnection httpURLConnection=(HttpURLConnection)conn;
            httpURLConnection.setRequestProperty("Content-Type","application/json");
            httpURLConnection.setRequestProperty("authorization",authorization);
//            String encodeEntity = (new BASE64Encoder()).encodeBuffer(entity.getBytes());
            httpURLConnection.setRequestProperty("entity",entity);
            httpURLConnection.setDoOutput(true);
            httpURLConnection.setDoInput(true);
            out = new PrintWriter(httpURLConnection.getOutputStream());
            out.write(entity);
            out.flush();
            httpURLConnection.connect();
            in = new BufferedReader(new InputStreamReader(httpURLConnection.getInputStream()));
            String line;
            while ((line = in.readLine())!=null){
                response +=line;
            }

        } catch (Exception e){
            e.printStackTrace();
        }finally {
            try {
                if ( out!=null){
                    out.close();
                }
                if (in!=null){
                    in.close();
                }
            }catch (Exception ex){
                ex.printStackTrace();
            }
        }
        return  response;
    }

}

(2)校验方

校验的时候直接在拦截器进行拦截,然后进行校验。

       //如果是从Swagger中,则直接走下面--earnest
        String entity = request.getHeader("entity");
        //判断这个url是否是外部的地址,如果是外部的地址就走这个,如果不是就走下面
        if(urlMatchUtil.match("/app/orderApi",requestURI) && entity!=null && entity!=""){
            //获取到传入的参数,传入的参数中有code值,通过code值,就可以查询appId,然后就可以进行判断了。

            //对entity进行解码
//            byte[] bytes1 = (new BASE64Decoder()).decodeBuffer(entity);
//            String entityInfo = new String(bytes1);
            TenantEntity tenantEntity = JSON.parseObject(entity, TenantEntity.class);
            //获取到code值
            String code = tenantEntity.getCode();
            //通过code值查询数据库获取到appId
            String appId = "10037e6f66f2d0f901672aa27d690006";
            //获得了appId和entity,然后对他们进行加密判断传进来的时候合法
            String appIdEntity = appId + entity;
            //MessageDigest对象开始被初始化
            MessageDigest md5 = MessageDigest.getInstance("MD5");
            //通过调用update方法处理数据
            md5.update(appIdEntity.getBytes("utf-8"));
            //调用digest方法完成哈希计算
            String appIdEncode = Base64.encodeBase64String(md5.digest());
            String authorization = request.getHeader("authorization");
            appIdEncode = new String(appIdEncode.getBytes("ISO-8859-1"),"UTF-8");
            authorization = new String(authorization.getBytes("ISO-8859-1"),"UTF-8");
            System.out.println(appIdEncode);
            System.out.println(authorization);
            if (appIdEncode.trim().equals(authorization.trim())){
                return super.preHandle(request,response,handler);
            }else {
                throw BizException.wrap(R.FAIL_CODE,"参数不合法");
            }
        }

五、项目实战优化

这次优化主要优化了将请求实体进行加密、加入了统一返回,不用再分别添加请求头,再一个就是增加了时间戳,api中加入时间戳的目的,因为这些api是异步请求的,为了防止重复请求,按照调用顺序被处理,需要精确获取客户端时间等原因,需要时间戳,在URL中加时间戳就会保证每一次发起的请求都是一个不同于之前的请求,这样就能避免浏览器对URL的缓存。

(1)请求方

    public static String getOpenBodySig(String appId,String entity ) throws Exception{
        String  timestamp = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
        //MessageDigest对象开始被初始化
        MessageDigest md5 = MessageDigest.getInstance("MD5");
        //通过调用update方法处理数据
        String encodeBody= appId+entity;
        md5.update(encodeBody.getBytes("utf-8"));
        //调用digest方法完成哈希计算
        String appIdEncode = Base64.encodeBase64String(md5.digest());
        //appId已进行加密
//        byte[] bytes = DigestUtils.md5(appId);
        //将appId和entity一起进行加密
        //接下来是需要把商家的信息也封装起来,然后传入到token中,到过滤器进行解析
        System.out.println("------------->"+ appIdEncode);
//        String token = JSONObject.toJSONString(appIdEncode);
        //对entity进行加密
        String entityBase = Base64.encodeBase64String(entity.getBytes("UTF-8"));
        return "OPEN-BODY-SIG appIdEncode="+"\""+appIdEncode+"\""+", entityBase="+"\""+entityBase+"\""+",timestamp="+"\""+timestamp+"\"";
    }

(2)验证方

        //如果是从Swagger中,则直接走下面--earnest
        //判断这个url是否是外部的地址,如果是外部的地址就走这个,如果不是就走下面
        if (urlMatchUtil.match("/app/orderApi", requestURI)) {
            //获取到传入的参数,传入的参数中有code值,通过code值,就可以查询appId,然后就可以进行判断了。
            String authorization = request.getHeader("authorization");
            //接下来,我们需要截取 authorization,然后获取相应的值
            int i = authorization.indexOf("=");
            int a = authorization.indexOf(",");
            //"r1F1Jb/GowKLkqCwd6y0eg=="
            String appIdEncodeInfo = authorization.substring(i + 1, a);
            int appIdEncodeFirst = appIdEncodeInfo.indexOf("\"");
            int appIdEncodeLast = appIdEncodeInfo.lastIndexOf("\"");
            //这个是最终的截取的appId加密的值
            String appIdCode = appIdEncodeInfo.substring(appIdEncodeFirst + 1, appIdEncodeLast);
            String entityBase = org.apache.commons.lang3.StringUtils.substringAfter(authorization, "entityBase=");
            //进行二次截取
            String entityBaseInfo = org.apache.commons.lang3.StringUtils.substringBefore(entityBase, ",timestamp=");
            int entityBaseFist = entityBaseInfo.indexOf("\"");
            int entityBaseLast = entityBaseInfo.lastIndexOf("\"");
            //这个是最终截取的entity值
            String entityCode = entityBaseInfo.substring(entityBaseFist + 1, entityBaseLast);
            //获取到截取的时间戳
            String timestampInfo = org.apache.commons.lang3.StringUtils.substringAfterLast(authorization, "timestamp=");
            int timestampFirst = timestampInfo.indexOf("\"");
            int timestapLast = timestampInfo.lastIndexOf("\"");
            //这个是最终截取的时间戳
            String timeStampCode = timestampInfo.substring(timestampFirst + 1, timestapLast);
            DateFormat df1 = new SimpleDateFormat("yyyyMMddHHmmss");
            Date time1 = df1.parse(timeStampCode);
            long time = time1.getTime();
            //接下来要做的是对时间戳进行判断了
            if (StringUtils.isEmpty(timeStampCode)) {
                throw BizException.wrap(R.FAIL_CODE,"时间戳为空");
            }
            if (System.currentTimeMillis()-time>requestTimestampLimit){
                throw  BizException.wrap(R.FAIL_CODE,"请求时间已经太长了");
            }
            //接下来就是对entityBaseInfo进行解码
            byte[] bytes = Base64.decodeBase64(entityCode);
            String entityTenant = new String(bytes);
            TenantEntity tenantEntity = JSON.parseObject(entityTenant, TenantEntity.class);
            //获取到code值
            String code = tenantEntity.getCode();
            if (StringUtils.isEmpty(code)){
                throw BizException.wrap(R.FAIL_CODE,"此商家编码为空");
            }
            //判断数据库中是否有此商家
            boolean checkTenant = tenantService.check(code);
            if (!checkTenant) {
                throw BizException.wrap(R.FAIL_CODE,"没有此商家编码");
            }
            //通过code获取appId
            R<String> appId = dockmentService.getAppIdByCode(code);
            if (StringUtils.isEmpty(appId)){
                throw  BizException.wrap(R.FAIL_CODE,"此商家的appId为空,请联系管理员");
            }
            //通过code值查询数据库获取到appId
//            String appId = "10037e6f66f2d0f901672aa27d690006";
            //获得了appId和entity,然后对他们进行加密判断传进来的时候合法
            String appIdEntity = appId + entityTenant;
            //MessageDigest对象开始被初始化
            MessageDigest md5 = MessageDigest.getInstance("MD5");
            //通过调用update方法处理数据
            md5.update(appIdEntity.getBytes("utf-8"));
            //调用digest方法完成哈希计算
            String appIdEncode = Base64.encodeBase64String(md5.digest());
            //统一编码
            appIdEncode = new String(appIdEncode.getBytes("ISO-8859-1"), "UTF-8");
            appIdCode = new String(appIdCode.getBytes("ISO-8859-1"), "UTF-8");
            if (appIdEncode.trim().equals(appIdCode.trim())) {
                return super.preHandle(request, response, handler);
            } else {
                throw BizException.wrap(R.FAIL_CODE, "参数不合法");
            }
        }

六、一起讨论

希望有小伙伴们一起讨论一下,在使用Mac、MessageDigest请求进来之后,服务方如何去做校验的,可以在下方留言。

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

TGB-Earnest

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值