黑马点评2:基于Session实现登录

1. 业务流程

2. 发送短信验证码


src/main/java/com/hmdp/controller/UserController.java

    /**
     * 发送手机验证码
     */
    @PostMapping("code")
    public Result sendCode(@RequestParam("phone") String phone, HttpSession session) {
        // 发送短信验证码并保存验证码
        return userService.sendCode(phone, session);//需要写的,alt+enter到IUserService.java
    }

src/main/java/com/hmdp/service/IUserService.java

public interface IUserService extends IService<User> {//ctrl+b到UserServiceImpl.java
    Result sendCode(String phone, HttpSession session);//需要写的
}

src/main/java/com/hmdp/service/impl/UserServiceImpl.java

public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {

    @Override
    public Result sendCode(String phone, HttpSession session) {//Result:自定义的类,封装方法的返回结果
        // 1.校验手机号
        if(RegexUtils.isPhoneInvalid(phone)){//RegexUtils.isPhoneInvalid(phone):调用工具类方法(自定义的),检查手机号格式是否有效。
            // 2.如果不符合,返回错误信息
            return Result.fail("手机号格式错误!");
        }
        // 3.符合,生成验证码
        String code = RandomUtil.randomNumbers(6);//RandomUtil.randomNumbers(6):调用工具类方法(官方),生成一个6位的随机数字字符串,作为验证码。

        // 4.保存验证码到 session
        session.setAttribute("code",code);//HttpSession 对象用于在服务器端存储与特定用户会话相关的数据

        // 5.发送验证码(模拟发送),在后台查看
        log.debug("发送短信验证码成功,验证码:{}",code);
        // 返回ok
        return Result.ok();
    }

3. 登录功能

src/main/java/com/hmdp/controller/UserController.java

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

src/main/java/com/hmdp/service/IUserService.java

public interface IUserService extends IService<User> {

    Result sendCode(String phone, HttpSession session);

    Result login(LoginFormDTO loginForm, HttpSession session);
}

src/main/java/com/hmdp/service/impl/UserServiceImpl.java

@Override
    public Result login(LoginFormDTO loginForm, HttpSession session) {
        // 1.校验手机号(每个请求要做独立的校验)
        String phone  = loginForm.getPhone();
        if(RegexUtils.isPhoneInvalid(phone)){
            return Result.fail("手机号格式错误!");
        }
        // 2. 校验验证码
        Object cacheCode = session.getAttribute("code");
        String code = loginForm.getCode();
        if( cacheCode == null || !cacheCode.toString().equals(code)){
            // 3.  不一致,报错,反向校验,避免if的嵌套越来越深
            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",user);

        return null;
    }
private User createUserWithPhone(String phone) {
        // 1.创建用户
        User user = new User();
        user.setPhone(phone);
        user.setNickName(USER_NICK_NAME_PREFIX+RandomUtil.randomString(10));//USER_NICK_NAME_PREFIX为了统一一点,是自定义的常量
        // 2.保存用户
        save(user);
        return user;
    }

这里代码改了好像和前端不匹配,所以还是用回了拿到的原版代码,此时正确的效果应该是能登录创建出新用户,但是还是保留在登录界面。

4. 登录验证功能


拦截器

src/main/java/com/hmdp/utils/LoginInterceptor.java

public class LoginInterceptor implements HandlerInterceptor{
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 1. 获取session
        HttpSession session = request.getSession();
        // 2. 获取session中的用户
        Object user = session.setAttribute("user");
        // 3. 判断用户是否存在
        if(user == null){
            // 4. 不存在,拦截,返回401状态码(未授权的意思)
            response.setStatus(401);
            return false;
        }
        // 5. 存在,保存用户信息到TreadLocal
        UserHolder.saveUser((User) user);
        // 6. 放行
        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        //移除用户
        UserHolder.removeUser();
    }
}

src/main/java/com/hmdp/config/MvcConfig.java

    @Override
    public void addInterceptors(InterceptorRegistry registry) {//添加拦截器
        // 登录拦截器
        registry.addInterceptor(new LoginInterceptor())
                .excludePathPatterns(//排除不需要拦截的路径
                        "/shop/**",
                        "/voucher/**",
                        "/shop-type/**",
                        "/upload/**",
                        "/blog/hot",
                        "/user/code",
                        "/user/login"
                );
    }
}

src/main/java/com/hmdp/controller/UserController.java

    @GetMapping("/me")
    public Result me(){
        // 获取当前登录的用户并返回
        User user = UserHolder.getUser();
        return Result.ok(user);
    }

隐藏用户敏感信息

src/main/java/com/hmdp/service/impl/UserServiceImpl.java

@Override
    public Result login(LoginFormDTO loginForm, HttpSession session) {
        ...
        // 7.保存用户信息到 session中
        session.setAttribute("user",BeanUtil.copyProperties(user, UserDTO.class));

        return null;
    }

src/main/java/com/hmdp/utils/LoginInterceptor.java

public class LoginInterceptor implements HandlerInterceptor{
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        ...
        // 5. 存在,保存用户信息到TreadLocal
        UserHolder.saveUser((UserDTO) user);
        // 6. 放行
        return true;
    }
}

src/main/java/com/hmdp/utils/UserHolder.java

public class UserHolder {
    private static final ThreadLocal<UserDTO> tl = new ThreadLocal<>();

    public static void saveUser(UserDTO user){
        tl.set(user);
    }

    public static UserDTO getUser(){
        return tl.get();
    }

    public static void removeUser(){
        tl.remove();
    }
}

5. 集群的session共享问题


Redis替代

posted @ 2025-02-13 15:43  tse121  阅读(92)  评论(0)    收藏  举报