Как правильно аутентифицировать клиента AngularJS на сервере
Я создаю веб-приложение AngularJS, в котором используется API RESTful (Джерси).
На стороне сервера я использую сервер приложений Java (подробно Glassfish 4).
Моя настройка выглядит следующим образом:
- AngularJS webapp развертывается как один файл войны на сервере приложений Java EE.
- RESTfulAPI (и бэкэнд-логика) также развертывается как военный файл на тот же сервер приложений Java EE.
- Функция AngularJS webapp вызывает API REST для получения требуемых данных.
- API RESTful:
-
/api/public/*
для общедоступного неограниченного доступа (например,/api/public/users/signup для регистрации в качестве нового пользователя). Любой пользователь имеет доступ к этой области. Нет входа в систему
-
/api/private/*
для ограниченного доступа (например,/api/private/account/{id} для получения определенных данных учетной записи)
- Частные ресурсы защищены внутренними концепциями безопасности Java EE (см. ниже подробности для web.xml).
web.xml
<security-constraint>
<web-resource-collection>
<web-resource-name>PRIVATE REST API</web-resource-name>
<url-pattern>/private/*</url-pattern>
<http-method>GET</http-method>
<http-method>POST</http-method>
<http-method>HEAD</http-method>
<http-method>PUT</http-method>
<http-method>OPTIONS</http-method>
<http-method>TRACE</http-method>
<http-method>DELETE</http-method>
</web-resource-collection>
<auth-constraint>
<description>Have to be a USER</description>
<role-name>USERS</role-name>
</auth-constraint>
</security-constraint>
<login-config>
<auth-method>BASIC</auth-method>
<realm-name>userauth</realm-name>
</login-config>
<security-role>
<description/>
<role-name>USERS</role-name>
</security-role>
Эта настройка работает для меня, но только если я вызываю URL-адреса вручную в браузере.
Если я не аутентифицирован и не адресую защищенную область, браузер запрашивает имя пользователя и пароль. Если предоставленные учетные данные действительны, я оттуда имею доступ к запрещенной зоне: Хорошо.
Но как мне получить работу с AngularJS?
Для входа в систему есть вызов API POST: /api/public/users/login
, где вы должны предоставить учетные данные из формы (имя пользователя и пароль).
Код на стороне клиента:
ctrls.controller('LoginController', function($scope, $http, $log, $location, UserService, RemoteServerHelperService) {
$scope.loginDataWrong = undefined;
$scope.login = function(credentials) {
$http.post(RemoteServerHelperService.buildURL('/public/users/login'), credentials)
.success(function (data, status) {
UserService.setLoggedIn(credentials.email);
$scope.loginDataWrong = undefined;
$location.path('/app');
$log.info("login attempt: successfull. User.loggedIn:" + UserService.isLoggedIn());
}).error(function (data, status, headers, config) {
UserService.invalidate();
$scope.loginDataWrong = true;
$log.info("login attempt: failed. User.loggedIn:" + UserService.isLoggedIn());
});
};
});
Клиентский код, похоже, работает. У меня также есть несколько путей для защиты контента на стороне клиента и т.д. Есть несколько сообщений, которые подробно описывают код на стороне клиента. Поэтому этого "фрагмента" должно быть достаточно.
Код на стороне сервера:
@POST
@Path("/login")
@Consumes(MediaType.APPLICATION_JSON)
public Response login(Credentials credentials, @Context HttpServletRequest request) {
if (isAlreadyAuthenticated(request)) {
try {
request.logout();
} catch (ServletException ex) {
return Response.status(Status.CONFLICT).build();
}
}
// not authenticated
try {
request.login(credentials.getEmail(), credentials.getPassword());
return Response.ok().build();
} catch (ServletException ex) {
return Response.status(Status.CONFLICT).build();
}
}
Но, однако, это не "аутентифицирует" клиентскую сторону для дальнейших запросов. Когда я делаю вызов API в ограниченной области после успешного входа в систему, я получаю ответ 403 FORBIDDEN
со стороны сервера. Поэтому я предполагаю, что я делаю что-то неправильно. Есть ли у вас какие-либо идеи о том, как аутентифицировать клиента на сервере для получения дополнительных запросов?
Возможным решением может быть переход на аутентификацию на основе FORM и просто вызов j_security_check с j_username и j_password, но на данный момент я хочу придерживаться BASIC-аутентификации и выполнять проверку подлинности вручную через RESTful API.
Настройка $httpProvider.defaults.withCredentials = true;
каким-то образом не имеет никакого эффекта.
Update:
Благодаря кончику потери, я решил проблему.
Я удалил метод login()
, потому что я хочу, чтобы Java EE-Server позаботился об аутентификации.
Для доступа к защищенным областям веб-браузер AngularJS должен правильно настроить HTTP-заголовок. В моем случае $http.defaults.headers.common['Authorization'] = 'Basic <username:pw>';
, где username:password
должен быть закодирован Base64.
После того, как я правильно установил заголовки, никакая подсказка пароля не отображается, и веб-приложение AngularJS может получить доступ к API REST.
Ответы
Ответ 1
Благодаря кончику потери, я решил проблему. Я удалил метод login(), потому что я хочу, чтобы Java EE-Server выполнял проверку подлинности.
Для доступа к защищенным областям веб-браузер AngularJS должен правильно настроить HTTP-заголовок. В моем случае $http.defaults.headers.common ['Authorization'] = 'Basic'; где username: пароль должен быть закодирован Base64.
После того, как я правильно установил заголовки, никакая подсказка пароля не отображается, и веб-приложение AngularJS может получить доступ к API REST.