实战篇--短信登录

实战篇–短信登录

导入黑马点评项目

百度网盘拉下来就可以了

img

基于Session实现登录

  • 业务流程图

img

集群session共享问题

img

用redis解决

基于Redis实现共享session登录

img

根据实际选择数据结构,这里选择hash和string

img

img

  • 发送短信验证码

userServiceImpl

    @Override
    public Result sendCode(String phone, HttpSession session) {
        // 1.校验手机号
        if(RegexUtils.isPhoneInvalid(phone)){
            // 2.如果不符合
            return Result.fail("手机号格式错误!");
        }
        // 3.如果符合生成验证码
        String code = RandomUtil.randomNumbers(6);
        // 4.用redis保存登录的验证码,并设置过期时间
        stringRedisTemplate.opsForValue().set(LOGIN_CODE_KEY+phone,code,LOGIN_CODE_TTL, TimeUnit.MINUTES);
        // 5.发送验证码
        log.debug("发送短信验证码成功:"+code);
        return Result.ok();
    }
  • 登录

userServiceImpl

    @Override
    public Result login(LoginFormDTO loginForm, HttpSession session) {
        // 1.校验手机号
        if(RegexUtils.isPhoneInvalid(loginForm.getPhone())){
            // 2.如果不符合
            return Result.fail("手机号格式错误!");
        }
        // 从redis中取出对应手机号的验证码
        String code = stringRedisTemplate.opsForValue().get(LOGIN_CODE_KEY + loginForm.getPhone());
        if (code == null || !Objects.equals(loginForm.getCode(), code)) {
            return Result.fail("验证码错误!");
        }
    	// 查询用户是否存在
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("phone",loginForm.getPhone());
        User user = this.baseMapper.selectOne(queryWrapper);
        if (user == null) {
            // 用户不存在就直接创建新用户写入数据库
            user = new User();
            user.setPhone(loginForm.getPhone());
            user.setNickName(USER_NICK_NAME_PREFIX+RandomUtil.randomString(10));
            this.baseMapper.insert(user);
        }
        // 随机生成uuid当作token返回给前端
        String token = UUID.randomUUID().toString(true);
        UserDTO userDTO = new UserDTO();
        // 过滤隐私内容
        BeanUtils.copyProperties(user,userDTO);
        // 1.BeanUtil.beanToMap将bean转换成map
        // 2.setIgnoreNullValue忽略null值
        // 3.setFieldValueEditor((fieldName,fieldValue)->return fieldValue.toString()) 对每个字段和值处理,将值转成字符串方便写入redis
        Map<String, Object> stringObjectMap = BeanUtil.beanToMap(userDTO,new HashMap<>(),
                CopyOptions.create().setIgnoreNullValue(true).setFieldValueEditor((fieldName,fieldValue)-> fieldValue.toString()));
        // 将token写入redis
        stringRedisTemplate.opsForHash().putAll(LOGIN_USER_KEY+token,stringObjectMap);
        // 设置过期时间
        stringRedisTemplate.expire(LOGIN_USER_KEY+token,LOGIN_USER_TTL,TimeUnit.MINUTES);
        // 将token返回给前端,下次前端来的时候带上token就能知道是那个用户
        return Result.ok(token);
    }
  • 自定义过滤器

对用户状态进行刷新,不对未登录用户进行拦截

RefreshTokenInterceptor

package com.hmdp.utils;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.hmdp.dto.UserDTO;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import static com.hmdp.utils.RedisConstants.LOGIN_USER_KEY;
import static com.hmdp.utils.RedisConstants.LOGIN_USER_TTL;

/**
 * @author xc
 * @date 2023/4/10 21:52
 */
public class RefreshTokenInterceptor implements HandlerInterceptor {

    
    private StringRedisTemplate redisTemplate;

    // 想要在我们自定义的类中使用redis,因为这不是spring的bean,不能被管理,所以不能用自动注入
    public RefreshTokenInterceptor(StringRedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 从请求头中拿到前端带来的token
        String token = request.getHeader("authorization");
        // 没有token直接放行
        if (StrUtil.isBlank(token)) {
            return true;
        }
        // 从redis中没有拿到用户信息直接放行
        Map<Object, Object> entries = redisTemplate.opsForHash().entries(LOGIN_USER_KEY + token);
        if (entries.isEmpty()) {
            return true;
        }
    	// 此时刷新以下用户登录时间
        UserDTO userDTO = BeanUtil.fillBeanWithMap(entries, new UserDTO(),false);
        UserHolder.saveUser(userDTO);
        redisTemplate.expire(LOGIN_USER_KEY + token,LOGIN_USER_TTL, TimeUnit.MINUTES);
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
    }
}

对未登录用户进行拦截

LoginInterceptor

package com.hmdp.utils;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * @author xc
 * @date 2023/4/10 21:52
 */
public class LoginInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 在该线程中没有用户信息就拦截
        if (UserHolder.getUser() == null) {
            response.setStatus(401);
            return false;
        }
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
    }
}

自定义MVCConfig

package com.hmdp.config;

import com.hmdp.utils.LoginInterceptor;
import com.hmdp.utils.RefreshTokenInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import javax.annotation.Resource;

/**
 * @author xc
 * @date 2023/4/10 22:01
 */
@Configuration
public class MVCConfig implements WebMvcConfigurer {

    // 因为此bean是springboot管理的可以直接注入
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 对未登录用户访问需要登录网页的拦截
        // order是拦截的优先级,数字越大越靠后
        registry.addInterceptor(new LoginInterceptor())
                .excludePathPatterns(
                        "/user/code",
                        "/voucher/**",
                        "/blog/hot",
                        "/shop/**",
                        "/shop-type/**",
                        "/user/login",
                        "/upload/**"
                ).order(1);
        // 所有页面刷新用户时间拦截
        registry.addInterceptor(new RefreshTokenInterceptor(stringRedisTemplate)).addPathPatterns("/**").order(0);
    }
}
  • 查询用户当前状态

UserController

    @GetMapping("/me")
    public Result me(){
        UserDTO user = UserHolder.getUser();
        return Result.ok(user);
    }
okenInterceptor(stringRedisTemplate)).addPathPatterns("/**").order(0);
    }
}
  • 查询用户当前状态

UserController

    @GetMapping("/me")
    public Result me(){
        UserDTO user = UserHolder.getUser();
        return Result.ok(user);
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值