All Downloads are FREE. Search and download functionalities are using the official Maven repository.

cn.bestwu.api.sign.ApiSignAlgorithm.kt Maven / Gradle / Ivy

There is a newer version: 1.2.4
Show newest version
package cn.bestwu.api.sign

import org.slf4j.LoggerFactory
import org.springframework.util.DigestUtils
import org.springframework.util.MultiValueMap
import org.springframework.util.StringUtils
import org.springframework.web.context.request.RequestContextHolder
import org.springframework.web.context.request.ServletRequestAttributes
import java.util.*
import javax.servlet.http.HttpServletRequest

/**
 * 签名算法

 * @author Peter Wu
 */
class ApiSignAlgorithm(private val properties: ApiSignProperties) {

    /**
     * 验证参数签名

     * @param request 请求
     */
    fun checkSign(request: HttpServletRequest) {
        val signParameter = properties.parameterName
        var sign: String? = request.getHeader(signParameter)
        if (sign == null) {
            sign = request.getParameter(signParameter)
        }

        if (skip(request)) {
            return
        }
        if (!StringUtils.hasText(sign)) {
            if (log.isDebugEnabled) {
                log.debug("客户端签名为空")
            }
            throw IllegalSignException()
        }
        if (sign!!.length != 32) {
            if (log.isDebugEnabled) {
                log.debug("客户端签名长度不匹配{}:{}", sign.length, sign)
            }
            throw IllegalSignException()
        }
        if (properties.isSimple) {
            val signParams = signParams(request)
            if (!sign.equals(signParams, ignoreCase = true)) {
                if (log.isDebugEnabled) {
                    log.debug("客户端参数签名错误,客户端:{},服务端:{}", sign, signParams)
                }
                throw IllegalSignException()
            }
        } else {
            val signParams = signParams(request).substring(0, 16)
            if (!sign.substring(0, 16).equals(signParams, ignoreCase = true)) {
                if (log.isDebugEnabled) {
                    log.debug("客户端参数签名错误,客户端:{},服务端:{}", sign.substring(0, 16), signParams)
                }
                throw IllegalSignException()
            }
            if (properties.isVerifyUserAgent) {
                val signUserAgent = signUserAgent(request.getHeader("user-agent")).substring(16, 24)
                if (!sign.substring(16, 24).equals(signUserAgent, ignoreCase = true)) {
                    if (log.isDebugEnabled) {
                        log.debug("客户端UserAgent签名错误,客户端:{},服务端:{}", sign.substring(16, 24), signUserAgent)
                    }
                    throw IllegalSignException()
                }
            }

            val clientTimeDifference = properties.allowableClientTimeDifference
            if (clientTimeDifference > 0) {
                val signTime = sign.substring(24, 32)
                val time = System.currentTimeMillis() / (clientTimeDifference * 1000)
                if (!signTime(time).substring(24, 32).equals(signTime, ignoreCase = true) && !signTime(time - 1)
                                .substring(24, 32).equals(signTime, ignoreCase = true) && !signTime(
                                time + 1).substring(24, 32).equals(signTime, ignoreCase = true)) {
                    if (log.isDebugEnabled) {
                        log.debug("客户端时间签名错误,客户端:{}", sign.substring(24, 32))
                    }
                    throw IllegalSignException()
                }
            }
        }

        if (log.isDebugEnabled) {
            log.debug("签名验证通过")
        }
    }

    /**
     * @param requestParams 请求参数
     * *
     * @param userAgent userAgent
     * *
     * @return 签名
     */
    fun sign(requestParams: MultiValueMap, userAgent: String): String {
        return if (properties.isSimple) {
            signParams(requestParams)
        } else
            signParams(requestParams).substring(0, 16) + signUserAgent(userAgent).substring(16, 24) + signTime(
                    System.currentTimeMillis() / properties.allowableClientTimeDifference).substring(24, 32)
    }

    /**
     * @param requestParams 请求参数
     * *
     * @return 签名
     */
    fun sign(requestParams: MultiValueMap): String {
        return if (properties.isSimple) {
            signParams(requestParams)
        } else
            signParams(requestParams).substring(0, 16) + signUserAgent("").substring(16, 24) + signTime(
                    System.currentTimeMillis() / properties.allowableClientTimeDifference).substring(24, 32)
    }

    /**
     * 签名时间

     * @param time 时间
     * *
     * @return 签名后的时间
     */
    private fun signTime(time: Long): String {
        var timestamp = time.toString()
        timestamp += properties.clientSecret
        timestamp = DigestUtils.md5DigestAsHex(timestamp.toByteArray())
        return timestamp
    }

    /**
     * 签名 user-agent

     * @param userAgent userAgent
     * *
     * @return 签名后 user-agent
     */
    private fun signUserAgent(userAgent: String): String {
        var useragent = userAgent
        useragent += properties.clientSecret
        useragent = DigestUtils.md5DigestAsHex(useragent.toByteArray())
        return useragent
    }

    /**
     * @return 签名后的参数
     */
    private fun signParams(request: HttpServletRequest): String {
        val requestParams = request.parameterMap
        val keys = ArrayList(requestParams.keys)
        keys.sort()
        var prestr = StringBuilder("")
        for (key in keys) {
            val values = requestParams[key]
            val value = StringBuilder()
            val length = values!!.size
            for (i in 0 until length) {
                value.append(values[i] ?: "")
                value.append(if (i == length - 1) "" else ",")
            }
            if (value.toString() == "" || key.equals("sign", ignoreCase = true) || key.equals("sign_type", ignoreCase = true)) {
                continue
            }
            prestr.append(key).append("=").append(value).append("&")
        }
        if (log.isDebugEnabled) {
            log.debug("待签名参数字符串:{}", prestr)
        }
        prestr = prestr.append(properties.clientSecret)
        return DigestUtils.md5DigestAsHex(prestr.toString().toByteArray())
    }

    /**
     * @param requestParams 请求参数
     * *
     * @return 签名后的参数
     */
    private fun signParams(requestParams: MultiValueMap): String {
        val keys = ArrayList(requestParams.keys)
        keys.sort()
        var prestr = StringBuilder("")
        for (key in keys) {
            val values = requestParams[key]
            val value = StringBuilder()
            val length = values!!.size
            for (i in 0 until length) {
                value.append(values[i])
                value.append(if (i == length - 1) "" else ",")
            }
            if (value.toString() == "" || key.equals("sign", ignoreCase = true) || key.equals("sign_type", ignoreCase = true)) {
                continue
            }
            prestr.append(key).append("=").append(value).append("&")
        }
        if (log.isDebugEnabled) {
            log.debug("待签名参数字符串:{}", prestr)
        }
        prestr = prestr.append(properties.clientSecret)
        return DigestUtils.md5DigestAsHex(prestr.toString().toByteArray())
    }


    /**
     * @param request 请求
     * *
     * @return request 是否跳过
     */
    private fun skip(request: HttpServletRequest): Boolean {
        return if (properties.isCanSkip) {
            request.getAttribute("SKIP_SIGN") as? Boolean ?: false
        } else {
            false
        }
    }

    companion object {

        private val log = LoggerFactory.getLogger(ApiSignAlgorithm::class.java)

        /**
         * 过滤签名验证
         */
        fun skip() {
            request!!.setAttribute("SKIP_SIGN", true)
        }

        /**
         * @return 当前请求
         */
        private val request: HttpServletRequest?
            get() {
                val requestAttributes = RequestContextHolder
                        .getRequestAttributes() as? ServletRequestAttributes
                return requestAttributes?.request
            }
    }

}





© 2015 - 2024 Weber Informatics LLC | Privacy Policy