package io.geekidea.springbootplus.framework.shiro.signature;

import lombok.extern.slf4j.Slf4j;

import java.util.Collections;
import java.util.Enumeration;
import java.util.LinkedHashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.springframework.util.CollectionUtils;

import com.alibaba.fastjson.JSON;
import com.google.common.collect.Maps;

/**
 * 签名检验员
 * @Author luozh
 * @Date 2022年04月14日 17:31
 * @Version 1.0
 */
@Slf4j
public class SignatureChecker {

    /**
     * 时间戳请求最小限制(600s)
     * 设置的越小，安全系数越高，但是要注意一定的容错性
     */
    private static final long MAX_REQUEST = 10 * 60 * 1000L;

    /**
     * 签名校验
     * @Author luozh
     * @Date 2022年04月14日 05:32:32
     * @param request 请求信息
     * @param clientSignature 签名
     * @param appSecret app秘钥
     * @Return appKey
     */
    public static void check(HttpServletRequest request, String clientSignature, String appSecret) {
        // 获取到入参
        Map<String, String> params = getParameterMap(request);
        // 获取请求头
        Map<String, String> headers = getHeaderMap(request);
        // resourcePath
        String resourcePath = request.getRequestURI();
        // request methos
        String requestMethod = request.getMethod();

        String canonicalString = SignUtils.buildCanonicalString(requestMethod, resourcePath, headers, params);
        log.info("服务端api签名字符串: {}", canonicalString);
        log.info("服务端api appSecret: {}", appSecret);
        String serverSignature = new HmacSHA256Signature().computeSignature(appSecret, canonicalString);
        log.info("服务端api签名结果: {}", serverSignature);


        if (!serverSignature.equals(clientSignature)) {
            // 返回给用户的response中告诉用户正确的用于验证加密的签名字符串
            throw new AuthenticationException("token 错误");
        }
    }

    public static Map<String, String> getHeaderMap(HttpServletRequest servletRequest) {
        Map<String, String> headerMap = new LinkedHashMap<>();
        Enumeration<String> names = servletRequest.getHeaderNames();
        while (names.hasMoreElements()) {
            String key = names.nextElement();
            headerMap.put(key, servletRequest.getHeader(key));
        }

        return headerMap;
    }

    /**
     * 获取请求入参
     *
     * @param servletRequest
     * @return
     */
    public static Map<String, String> getParameterMap(HttpServletRequest servletRequest) {
        Map<String, String> paramMap = new LinkedHashMap<>();

        if (servletRequest instanceof RequestWrapper) {
            RequestWrapper requestWrapper = (RequestWrapper) servletRequest;
            Map<String, String> body = getParameterMap(requestWrapper.getBody());
            if (!CollectionUtils.isEmpty(body)) {
                paramMap.putAll(body);
            }
        }
        Enumeration<String> names = servletRequest.getParameterNames();
        while (names.hasMoreElements()) {
            String key = names.nextElement();
            paramMap.put(key, servletRequest.getParameter(key));
        }

        return paramMap;
    }

    /**
     * 获取参数对象
     *
     * @param params
     * @return
     */
    public static Map<String, String> getParameterMap(String params) {
        try {
            return (Map<String, String>) JSON.parseObject(params, Map.class);
        } catch (Exception e) {
            return convertParameterToMap(params);
        }
    }


    /**
     * 将参数转换为Map类型
     *
     * @param param
     * @return
     */
    public static Map<String, String> convertParameterToMap(String param) {
        if (StringUtils.isEmpty(param)) {
            return Collections.emptyMap();
        }
        Map<String, String> pMap = Maps.newLinkedHashMap();
        String[] pArray = StringUtils.split(param, "&");
        for (int i = 0; i < pArray.length; i++) {
            String[] array = StringUtils.split(pArray[i], "=");
            if (array.length == 2) {
                pMap.put(array[0], array[1]);
            }
        }
        return pMap;
    }

}
