«

PHP前后端分离,数据防篡改解决方案

Risin9 发布于 阅读:27 PHP



在前后端分数据传输中,经常需要考虑数据安全问题,确保数据不被篡改,最近正好做的项目有所涉及,记录一下前后端数据加密过程

1.前端使用layui-vue,在http拦截器中统一加入数据加密逻辑

/**
 * 需要加签、验签的路径集合
 * 例:"/user",将匹配以"/user"开头的所有API
 */
const blackBeginUrl = ["/login", "/role", '/admin']

class Http {
    service;
    constructor(config: TAxiosOption) {
        this.service = axios.create(config)

        /* 请求拦截 */
        this.service.interceptors.request.use((config: InternalAxiosRequestConfig) => {
            const userInfoStore = useUserStore();
            if (userInfoStore.token) {
                (config.headers as AxiosRequestHeaders).token = userInfoStore.token as string
            } else {
                if(router.currentRoute.value.path!=='/login') {
                    router.push('/login');
                }
            }

            // 数据加密过程
            let needSign = false;
            for (let blackUrl of blackBeginUrl) {
                // @ts-ignore
                if (config.url.indexOf(blackUrl) != -1) {
                    needSign = true;
                    break;
                }
            }
            if(needSign){
            // 这里默认post请求的Content-Type:application/json
                let requestData = "";
                console.log(config.method)
                if (config.method === "get") {
                    requestData = config.params
                } else if (config.method == "post") {
                    requestData = config.data
                }
                // 时间戳,作为slat的必备组成之一
                const timestamp = new Date().getTime().toString();
                config.headers['Timestamp'] = timestamp
                // 随机字符串,作为slat的必备组成之一
                const randomStr = "K:*C8bw6zJ"
                // slat = 时间戳 + 随机字符串 (自定义slat公式)
                const slat = timestamp.concat('', randomStr);
                const signature = CryptoHmacSha1(JSON.stringify(requestData), slat);
                config.headers['Signature'] = signature;
            }
            return config
        }, error => {
            return Promise.reject(error);
        })

2.后端使用的是Thinkphp6框架,在中间件中加入数据解密逻辑

<?php
declare (strict_types = 1);

namespace app\middleware;

use response\ApiCode;

class CheckRequestMiddleware
{
    /**
     * 处理后台接口签名
     *
     * @param \think\Request $request
     * @param \Closure       $next
     * @return Response
     */
    public function handle($request, \Closure $next)
    {
        //验证请求头时间
        $timestamp = $request->header('Timestamp');
        $serTimestamp = time() * 1000;
        if($timestamp - $serTimestamp > 60 * 1000){
            //签名时间戳过期,返回提示信息
           return json(['code' => ApiCode::CODE_ERROR, 'data'=>[], 'msg'=> ApiCode::msg['CODE_ERROR']]);
        }
        //验证签名
        $signature = $request->header('Signature');
        if($signature){
            $params = $request->param();
            $randomStr = env('signature.random');
            $salt = $timestamp . $randomStr;
            //生成临时签名验证
            $temSignature = hash_hmac('sha1', json_encode($params, JSON_UNESCAPED_UNICODE), $salt);
            if($temSignature !== $signature){
                //数据签名验证失败,可能被篡改,返回提示信息
                return json(['code' => ApiCode::SIGN_ERROR, 'data'=>[], 'msg'=> ApiCode::msg['SIGN_ERROR']]);
            }
        }

        //数据签名验证通过,继续处理请求
        return $next($request);
    }
}

3.模拟请求,查看效果


PHP

收到1条评论
avatar
Risin9 2024-06-18 10:29
写的很好
回复