用户登录流程及会话管理详解

*用户登录流程及会话管理详解

  1. 连接服务器后端,得到一个 session 返回给前端

    • 用户在前端(浏览器)输入登录信息(如用户名和密码)。
    • 前端将登录信息发送到后端服务器进行验证。
  2. 登录成功后,得到成功登录的 session,返回给前端一个设置 cookie 的命令

    • 服务器验证用户的登录信息(通常通过数据库查询)。
    • 验证成功后,服务器创建一个新的 session 并将用户信息存储在 session 中。
    • 服务器将 session ID 发送回前端,通常通过设置 HTTP 响应头中的 Set-Cookie
  3. 前端接收到后端命令,设置 cookie,保存到浏览器中

    • 浏览器接收到服务器的响应,并自动将 session ID 存储在 cookie 中。
    • 这可以通过 document.cookie 或者浏览器的内置功能来实现。
  4. 前端再次请求后端的时候(相同域名),在请求头上带上 cookie 去请求

    • 浏览器在后续的请求中,会自动将 cookie 中的 session ID 发送到服务器。
    • 这由浏览器自动处理,开发者无需手动设置。
  5. 后端拿到前端传来的 cookie,找到对应的 session

    • 服务器接收到请求后,会检查请求头中的 cookie。
    • 服务器提取出 session ID 并查找对应的 session 信息。
  6. 后端从 session 中可以取出基于该 session 存储的变量值(用户的登录信息,登录名)

    • 服务器找到 session 后,可以从中获取存储的用户信息。
    • 服务器可以根据需要对用户进行身份验证或授权操作。

代码分析

1
2
3
4
5
6
7
8
9
10

@Override
public User userLogin(String userAccount, String userPassword, HttpServletRequest request) {

//校验and脱敏....

request.getSession().setAttribute(UserConstant.USER_LOGIN_STATE, safetyUser);

return safetyUser;
}

request.getSession().setAttribute(UserConstant.USER_LOGIN_STATE, safetyUser); 这段代码的作用是将经过脱敏处理的 safetyUser 对象存储在当前会话(session)中,如果会话不存在则创建一个新的。然后使用 UserConstant.USER_LOGIN_STATE 作为这个对象的键(key)。如果之前的会话中已经存在同名属性(即 UserConstant.USER_LOGIN_STATE),则会更新该属性对应的值为新的 safetyUser 对象。

这段代码中向session中存放一个共享数据safetyUser,并且他的键是UserConstant.USER_LOGIN_STATE。所以在下一次请求中可以通过cookie承载的session ID找到对应的session,然后通过UserConstant.USER_LOGIN_STATE找到session存储的safetyUser。

如果整个后端程序中只使用一个固定的键来管理会话(session),那么对每个用户浏览器的会话只能存储一个信息对象。每次向会话中存储新的信息时,会替换之前存储的信息,因为会话存储是基于键值对的。这种方式适合简单的应用场景,例如只需存储当前用户的登录状态。

在典型的Web应用程序中,Java程序员编写的Java代码负责创建和管理会话(session),通常通过Servlet容器或者框架(如Spring框架)来实现。一旦会话被创建,服务器会生成一个唯一的会话标识符(Session ID),并将它发送回给前端,通常通过HTTP响应的 Set-Cookie 头部。
前端开发人员负责处理这个 Set-Cookie 头部,然后将会话ID存储在用户的浏览器中,通常是作为cookie。浏览器会在后续的请求中自动将这个cookie(即会话ID)发送回服务器。

  • 示例代码

以下是使用 Java Servlet 的一个简单示例,展示如何实现上述流程:

后端代码(Java Servlet)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 从请求中获取用户名和密码
String username = request.getParameter("username");
String password = request.getParameter("password");

// 假设这里有一个简单的用户验证逻辑
if ("admin".equals(username) && "password123".equals(password)) {
// 用户验证通过,创建一个会话并存储用户信息
User user = new User(username);
request.getSession().setAttribute("currentUser", user);

// 重定向到登录成功页面
response.sendRedirect(request.getContextPath() + "/dashboard.jsp");
} else {
// 用户验证失败,重定向回登录页面或返回错误信息
response.sendRedirect(request.getContextPath() + "/login.jsp?error=1");
}
}
}

开启会话过期

  1. 使用Servlet API设置会话过期时间

在Java Servlet中,可以通过调用HttpSession对象的setMaxInactiveInterval(int interval)方法来设置会话的最大非活动时间。这个方法定义了会话在客户端最后一次访问后保持活动状态的时间长度,单位为秒。

1
2
3
4
5
6
// 获取当前请求的HttpSession对象
HttpSession session = request.getSession();

// 设置会话过期时间为30分钟(1800秒)
session.setMaxInactiveInterval(1800);

  1. 使用Spring Session管理会话超时

在Spring Boot应用程序中,可以通过配置文件或Java配置来设置会话超时时间。

1
2
3
4

# 设置会话超时时间为30分钟(以秒为单位)
spring.session.timeout.seconds=1800

1
2
3
4
5
6
7
# application.yml

spring:
session:
timeout:
seconds: 1800

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import org.springframework.session.web.context.AbstractHttpSessionApplicationInitializer;
import org.springframework.session.web.http.CookieSerializer;
import org.springframework.session.web.http.DefaultCookieSerializer;
import org.springframework.session.jdbc.config.annotation.web.http.EnableJdbcHttpSession;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableJdbcHttpSession
public class SessionConfig extends AbstractHttpSessionApplicationInitializer {
@Bean
public CookieSerializer cookieSerializer() {
DefaultCookieSerializer serializer = new DefaultCookieSerializer();
serializer.setCookieMaxAge(1800); // 设置Cookie的最大过期时间为30分钟
return serializer;
}
}


获取会话状态

开始学的时候一直不能理解为何叫获取用户的会话状态,其实就是获取用户的信息。在上文中介绍了可以在session中存入要共享的信息(主要是一次请求结束后信息就没了),所以我们可以在用户成功登入的时候将用户的信息存入session中。以便我们在后面的请求中获取用户的信息,或者判断用户的登入状态。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@GetMapping("/current")
public BaseResponse<User> getCurrentUser(HttpServletRequest request) {
//根据键从session中获取对象。
Object userObj = request.getSession().getAttribute(UserConstant.USER_LOGIN_STATE);
User currentUser = (User) userObj;
//判断用户的登入信息
if (currentUser == null) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
//取用信息
currentUser.getId();
....
}

退出登入

登入是将用户对象存入session,那退出登录就是把session中对应键用户对象移除呗。

1
2
//移除用户登入态
request.getSession().removeAttribute(UserConstant.USER_LOGIN_STATE);

[!NOTE]
在向session存入的对象最好是脱敏的用户信息的对象。