36

デバイスが完全なインターネット アクセスを持っているかどうか、つまり、ユーザーがキャプティブ ポータル(ウォールド ガーデンとも呼ばれる) に限定されていないかどうか、つまり、ユーザーが完全にアクセスするためにフォームで資格情報を送信することを強制する制限されたサブネットではないことを確実に検出する必要があります。アクセス。

私のアプリは認証プロセスを自動化しているため、ログオン アクティビティを開始する前に、完全なインターネット アクセスが利用できないことを知っておくことが重要です。

問題は、ネットワーク インターフェイスが稼働しており、接続状態にあることを確認する方法ではありません。これは、サンドボックス化されたイントラネット セグメントではなく、デバイスが無制限のインターネット アクセスを持っていることを確認することです。

HTTP 200すべての要求がログイン ページにルーティングされるため、既知のホストに接続しても例外はスローされず、有効な応答コードが返されるため、これまでに試したすべてのアプローチが失敗しました。

ここに私が試したすべてのアプローチがありますが、上記の理由ではtrueなく、すべて返されます。false

1:

InetAddress.getByName(host).isReachable(TIMEOUT_IN_MILLISECONDS);
isConnected = true; <exception not thrown>

2:

Socket socket = new Socket();
SocketAddress sockaddr = new InetSocketAddress(InetAddress.getByName(host), 80);
socket.connect(sockaddr, pingTimeout);
isConnected = socket.isConnected();

3:

URL url = new URL(hostUrl));
URLConnection urlConn = url.openConnection();
HttpURLConnection httpConn = (HttpURLConnection) urlConn;
httpConn.setAllowUserInteraction(false);
httpConn.setRequestMethod("GET");
httpConn.connect();
responseCode = httpConn.getResponseCode();
isConnected = responseCode == HttpURLConnection.HTTP_OK;

では、ログイン リダイレクト ページではなく、実際のホストに接続していることを確認するにはどうすればよいでしょうか? 明らかに、使用している「ping」ホストからの実際の応答本文を確認できましたが、適切な解決策のようには見えません。

4

6 に答える 6

44

参考までに、Android 4.0.1 AOSP コード ベースの「公式」メソッドを次に示します: WifiWatchdogStateMachine.isWalledGardenConnection()。将来リンクが壊れた場合に備えて、以下のコードを含めます。

private static final String mWalledGardenUrl = "http://clients3.google.com/generate_204";
private static final int WALLED_GARDEN_SOCKET_TIMEOUT_MS = 10000;

private boolean isWalledGardenConnection() {
    HttpURLConnection urlConnection = null;
    try {
        URL url = new URL(mWalledGardenUrl); // "http://clients3.google.com/generate_204"
        urlConnection = (HttpURLConnection) url.openConnection();
        urlConnection.setInstanceFollowRedirects(false);
        urlConnection.setConnectTimeout(WALLED_GARDEN_SOCKET_TIMEOUT_MS);
        urlConnection.setReadTimeout(WALLED_GARDEN_SOCKET_TIMEOUT_MS);
        urlConnection.setUseCaches(false);
        urlConnection.getInputStream();
        // We got a valid response, but not from the real google
        return urlConnection.getResponseCode() != 204;
    } catch (IOException e) {
        if (DBG) {
            log("Walled garden check - probably not a portal: exception "
                    + e);
        }
        return false;
    } finally {
        if (urlConnection != null) {
            urlConnection.disconnect();
        }
    }
}

このアプローチは特定の URL に依存し、mWalledGardenUrl = "http://clients3.google.com/generate_204"常に204応答コードを返します。これは、DNS が干渉された場合でも機能します。その場合200、期待される代わりにコードが返されるため204です。Android デバイスでインターネットにアクセスできないというメッセージが表示されないようにするために、この特定の URL へのリクエストをスプーフィングするキャプティブ ポータルを見てきました。

Google には、このテーマのバリエーションがあります。フェッチすると、長さゼロの応答本文を持つコードhttp://www.google.com/blank.htmlが返されます。200したがって、空でないボディを取得した場合、これは、ウォールド ガーデンの背後にいることを把握する別の方法になります。

Apple はキャプティブ ポータルを検出するための独自の URL を持ってます。 com/library/test/success.htmlまたはhttp://captive.apple.com/hotspot-detect.htmlの HTTP ステータス コードと200を含む本文を返す必要がありますSuccess

: このアプローチは、国全体がウォールド ガーデンである中国など、インターネット アクセスが制限されている地域や、一部の Google/Apple サービスがブロックされる可能性がある地域では機能しません。これらの一部はブロックされない可能性があります: http://www.google.cn/generate_204http://g.cn/generate_204http://gstatic.com/generate_204またはhttp://connectivitycheck.gstatic.com/generate_204— ただし、これらはすべて Google に属しているため、動作が保証されていません。

于 2012-12-25T11:36:58.087 に答える
5

別の考えられる解決策は、HTTPS 経由で接続し、ターゲット証明書を検査することです。ウォールド ガーデンが実際に HTTPS 経由でログイン ページを提供するのか、単に接続をドロップするのかは不明です。どちらの場合でも、目的地が期待したものではないことがわかるはずです。

もちろん、TLS と証明書のチェックのオーバーヘッドもあります。残念ながら、これは認証された接続の代償です。

于 2012-12-25T12:00:06.987 に答える
1

接続のリダイレクトを防止するとうまくいくと思います。

URL url = new URL(hostUrl));
HttpURLConnection httpConn = (HttpURLConnection)url.openConnection();

/* This line prevents redirects */
httpConn.setInstanceFollowRedirects( false );

httpConn.setAllowUserInteraction( false );
httpConn.setRequestMethod( "GET" );
httpConn.connect();
responseCode = httpConn.getResponseCode();
isConnected = responseCode == HttpURLConnection.HTTP_OK;

それがうまくいかない場合、それを行う唯一の方法は、応答の本文を確認することだと思います。

于 2012-12-19T18:24:35.887 に答える
0

これは Android 4.2.2 以降のバージョンで実装されています - 私は彼らのアプローチが速くて興味深いと思います:

CaptivePortalTracker.java は次のようにウォールド ガーデンを検出します - www.google.com/generate_204 に接続してみてください - HTTP レスポンスが 204 であることを確認します

チェックが失敗した場合、私たちはウォールド ガーデンにいます。

private boolean isCaptivePortal(InetAddress server) {
    HttpURLConnection urlConnection = null;
    if (!mIsCaptivePortalCheckEnabled) return false;

    mUrl = "http://" + server.getHostAddress() + "/generate_204";
    if (DBG) log("Checking " + mUrl);
    try {
        URL url = new URL(mUrl);
        urlConnection = (HttpURLConnection) url.openConnection();
        urlConnection.setInstanceFollowRedirects(false);
        urlConnection.setConnectTimeout(SOCKET_TIMEOUT_MS);
        urlConnection.setReadTimeout(SOCKET_TIMEOUT_MS);
        urlConnection.setUseCaches(false);
        urlConnection.getInputStream();
        // we got a valid response, but not from the real google
        return urlConnection.getResponseCode() != 204;
    } catch (IOException e) {
        if (DBG) log("Probably not a portal: exception " + e);
        return false;
    } finally {
        if (urlConnection != null) {
            urlConnection.disconnect();
        }
    }
}
于 2014-01-28T20:44:33.810 に答える
0

これは、AOSP のようにここで行うのが最適です

https://github.com/aosp-mirror/platform_frameworks_base/blob/e3a0f42e8e8678f6d90ddf104d485858fbb2e35b/services/core/java/com/android/server/connectivity/NetworkMonitor.java

private static final String GOOGLE_PING_URL = "http://google.com/generate_204";
private static final int SOCKET_TIMEOUT_MS = 10000;

public boolean isCaptivePortal () {

try {
            URL url = new URL(GOOGLE_PING_URL);
            urlConnection = (HttpURLConnection) url.openConnection();
            urlConnection.setConnectTimeout(SOCKET_TIMEOUT_MS);
            urlConnection.setReadTimeout(SOCKET_TIMEOUT_MS);
            urlConnection.setUseCaches(false);
            urlConnection.getInputStream();
            return (urlConnection.getResponseCode() != 204)
                    && (urlConnection.getResponseCode() >= 200)
                    && (urlConnection.getResponseCode() <= 399);
        } catch (Exception e) {
            // for any exception throw an exception saying check was unsuccesful
        } finally {
            if (urlConnection != null) {
                urlConnection.disconnect();
            }
        }
}

これはおそらくプロキシ ネットワークでは機能しないことに注意してください。AOSP url のように、より高度な処理を行う必要があります。

于 2019-01-24T05:12:32.400 に答える