javascript 获得本地 IP 地址

参考网页:

构思

通过 PHP 获得的客户端本地 IP 地址,往往不正确,在局域网内含有子网的时候,特别不方便,但是 javascript 可以读取本地的 IP 地址。 设想 javascript 把读取的 IP 地址传过来。最好的办法是采用 ajax 的方式传回。但是这个方法有局限性。做到代码里,不好直接使用,而且耦合特别严重,不适合再次使用。想了好久,最后用 InputWidget 的方式实现了。这个有一个非常不好的地方是,这个IP是可以被浏览器修改的,安全性完全没有保障。不过 ajax 的方式传回数据依旧会被修改。不如这么简单省事点。

代码实现

thanks to WebRTC, it is very easy to get local IP in WebRTC supported browsers( at least for now). I have modified the source code, reduced the lines, not making any stun requests since you only want Local IP not the Public IP, the below code works in latest Firefox and Chrome, just run the snippet and check for yourself:

function findIP(onNewIP) { //  onNewIp - your listener function for new IPs
  var myPeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection; //compatibility for firefox and chrome
  var pc = new myPeerConnection({iceServers: []}),
    noop = function() {},
    localIPs = {},
    ipRegex = /([0-9]{1,3}(\.[0-9]{1,3}){3}|[a-f0-9]{1,4}(:[a-f0-9]{1,4}){7})/g,
    key;

  function ipIterate(ip) {
    if (!localIPs[ip]) onNewIP(ip);
    localIPs[ip] = true;
  }
  pc.createDataChannel(""); //create a bogus data channel
  pc.createOffer(function(sdp) {
    sdp.sdp.split('\n').forEach(function(line) {
      if (line.indexOf('candidate') < 0) return;
      line.match(ipRegex).forEach(ipIterate);
    });
    pc.setLocalDescription(sdp, noop, noop);
  }, noop); // create offer and set local description
  pc.onicecandidate = function(ice) { //listen for candidate events
    if (!ice || !ice.candidate || !ice.candidate.candidate || !ice.candidate.candidate.match(ipRegex)) return;
    ice.candidate.candidate.match(ipRegex).forEach(ipIterate);
  };
}

var ul = document.createElement('ul');
ul.textContent = 'Your IPs are: '
document.body.appendChild(ul);

function addIP(ip) {
  console.log('got ip: ', ip);
  var li = document.createElement('li');
  li.textContent = ip;
  ul.appendChild(li);
}

findIP(addIP);

一个简单的例子

<html>
<body>
</body>
<script type="text/javascript">
function findIP(onNewIP) { //  onNewIp - your listener function for new IPs
  var myPeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection; //compatibility for firefox and chrome
  var pc = new myPeerConnection({iceServers: []}),
    noop = function() {},
    localIPs = {},
    ipRegex = /([0-9]{1,3}(\.[0-9]{1,3}){3}|[a-f0-9]{1,4}(:[a-f0-9]{1,4}){7})/g,
    key;

  function ipIterate(ip) {
    if (!localIPs[ip]) onNewIP(ip);
    localIPs[ip] = true;
  }
  pc.createDataChannel(""); //create a bogus data channel
  pc.createOffer(function(sdp) {
    sdp.sdp.split('\n').forEach(function(line) {
      if (line.indexOf('candidate') < 0) return;
      line.match(ipRegex).forEach(ipIterate);
    });
    pc.setLocalDescription(sdp, noop, noop);
  }, noop); // create offer and set local description
  pc.onicecandidate = function(ice) { //listen for candidate events
    if (!ice || !ice.candidate || !ice.candidate.candidate || !ice.candidate.candidate.match(ipRegex)) return;
    ice.candidate.candidate.match(ipRegex).forEach(ipIterate);
  };
}

var ul = document.createElement('ul');
ul.textContent = 'Your IPs are: '
document.body.appendChild(ul);

function addIP(ip) {
  console.log('got ip: ', ip);
  var li = document.createElement('li');
  li.textContent = ip;
  ul.appendChild(li);
}

findIP(addIP);
</script>
</html>

功能设想

为了在 Yii 2.0 中使用该功能,很方便的使用,最好做成插件的形式。代码可否简化?能跟 PHP 很好的对接吗? 先跟 PHP 对接,然后考虑放到 Yii 2.o 中。使用不断试错的方法来实现。

1、怎么把检测到的 IP 地址传回来?

第一想法是采用 ajax 的方式,在服务器端接收该 IP 地址。问题是把 ajax 的代码放在哪里呢?其中的 onNewIP 是个不错的突破口。 下面开始对代码进行第一次试错。

function findIP(onNewIP) { //  onNewIp - your listener function for new IPs
// 略
}

function writeIP(ip) {
  document.write(ip);
  document.close();
}

findIP(writeIP);

解释

  • 本打算使用 ajax 的方式,但是还需要一个回调函数,太复杂了。
  • 在 widget 里没有找到加入 actionCallBack 的方法。
  • 遂采用直接打印输出的方式回传。

2、做成 InputWidget 类

参考:

下面是怎么修改了。

3、中间过程不说了,下面把可以运行的代码贴下来。

我使用的高级模板,把这些代码放在了三个文件里。分别是

common\
    assets\
        WebRTCLocalIPAsset.php
        js\
            webrtclocalip.js
    components\
        WebRTCLocalIPWidget.php

三个文件的代码如下

./common/assets/js/webrtclocalip.js

var id = 'webrtclocalip';

function setoptions(options){
    id=options.id;
}

function findIP(onNewIP) { //  onNewIp - your listener function for new IPs
    var promise = new Promise(function (resolve, reject) {
        try {
            var myPeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection; //compatibility for firefox and chrome
            var pc = new myPeerConnection({ iceServers: [] }),
            noop = function () { },
            localIPs = {},
            ipRegex = /([0-9]{1,3}(\.[0-9]{1,3}){3}|[a-f0-9]{1,4}(:[a-f0-9]{1,4}){7})/g,
            key;
            function ipIterate(ip) {
                if (!localIPs[ip]) onNewIP(ip);
                localIPs[ip] = true;
            }
            pc.createDataChannel(""); //create a bogus data channel
            pc.createOffer(function (sdp) {
                sdp.sdp.split('\n').forEach(function (line) {
                    if (line.indexOf('candidate') < 0) return;
                    line.match(ipRegex).forEach(ipIterate);
                });
                pc.setLocalDescription(sdp, noop, noop);
            }, noop); // create offer and set local description

            pc.onicecandidate = function (ice) { //listen for candidate events
                if (ice && ice.candidate && ice.candidate.candidate && ice.candidate.candidate.match(ipRegex)) {
                    ice.candidate.candidate.match(ipRegex).forEach(ipIterate);
                }
                resolve("FindIPsDone");
                return;
            };
        }
        catch (ex) {
            reject(Error(ex));
        }
    });// New Promise(...{ ... });
    return promise;
};

//This is the callback that gets run for each IP address found
function foundNewIP(ip) {
    if (typeof window.ipAddress === 'undefined')
    {
        window.ipAddress = ip;
    }
    else
    {
        window.ipAddress += " - " + ip;
    }
}

function load() {

    //This is How to use the Waitable findIP function, and react to the
    //results arriving
    var ipWaitObject = findIP(foundNewIP);        // Puts found IP(s) in window.ipAddress
    ipWaitObject.then(
        function (result) {
            // for not hidden
            document.getElementById(id).textContent = window.ipAddress;
            // for hidden
            document.getElementById(id).value = window.ipAddress;
        },
        function (err) {
            alert ("无法找到 IP 地址" + err)
        }
    );
}

./common/assets/WebRTCLocalIPAsset.php

<?php

namespace common\assets;

use yii\web\AssetBundle;

class WebRTCLocalIPAsset extends AssetBundle
{
    public $sourcePath = '@common/assets';
    public $css = [];
    public $js = [
        'js/webrtclocalip.js',
    ];
}

./common/components/WebRTCLocalIPWidget.php

<?php
namespace common\components;

use yii\widgets\InputWidget;
use Yii;
use yii\helpers\Html;
use yii\helpers\Url;
use common\assets\WebRTCLocalIPAsset;

class WebRTCLocalIPWidget extends InputWidget
{
    public $localip;
    public $hidden = true;
    public $attributes;
    public $id = 'webrtclocalip';
    public $name = 'webrtclocalip';

    public function init()
    {
        parent::init();
    }

    public function run()
    {
        $view = $this->getView();
        $this->attributes['id'] = $this->id;
        if ($this->hasModel()) {
            if ($this->hidden) {
                $input = Html::activeHiddenInput($this->model, $this->attribute, $this->attributes);
            } else {
                $input = Html::activeTextInput($this->model, $this->attribute, $this->attributes);
            }
        } else {
            if ($this->hidden) {
                $input = Html::hiddenInput($this->name, '', $this->attributes);
            } else {
                $input = Html::textInput($this->name, '', $this->attributes);
            }
        }
        echo $input;
        WebRTCLocalIPAsset::register($view);
        $view->registerJs("setoptions({id: '" . $this->id . "'});load();");
    }
}

4、用法

类似一般的 widget

        <?= $form
            ->field($model, 'ip')
            ->label(false)
            ->widget(WebRTCLocalIPWidget::className(), [
                'id' => 'loginform-ip',
                'hidden' => true,
            ]) ?>

解释

  • WebRTCLocalIPWidget::className() 需要使用 use 语句
  • 'id' => 'loginform-ip', 需要查看网页代码检测该插件的 id,然后写入。这个暂时没有找到好的解决方案。

后期

正在把他作为一个插件,敬请期待

https://github.com/bubifengyun/yii2-webrtc-get-client-local-ip

转载于:https://my.oschina.net/bubifengyun/blog/690253

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值