架构
短信登录
基于session实现登录
流程图
代码实现
@Slf4j
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
/**
* session用户key
*/
public static final String USER_CONSTANT = "user";
@Override
public Result sendCode(String phone, HttpSession session) {
//校验手机号码
boolean phoneInvalid = RegexUtils.isPhoneInvalid(phone);
if (phoneInvalid) {
return Result.fail("手机号码格式错误!");
}
//生成6位数的验证码
String code = RandomUtil.randomNumbers(6);
session.setAttribute("code", code);
//发送验证码
log.info("send code success,code={}", code);
return Result.ok();
}
@Override
public Result login(LoginFormDTO loginForm, HttpSession session) {
//校验手机号码
if (Objects.isNull(loginForm)) {
return Result.fail("参数为空!");
}
String phone = loginForm.getPhone();
if (RegexUtils.isPhoneInvalid(phone)) {
return Result.fail("手机号码格式错误!");
}
//验证码校验
String code = (String) session.getAttribute("code");
if (StringUtils.isBlank(code) || !StringUtils.equals(code, loginForm.getCode())) {
return Result.fail("验证码错误!");
}
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(User::getPhone, phone);
User user = getOne(wrapper);
if (!Objects.nonNull(user)) {
//注册新用户
user = getNewUserByPhone(phone);
save(user);
}
session.setAttribute(USER_CONSTANT, BeanUtil.copyProperties(user, UserDTO.class));
return Result.ok();
}
/**
* 根据手机号码创建新用户
*
* @param phone 手机号码
* @return
*/
private User getNewUserByPhone(String phone) {
User user = new User();
user.setCreateTime(LocalDateTime.now());
user.setPhone(phone);
user.setNickName(SystemConstants.USER_NICK_NAME_PREFIX + RandomUtil.randomString(10));
user.setUpdateTime(LocalDateTime.now());
return user;
}
}
集群session共享问题
session数据拷贝可以解决这个问题,但是多台tomcat之间存储相同的数据会浪费内存空间,拷贝会有数据延迟。
session每个浏览器有不同的code,tomcat里保存里很多code。
基于Redis实现session登录
验证码流程图
代码实现
public Result sendCode(String phone, HttpSession session) {
//校验手机号码
boolean phoneInvalid = RegexUtils.isPhoneInvalid(phone);
if (phoneInvalid) {
return Result.fail("手机号码格式错误!");
}
//生成6位数的验证码
String code = RandomUtil.randomNumbers(6);
//保存验证码到redis
stringRedisTemplate.opsForValue().set(LOGIN_CODE_KEY + phone, code, LOGIN_CODE_TTL, TimeUnit.SECONDS);
//发送验证码
log.info("send code success,code={}", code);
return Result.ok();
}
校验流程图
代码实现
登录
public Result login(LoginFormDTO loginForm, HttpSession session) {
//校验手机号码
if (Objects.isNull(loginForm)) {
return Result.fail("参数为空!");
}
String phone = loginForm.getPhone();
if (RegexUtils.isPhoneInvalid(phone)) {
return Result.fail("手机号码格式错误!");
}
//验证码校验
String code = (String) session.getAttribute("code");
if (StringUtils.isBlank(code) || !StringUtils.equals(code, loginForm.getCode())) {
return Result.fail("验证码错误!");
}
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(User::getPhone, phone);
User user = getOne(wrapper);
if (!Objects.nonNull(user)) {
//注册新用户
user = getNewUserByPhone(phone);
save(user);
}
session.setAttribute(USER_CONSTANT, BeanUtil.copyProperties(user, UserDTO.class));
return Result.ok();
}
登录拦截器
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.http.HttpStatus;
import com.hmdp.dto.UserDTO;
import com.hmdp.utils.UserHolder;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils