2015-09-21 3 views
5

как вопрос allready говорит, я пытаюсь выполнить проверку подлинности в android.
До сих пор я не использовал DefaultHttpClient и это метод аутентификации (с использованием UsernamePasswordCredentials и так далее), но это не рекомендуется, так как Android 5 и будет удален в Android 6.
Так я о том, чтобы перейти от DefaultHttpClient к HttpUrlConnection.
Сейчас я пытаюсь добиться дайджест-проверки подлинности, который должен работать довольно просто, как объяснено here:Дайджест-аутентификация в Android с использованием HttpURLConnection

Authenticator.setDefault(new Authenticator() { 
    protected PasswordAuthentication getPasswordAuthentication() { 
     return new PasswordAuthentication(username, password); 
    } 
}); 

Но getPasswordAuthentication не получает не призывал к каким-то причинам.
Во время поиска этой проблемы я нашел разные сообщения, так как аутентификация дайджеста не поддерживается HttpUrlConnection в android, но эти сообщения с 2010-2012 годов, поэтому я не уверен, что это все еще так. Также мы используем HttpUrlConnection с аутентификацией дайджеста в нашем рабочем приложении Java, где он работает.

Я также нашел несколько сообщений, говорящих о OkHttp. OkHttp, похоже, используется Android под капотом (точнее, HttpUrlConnectionImpl). Но это HttpUrlConnectionImpl немного странно, оно даже не показано в иерархии типа Eclipse, и я не могу его отладить. Также он должен быть com.squareup.okhttp.internal.huc.HttpUrlConnectionImpl, а в андроиде - com.android.okhttp.internal.http.HttpUrlConnectionImpl.

Так что я просто не могу выполнить проверку подлинности с помощью этого HttpUrlConnection в android.
Может ли кто-нибудь сказать мне, как это сделать без внешних библиотек?

EDIT:
сервер запрашивает аутентификацию дайджеста:

WWW-Authenticate: Digest realm="Realm Name",domain="/domain",nonce="nonce",algorithm=MD5,qop="auth" 

Так Basic-аутентификация не должна»работать, так как сервер запрашивает дайджест.

ответ

3

Ответ, что HttpUrlConnection не поддерживает дайджеста.

Вы должны сами реализовать RFC2617.

В качестве базовой линии можно использовать следующий код: HTTP Digest Auth for Android.

шаги включают (см RFC2617 для справки):

  • Если вы получите 401 ответ, перебрать все WWW-Authenticate заголовки и анализировать их:
    • Проверьте, если алгоритм MD5 или не определено, (опционально выберите опцию auth qop), иначе игнорируйте вызов и переходите к следующему заголовку.
    • Получите учетные данные, используя Authenticator.requestPasswordAuthentication.
    • Рассчитать H (A1) с использованием имени пользователя, области и пароля.
    • Храните URL-адрес канонического корня, область, HA1, имя пользователя, nonce (+ опционально алгоритм, непрозрачный, а клиент выбрал опцию qop, если присутствует).
    • Повторите запрос.
  • На каждом запросе, перебрать все сферы вы информацию о сеансе связи, сохраненной для канонической корневой URL:
    • Вычислить H (A2), используя метод запроса и путь.
    • Рассчитать H (A3) с использованием HA1, nonce (+ опционально nc, cnonce, qop) и HA2.
    • Постройте и добавьте заголовок Authorization на ваш HttpUrlConnection.
  • Внедрение какой-либо обрезки сеанса.

Используя Authenticator, вы можете убедиться, что, как только HttpUrlConnection поддерживает переваривать изначально, ваш код не используется больше (потому что вы не будете получать 401 в первую очередь).

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

Если вы хотите пойти дальше, вы, вероятно, хотели бы реализовать SHA256, а также: RFC7616

+0

Спасибо за этот ответ. Это только версия андроидов «HttpUrlConnection», которая не поддерживает дайджест или также «java.net.HttpURLConnection» по умолчанию? – Springrbua

+0

@Springrbua Только на Android его не поддерживают, по крайней мере, насколько мне известно. Возможно, вы найдете источники для реализации JDK. – Nappy

+0

Хорошо, спасибо за ваш ответ. На данный момент я буду придерживаться «DefaultHttpClient», но мне кажется, что мне нужно рано или поздно переключиться, поэтому мне придется самому реализовать дайджест. Благодаря! – Springrbua

1

ли вы пытаетесь установить заголовок вручную, как:

String basic = "Basic " + new String(Base64.encode("username:password".getBytes(),Base64.NO_WRAP)); 
connection.setRequestProperty ("Authorization", basic); 

также быть в курсе некоторых вопросов в JellyBeans и ошибка при попытке выполнить запрос на запись: HTTP Basic Authentication issue on Android Jelly Bean 4.1 using HttpURLConnection

EDIT: Для Дайджест аутентификации

Посмотрите здесь https://code.google.com/p/android/issues/detail?id=9579

Espec ially это может работать:

try { 
     HttpClient client = new HttpClient(
       new MultiThreadedHttpConnectionManager()); 

     client.getParams().setAuthenticationPreemptive(true); 
     Credentials credentials = new UsernamePasswordCredentials("username", "password"); 
     client.getState().setCredentials(AuthScope.ANY, credentials); 
     List<String> authPrefs = new ArrayList<String>(2); 
     authPrefs.add(AuthPolicy.DIGEST); 
     authPrefs.add(AuthPolicy.BASIC); 
     client.getParams().setParameter(AuthPolicy.AUTH_SCHEME_PRIORITY, 
       authPrefs); 
     GetMethod getMethod = new GetMethod("your_url"); 
     getMethod.setRequestHeader("Accept", "application/xml"); 
     client.executeMethod(getMethod); 
     int status = getMethod.getStatusCode(); 
     getMethod.setDoAuthentication(true); 
     System.out.println("status: " + status); 
     if (status == HttpStatus.SC_OK) { 
      String responseBody = getMethod.getResponseBodyAsString(); 
      String resp = responseBody.replaceAll("\n", " "); 
      System.out.println("RESPONSE \n" + resp); 
     } 
    } catch (Exception e) { 
     e.printStackTrace(); 
    } 
+1

Я редактирую свой вопрос и добавляю заголовок WWW-Authenticate, заданный сервером. Он говорит 'digest', поэтому я его ожидаю и требует проверки дайджеста ... – Springrbua

+0

Чек, изменил мой ответ, надеюсь, что это поможет. Кажется, что есть несколько ошибок в «Authenticator» на Android –

+0

Также проверьте http://stackoverflow.com/questions/2954434/apache-httpclient-digest-authentication –

2

Это правильно, что HttpUrlConnection не поддерживает проверку подлинности Digest. Если ваш клиент должен пройти аутентификацию с помощью Digest, у вас есть несколько вариантов:

  • Напишите свою собственную реализацию протокола HTTP. Это может быть хорошим вариантом, если вы знаете, на каких серверах вы должны пройти аутентификацию, и можете игнорировать части спецификации дайджеста, которые вам не нужны. Вот пример, где реализован подмножество дайджеста: https://gist.github.com/slightfoot/5624590.
  • Используйте внешний lib bare-bones-digest, который представляет собой Digest lib для Android. Вы можете использовать его для анализа проблем дайджеста и получения ответов на них. Он поддерживает общие случаи использования дайджеста и некоторые из редко используемых и может использоваться поверх HttpURLConnection.
  • Используйте OkHttp вместе с okhttp-digest, который является плагином, который добавляет поддержку Http Digest для OkHttp. Поддержка дайджеста с помощью OkHttp проста, просто добавьте okhttp-digest в качестве аутентификатора, и у вас будет прозрачная поддержка дайджеста Http. Если вы уже используете OkHttp или согласны с переключением на него, это может быть привлекательным вариантом.
  • Используйте Apache HttpClient, который поддерживает дайджест. Вопрос прямо указывает, что HttpClient не является вариантом, поэтому я включаю его в основном для завершения. Google не рекомендует использовать HttpClient и устарел.
+0

Несколько месяцев назад я заменил «DefaultHttpClient» на «HttpUrlConnection», и я реализовал «дайджест-аутентификацию», используя (this) [https://gist.github.com/slightfoot/5624590] в качестве шаблона. Я могу добавить свой код в качестве ответа, он может помочь кому-то еще! – Springrbua

1

я, наконец, заменить устаревший DefaultHttpClient с моей собственной реализацией HttpUrlConnection и я реализовал digest atuhentication себя, используя this в качестве шаблона.
код выглядит, наконец-то вроде этого:

// requestMethod: "GET", "POST", "PUT" etc. 
// Headers: A map with the HTTP-Headers for the request 
// Data: Body-Data for Post/Put 
int statusCode = this.requestImpl(requestMethod, headers, data); 
if (statusCode == HttpURLConnection.HTTP_UNAUTHORIZED && hasUserNameAndPassword) { 
    String auth = getResponseHeaderField("WWW-Authenticate"); 
    // Server needs Digest authetication 
    if(auth.startsWith("Digest")){ 
      // Parse the auth Header 
      HashMap<String, String> authFields = parseWWWAuthenticateHeader(auth); 
      // Generate Auth-Value for request 
      String requestAuth = generateDigestAuth(authFields); 
      headers.put("Authorization", authStr); 
      statusCode = this.requestImpl(requestMethod, headers, data); 
    } 
} 

Так основно я делаю запрос и, если она возвращает 401, я смотрю, если сервер хочет digest authentication, и если у меня есть имя пользователя и пароль. Если это так, я анализирую заголовок auth ответа, который содержит всю необходимую информацию об аутентификации.
Чтобы проанализировать заголовок auth, я использую какой-то StateMachine, который описан here.
После разбора заголовка ответа аутентификации, сгенерировать заголовок запроса аутентификации, используя информацию из ответа:

String digestAuthStr = null; 

    String uri = getURL().getPath(); 
    String nonce = authFields.get("nonce"); 
    String realm = authFields.get("realm"); 
    String qop = authFields.get("qop"); 
    String algorithm = authFields.get("algorithm"); 
    String cnonce = generateCNonce(); 
    String nc = "1"; 
    String ha1 = toMD5DigestString(concatWithSeparator(":", username, realm, password)); 
    String ha2 = toMD5DigestString(concatWithSeparator(":", requestMethod, uri)); 
    String response = null; 
    if (!TextUtils.isEmpty(ha1) && !TextUtils.isEmpty(ha2)) 
     response = toMD5DigestString(concatWithSeparator(":", ha1, nonce, nc, cnonce, qop, ha2)); 

    if (response != null) { 
     StringBuilder sb = new StringBuilder(128); 
     sb.append("Digest "); 
     sb.append("username").append("=\"").append(username).append("\", "); 
     sb.append("realm").append("=\"").append(realm).append("\", "); 
     sb.append("nonce").append("=\"").append(nonce).append("\", "); 
     sb.append("uri").append("=\"").append(uri).append("\", "); 
     sb.append("qop").append("=\"").append(qop).append("\", "); 
     sb.append("nc").append("=\"").append(nc).append("\", "); 
     sb.append("cnonce").append("=\"").append(cnonce).append("\""); 
     sb.append("response").append("=\"").append(response).append("\""); 
     sb.append("algorithm").append("=\"").append(algorithm).append("\""); 
     digestAuthStr = sb.toString(); 
    } 

Для создания клиент-Nonce Я использую следующий код:

private static String generateCNonce() { 
    String s = ""; 
    for (int i = 0; i < 8; i++) 
     s += Integer.toHexString(new Random().nextInt(16)); 
    return s; 
} 

I надеюсь, это поможет кому-то. Если код содержит какие-либо ошибки, сообщите мне, чтобы я мог исправить это. Но сейчас это работает.

+0

Это очень компактная и эффективная реализация дайджеста, но хорошо знать, что она реализует только небольшой подмножество дайджеста. Это нормально, если вы, например, работаете только с одним конкретным сервером и можете с этим протестировать. Если вам нужно быть совместимым с множеством разных серверов и обрабатывать такие вещи, как множественные вызовы, экранированные символы в цитируемых строках, дайджест SHA-256, непрозрачная директива, повторное использование вызовов и т. Д., То вам, вероятно, лучше использовать одну из библиотек (bare-bones-digest, okhtt-digest, Apache HttpClient и т. д.), чтобы покрыть угловые случаи. – user829876

+0

@ user829876 Я согласен с вами. Если вам нужна полная поддержка дайджеста, вам обязательно нужно пойти с известной и, следовательно, проверенной библиотекой. – Springrbua

0

для Android, я обнаружил, что скелетное переваривать библиотека работала хорошо: https://github.com/al-broco/bare-bones-digest

  1. Добавьте одну строку в build.gradle
  2. Используйте пример кода в приведенном выше URL

Работает!

Смежные вопросы