当前位置: 首页 > news >正文

登录认证-上篇:基于 Session 的传统身份验证

基于 Session 的登录机制的主要作用:

1. 核心作用:维持登录状态 (Maintaining Login State)

这是最基本的作用。一旦用户登录成功,服务器就在 Session 中做一个标记(session.setAttribute("user", ...))。浏览器持有的 JSESSIONID Cookie 就变成了这个标记的“钥匙”。只

要钥匙有效(Session 未过期),服务器就认为用户是登录状态,无需再次输入用户名密码。

2. 服务器端数据存储 (Server-Side Storage)

Session 是服务器内存(或数据库/Redis)中的一块存储空间。你可以把与当前用户会话相关的任何数据存进去,而不仅仅是用户ID。

在代码中:

存了验证码 (session.setAttribute("code", code))

存了用户信息 (session.setAttribute("user", userDTO))

其他常见用途:

购物车:用户将商品加入购物车,在付款前这些信息可以暂存在 Session 中。

多步表单数据:比如一个需要分三步填写的注册表单,每一步的数据都可以先存到 Session,最后一步再统一提交到数据库。

用户偏好设置:如语言、主题等,在本次会话中生效。

3. 安全性 (Security)

敏感信息不暴露给客户端:用户的身份信息(如ID、角色、权限)只保存在服务器的 Session 中,客户端只有一个无意义的 JSESSIONID。这比把用户信息直接放在客户端的 Cookie 里要安全得多。

访问控制: LoginInterceptor 完美体现了这一点。它可以拦截所有请求,检查 Session 中是否存在用户信息,从而实现统一的权限校验。没有合法 Session 的请求一律拒绝(返回401或跳转到登录页)。

控制会话生命周期:服务器可以主动设置 Session 的过期时间(如30分钟无操作则自动过期),强制用户重新登录,这在安全性上很重要。

image

具体代码如下:

UserController

package com.hmdp.controller;import com.hmdp.dto.LoginFormDTO;
import com.hmdp.dto.Result;
import com.hmdp.dto.UserDTO;
import com.hmdp.entity.UserInfo;
import com.hmdp.service.IUserInfoService;
import com.hmdp.service.IUserService;
import com.hmdp.utils.UserHolder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;import javax.annotation.Resource;
import javax.servlet.http.HttpSession;/*** <p>* 前端控制器* </p>** @author ztn* @since 2025-9-10*/
@Slf4j
@RestController
@RequestMapping("/user")
public class UserController {@Resourceprivate IUserService userService;@Resourceprivate IUserInfoService userInfoService;/*** 发送手机验证码*/@PostMapping("code")public Result sendCode(@RequestParam("phone") String phone, HttpSession session) {// TODO 发送短信验证码并保存验证码return userService.sendCode(phone,session);}/*** 登录功能* @param loginForm 登录参数,包含手机号、验证码;或者手机号、密码*/@PostMapping("/login")public Result login(@RequestBody LoginFormDTO loginForm, HttpSession session){// TODO 实现登录功能return userService.login(loginForm,session);}/*** 登出功能* @return 无*/@PostMapping("/logout")public Result logout(){// TODO 实现登出功能return Result.fail("功能未完成");}@GetMapping("/me")public Result me(){// TODO 获取当前登录的用户并返回UserDTO user = UserHolder.getUser();return Result.ok(user);}@GetMapping("/info/{id}")public Result info(@PathVariable("id") Long userId){// 查询详情UserInfo info = userInfoService.getById(userId);if (info == null) {// 没有详情,应该是第一次查看详情return Result.ok();}info.setCreateTime(null);info.setUpdateTime(null);// 返回return Result.ok(info);}
}

UserServiceImpl

package com.hmdp.service.impl;import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hmdp.dto.LoginFormDTO;
import com.hmdp.dto.Result;
import com.hmdp.dto.UserDTO;
import com.hmdp.entity.User;
import com.hmdp.mapper.UserMapper;
import com.hmdp.service.IUserService;
import com.hmdp.utils.RegexUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;import javax.servlet.http.HttpSession;
import java.util.Random;import static com.hmdp.utils.SystemConstants.USER_NICK_NAME_PREFIX;/*** <p>* 服务实现类* </p>** @author ztn* @since 2025-9-10*/
@Slf4j
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {@Overridepublic Result sendCode(String phone, HttpSession session) {//1.校验手机号,isPhoneInvalid是无效if(RegexUtils.isPhoneInvalid(phone)){//2.如果不符合,返回错误信息return Result.fail("手机号格式错误!");}//3.符合,生成验证码String code = RandomUtil.randomNumbers(6);//4.保存验证码到sessionsession.setAttribute("code",code);//5.发送验证码log.debug("发送短信验证码成功,验证码:{}",code);//返回okreturn Result.ok();}@Overridepublic Result login(LoginFormDTO loginForm, HttpSession session) {String phone = loginForm.getPhone();//1.校验手机号,isPhoneInvalid是无效if(RegexUtils.isPhoneInvalid(phone)){//2.如果不符合,返回错误信息return Result.fail("手机号格式错误!");}//2.校验验证码Object cachecode = session.getAttribute("code");String code = loginForm.getCode();if(cachecode == null && !cachecode.toString().equals(code)){//3.不一致,报错return Result.fail("验证码错误");}//4.一致,根据手机号查询用户select * from tb_user where phone = ?User user = query().eq("phone", phone).one();//5.判断用户是否存在if(user == null){//6.不存在,创建用户并保存user = createUserWithphone(phone);}//7.保存用户信息到session中session.setAttribute("user", BeanUtil.copyProperties(user, UserDTO.class));return Result.ok();}private User createUserWithphone(String phone) {//1.创建用户User user = new User();user.setPhone(phone);user.setNickName(USER_NICK_NAME_PREFIX+RandomUtil.randomString(10));//2.保存用户save(user);return user;}
}

LoginInterceptor

package com.hmdp.utils;import com.hmdp.dto.UserDTO;
import org.springframework.lang.Nullable;
import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;public class LoginInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//1.获取sessionHttpSession session = request.getSession();//2.获取session中的用户Object user = session.getAttribute("user");//3.判断用户是否存在if (user == null) {//4.不存在,拦截response.setStatus(401);return false;}//5.存在,保存用户信息到ThreadLocalUserHolder.saveUser((UserDTO) user);//6.放行return true;}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {//移除用户UserHolder.removeUser();}
}

MvcConfig

package com.hmdp.config;import com.hmdp.utils.LoginInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configuration
public class MvcConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new LoginInterceptor()).excludePathPatterns(//放行"/user/code","/user/login","/blog/hot","/shop/**","/upload/**","/shop-type/**","/voucher/**");}}

注:本篇文章未实现调用第三方接口实现短信登录,并只作为笔记。

http://www.hskmm.com/?act=detail&tid=1027

相关文章:

  • 【A】chipi chipi chapa chapa
  • vLLM框架本地布署Qwen3-32B模型 - yi
  • 项目管理软件中有哪些不同的模块以及如何导出其报告?
  • Kubernetes命名空间(Namespace)
  • linux安装python
  • 【IEEE、电力学科品牌会议】第五届智能电力与系统国际学术会议(ICIPS 2025)
  • Vllm部署大模型
  • CE第9关X64版本问题记录
  • 题解:P14013 [POCamp 2023] 送钱 / The Generous Traveler
  • 第十三届 TCCT 随机系统与控制专题研讨会 暨2025年智能控制与计算科学国际学术会议 (ICICCS 2025)
  • 注释
  • Microsoft 推出 .NET 10 RC 1
  • 2025 第九届控制工程与先进算法国际论坛(IWCEAA 2025)
  • 高等代数 I
  • kotlin中的netty
  • 多态
  • 数学分析 I note
  • 高等代数 I note
  • JAVA反编译神器CFR
  • 记录一下由于VS中qt的插件自动升级引发的编译问题
  • flutter右滑返回直接返回到native问题
  • ck随笔
  • 如何用变量与函数实现随机生成数字交互?附完整教程
  • 离散数学与分析 note
  • Java基础
  • Linux系统简单源码安装NGINX版本1.28.0
  • 终结“网络无助感”:Tenable CEO解析漏洞管理与安全心态
  • 部分算法记录
  • Kubernetes资源管理方式
  • 2025公众号排版工具深度测评报告:10款主流产品功能对比与场景化选择指南