Check if a User Is Logged-in With Servlets and JSP – 用Servlets和JSP检查用户是否已登录

最后修改: 2022年 2月 25日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this tutorial, we’ll learn how to check a user’s login and ensure that the user has filled the login form with valid credentials and started a session. However, we’ll do this without using Spring Security and using only JSPs and servlets. Consequently, we’ll need a servlet container that can support it, like Tomcat 9.

在本教程中,我们将学习如何检查用户的登录,并确保用户已经用有效的凭证填写了登录表单并开始了会话。但是,我们将在不使用Spring Security的情况下完成这项工作,并且只使用JSP和servlets因此,我们将需要一个能够支持它的Servlet容器,比如Tomcat 9。

By the end, we’ll have a good understanding of how things work under the hood.

到最后,我们将很好地理解事物在引擎盖下的工作原理。

2. Persistence Strategy

2.持久性战略

Firstly, we need users. To keep it simple, we’ll use a preloaded map. Let’s define it along with our User:

首先,我们需要用户。为了保持简单,我们将使用一个预装地图。让我们把它和我们的User一起定义。

public class User {
    static HashMap<String, User> DB = new HashMap<>();
    static {
        DB.put("user", new User("user", "pass"));
        // ...
    }

    private String name;
    private String password;

    // getters and setters
}

3. Filtering Requests

3.筛选请求

We’ll start by creating a filter to check sessionless requests, blocking direct access to our servlets:

我们将首先创建一个过滤器来检查无会话请求,阻止对我们的Servlet的直接访问。

@WebFilter("/*")
public class UserCheckFilter implements Filter {
    
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) {
        // ...
        request.setAttribute("origin", request.getRequestURI());

        if (!request.getRequestURI().contains("login") && request.getSession(false) == null) {
            forward(request, response, "/login.jsp");
            return;
        }

        chain.doFilter(request, response);
    }
}

Here, by defining “/*” as our URL pattern on the @WebFilter, all requests will pass through our filter first. Then, if there’s no session yet, we redirect the request to our login page, storing the origin for later use. Finally, we return early, preventing our servlet from processing without a proper session.

在这里,通过定义”/*“作为我们在@WebFilter上的URL模式,所有请求将首先通过我们的过滤器。然后,如果还没有会话,我们将请求重定向到我们的登录页面,存储origin供以后使用。最后,我们提前返回,防止我们的servlet在没有适当会话的情况下处理。

4. Creating a Login Form With JSP

4.用JSP创建一个登录表格

To build our login form, we’ll need to import the core Taglib from JSTL. Also, let’s set our session attribute to “false” in our page directive. As a result, a new session is not created automatically, and we can have full control:

为了构建我们的登录表单,我们需要从JSTL导入核心Taglib。另外,让我们在page指令中把我们的session属性设置为”false“。这样一来,新的会话就不会自动创建,我们就可以完全控制了。

<%@ page session="false"%>
<%@ taglib uri="http://java.sun.com/jstl/core_rt" prefix="c"%>

<form action="login" method="POST">
    ...
</form>

Then, inside our form, we’ll have a hidden input to save the origin:

然后,在我们的form里面,我们将有一个隐藏的输入来保存origin

<input type="hidden" name="origin" value="${origin}">

Next, we’ll include a conditional element to output errors:

接下来,我们将包括一个条件元素来输出错误。

<c:if test="${not empty error}">
    * error: ${error} 
</c:if>

Finally, let’s add some input tags so the user can enter and submit the credentials:

最后,让我们添加一些input标签,以便用户可以输入并提交证书。

<input type="text" name="name">
<input type="password" name="password"> 
<input type="submit">

5. Setting up Our Login Servlet

5.设置我们的登录小程序

In our servlet, we’ll forward the request to our login form if it’s a GET. And most importantly, we validate the login if it’s a POST:

在我们的servlet中,如果请求是GET,我们就将其转发到我们的登录表单。最重要的是,如果它是一个POST,我们将验证该登录。

@WebServlet("/login")
public class UserCheckLoginServlet extends HttpServlet {
    // ...
}

So, in our doGet() method, we’ll just redirect to our login JSP, passing the origin forward:

因此,在我们的doGet()方法中,我们将只是重定向到我们的登录JSP,向前传递origin

protected void doGet(HttpServletRequest request, HttpServletResponse response) {
    String referer = (String) request.getAttribute("origin");
    request.setAttribute("origin", referer);
    forward(request, response, "/login.jsp");
}

In our doPost(), we validate credentials and create a session, passing the User object forward and redirecting to origin:

在我们的doPost()中,我们验证凭证并创建一个会话,向前传递User对象并重定向到origin

protected void doPost(HttpServletRequest request, HttpServletResponse response) {
    String key = request.getParameter("name");
    String pass = request.getParameter("password");

    User user = User.DB.get(key);
    if (!user.getPassword().equals(pass)) {
        request.setAttribute("error", "invalid login");
        forward(request, response, "/login.jsp");
        return;
    }
        
    HttpSession session = request.getSession();
    session.setAttribute("user", user);

    response.sendRedirect(request.getParameter("origin"));
}

In case of invalid credentials, we set a message in our error variable. Otherwise, we update the session with our User object.

在凭证无效的情况下,我们在error变量中设置一条信息。否则,我们用我们的User对象更新会话。

6. Checking Login Info

6.检查登录信息

Finally, let’s create our home page. It just shows session information and has a logout link:

最后,让我们创建我们的主页。它只是显示会话信息并有一个注销链接。

<body>
    current session info: ${user.name}

    <a href="logout">logout</a>
</body>

All that our home servlet does is forward the User to the home page:

我们的主页Servlet所做的就是将User转发到主页。

@WebServlet("/home")
public class UserCheckServlet extends HttpServlet {

    protected void doGet(HttpServletRequest request, HttpServletResponse response) {
        User user = (User) session.getAttribute("user");
        request.setAttribute("user", user);

        forward(request, response, "/home.jsp");
    }
}

And this is how it looks:

而这就是它的样子。

login success

7. Logging Out

7.注销

To log out, we simply invalidate the current session and redirect home. After that, our UserCheckFilter will detect a sessionless request and redirect us back to the login page, restarting the process:

要注销,我们只需使当前会话无效并重定向到首页。之后,我们的UserCheckFilter将检测到一个无会话的请求,并将我们重定向到登录页面,重新启动这个过程。

@WebServlet("/logout")
public class UserCheckLogoutServlet extends HttpServlet {

    protected void doGet(HttpServletRequest request, HttpServletResponse response) {
        request.getSession().invalidate();

        response.sendRedirect("./");
    }
}

8. Conclusion

8.结语

In this article, we went through the creation of a full login cycle. We saw how we now have full control over access to our servlets, using a single filter. In short, with this approach, we can always be sure that there’s a valid session where we need one. Similarly, we could expand that mechanism to implement finer access control.

在这篇文章中,我们经历了一个完整的登录周期的创建。我们看到我们现在如何通过一个过滤器来完全控制对我们的servlets的访问。简而言之,通过这种方法,我们总是可以确定在我们需要的地方有一个有效的会话。同样地,我们可以扩展这种机制,以实现更精细的访问控制。

And as always, the source code is available over on GitHub.

一如既往,源代码可在GitHub上获得。