目录
基于Session的短信登录
①首先看他的业务逻辑
②进行代码逻辑处理
基于Redis的短信登录
①首先看他的业务逻辑
②进行代码逻辑处理
Controller:
Service接口:
Service实例:
Mapper:
封装ThreadLocal线程的数据操作:
自定义拦截器:
拦截器配置文件:
Redis常量配置文件:
正则表达式文件:
基于Session的短信登录
①首先看他的业务逻辑
②进行代码逻辑处理
Controller层:
@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层:
@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:
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接口:
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实例:
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:
import com.hmdp.entity.User;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
public interface UserMapper extends BaseMapper<User> {
}
封装ThreadLocal线程的数据操作:
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();
}
}
自定义拦截器:
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();
}
}
拦截器配置文件:
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常量配置文件:
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;
}
正则表达式文件:
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中并设置有限期。 点击登录后就会进入我们正式的”我的“页面。