邮箱登录
框架搭建
实体类 User(正常创建)
Mapper接口 UserMapper(正常创建)
业务层接口 UserService
import com.baomidou.mybatisplus.extension.service.IService;
import com.XXX.XXX.entity.User;
import javax.servlet.http.HttpSession;
public interface UserService extends IService<User> {
/**
* 发送验证码
*/
void sendCode(String email, HttpSession session);
/**
* 验证码登陆账号,如果是新用户,则自动注册
*/
User loginByVerificationCode(String email, String code, HttpSession session);
}
业务层实现类 UserServicelmpl
@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
/**
* 邮箱发送器
*/
@Autowired
private JavaMailSender javaMailSender;
/**
* 发送验证码的邮箱地址
*/
@Value("${spring.mail.username}")
private String FROM;
/**
* 失效时间
*/
@Value("${spring.mail.timeout}")
private int TIME_OUT_MINUTES;
/**
* RedisTemplate redis操作模板
*/
@Autowired
private RedisTemplate<Object, Object> redisTemplate;
public void sendCode(String email, HttpSession session) {
// 生成4位验证码
String code = ValidateCodeUtils.generateValidateCode(4).toString();
log.info("Session:{}, Code:{}, to {}", session, code, email); // Slf4j的日志输出
// 发送邮件
// 构建一个邮件对象
SimpleMailMessage simpleMailMessage = new SimpleMailMessage();
// 设置邮件发送者
simpleMailMessage.setFrom(FROM);
// 设置邮件接收者
simpleMailMessage.setTo(email);
// 设置邮件主题
simpleMailMessage.setSubject("[悦刻外卖]登陆验证码");
// 设置邮件内容
simpleMailMessage.setText("欢迎使用外卖平台\n您的验证码为:" + code + ",请在" + TIME_OUT_MINUTES + "分钟内使用!\n【该邮件为系统自动发送,请勿回复】");
// 将验证码存入redis,设置失效时间TIME_OUT_MINUTES分钟
redisTemplate.opsForValue().set(email, code, TIME_OUT_MINUTES, java.util.concurrent.TimeUnit.MINUTES);
// 发送邮件
try {
javaMailSender.send(simpleMailMessage);
} catch (MailException e) {
e.printStackTrace();
throw new CustomException("致命错误!");
}
}
/**
* 验证码登陆账号,如果是新用户,则自动注册 */
@Override
public User loginByVerificationCode(String email, String code, HttpSession session) {
// 获取redis中的验证码
Object verificationCodeInRedis = redisTemplate.opsForValue().get(email);
// 验证码是否正确
if (verificationCodeInRedis != null && verificationCodeInRedis.equals(code)) {
// 验证码正确
// 查询用户是否存在
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(User::getEmail, email);
User user = this.getOne(queryWrapper);// 查询用户
// 用户不存在,自动注册
if (user == null) {
user = new User();
user.setEmail(email);
user.setStatus(1); // 设置用户状态为正常
this.save(user);
}
// 将用户信息存入session
session.setAttribute("user", user.getId());
// 将redis中的验证码删除
redisTemplate.delete(email);
return user;
} else {
// 验证码错误
throw new CustomException("验证码错误!");
}
}
}
控制层 UserController
这里有个问题,如果这个人登陆过前端,则在数据库中就会有记录,那么他就会在登录后端时成功,但是后端不允许顾客登录,所以这里不算严谨
@Slf4j
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
/**
* 发送邮箱验证码
*/
@PostMapping("/sendMsg")
public R<String> sendMsg(@RequestBody User user, HttpSession session) {
log.info("发送验证码,用户信息:{},session:{}", user, session);
// 获得前端登陆邮箱地址
String email = user.getEmail();
// 判断邮箱地址是否为空
if (!StringUtils.isEmpty(email)) {
// 发送验证码
userService.sendCode(email, session); // 通过工具类发送验证码,sendCode方法在UserService接口中
return R.success("发送成功,请及时查看邮箱");
}
return R.error("发送失败");
}
/**
* 移动端用户登录
*/
@PostMapping("/login")
public R<User> login(@RequestBody Map map, HttpSession session) {
log.info("用户登陆,用户信息:{},session:{}", map.toString(), session);
// 从map里获取邮箱地址和验证码
String email = (String) map.get("email");
String code = (String) map.get("code");
// 判断邮箱地址和验证码是否为空
if (!StringUtils.isEmpty(email) && !StringUtils.isEmpty(code)) {
// 验证码登陆
User user = userService.loginByVerificationCode(email, code, session);// 通过工具类发送验证码,login方法在UserService接口中
return R.success(user);
}
return R.error("登陆失败,请检查邮箱地址和验证码");
}
}
工具类ValidateCodeUtils(随机生成验证码工具类)
import java.util.Random;
/**
* 随机生成验证码工具类
*/
public class ValidateCodeUtils {
/**
* 随机生成验证码
* @param length 长度为4位或者6位
* @return
*/
public static Integer generateValidateCode(int length){
Integer code =null;
if(length == 4){
code = new Random().nextInt(9999);//生成随机数,最大为9999
if(code < 1000){
code = code + 1000;//保证随机数为4位数字
}
}else if(length == 6){
code = new Random().nextInt(999999);//生成随机数,最大为999999
if(code < 100000){
code = code + 100000;//保证随机数为6位数字
}
}else{
throw new RuntimeException("只能生成4位或6位数字验证码");
}
return code;
}
/**
* 随机生成指定长度字符串验证码
* @param length 长度
* @return
*/
public static String generateValidateCode4String(int length){
Random rdm = new Random();
String hash1 = Integer.toHexString(rdm.nextInt());
String capstr = hash1.substring(0, length);
return capstr;
}
}
修改LoginCheckFilter(过滤器)
增加判断条件,之前是判断后端是否登录,现在则是判断前端是否登录,二者代码差不多
// 4.判断用户登陆状态,如果为已登录,则放行 前端用户
if (request.getSession().getAttribute("user") != null) {
log.info("本次请求{},用户id={},已登录,直接放行", requestURI, request.getSession().getAttribute("user")); // Slf4j的日志输出
Long userId = (Long) request.getSession().getAttribute("user");
// 在该线程中保存当前用户的id,BaseContext为common包中的工具类,用于保存当前线程的数据
BaseContext.setCurrentId(userId);
filterChain.doFilter(request, response); // 放行
return; // 结束方法
}
完善front/api/login.js
(如果没有设计这个api会出现ReferenceError: sendMsgApi is not defined api报错,但是这个报错的原因不一定是因为这个原因,还有可能是因为浏览器的原因,换个浏览器可能就会好)
function sendMsgApi(data) {
return $axios({
'url': '/user/sendMsg',
'method': 'post',
data
})
}
添加配置application.yml
因为这里用了redis,所以在运行过程中如果只是配置了会出现java.net.ConnectException: Connection refused: no further information报错,这里是因为redis没有开启
# 邮箱验证配置信息|必填
mail:
host: smtp.qq.com # 如果你需要使用非qq邮箱,请自行配置host
username: 3028375407@qq.com # 请自行配置邮箱地址
password: cmouqanbpxordfcc # 请自行配置邮箱smtp授权码 具体请看本项目根目录下的README.md
port: 587
default-encoding: UTF-8
timeout: 10 # 验证码有效时间 单位:分钟
properties:
mail:
smtp:
socketFactoryClass: javax.net.ssl.SSLSocketFactory
# 开启debug以后,邮件发送过程的日志会在控制台上打印出来
debug: true
# redis缓存相关配置|必填
redis:
host: localhost
port: 6379 # 默认端口
password: # 默认为空
database: 0 # 默认使用0号数据库
以下是如何开启redis :
首先找到redis目录,右击点击在终端打开,输入redis-server,再回车即可,这样redis默认端口号也是6379
添加maven配置。pom.xml
<!-- 邮箱验证码服务dependency -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<!-- Jedis -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.8.0</version>
</dependency>
<!-- Spring Data Redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- Spring Cache | not used -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>