java getUserPrincipal 返回 null

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/28280614/
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-11-02 13:19:58  来源:igfitidea点击:

getUserPrincipal returns null

javaangularjs

提问by Yan Ivan Evdokimov

I am using AngularJS for front-end and Java EE for back-end. I have created two methods to login and logout using RESTful services:

我在前端使用 AngularJS,后端使用 Java EE。我创建了两种使用 RESTful 服务登录和注销的方法:

@POST
@Path("login")
@Produces("application/json")
public Response login(@Context HttpServletRequest req, UserTable user) {
    if (req.getUserPrincipal() == null) {
        try {
            req.login(user.getUsername(), user.getPassword());
        } catch (ServletException e) {
            return Response.status(Response.Status.BAD_REQUEST).type("text/plain").entity("Login or Password is incorrect").build();
        }
    } else {
        return Response.status(Response.Status.OK).type("text/plain").entity("You are already logged in").build();
    }
    return Response.status(Response.Status.OK).type("text/plain").entity("Login successfull").build();
}

@GET
@Path("logout")
@Produces("application/json")
public Response logout(@Context HttpServletRequest req) {
    try {
        req.logout();
        req.getSession().invalidate();
    } catch (ServletException e) {
        return Response.status(Response.Status.BAD_REQUEST).type("text/plain").entity("Can not logout").build();
    }
    return Response.status(Response.Status.OK).type("text/plain").entity("Logout successfull").build();
}

On front-end side, I use these methods:

在前端,我使用这些方法:

    $http.post(apiUrl + 'usertable/login', {
        username: 'admin',
        password: 'admin'
    });

    $http.get(apiUrl + 'usertable/logout').success(function (a) {
        var o = a;
    });

I can successfully login:

我可以成功登录:

FINEST:   JDBC login succeeded for: admin groups:[Admin]
FINE:   JAAS login complete.
FINE:   JAAS authentication committed.
FINE:   Password login succeeded for : admin

But when I try to logout, or login again, getUserPrincipal() returns null. What am I doing wrong?

但是当我尝试注销或再次登录时,getUserPrincipal() 返回 null。我究竟做错了什么?

回答by Evandro Pomatti

Create the HTTP Session manually beforeyou call the loginmethod.

调用login方法之前手动创建 HTTP 会话。

You can do this by calling the HttpServletRequest.getSession()method, like this:

您可以通过调用该HttpServletRequest.getSession()方法来执行此操作,如下所示:

req.getSession(true); // Creates a new HTTP Session BEFORE the login.
req.login(user.getUsername(), user.getPassword());

Make sure the server is returning the JSESSIONID cookie in the response. After that the browser will take care of it and send it to the server without you having to worry about it or handling it yourself.

确保服务器在响应中返回 JSESSIONID cookie。之后,浏览器会处理它并将其发送到服务器,而您无需担心或自行处理。

Response from the Login

来自登录的响应

enter image description here

在此处输入图片说明

Request for further calls(service calls, check the login, logout, etc)

请求进一步调用(服务调用、检查登录、注销等)

enter image description here

在此处输入图片说明

Update

更新

Maybe you need to tell AngularJS to send the Cookies. For what I have read just now it seems that the default is not send cookies.

也许您需要告诉 AngularJS 发送 Cookie。对于我刚刚阅读的内容,默认设置似乎是不发送 cookie。

Try this post $http doesn't send cookie in Requests

试试这篇文章$http 不会在请求中发送 cookie

Update 2

更新 2

Cross-domain policies can be a pain but they need to exist in order to secure access to content in the web. The Java server is behaving normally, authentication is happening and the cookies are being shared. It is all good with the Java server since the beginning.

跨域策略可能很痛苦,但它们需要存在以确保对 Web 内容的访问。Java 服务器运行正常,正在进行身份验证并且正在共享 cookie。从一开始,Java 服务器就很好。

The problem is the same for any middleware technology when it come to cross-domain.

当涉及到跨域时,任何中间件技术的问题都是一样的。

The browser is blocking your application because your client domain (where your JavaScript is running) is not allowed to access the Java server domain. The applications (your AngularJS and the Java Server) are executing in different domains, therefore JavaScript calls "under the hoods" are blocked by browsers. Old IE versions used to allow it in the past.

浏览器阻止了您的应用程序,因为您的客户端域(运行 JavaScript 的地方)不允许访问 Java 服务器域。应用程序(您的 AngularJS 和 Java 服务器)在不同的域中执行,因此 JavaScript 调用“在幕后”被浏览器阻止。旧的 IE 版本过去曾经允许它。

Example:

例子:

  • JavaScript code is loaded from localhost or local file system
  • Java Server is running at http://192.168.1.15:8080(replace this for your IP or any address)
  • JavaScript 代码从 localhost 或本地文件系统加载
  • Java Server 在http://192.168.1.15:8080运行(将其替换为您的 IP 或任何地址)

Maybe JSONPcan save you:

也许JSONP可以拯救你:

JSONP allows a page to receive JSON data from a different domain by adding a element to the page which loads a JSON response with a callback from a different domain.

JSONP 允许页面通过向页面添加一个元素来接收来自不同域的 JSON 数据,该元素通过来自不同域的回调加载 JSON 响应。

Cross-domain policy will verify: protocol, hostnameand port, for both applications. If one is different, security takes place and the program fails.

跨域策略将为两个应用程序验证:协议主机名端口。如果一个不同,安全就会发生并且程序失败。

More info here: Same-origin policy

更多信息:同源政策

There is also nothing wrong with your AngularJS. I ran across this situation in the past, and you can replicate it using plain Java Script (this is functional code that you can adapt to you problem):

您的 AngularJS 也没有任何问题。我过去遇到过这种情况,你可以使用普通的 Java Script 复制它(这是你可以适应你的问题的功能代码):

function loadXMLDoc() {
    var xmlhttp=new XMLHttpRequest();
    xmlhttp.onreadystatechange=function() {
        if (xmlhttp.readyState==4 && xmlhttp.status==200) {
            document.getElementById("myDiv").innerHTML=xmlhttp.responseText;
        }
    }
    xmlhttp.open("POST","http://192.168.9.117:8080/restful-login-web/rest/loginservice/login",true);
    xmlhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded");
    xmlhttp.send("username=evandro&password=senha123");  
}

The following error will show up in your browsers console:

以下错误将显示在您的浏览器控制台中:

XMLHttpRequest cannot load http://192.168.9.117:8080/restful-login-web/rest/loginservice/login. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'null' is therefore not allowed access.

It will also happen with GET calls.

GET 调用也会发生这种情况。

You can allow your external application to use JavaScript to communicate with the Java server setting the Access-Control-Allow-Originsomewhat like this (.header("Access-Control-Allow-Origin", "*")):

您可以允许您的外部应用程序使用 JavaScript 与 Java 服务器进行通信,设置Access-Control-Allow-Origin有点像 ( .header("Access-Control-Allow-Origin", "*")):

return Response.status(Response.Status.OK).header("Access-Control-Allow-Origin", "*").type("text/plain").entity("Login successfull").build();

Warning:Replace the "*" for the domain in which your application client is running.

警告:替换运行应用程序客户端的域的“*”。

I suggest you to investigate on the cross-domain policy subject. If you can, maybe a simple solution is to run the client JavaScript inside Java container. Then both applications will be automatically in the same domain.

我建议您调查跨域策略主题。如果可以,也许一个简单的解决方案是在 Java 容器中运行客户端 JavaScript。然后两个应用程序将自动位于同一域中。

What I mean with that is that if you use a JSP/Servlet to publish your application client Java Script, you wouldn't have this problem. If you are developing the JavaScript in plain files, just wrap them inside the Java Web application. Since you will execute a Java Script code retrieved from "xyz.com" domain, the Java code running in the same "xyz.com" will be allowed to exchange information by the browser. This will work.

我的意思是,如果您使用 JSP/Servlet 来发布您的应用程序客户端 Java Script,您就不会遇到这个问题。如果您在纯文件中开发 JavaScript,只需将它们包装在 Java Web 应用程序中。由于您将执行从“xyz.com”域中检索到的Java Script 代码,因此浏览器将允许运行在同一个“xyz.com”中的Java 代码交换信息。这将起作用。

I am sorry, it took me more time than it should to realize you were doing an AJAX call to a different domain.

很抱歉,我花了更多的时间才意识到您正在对不同的域进行 AJAX 调用。

If you need to communicate different domains (maybe the server is an external service) and you can't find any help I suggest you to open another question specifically for the cross-domain issue. As I understand this problem is no longer associated with the Java server itself.

如果您需要通信不同的域(可能服务器是外部服务)并且找不到任何帮助,我建议您专门针对跨域问题打开另一个问题。据我了解,这个问题不再与 Java 服务器本身相关。

回答by ZakiMak

RESTful services are meant to be stateless.Requests which require authorization and authentication send authentication tokens on every request which is then verified by the server. The idea behind this is that given a token, you can make request from any client(desktop, phone) to get the resource. This will be handy for third party integration and a must if your application will be behind load balancers.

RESTful 服务是无状态的。需要授权和身份验证的请求会在每个请求上发送身份验证令牌,然后由服务器验证。这背后的想法是,给定一个令牌,您可以从任何客户端(桌面、电话)发出请求以获取资源。这对于第三方集成很方便,如果您的应用程序将落后于负载均衡器,则必须这样做。

This particular read is quite helpful to understand some concepts about RESTful services:

这篇特别的文章对于理解有关 RESTful 服务的一些概念非常有帮助:

Do sessions really violate RESTfulness?

会话真的违反了 REST 性吗?

When using session, it has to be tied to single server. From looking at your problem and reading further comments it seems the application is losing track of the session connected with the user.

使用会话时,它必须绑定到单个服务器。从查看您的问题并阅读更多评论来看,应用程序似乎正在失去与用户连接的会话的跟踪。