Java 如何正确地向服务器验证 AngularJS 客户端

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/18300192/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 00:15:28  来源:igfitidea点击:

How to proper authenticate an AngularJS client to the server

javajakarta-eeauthenticationangularjs

提问by zip

I'm building an AngularJS web-app that uses a RESTful API (Jersey).

我正在构建一个使用 RESTful API(泽西岛)的 AngularJS 网络应用程序。

On the server side I am using a Java Application Server (in detail Glassfish 4).

在服务器端,我使用 Java 应用程序服务器(详细介绍 Glassfish 4)。

My setup is as follows:

我的设置如下:

  • AngularJS webapp is deployed as a single war file to the Java EE Application server.
  • The RESTfulAPI (and the backend logic) is also deployed as a war file to the same Java EE Application server.
  • The AngularJS webapp calls the REST API to get the data it needs.
  • The RESTful API:
    • /api/public/*for public not restricted access (e.g. /api/public/users/signup to register as a new user). Any user is allowed to access this area. No login required
    • /api/private/*for restricted access (e.g. /api/private/account/{id} to retrieve some account specific data)
  • The private resources are protected with the Java EE internal security concepts (see below for web.xml details).
  • AngularJS webapp 作为单个 war 文件部署到 Java EE 应用程序服务器。
  • RESTfulAPI(和后端逻辑)也作为 war 文件部署到同一个 Java EE 应用程序服务器。
  • AngularJS webapp 调用 REST API 来获取它需要的数据。
  • RESTful API:
    • /api/public/*用于公共不受限制的访问(例如 /api/public/users/signup 以注册为新用户)。任何用户都可以访问该区域。无需登录
    • /api/private/*用于受限访问(例如 /api/private/account/{id} 以检索某些帐户特定数据)
  • 私有资源受 Java EE 内部安全概念保护(有关 web.xml 的详细信息,请参见下文)。

Web.xml

网页.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>

This setup works for me, but only if I invoke the URLs manually in the browser. If I am not authenticated and address a secured area, the browser asks for username and password. If the provided credentials are valid, I from there on have access to the restricted area: Fine.

此设置对我有用,但前提是我在浏览器中手动调用 URL。如果我未通过身份验证并访问安全区域,则浏览器会要求输入用户名和密码。如果提供的凭据有效,我就可以从那里访问受限区域:很好。

But how do I get that working with AngularJS? To login there is a POST API call: /api/public/users/loginwhere you have to provide credentials from a form (username and password).

但是我如何让它与 AngularJS 一起工作?要登录有一个 POST API 调用:/api/public/users/login您必须从表单(用户名和密码)中提供凭据。

The client side code is:

客户端代码是:

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());
        });
    };
});

The client side code seems to work. I also have some routes in place to secure content on the client side, and so on. There are several posts out there which describe the client side code in detail. So this "snippet" should be enough.

客户端代码似乎有效。我也有一些路线来保护客户端的内容,等等。有几篇文章详细描述了客户端代码。所以这个“片段”应该足够了。

The server side code is:

服务器端代码是:

@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();
    }
}

But however, this does not "authenticate" the client side for further requests. When I make a API call on a restricted area after successfully login I get a 403 FORBIDDENresponse from the server side. So I assume that I am doing something wrong. Do you have any ideas how to authenticate the client to the server for further requests?

但是,这不会“验证”客户端的进一步请求。当我在成功登录后在受限区域进行 API 调用时,我会收到403 FORBIDDEN服务器端的响应。所以我认为我做错了什么。您对如何向服务器验证客户端以进行进一步请求有任何想法吗?

A possible solution could be to switch to FORM-based authentication and simply invoke j_security_check with j_username and j_password, but for now I want to stick with BASIC-Authentication and perform the authentication manually via the RESTful API.

一个可能的解决方案可能是切换到基于 FORM 的身份验证并简单地使用 j_username 和 j_password 调用 j_security_check,但现在我想坚持使用 BASIC-Authentication 并通过 RESTful API 手动执行身份验证。

The setting $httpProvider.defaults.withCredentials = true;somehow doesn't seem to have any effect.

该设置$httpProvider.defaults.withCredentials = true;不知何故似乎没有任何影响。

Update:

更新:

Thanks to the tip of lossleader I solved the problem. I removed the login()method, because I want the Java EE-Server to take care of authentication.

感谢 lossleader 的提示,我解决了这个问题。我删除了该login()方法,因为我希望 Java EE-Server 负责身份验证。

To access secured areas the AngularJS webapp has to set the HTTP-Header correctly. In my case $http.defaults.headers.common['Authorization'] = 'Basic <username:pw>';where username:passwordhas to be Base64 encoded.

要访问安全区域,AngularJS webapp 必须正确设置 HTTP-Header。在我的情况下$http.defaults.headers.common['Authorization'] = 'Basic <username:pw>';username:password必须是 Base64 编码的。

After I set the headers correctly, no Password Prompt is shown and the AngularJS webapp can access the REST API.

正确设置标头后,不会显示密码提示,AngularJS webapp 可以访问 REST API。

采纳答案by zip

Thanks to the tip of lossleader I solved the problem. I removed the login() method, because I want the Java EE-Server to take care of authentication.

感谢 lossleader 的提示,我解决了这个问题。我删除了 login() 方法,因为我希望 Java EE-Server 负责身份验证。

To access secured areas the AngularJS webapp has to set the HTTP-Header correctly. In my case $http.defaults.headers.common['Authorization'] = 'Basic '; where username:password has to be Base64 encoded.

要访问安全区域,AngularJS webapp 必须正确设置 HTTP-Header。在我的情况下 $http.defaults.headers.common['Authorization'] = 'Basic'; 其中 username:password 必须是 Base64 编码的。

After I set the headers correctly, no Password Prompt is shown and the AngularJS webapp can access the REST API.

正确设置标头后,不会显示密码提示,AngularJS webapp 可以访问 REST API。