Intereting Posts
RestKit в android? Firebase и Crashlytics – Какой из них использовать? Данные многостраничной формы POST с использованием Retrofit 2.0, включая изображение Как сбросить представление в исходное состояние после использования аниматоров для анимации его некоторых свойств? Как открыть или имитировать щелчок на андроид Preference, созданный с помощью XML, программным путем? Android SDK DDMS в Eclipse не распознает мой Android-телефон Несколько файлов dex определяют Lcom / google / firebase / FirebaseException Исключение Null Pointer начиная с IntentService Не может избавиться от ошибки "/ usr / bin / ld: не удается найти -lncurses" «Невозможно нарисовать переработанные растровые изображения» при отображении растровых изображений в приложении Галерея, прикрепленных к адаптеру Android – Как применять diffenernt Image Effects для растровых изображений, таких как сепия, черно-белый, размытие и т. Д. Android вырастает в куче Gps статус включен / отключен широковещательный приемник Использование Android Studio с Java 1.7 Панель действий Android – динамическое удаление панели действий

Как проверить неограниченный доступ в Интернет? (Обнаружение портативного портала)

Мне нужно надежно определить, имеет ли устройство полный доступ к Интернету, т. Е. Что пользователь не ограничен узким порталом (также называемым огороженным садом ), то есть ограниченная подсеть, которая заставляет пользователей отправлять свои учетные данные в форме, чтобы получить полную доступ.

Мое приложение автоматизирует процесс аутентификации, поэтому важно знать, что полный доступ в Интернет недоступен до начала действия входа в систему.

Речь идет не о том, как проверить, что сетевой интерфейс вставлен и подключен. Речь идет о том, чтобы обеспечить неограниченный доступ к Интернету, в отличие от изолированного сегмента интрасети.

Все подходы, которые я пробовал до сих пор, терпят неудачу, потому что подключение к любому известному хосту не приведет к исключению, а вернет действительный код ответа 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; 

Итак, как я могу убедиться, что я подключился к реальному хосту вместо страницы перенаправления входа? Очевидно, я мог проверить фактическое тело ответа от используемого мной «пингового» хоста, но это не похоже на правильное решение.

Solutions Collecting From Web of "Как проверить неограниченный доступ в Интернет? (Обнаружение портативного портала)"

Для справки, вот «официальный» метод из базы данных AOSP Android 4.0.1: 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 будет помешано, поскольку в этом случае вместо ожидаемого 204 будет возвращен код 200 . Я видел некоторые невольные порталы, подменяющие запросы на этот конкретный URL-адрес, чтобы предотвратить доступ к Интернету на устройствах Android.

В Google есть вариация этой темы: выборка http://www.google.com/blank.html вернет код 200 с телом ответа нулевой длины. Поэтому, если вы получите непустое тело, это будет другим способом выяснить, что вы находитесь за огороженным садом.

У Apple есть свои собственные URL-адреса для обнаружения невольных порталов: при подключении к сети IOS и устройства MacOS будут подключаться к URL-адресу, подобному http://www.apple.com/library/test/success.html , http://attwifi.apple. Com / library / test / success.html или http://captive.apple.com/hotspot-detect.html, который должен вернуть код состояния HTTP из 200 и тело, содержащее Success .

ПРИМЕЧАНИЕ . Этот подход не будет работать в районах с ограниченным доступом к Интернету, таких как Китай, где вся страна представляет собой огороженный сад и где большинство служб Google / Apple блокируются или фильтруются. Некоторые из них могут не блокироваться: http://www.google.cn/generate_204 , http://g.cn/generate_204 , http://gstatic.com/generate_204 или http://connectivitycheck.gstatic.com/generate_204 – но все они принадлежат Google, поэтому не гарантируется работа.

Другим возможным решением может быть подключение через HTTPS и проверка целевого сертификата. Не уверен, что стеновые сады фактически обслуживают страницу входа через HTTPS или просто отбрасывают соединения. В любом случае вы должны уметь видеть, что ваш пункт назначения не тот, который вы ожидали.

Конечно, у вас также есть накладные расходы на TLS и проверки сертификатов. Такова цена аутентифицированных подключений, к сожалению.

Я считаю, что предотвращение перенаправления для вашего соединения будет работать.

 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; 

Если это не сработает, я думаю, что единственный способ сделать это – проверить тело ответа.

Это было реализовано в версии Android 4.2.2+ – я нахожу их подход быстрым и интересным:

CaptivePortalTracker.java обнаруживает огороженный сад следующим образом. Попробуйте подключиться к http://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(); } } } 

Если вы уже используете retrofit вы можете сделать это путем retrofit . Просто сделайте страницу ping.html и отправьте запрос главы на него с помощью модификации и убедитесь, что ваш http-клиент настроен следующим образом: ( followRedirects(false) часть является самой важной частью)

 private OkHttpClient getCheckInternetOkHttpClient() { return new OkHttpClient.Builder() .readTimeout(2L, TimeUnit.SECONDS) .connectTimeout(2L, TimeUnit.SECONDS) .followRedirects(false) .build(); } 

Затем создайте свою модификацию, как показано ниже:

 private InternetCheckApi getCheckInternetRetrofitApi() { return (new Retrofit.Builder()) .baseUrl("[base url of your ping.html page]") .addConverterFactory(GsonConverterFactory.create(new Gson())) .client(getCheckInternetOkHttpClient()) .build().create(InternetCheckApi.class); } 

Ваш InternetCheckApi.class будет выглядеть так:

 public interface InternetCheckApi { @Headers({"Content-Typel: application/json"}) @HEAD("ping.html") Call<Void> checkInternetConnectivity(); } 

То вы можете использовать его, как показано ниже:

 getCheckInternetOkHttpClient().checkInternetConnectivity().enqueue(new Callback<Void>() { public void onResponse(Call<Void> call, Response<Void> response) { if(response.code() == 200) { //internet is available } else { //internet is not available } } public void onFailure(Call<Void> call, Throwable t) { //internet is not available } } ); 

Обратите внимание, что ваш клиент проверки HTTP-клиента должен быть отделен от вашего основного http-клиента.