【问题标题】:getUserPrincipal returns nullgetUserPrincipal 返回空值
【发布时间】:2015-04-01 13:38:59
【问题描述】:

我在前端使用 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();
}

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

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

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

我可以成功登录:

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

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

【问题讨论】:

  • 听起来问题出在您的服务器端。设置一些断点以查看正在采用的路径并沿途检查数据。这应该会引导您解决问题。
  • 如果您在注销后再次尝试登录,getUserPrincipal 将始终为您提供 null,因为用户未通过身份验证。这就是 req.logout() 方法正在做的事情。
  • @rahpuser 我已经尝试使用登录方法并且没有注销我第二次使用它,但是第二次 getUserPrincipal() 等于 null。
  • Cookie 是否在交互过程中保存?
  • @SoluableNonagon 我已经做到了。我试图写这样的东西: Principal p = req.getUserPrincipal();在登录和注销方法中。每次 'p' 都等于 null。

标签: java angularjs


【解决方案1】:

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

您可以通过调用HttpServletRequest.getSession() 方法来做到这一点,如下所示:

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

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

来自登录的响应

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

更新

也许你需要告诉 AngularJS 发送 Cookie。就我刚才读到的,似乎默认是不发送cookies。

试试这个帖子$http doesn't send cookie in Requests

更新 2

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

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

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

例子:

  • JavaScript 代码从 localhost 或本地文件系统加载
  • Java 服务器在 http://192.168.1.15:8080 运行(将其替换为您的 IP 或任何地址)

也许JSONP可以救你:

JSONP 允许页面通过以下方式接收来自不同域的 JSON 数据 将元素添加到加载 JSON 响应的页面 来自不同域的回调。

跨域策略将验证:protocolhostnameport,适用于这两个应用程序。如果一个不同,安全发生并且程序失败。

更多信息在这里:Same-origin policy

您的 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");  
}

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

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.

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

您可以允许您的外部应用程序使用 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();

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

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

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

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

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

【讨论】:

  • 服务器已成功返回 JSESSIONID(如您的第一个屏幕截图所示)。但是,当我调用注销时,getUserPrincipal() 仍然有空值
  • @YanIvanEvdokimov 是在注销请求中发送的 JSESSIONID cookie,在登录后收到了吗?
  • 不幸的是,没有。它看起来像在您的屏幕截图中,但没有 Cookies 选项卡
  • @YanIvanEvdokimov 检查我的更新。我正在使用 JSP 页面进行测试,看来您需要告诉 AngularJS 中的 $http 发送 Cookie。
  • 现在抛出这个错误:A wildcard '*' cannot be used in the 'Access-Control-Allow-Origin' header when the credentials flag is true.
【解决方案2】:

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

这篇特别的阅读对于理解一些关于 RESTful 服务的概念很有帮助:

Do sessions really violate RESTfulness?

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-07-07
    • 1970-01-01
    • 2021-12-21
    • 2015-11-07
    • 2020-05-20
    • 2020-11-08
    • 2013-10-09
    • 2012-09-25
    相关资源
    最近更新 更多