黑马点评实战片笔记---基于session登录

发布于:2025-07-16 ⋅ 阅读:(25) ⋅ 点赞:(0)

 实现验证码发送功能

/**
     * 发送手机验证码
     */
    @PostMapping("code")
    public Result sendCode(@RequestParam("phone") String phone, HttpSession session) {
        // TODO 发送短信验证码并保存验证码
        return userService.sendCode(phone,session);
    }


@Override
    public Result sendCode(String phone, HttpSession session) {
        //使用封装好的类来校验手机号格式
        if(RegexUtils.isPhoneInvalid(phone)) {
            return Result.fail("手机号格式错误");
        }
        //符合就生成
        String code = RandomUtil.randomNumbers(6);
        //保存验证码
        session.setAttribute("code",code);

        log.debug("发送短信验证码成功,验证码:{}",code);

        return Result.ok();
    }

 这里的result就是统一给前端返回的格式,上面只实现了发送验证码功能

实现验证码登录注册功能

/**
* 登录功能
* @param loginForm 登录参数,包含手机号、验证码;或者手机号、密码
*/
@PostMapping("/login")
public Result login(@RequestBody LoginFormDTO loginForm, HttpSession session){
    // TODO 实现登录功能
    return userService.login(loginForm,session);
}


@Override
    public Result login(LoginFormDTO loginForm, HttpSession session) {
        String phone = loginForm.getPhone();
        String code = loginForm.getCode();
        // 1、判断手机号是否合法
        if (RegexUtils.isPhoneInvalid(phone)) {
            return Result.fail("手机号格式不正确");
        }
        // 2、判断验证码是否正确
        //从session获取的验证码,这里获取到的是之前在sendCode中设置的
        String sessionCode = (String) session.getAttribute(LOGIN_CODE);
        if (code == null || !code.equals(sessionCode)) {
            return Result.fail("验证码不正确");
        }

        // 3、判断手机号是否是已存在的用户
        User user = query().eq("phone", phone).one();//这里的是mybatis-plus帮我们实现的

        if (Objects.isNull(user)) {
            // 用户不存在,需要注册
            user = createUserWithPhone(phone);
        }
        // 4、保存用户信息到Session中,便于后面逻辑的判断(比如登录判断、随时取用户信息,减少对数据库的查询)
        session.setAttribute(LOGIN_USER, user);
        return Result.ok();
    }

    /**
     * 根据手机号创建用户
     */
    private User createUserWithPhone(String phone) {
        User user = new User();
        user.setPhone(phone);
        user.setNickName(SystemConstants.USER_NICK_NAME_PREFIX + RandomUtil.randomString(10));
        save(user);//调用mybatis-plus的save保存到数据库中
        return user;
    }

query()是mybatis-plus帮我们实现的,把user对象保存在服务端中这是cookie和session最大的不同,

客户端会通过cookie携带sessionid,服务端会通过sessionid识别用户对象,session机制是依赖与cookie的

public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
指定了操作的实体类和对应的数据库表,应为User里有相应注释
@TableName("tb_user")
public class User implements Serializable {

校验登录状态 

如果后续有其他controller需要校验登录状态那还要一个一个写吗,我们想到可以直接使用拦截器


public class LoginInterceptor implements HandlerInterceptor {
    /**
     * 前置拦截器,用于判断用户是否登录
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        HttpSession session = request.getSession();
        // 1、判断用户是否存在
        User user = (User) session.getAttribute(LOGIN_USER);
        if (Objects.isNull(user)){
            // 用户不存在,直接拦截
            response.setStatus(HttpStatus.HTTP_UNAUTHORIZED);
            return false;
        }
        // 2、用户存在,则将用户信息保存到ThreadLocal中,方便后续逻辑处理
        // 比如:方便获取和使用用户信息,session获取用户信息是具有侵入性的
        ThreadLocalUtls.saveUser(user);

        return HandlerInterceptor.super.preHandle(request, response, handler);
    }
}

前置拦截器在controller调用之前执行,后置是调用之后执行

 配置完拦截器后,还需要将我们自定义的拦截器添加到SpringMVC的拦截器列表中,才能生效

public class LoginInterceptor implements HandlerInterceptor {
    /**
     * 前置拦截器,用于判断用户是否登录
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        HttpSession session = request.getSession();
        // 1、判断用户是否存在
        User user = (User) session.getAttribute(LOGIN_USER);
        if (Objects.isNull(user)){
            // 用户不存在,直接拦截
            response.setStatus(HttpStatus.HTTP_UNAUTHORIZED);
            return false;
        }
        // 2、用户存在,则将用户信息保存到ThreadLocal中,方便后续逻辑处理
        // 比如:方便获取和使用用户信息,session获取用户信息是具有侵入性的
        ThreadLocalUtls.saveUser(user);

        return HandlerInterceptor.super.preHandle(request, response, handler);
    }
}

Session集群共享问题

在分布式集群环境中,会话(Session)共享是一个常见的挑战。默认情况下,Web 应用程序的会话是保存在单个服务器上的,当请求不经过该服务器时,会话信息无法被访问。

Session集群共享问题造成哪些问题?

服务器之间无法实现会话状态的共享。比如:在当前这个服务器上用户已经完成了登录,Session中存储了用户的信息,能够判断用户已登录,但是在另一个服务器的Session中没有用户信息,无法调用显示没有登录的服务器上的服务
如何解决Session集群共享问题?

方案一:Session拷贝(不推荐)

Tomcat提供了Session拷贝功能,通过配置Tomcat可以实现Session的拷贝,但是这会增加服务器的额外内存开销,同时会带来数据一致性问题

方案二:Redis缓存(推荐)

Redis缓存具有Session存储一样的特点,基于内存、存储结构可以是key-value结构、数据共享
 


网站公告

今日签到

点亮在社区的每一天
去签到