我正在构建一个使用 RESTful API (Jersey) 的 AngularJS Web 应用程序。
在服务器端,我使用 Java 应用程序服务器(具体为 Glassfish 4)。
我的设置如下:
- AngularJS webapp 作为单个 war 文件部署到 Java EE 应用程序服务器。
- RESTfulAPI(和后端逻辑)也作为 war 文件部署到同一 Java EE 应用程序服务器。
- AngularJS Web 应用程序调用 REST API 来获取所需的数据。
- The RESTful API:
-
/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 一起工作呢?
要登录,需要调用 POST API:/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_username 和 j_password 调用 j_security_check,但现在我想坚持使用 BASIC-Authentication 并通过 RESTful API 手动执行身份验证。
那个设定$httpProvider.defaults.withCredentials = true;
不知何故似乎没有任何效果。
Update:
感谢lossleader的提示我解决了这个问题。
我删除了login()
方法,因为我希望 Java EE 服务器负责身份验证。
要访问安全区域,AngularJS Web 应用程序必须正确设置 HTTP 标头。就我而言$http.defaults.headers.common['Authorization'] = 'Basic <username:pw>';
where username:password
必须是 Base64 编码。
正确设置标头后,不会显示密码提示,AngularJS Web 应用程序可以访问 REST API。