云计算百科
云计算领域专业知识百科平台

基于Session实现登录流程

一、前言:为什么还要学 Session 登录?

在 JWT、OAuth2、Token 风靡的今天,你可能会问:

“现在还有人用 Session 吗?”

答案是:有!而且很多!

  • 企业内部系统(如 OA、ERP)仍广泛使用 Session
  • 对安全性要求高、需强会话控制的场景(如银行后台)
  • 不需要跨域、不追求无状态的中小型 Web 应用

Session 是理解 Web 认证机制的基石。掌握它,你才能真正理解“登录”背后的原理。

本文将带你:
✅ 深入 Session 工作原理
✅ 手动实现完整登录/登出流程
✅ 在 Spring Boot 中安全使用 Session
✅ 避开常见安全陷阱


二、Session 登录原理图解

+—————-+ +——————+
| 浏览器 | | 服务器 |
+—————-+ +——————+
| |
1. 提交用户名密码 | POST /login |
|————————>|
| |
2. 验证成功 | 创建 Session |
| 存储用户信息 |
| Set-Cookie: JSESSIONID=abc123
|<————————|
| |
3. 后续请求 | Cookie: JSESSIONID=abc123
|————————>|
| 服务器根据 ID 找到 Session
| 确认用户已登录 |
| |
4. 点击退出 | GET /logout |
|————————>|
| 删除 Session |
| Clear Cookie |
|<————————|

🔑 核心机制:

  • 服务器创建 HttpSession,生成唯一 ID(如 JSESSIONID)
  • 通过 Set-Cookie 将 ID 返回给浏览器
  • 浏览器后续请求自动携带该 Cookie
  • 服务器通过 ID 查找 Session,验证登录状态

三、Spring Boot 实现登录流程

1. 项目依赖(无需额外引入)

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!– 内嵌 Tomcat 自带 Session 支持 –>

💡 Spring Boot 默认使用 内存存储 Session(重启丢失),生产环境可集成 Redis(见后文)。


2. 用户实体与模拟数据库

public class User {
private String username;
private String password; // 实际应存 BCrypt 加密后的值
// getter/setter…
}

@Component
public class UserDatabase {
private static final Map<String, User> users = new HashMap<>();

static {
// 模拟用户:admin / 123456
users.put("admin", new User("admin", "123456"));
}

public User findByUsername(String username) {
return users.get(username);
}
}


3. 登录控制器(核心逻辑)

@Controller
public class LoginController {

@Autowired
private UserDatabase userDatabase;

// 跳转到登录页
@GetMapping("/login")
public String loginPage() {
return "login"; // 返回 templates/login.html
}

// 处理登录请求
@PostMapping("/login")
public String login(@RequestParam String username,
@RequestParam String password,
HttpSession session,
RedirectAttributes redirectAttributes) {

// 1. 校验参数
if (username == null || username.trim().isEmpty()) {
redirectAttributes.addFlashAttribute("error", "用户名不能为空");
return "redirect:/login";
}

// 2. 查询用户
User user = userDatabase.findByUsername(username);
if (user == null || !user.getPassword().equals(password)) {
redirectAttributes.addFlashAttribute("error", "用户名或密码错误");
return "redirect:/login";
}

// 3. 登录成功:将用户信息存入 Session
session.setAttribute("currentUser", user.getUsername());
session.setMaxInactiveInterval(30 * 60); // 30分钟无操作自动过期

return "redirect:/home";
}

// 主页(需登录)
@GetMapping("/home")
public String home(HttpSession session, Model model) {
String currentUser = (String) session.getAttribute("currentUser");
if (currentUser == null) {
return "redirect:/login";
}
model.addAttribute("user", currentUser);
return "home";
}

// 退出登录
@GetMapping("/logout")
public String logout(HttpSession session) {
session.invalidate(); // 销毁整个 Session
return "redirect:/login";
}
}


4. 简单前端页面(Thymeleaf)

templates/login.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head><title>登录</title></head>
<body>
<h2>用户登录</h2>
<div th:if="${error}" style="color:red;" th:text="${error}"></div>

<form method="post" action="/login">
用户名: <input type="text" name="username" required><br><br>
密码: <input type="password" name="password" required><br><br>
<button type="submit">登录</button>
</form>
</body>
</html>

templates/home.html

<!DOCTYPE html>
<html>
<head><title>主页</title></head>
<body>
<h2>欢迎,<span th:text="${user}"></span>!</h2>
<a href="/logout">退出登录</a>
</body>
</html>


四、关键细节解析

1. HttpSession 是什么?

  • 由 Servlet 容器(如 Tomcat)提供
  • 每个会话一个实例,线程安全
  • 数据存储在服务端内存中
  • 生命周期:创建 → 使用 → 超时/手动销毁

2. session.setAttribute() 存什么?

  • ✅ 推荐:只存必要标识(如 userId、username)
  • ❌ 避免:存大对象(如用户完整信息、订单列表)

示例:

session.setAttribute("userId", 1001); // 好
session.setAttribute("user", fullUserObject); // 不推荐

3. 如何保护接口不被未登录访问?

方式一:每个 Controller 手动检查

@GetMapping("/profile")
public String profile(HttpSession session) {
if (session.getAttribute("currentUser") == null) {
return "redirect:/login";
}
// …
}

方式二:使用拦截器(推荐)

@Component
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
HttpSession session = request.getSession(false);
if (session == null || session.getAttribute("currentUser") == null) {
response.sendRedirect("/login");
return false;
}
return true;
}
}

@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor())
.addPathPatterns("/home", "/profile", "/settings/**")
.excludePathPatterns("/login", "/css/**", "/js/**");
}
}


五、安全加固:避开常见陷阱

🔒 坑 1:密码明文存储/传输

  • 修复:前端 HTTPS + 后端 BCrypt 加密

    // 存储时加密
    String hashedPassword = BCrypt.hashpw(rawPassword, BCrypt.gensalt());
    // 验证时比对
    if (BCrypt.checkpw(inputPassword, storedHash)) { … }

🔒 坑 2:Session 劫持(XSS + Cookie 窃取)

  • 修复:设置 Cookie 安全属性

    # application.yml
    server:
    servlet:
    session:
    cookie:
    http-only: true # 禁止 JS 访问 Cookie
    secure: false # 生产环境 HTTPS 时设为 true
    same-site: strict # 防 CSRF(部分浏览器支持)

🔒 坑 3:Session 固定攻击

  • 现象:攻击者诱导用户使用已知 Session ID
  • 修复:登录成功后更换 Session ID

    // 在 login 成功后
    session = request.getSession(); // 获取当前 session
    session.invalidate(); // 销毁旧 session
    session = request.getSession(true); // 创建新 session
    session.setAttribute("currentUser", username);

✅ Spring Security 默认已处理此问题,但手写登录需注意!


六、生产优化:Session 共享(集群部署)

单机 Session 存内存没问题,但多实例部署时会丢失会话!

解决方案:将 Session 存入 Redis

  • 添加依赖:

<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>

  • 配置启用:

@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 1800)
public class SessionConfig {}

  • 配置 Redis 连接(application.yml)

🎉 此时所有实例共享 Session,无缝支持水平扩展!


七、Session vs Token:如何选择?

特性SessionJWT/Token
状态 有状态(服务端存储) 无状态
跨域 困难(Cookie 限制) 容易(Header 传递)
安全性 高(可主动销毁) 依赖签名,无法主动失效
适用场景 传统 Web 应用 前后端分离、移动端

📌 建议:

  • 内部管理系统 → Session
  • App/小程序/H5 → Token

八、结语

感谢您的阅读!如果你有任何疑问或想要分享的经验,请在评论区留言交流!

赞(0)
未经允许不得转载:网硕互联帮助中心 » 基于Session实现登录流程
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!