Обычная проверка подлинности в REST-приложении
Окружающая среда:
- JAVA
- Glassfish
- REST-сервисы на разных машинах.
- HTML5-клиент с
AJAX и JQuery
- Джерси
Это то, что я реализовал до сих пор:
HTML5-клиент ###
$('#btnSignIn').click(function () {
var username = $("#username").val();
var password = $("#password").val();
function make_base_auth(user, password) {
var tok = user + ':' + password;
var final = "Basic " + $.base64.encode(tok);
console.log("FINAL---->" + final);
alert("FINAL---->" + final);
return final;
}
$.ajax({
type: "GET",
contentType: "application/json",
url: "http://localhost:8080/SesameService/webresources/users/secured/login",
crossDomain: true,
dataType: "text",
async: false,
data: {},
beforeSend: function (xhr) {
xhr.setRequestHeader('authorization', make_base_auth(username, password));
},
success: function () {
alert('Thanks for your signin in! ');
},
error: function (jqXHR, textStatus, errorThrown) {
console.log(textStatus, errorThrown);
alert(' Error in signIn-process!! ' + textStatus);
}
});
});
SERVER
В разделе "Безопасность" у меня нет диспетчера безопасности, он отключен!
Я настроил BASIC-аутентификацию на Glassfish, и мой web.xml выглядит следующим образом:
<servlet-mapping>
<servlet-name>ServletAdaptor</servlet-name>
<url-pattern>/webresources/*</url-pattern>
</servlet-mapping>
<security-constraint>
<web-resource-collection>
<web-resource-name>REST Protected resources</web-resource-name>
<description/>
<url-pattern>/users/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>admin</role-name>
<role-name>customer</role-name>
<role-name>user</role-name>
</auth-constraint>
</security-constraint>
<login-config>
<auth-method>BASIC</auth-method>
<realm-name>jdbcRealm</realm-name>
</login-config>
<security-role>
<role-name>admin</role-name>
</security-role>
<security-role>
<role-name>user</role-name>
</security-role>
<security-role>
<description/>
<role-name>customer</role-name>
</security-role>
GlassFish
![enter image description here]()
LOG
FINE: [Web-Security] Setting Policy Context ID: old = null ctxID = SesameService/SesameService
FINE: [Web-Security] hasUserDataPermission perm: ("javax.security.jacc.WebUserDataPermission" "/webresources/users/secured/login" "GET")
FINE: [Web-Security] hasUserDataPermission isGranted: true
FINE: [Web-Security] Policy Context ID was: SesameService/SesameService
FINE: [Web-Security] hasResource isGranted: true
FINE: [Web-Security] hasResource perm: ("javax.security.jacc.WebResourcePermission" "/webresources/users/secured/login" "GET")
Вопрос:
-
Если я шифрую (НЕ кодирую) пароль в клиенте, когда пользователь регистрируется и передает его под SSL/HTTPS, является ли этот безопасный и хороший способ реализовать это?
-
Если я использую REST-сервис без клиента, он всегда открыт, ПОЧЕМУ? Нет BASIC-аутентификации? Я понял что-то не так с этими шаблонами url?
http://localhost:8080/SesameService/webresources/users/secured/login
-
ЕСЛИ я получу эту работу, как проверить это, потому что теперь, если я аутентифицирую один раз, я всегда уполномочен? Возможно ли "выйти из системы" программно внутри REST-сервиса или вообще как реализовать выход из системы?
-
При использовании авторизации в заголовке с обязательным именем пользователя с кодировкой base64: пароль должен ли я также закодировать свое имя пользователя и пароль для БД? Я попробовал это и добавил Encoding (допустимые значения Hex и Base64) в jdbcRealm в Glassfish, и кажется, что пароля достаточно, но что происходит, когда оба закодированы на клиенте?
ОБНОВЛЕНИЕ: Я изменил web.xml и теперь BASIC-аутентификация работает при вызове REST-сервиса прямо в браузере: http://localhost:8080/SesameService/users/secured/login
Изменения:
Теперь я получаю HTTP/1.1 401 Unauthorized при попытке аутентификации с HTML5-клиента.
Заголовки запроса: `
Origin: http://localhost:8383
Host:`localhost:8080`
Connection:keep-alive
Access-Control-Request-Method:GET
Access-Control-Request-Headers:authorization,content-type`
Ответ:
x-powered-by:Servlet/3.0 JSP/2.2 (GlassFish Server Open Source Edition 3.1.2.2 Java/Oracle Corporation/1.7)
WWW-Authenticate:Basic realm="jdbcRealm"
Server:GlassFish Server Open Source Edition 3.1.2.2
Pragma:No-cache
Expires:Thu, 01 Jan 1970 02:00:00 EET
Date:Sat, 13 Apr 2013 15:25:06 GMT
Content-Type:text/html
Content-Length:1073
Cache-Control:no-cache
ОБНОВЛЕНИЕ 2
Когда я пытаюсь выполнить аутентификацию с помощью JavaScript + Заголовок авторизации, я получил ошибку 401 и что в журнале:
FINE: [Web-Security] Setting Policy Context ID: old = null ctxID = SesameService/SesameService
FINE: [Web-Security] hasUserDataPermission perm: ("javax.security.jacc.WebUserDataPermission" "/users/secured/login" "OPTIONS")
FINE: [Web-Security] hasUserDataPermission isGranted: true---->!!!!!!!!!!!!!
FINE: [Web-Security] Policy Context ID was: SesameService/SesameService
FINE: [Web-Security] Codesource with Web URL: file:/SesameService/SesameService
FINE: [Web-Security] Checking Web Permission with Principals : null------->!!!!!!!
FINE: [Web-Security] Web Permission = ("javax.security.jacc.WebResourcePermission" "/users/secured/login" "OPTIONS")
FINEST: JACC Policy Provider: PolicyWrapper.implies, context (SesameService/SesameService)- result was(false) permission (("javax.security.jacc.WebResourcePermission" "/users/secured/login" "OPTIONS"))
FINE: [Web-Security] hasResource isGranted: false------->!!!!!!!!!
FINE: [Web-Security] hasResource perm: ("javax.security.jacc.WebResourcePermission" "/users/secured/login" "OPTIONS")
FINEST: JACC Policy Provider: PolicyWrapper.getPermissions(cs), context (null) codesource ((null <no signer certificates>)) permissions: [email protected] (
**** ОБНОВЛЕНИЕ 3 ****
Я не могу быть первым и единственным человеком, который пытается аутентифицироваться с использованием BASIC в случае с кросс-доменом.
Я сменил фильтры своего креста:
response.getHttpHeaders(). putSingle ( "Access-Control-Allow-Headers", "Авторизация" );
Ошибка NO 401, но все еще ошибка в JavaScript. IN Журнал Glassfish:
FINEST: JACC Policy Provider:
getPolicy (SesameService/SesameService) is NOT in service----->!!!!!!!!
FINE: JACC Policy Provider: file arrival check type: granted arrived: false exists: false lastModified: 0 storedTime: 1365968416000 state: deleted SesameService/SesameService
FINE: JACC Policy Provider: file arrival check type: excluded arrived: false exists: false lastModified: 0 storedTime: 0 state: deleted SesameService/SesameService
FINE: TM: getTransaction: tx=null, tm=null
FINE: TM: [email protected]7fe9a8
FINE: TM: resourceTable before: 0
FINE: TM: resourceTable after: 0
Кстати, потому что я никогда не получаю эту работу, делает это так же, как вызов REST-сервиса прямо в своем собственном домене. Итак, открывается первый клиентский запрос, запросы сервера и имя пользователя-пароля, затем запрос клиента и сервер аутентифицируются и отвечают на эту страницу? Я пытаюсь его получить: запрос с заголовком авторизации в нем, ответ от сервера с результатом службы останова и его. Любая идея, как обеспечить REST-услуги? Легче этого? Это невозможно.
ОБНОВЛЕНИЕ 4
Я просто попытался переместить моего HTML5-клиента в веб-проект java, только чистые html-страницы и в том же домене, а BASIC-аутентификация работает на 100%. Поэтому причина кросс-доменная среда.
Ответы
Ответ 1
Если я шифрую (НЕ кодирую) пароль в клиенте, когда пользователь регистрируется и передает его под SSL/HTTPS, является ли это безопасным и хорошим способом реализовать это?
Скорее всего, вы должны просто передать пароль в виде простого текста через SSL/HTTPS.
Вся связь в SSL/HTTPS зашифровывается, поэтому, вероятно, не рекомендуется также шифровать пароль, если вам не нужно гарантировать, что веб-сервер (технически терминатор HTTPS) не сможет увидеть пароль.
Если я использую REST-сервис без клиента, он всегда открыт, ПОЧЕМУ? Нет BASIC-аутентификации? Я понял что-то не так с этими шаблонами url?
Не уверен, что я понимаю вопрос. Однако BASIC auth не является хорошим шаблоном для аутентификации в REST, поскольку он передает пароль в виде обычного текста.
ЕСЛИ я получу эту работу, как проверить это, потому что теперь, если я аутентифицирую один раз, я всегда уполномочен? Возможно ли "выйти из системы" программно внутри REST-сервиса или вообще как реализовать выход из системы?
В Basic Auth имя пользователя и пароль передаются клиентом в каждом HTTP-запросе. Если учетные данные не передаются клиентом, сервер отклоняет запрос. Как таковой нет понятия сеанса.
Однако, что касается сервера Java EE, логин создает сеанс пользователя, и будущие запросы того же пользователя будут использовать один и тот же сеанс. Если вы его сконфигурируете, этот сеанс будет отсутствовать.
Если выходить из строя важно (например, управлять сеансами пользователя), вам необходимо создать сервлет (/logout) для этого, который отменяет сеанс HTTP.
Стандартная модель безопасности Java работает следующим образом: Когда пользователь входит в систему безопасности, сервер Java EE хранит безопасный файл cookie в вашем браузере. Браузер отправляет этот файл cookie обратно на сервер Java EE в каждом запросе в той же области. Сервер Java EE проверяет этот файл cookie в каждом запросе и использует его для идентификации пользователя, подключая запрос к сеансу пользователя.
Таким образом, вы, вероятно, захотите сделать службу REST в той же области безопасности, что и веб-приложение, поэтому браузер и сервер работают без проблем.
При использовании авторизации в заголовке с обязательным именем пользователя с кодировкой base64: пароль должен ли я также кодировать свое имя пользователя и пароль для БД? Я попробовал это и добавил Encoding (допустимые значения Hex и Base64) в jdbcRealm в Glassfish, и кажется, что пароля достаточно, но что происходит, когда оба закодированы на клиенте?
Нет, не кодируйте имя пользователя или пароль - все это обрабатывается глубоко в браузере и Glassfish. Если вы кодируете на клиенте, клиент будет кодировать кодировку, и сервер отклонит пароль.
Не могли бы вы рассказать мне, можно ли использовать j_security_check с html5-страницы с javaScript или снова в проблемах:) Я создал пару файлов Primefaces + jsf-application, где я использовал FORM-auth, и не было никаких проблема, но для меня это было катастрофой.
Вы должны уметь комфортно работать с j_security_check, полагая, что службы RESTful остаются в одном домене и области безопасности (тогда вход в веб-приложение позволит браузеру отправить правильный файл cookie в URI REST).
Обратите внимание, однако, что другим приложениям будет трудно получить доступ к службам REST. В основном, они должны будут войти в систему через j_security_check, а затем сохранить файлы cookie, отправленные Glassfish. Если вам понадобятся другие приложения для доступа к этим службам программно, вам понадобится другое решение:
- Вы можете настроить область безопасности, чтобы позволить другим аутентификаторам быть "достаточными"; настройте HTTP BASIC Auth и убедитесь, что ни один из них не помечен как "необходимый"
- Разверните службы RESTful дважды, а другой - другой URI. Если вы хотите использовать HTTP BASIC Auth, это может быть конечная точка SSL/HTTPS, чтобы обеспечить надежную обработку паролей.
Ответ 2
Я работаю над Spring инфраструктурой MVC с помощью Spring безопасности и использую базовую аутентификацию:
В основном, при базовой аутентификации HTTP имя пользователя и пароль преобразуются в ключ или токен доступа с помощью класса Base64 (из пакета util).
И этот ключ устанавливается в заголовок URL-адреса HTTP, а затем попадает на сервер.
Он отлично работает и на сервере: декодирует ключ пароля имени пользователя одним и тем же Base64, а затем совпадает с именем пользователя и паролем базы данных. если аутентификация завершена, то дальнейший процесс.
Это обеспечивает безопасность API-адресов. У каждого органа нет доступа.
код:
Создать токен доступа (ключ):
ПРИМЕЧАНИЕ. Мы передаем строку в Base64 (из пакета util) "YourUsername: YourPassword" для кодирования.
Да, имя пользователя и пароль разделены ":".
String encodeddata = new String(Base64.encodeBase64("username:password".getBytes()));
System.out.println("encodedBytes " + encodeddata);
Код для установки ключа в заголовок:
//ЗАПРОС НА URL С КЛЮЧОМ
HttpClient client = HttpClientBuilder.create().build();
HttpGet get = new HttpGet("http://google.com/Abc/api/vou/redeemed");
get.setHeader("Authorization", "Basic "+encodeddata); //key getting above
Примечание. Вы можете использовать это с помощью метода Get и Post. Здесь я использую только метод get.
//RESPONSE
HttpResponse jresponse = client.execute(get);
System.out.println("Response Code : "+ jresponse.getStatusLine().getStatusCode());
BufferedReader rd = new BufferedReader(new InputStreamReader(jresponse.getEntity().getContent()));
StringBuffer result = new StringBuffer();
String line = "";
while ((line = rd.readLine()) != null) {
result.append(line);
}
System.out.println("------Response is ::: "+ result);
//DECODE Key
String usernameAndPassword = null;
try {
usernameAndPassword = new String(Base64.getDecoder().decode(encodedUserPassword));
} catch (Exception e) {
System.out.println("SERVER_ERROR");
}
System.out.println(usernameAndPassword);
final StringTokenizer tokenizer = new StringTokenizer(encodedUserPassword, ":");
final String username = tokenizer.nextToken();
final String password = tokenizer.nextToken();
System.out.println(username);
System.out.println(password);