ThinkPHP6利用前端跨域请求限制实现域名授权

本文介绍了作者晓杰在开发活码引流系统时,如何利用ThinkPHP6和前端跨域请求限制来实现域名授权。通过创建授权表、新增授权站点并在后端设置`Access-Control-Allow-Origin`解决跨域问题。为减轻数据库压力,采用Redis进行授权检测,通过Redis Key的存在性判断来高效验证授权域名。

在这里插入图片描述

前言

晓杰最近在开发一个活码引流系统,功能基本已经开发完成,正在研究授权方面的问题,下面是晓杰研究出来的结果,分享给大家,不喜勿喷!

研究过程

由于前后端分离架构,前端使用了Vue2.6 后端使用了ThinkPHP6 ,刚开始开发的时候就遇到了跨域请求失败的问题。
在这里插入图片描述
后续将后台增加了 $header[‘Access-Control-Allow-Origin’] = * 才解决了跨域问题,到了授权问题晓杰就想着是否可以利用该机制进行授权认证,目前晓杰暂时使用该授权方案。

创建授权表

在这里插入图片描述

新增授权站点

    public static function add($param){
        try {
           $validate = new AppConfigValidate();
            if (!$validate->scene('beforAdd')->check($param)) {
                return Res::error($validate->getError());
            }
            $param['status']=1;
            if(!$validate->scene('add')->check($param)){
                return  Res::error($validate->getError());
            }
           AppConfigModel::add($param);
            LogService::write('添加','添加');
          return  Res::ok();
        }catch (\PDOException $e){
            return Res::error($e->getMessage());
        }
    }

正常的新增,在支付授权订单后进行回调

//利用订单回调字符串 修改站点授权状态
$param = [
   'id' => $order['order_str'],
   'status' =>2
   ];
if (!AppConfigModel::edit($param)) {
   return 'fail';
}
//修改成功后 查询授权站点域名
$appInfo = AppConfigModel::findById($order['order_str']);
$redis = new Redis();
//加入Redis 后面会讲到这个作用
$redisKey = "newSoft:whiteHost:".$appInfo['app_domain'];
$redis ->set($redisKey,1);

授权检测

晓杰刚开始直接用查询数据库方式进行授权检测,但是考虑因为这个授权检测每次请求都会进行查询数据库,并发多了对服务器压力会变成负担,所以就想了个方案,Redis,刚开始还用array存储白名单 然后再进行
in_array 判断,晓杰觉得还是繁琐,一不做二不休 直接利用RedisKey是否存在进行判断。下面是实现代码,如有不足之处希望大家指出。

<?php

namespace app\common\middleware;


use app\common\utils\Massage;
use app\common\utils\Res;
use app\group\appConfig\model\AppConfigModel;
use Closure;
use think\cache\driver\Redis;
use think\Config;

class AllowCrossDomain
{

    protected $cookieDomain;

    protected $header = [
        'Access-Control-Allow-Credentials' => 'true',
        'Access-Control-Max-Age'           => 1800,
        'Access-Control-Allow-Methods'     => 'GET, POST, PATCH, PUT, DELETE, OPTIONS',
        'Access-Control-Allow-Headers'     => 'Authorization, Content-Type, If-Match, If-Modified-Since, If-None-Match, If-Unmodified-Since, X-CSRF-TOKEN, X-Requested-With,token',
    ];

    public function __construct(Config $config)
    {
        $this->cookieDomain = $config->get('cookie.domain', '');
    }
    //校验白名单
    private static function checkWhiteHost($host){
        $redis = new Redis();
        $redisKey = "newSoft:whiteHost:localhost";
        //先判断localhost是否存在 不存在说明没有进行初始化
        if (empty($redis ->get($redisKey))){
            $map[] = ['status', '=', 2];
            $whiteHostList = AppConfigModel::list($map);
            $redis = new Redis();
            foreach ($whiteHostList as $key => $value){
                $redisKey = "newSoft:whiteHost:".$value['app_domain'];
                $redis ->set($redisKey,1);
            }
            $redisKey = "newSoft:whiteHost:localhost";
            $redis ->set($redisKey,1);
            $redisKey = "newSoft:whiteHost:127.0.0.1";
            $redis ->set($redisKey,1);
        }
        //组装RedisKey
        $redisKey = "newSoft:whiteHost:".$host;
        if (empty($redis ->get($redisKey))){
        //不存在返回false
            return false;
        }
        return true;
    }
    public function handle($request, Closure $next, ?array $header = [])
    {
        $header = !empty($header) ? array_merge($this->header, $header) : $this->header;

        if (!isset($header['Access-Control-Allow-Origin'])) {
            $origin = $request->header('origin');
            //获取请求host
            $host = parse_url($origin)['host'];
            //校验白名单
            if (!self::checkWhiteHost($host)){
            	//失败返回站点未授权
                return Res::error(Massage::WEBSITE_ERROR);
                die();
            }
            $header['Access-Control-Allow-Origin'] = $origin;
         
        }

        return $next($request)->header($header);
    }

}

#效果
刚测试没增加127.0.0.1这个域名白名单
在这里插入图片描述
增加进白名单后请求成功
在这里插入图片描述

本文作者

Soujer

<think>嗯,用户问的是如何在ThinkPHP5.1中设置问题。首先,我需要回忆一下问题的基本概念。是由于浏览器的同源策略导致的,当不同域名、端口或协议时,请求会被阻止。解决通常有几种方法,比如JSONP、CORS,或者服务器端设置响应头。 在ThinkPHP5.1中,常见的方式应该是通过中间件或者直接在控制器里设置响应头。用户可能是一个后端开发者,正在开发API接口,遇到了前端请求的问题,需要配置后端允许。 接下来,我需要考虑具体怎么在ThinkPHP5.1里实现。中间件是比较好的方式,可以统一处理所有请求。记得中间件的创建步骤,以及如何注册中间件。可能需要设置Access-Control-Allow-Origin、Access-Control-Allow-Headers、Access-Control-Allow-Methods等头部信息。如果是复杂请求,比如带有自定义头部的请求,还需要处理预检请求(OPTIONS方法)。 另外,用户可能不清楚中间件的使用方法,可能需要详细说明中间件的创建和注册步骤。或者,如果用户只是临时处理某个控制器的,也可以在控制器的初始化方法里设置头部,但这种方法不够灵活,每个控制器都要写一遍。 还可能要考虑是否允许携带凭证,比如Access-Control-Allow-Credentials,但这需要具体场景。另外,允许的方法如GET、POST等需要明确列出,不能使用通配符*,否则某些浏览器可能不支持。 用户可能遇到的错误包括设置头部后依然出现问题,这时候可能需要检查中间件是否正确注册,或者响应头是否在OPTIONS请求中正确返回。此外,某些情况下,Nginx或Apache的配置也可能影响,需要确认服务器配置是否正确。 是否需要处理OPTIONS请求?因为复杂请求会先发OPTIONS预检请求,如果后端没有正确处理,会导致后续请求失败。所以在中间件里,可能需要先判断请求方法是否为OPTIONS,直接返回响应,避免进入业务逻辑。 总结步骤应该是:创建中间件,设置响应头,处理OPTIONS请求,注册中间件到全局或特定路由。给出代码示例,说明各个头部的作用,以及可能的注意事项,比如通配符的使用限制,是否需要携带凭证等。 最后,确认用户使用的是ThinkPHP5.1版本,不同版本可能有不同的中间件机制,确保提供的方案适用于该版本。比如,5.1中间件是通过命令行生成还是手动创建,中间件的注册方式是否有变化。可能需要提醒用户根据实际情况调整路径和命名空间。</think>在 ThinkPHP 5.1 中解决问题,可以通过以下两种常见方式实现: --- ### 方法一:使用中间件统一处理(推荐) **步骤:** 1. 创建中间件文件 在 `application/http/middleware` 目录下新建 `Cors.php`(若无目录请手动创建): ```php <?php namespace app\http\middleware; class Cors { public function handle($request, \Closure $next) { $response = $request); // 设置头 $response->header([ 'Access-Control-Allow-Origin' => '*', // 允许所有域名(生产环境建议指定具体域名) 'Access-Control-Allow-Headers' => 'Authorization, Content-Type, Accept, Origin, X-Requested-With', 'Access-Control-Allow-Methods' => 'GET, POST, PUT, DELETE, OPTIONS', 'Access-Control-Max-Age' => 3600 // 预检请求缓存时间 ]); // 直接返回 OPTIONS 请求的响应 if ($request->isOptions()) { $response->code(204); } return $response; } } ``` 2. 注册中间件 修改 `application/config.php` 文件,在 `middleware` 配置项中添加中间件: ```php 'middleware' => [ // ...其他中间件 \app\http\middleware\Cors::class, ], ``` --- ### 方法二:在控制器中单独设置 在控制器的方法中直接添加响应头: ```php public function yourMethod() { // 设置头 header('Access-Control-Allow-Origin: *'); header('Access-Control-Allow-Headers: Authorization, Content-Type, Accept, Origin, X-Requested-With'); header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS'); // 处理 OPTIONS 预检请求 if (request()->isOptions()) { exit(); } // 你的业务逻辑... } ``` --- ### 注意事项 1. **安全性** - 生产环境建议将 `Access-Control-Allow-Origin` 设置为具体域名(如 `https://yourdomain.com`)而非 `*`。 - 若需要携带 Cookie 或认证信息,需设置 `Access-Control-Allow-Credentials: true`,且 `Access-Control-Allow-Origin` 不能为 `*`。 2. **预检请求 (OPTIONS)** 复杂请求(如自定义 Header 或非简单方法)会先发送 `OPTIONS` 请求,需确保服务器正确处理。 3. **缓存优化** `Access-Control-Max-Age` 可减少频繁的预检请求,但需根据业务需求调整时间。 --- 通过以上方法,即可在 ThinkPHP 5.1 中实现支持。推荐使用中间件方式,便于全局统一管理。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Soujer

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

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

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

打赏作者

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

抵扣说明:

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

余额充值