Redis---------实现短信登录业务

目录

基于Session的短信登录

①首先看他的业务逻辑

②进行代码逻辑处理

基于Redis的短信登录

①首先看他的业务逻辑

②进行代码逻辑处理

Controller:

Service接口:

Service实例:

Mapper:

封装ThreadLocal线程的数据操作:

自定义拦截器:

拦截器配置文件:

Redis常量配置文件:

正则表达式文件:


基于Session的短信登录

①首先看他的业务逻辑

②进行代码逻辑处理

Controller层:

java 复制代码
@Slf4j
@RestController
@RequestMapping("/user")
public class UserController {

    @Resource
    private IUserService userService;

    @Resource
    private IUserInfoService userInfoService;


    /**
     * 发送手机验证码
     */
    @PostMapping("code")
    public Result sendCode(@RequestParam("phone") String phone, HttpSession session) {

        return userService.sendCode(phone,session);
    }


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

        return userService.login(loginForm,session);
    }


    //跳转到我的页面
    @GetMapping("/me")
    public Result me(){
        User user = UserHolder.getUser();
        return Result.ok(user);
    }

Service层:

java 复制代码
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {

    //发送验证码
    @Override
    public Result sendCode(String phone, HttpSession session) {
        //1,校验手机号,看手机号是否合规
        if (RegexUtils.isPhoneInvalid(phone)) {
            //2,不符合则返回错误结果
            return Result.fail("手机号错误!");
        }

        //3,符合就生成验证码
        String code = RandomUtil.randomNumbers(6);

        //4,保存验证码在Session中
        session.setAttribute("code",code);

        //5,发送验证码给前端
        log.debug("发送验证码: "+code);

        return Result.ok();
    }

    //登陆
    @Override
    public Result login(LoginFormDTO loginForm, HttpSession session) {
        //1,校验手机号,看手机号是否合规
        String phone = loginForm.getPhone();
        if (RegexUtils.isPhoneInvalid(phone)) {
            //2,不符合则返回错误结果
            return Result.fail("手机号错误!");
        }

        //2,校验验证码
        Object Cachecode = session.getAttribute("code");//session里的验证码
        String code = loginForm.getCode();//前端提交过来的验证码

        if(Cachecode == null || !Cachecode.toString().equals(code)){
            //3,如果错误则返回
            return Result.fail("验证码错误!");
        }

        //4,正确的话就查询数据库中的数据
        User user = query().eq("phone", phone).one();

        //5,判断是否存在
        if(user == null){
            //6,如果没有查到就进行注册操作
            user= new User();
            user.setPhone(phone);
            user.setNickName( USER_NICK_NAME_PREFIX +RandomUtil.randomString(10));
            save(user);
        }

        //7,保存到Session中
        session.setAttribute("user",user);


        return Result.ok();
    }
}

基于Redis的短信登录

①首先看他的业务逻辑

②进行代码逻辑处理

Controller:
java 复制代码
import com.hmdp.dto.LoginFormDTO;
import com.hmdp.dto.Result;
import com.hmdp.entity.User;
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;


@Slf4j
@RestController
@RequestMapping("/user")
public class UserController {

    @Resource
    private IUserService userService;

    @Resource
    private IUserInfoService userInfoService;


    /**
     * 发送手机验证码,接受手机号参数以及session
     */
    @PostMapping("code")
    public Result sendCode(@RequestParam("phone") String phone, HttpSession session) {

        //把参数传到Service层去实现业务功能
        return userService.sendCode(phone,session);
    }

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

        把参数传到Service层去实现业务功能
        return userService.login(loginForm,session);
    }


    //进入我的页面发送的请求
    @GetMapping("/me")
    public Result me(){
        //在线程ThreadLocal把用户的数据拿出来并且返回到前端
        User user = UserHolder.getUser();
        System.out.println("user = " + user);
        //
        return Result.ok(user);
    }


}
Service接口:
java 复制代码
import com.baomidou.mybatisplus.extension.service.IService;
import com.hmdp.dto.LoginFormDTO;
import com.hmdp.dto.Result;
import com.hmdp.entity.User;

import javax.servlet.http.HttpSession;

public interface IUserService extends IService<User> {

    Result sendCode(String phone, HttpSession session);

    Result login(LoginFormDTO loginForm, HttpSession session);
}
Service实例:
java 复制代码
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hmdp.dto.LoginFormDTO;
import com.hmdp.dto.Result;
import com.hmdp.entity.User;
import com.hmdp.mapper.UserMapper;
import com.hmdp.service.IUserService;
import com.hmdp.utils.RegexUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpSession;
import java.util.concurrent.TimeUnit;
import static com.hmdp.utils.RedisConstants.*;
import static com.hmdp.utils.SystemConstants.USER_NICK_NAME_PREFIX;


@Service
//extends ServiceImpl<UserMapper, User>是mybatisplus中的IService接口,可以实现CRUD的快速操作
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {

    @Autowired
    //redis-java客户端
    private StringRedisTemplate stringRedisTemplate;

    //发送验证码
    @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();
    }

    //登录
    @Override
    public Result login(LoginFormDTO loginForm, HttpSession session) {
        //1,校验手机号,看手机号是否合规
        String phone = loginForm.getPhone();
        if (RegexUtils.isPhoneInvalid(phone)) {
            //2,不符合则返回错误结果
            return Result.fail("手机号错误!");
        }

        //2,校验验证码
        String Cachecode = stringRedisTemplate.opsForValue().get(LOGIN_CODE_KEY + phone);//redis里的验证码
        String code = loginForm.getCode();//前端提交过来的验证码

        //redis里查不出数据,代表已经过期
        if(Cachecode == null || !Cachecode.equals(code)){
            //3,如果错误则返回
            return Result.fail("验证码错误!");
        }

        //4,正确的话就查询数据库中的数据
        User user = query().eq("phone", phone).one();

        //5,判断是否存在
        if(user == null){
            //6,如果没有查到就进行注册操作
            user= new User();
            user.setPhone(phone);
            user.setNickName( USER_NICK_NAME_PREFIX +RandomUtil.randomString(10));
            save(user);
        }

        //7,把用户数据保存到Redis中
        String token = IdUtil.randomUUID();//生成token
        String object_json = JSONUtil.parse(user).toString();//序列化
        stringRedisTemplate.opsForValue().set(LOGIN_USER_KEY+token,object_json,LOGIN_USER_TTL,TimeUnit.MINUTES);

        return Result.ok(token);
    }



}
Mapper:
java 复制代码
import com.hmdp.entity.User;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;


public interface UserMapper extends BaseMapper<User> {

}
封装ThreadLocal线程的数据操作:
java 复制代码
import com.hmdp.entity.User;

//线程里的数据的封装操作
public class UserHolder {
    private static final ThreadLocal<User> tl = new ThreadLocal<>();

    //把数据存到线程中
    public static void saveUser(User user){
        tl.set(user);
    }

    //从线程当中取数据
    public static User getUser(){
        return tl.get();
    }

    //删除线程中的数据
    public static void removeUser(){
        tl.remove();
    }
}
自定义拦截器:
java 复制代码
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.hmdp.entity.User;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


import java.util.concurrent.TimeUnit;

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

public class LoginInterceptor implements HandlerInterceptor {

    private StringRedisTemplate stringRedisTemplate;

    public LoginInterceptor(StringRedisTemplate stringRedisTemplate) {
        this.stringRedisTemplate = stringRedisTemplate;
    }

    @Override
    //前置拦截器
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //校验登录状态
        //1,获取Token
        String token = request.getHeader("authorization");
        if (StrUtil.isBlank(token)) {
            response.setStatus(401);
            return false;
        }

        //2,获取Redis中的用户
        String s = stringRedisTemplate.opsForValue().get(LOGIN_USER_KEY+token);
        User user= JSONUtil.toBean(s, User.class);

        //3,判断用户是否存在
        if (user == null){
            //4,不存在直接拦截
            response.setStatus(401);
            return false;
        }

        //4,存在则保存信息到ThreadLocal
        UserHolder.saveUser(user);


        //5,刷新Token的有效期
        stringRedisTemplate.expire(LOGIN_USER_KEY+token,RedisConstants.LOGIN_USER_TTL, TimeUnit.MINUTES);
        return true;
    }


    @Override
    //后置拦截器
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        UserHolder.removeUser();
    }
}
拦截器配置文件:
java 复制代码
import com.hmdp.utils.LoginInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
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;

@Configuration
public class MVCCONFIG implements WebMvcConfigurer {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginInterceptor(stringRedisTemplate))
                //""里是添加要拦截的路径
                .addPathPatterns("/**")
                //""里是设置不拦截的路径
                .excludePathPatterns("/user/login","/user/code","blog/hot","/shop/**","./shoptype/**","/upload/**","/voucher/**");

    }
}
Redis常量配置文件:
java 复制代码
public class RedisConstants {
    public static final String LOGIN_CODE_KEY = "login:code:";
    public static final Long LOGIN_CODE_TTL = 2L;
    public static final String LOGIN_USER_KEY = "login:token:";
    public static final Long LOGIN_USER_TTL = 30L;
}
正则表达式文件:
java 复制代码
import cn.hutool.core.util.StrUtil;


public class RegexUtils {
    /**
     * 是否是无效手机格式
     * @param phone 要校验的手机号
     * @return true:符合,false:不符合
     */
    public static boolean isPhoneInvalid(String phone){
        return mismatch(phone, RegexPatterns.PHONE_REGEX);
    }

    /**
     * 是否是无效邮箱格式
     * @param email 要校验的邮箱
     * @return true:符合,false:不符合
     */
    public static boolean isEmailInvalid(String email){
        return mismatch(email, RegexPatterns.EMAIL_REGEX);
    }

    /**
     * 是否是无效验证码格式
     * @param code 要校验的验证码
     * @return true:符合,false:不符合
     */
    public static boolean isCodeInvalid(String code){
        return mismatch(code, RegexPatterns.VERIFY_CODE_REGEX);
    }

}

在这里做一个总结:做这种业务主要是要捋清楚处理的步骤以及逻辑,像我们的**发送验证码过程--->①前端发来数据请求参数和session②首先判断手机号是否符合正确的格式③如果不符合则返回错误,符合就可以生成验证码④验证码生成后将其保存到Redis中,并设置有效期⑤然后把验证码发送给前端。 前端收到验证码后填入并且点击登录按钮,就会发起一个带着参数的新的请求登录--->**①后端收到请求首先判断手机号是否符合正确的格式②然后从Redis中把保存的验证码取出来与前端发过来的参数验证码进行比较③如果不相等或者Redis中无数据(即验证码已经过期)则返回错误④如果相等则去查询mysql数据库中是否有该数据⑤如果没有的话就要进行注册操作,把数据存到数据库中⑥最后把该数据存在Redis中并设置有限期。 点击登录后就会进入我们正式的"我的"页面。

相关推荐
_.Switch16 分钟前
Python 自动化运维持续优化与性能调优
运维·开发语言·python·缓存·自动化·运维开发
徐*红16 分钟前
java 线程池
java·开发语言
尚学教辅学习资料16 分钟前
基于SSM的养老院管理系统+LW示例参考
java·开发语言·java毕设·养老院
2401_8576363916 分钟前
计算机课程管理平台:Spring Boot与工程认证的结合
java·spring boot·后端
1 9 J18 分钟前
Java 上机实践4(类与对象)
java·开发语言·算法
Code apprenticeship19 分钟前
Java面试题(2)
java·开发语言
J不A秃V头A22 分钟前
Python爬虫:获取国家货币编码、货币名称
开发语言·爬虫·python
也无晴也无风雨1 小时前
深入剖析输入URL按下回车,浏览器做了什么
前端·后端·计算机网络
憨子周1 小时前
2M的带宽怎么怎么设置tcp滑动窗口以及连接池
java·网络·网络协议·tcp/ip
FIN技术铺3 小时前
Redis集群模式之Redis Sentinel vs. Redis Cluster
数据库·redis·sentinel