注册发送邮箱验证码
This commit is contained in:
parent
fe6edf9e5d
commit
e4481507fa
@ -34,6 +34,8 @@ export default {
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
top:50%;
|
top:50%;
|
||||||
transform: translateY(-50%);
|
transform: translateY(-50%);
|
||||||
|
display: block;
|
||||||
|
left: 64px;
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
@ -1,8 +1,8 @@
|
|||||||
import {createMemoryHistory, createRouter} from "vue-router";
|
import {createRouter, createWebHashHistory} from "vue-router";
|
||||||
import store from "./store";
|
import store from "./store";
|
||||||
|
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
history: createMemoryHistory(),
|
history: createWebHashHistory(),
|
||||||
routes: [
|
routes: [
|
||||||
{
|
{
|
||||||
name: 'Main',
|
name: 'Main',
|
||||||
|
@ -127,6 +127,7 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
.login-logo{
|
.login-logo{
|
||||||
height: 60px;
|
height: 60px;
|
||||||
|
width: 200px;
|
||||||
}
|
}
|
||||||
.title {
|
.title {
|
||||||
background-color: #eaeaea;
|
background-color: #eaeaea;
|
||||||
|
@ -13,8 +13,8 @@
|
|||||||
</div>
|
</div>
|
||||||
<template #overlay>
|
<template #overlay>
|
||||||
<a-menu>
|
<a-menu>
|
||||||
<a-menu-item key="0">
|
<a-menu-item key="0" @click="logout">
|
||||||
<a href="http://www.alipay.com/">1st menu item</a>
|
<span>退出登录</span>
|
||||||
</a-menu-item>
|
</a-menu-item>
|
||||||
<a-menu-item key="1">
|
<a-menu-item key="1">
|
||||||
<a href="http://www.taobao.com/">2nd menu item</a>
|
<a href="http://www.taobao.com/">2nd menu item</a>
|
||||||
@ -73,13 +73,14 @@ import dayjs from "dayjs";
|
|||||||
import {DownloadOutlined, RedoOutlined, DeleteOutlined, ShareAltOutlined,SettingFilled} from '@ant-design/icons-vue';
|
import {DownloadOutlined, RedoOutlined, DeleteOutlined, ShareAltOutlined,SettingFilled} from '@ant-design/icons-vue';
|
||||||
import {useStore} from "vuex";
|
import {useStore} from "vuex";
|
||||||
import MainLogo from "../components/MainLogo.vue";
|
import MainLogo from "../components/MainLogo.vue";
|
||||||
|
import {useRouter} from "vue-router";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "Main",
|
name: "Main",
|
||||||
components: {MainLogo, DownloadOutlined, RedoOutlined, DeleteOutlined, ShareAltOutlined,SettingFilled},
|
components: {MainLogo, DownloadOutlined, RedoOutlined, DeleteOutlined, ShareAltOutlined,SettingFilled},
|
||||||
setup() {
|
setup() {
|
||||||
const fileList = ref<FileItem[]>([])
|
const fileList = ref<FileItem[]>([])
|
||||||
const store = useStore()
|
const store = useStore(),router = useRouter()
|
||||||
const userinfo = computed(() => store.state.loginUser)
|
const userinfo = computed(() => store.state.loginUser)
|
||||||
const loadFolderFiles = () => {
|
const loadFolderFiles = () => {
|
||||||
api.folder.list().then(list => {
|
api.folder.list().then(list => {
|
||||||
@ -94,7 +95,11 @@ export default {
|
|||||||
reload() {
|
reload() {
|
||||||
location.reload()
|
location.reload()
|
||||||
},
|
},
|
||||||
formatDate(time) {
|
logout(){
|
||||||
|
store.commit('clearToken')
|
||||||
|
router.replace('/login')
|
||||||
|
},
|
||||||
|
formatDate(time:string|number|Date) {
|
||||||
return dayjs(time).format('MM-DD HH:mm')
|
return dayjs(time).format('MM-DD HH:mm')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -116,7 +116,11 @@
|
|||||||
<artifactId>java-jwt</artifactId>
|
<artifactId>java-jwt</artifactId>
|
||||||
<version>3.8.3</version>
|
<version>3.8.3</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<!-- 邮件发送依赖 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-mail</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- <dependency>-->
|
<!-- <dependency>-->
|
||||||
<!-- <groupId>org.springframework.amqp</groupId>-->
|
<!-- <groupId>org.springframework.amqp</groupId>-->
|
||||||
|
@ -21,12 +21,14 @@ public class SaTokenConfig {
|
|||||||
"/api/user/reg",
|
"/api/user/reg",
|
||||||
"/api/user/forget",
|
"/api/user/forget",
|
||||||
"/api/user/reset",
|
"/api/user/reset",
|
||||||
|
"/api/user/check-email",
|
||||||
|
"/api/user/check-account",
|
||||||
"/swagger**",
|
"/swagger**",
|
||||||
"/swagger-resources/**",
|
"/swagger-resources/**",
|
||||||
"/v2/api-docs",
|
"/v2/api-docs",
|
||||||
"/picture/**"
|
"/picture/**"
|
||||||
)
|
)
|
||||||
.addExclude("/**")
|
.addExclude("/api/**")
|
||||||
.setError(new SaFilterErrorStrategy() {
|
.setError(new SaFilterErrorStrategy() {
|
||||||
@Override
|
@Override
|
||||||
public Object run(Throwable e) {
|
public Object run(Throwable e) {
|
||||||
|
@ -5,14 +5,17 @@ import cn.hutool.captcha.LineCaptcha;
|
|||||||
import cn.hutool.captcha.generator.RandomGenerator;
|
import cn.hutool.captcha.generator.RandomGenerator;
|
||||||
import cn.hutool.core.lang.UUID;
|
import cn.hutool.core.lang.UUID;
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.data.redis.core.RedisTemplate;
|
import org.springframework.data.redis.core.RedisTemplate;
|
||||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
import xyz.longicorn.driver.dto.ApiResult;
|
import xyz.longicorn.driver.dto.ApiResult;
|
||||||
|
import xyz.longicorn.driver.service.EmailService;
|
||||||
import xyz.longicorn.driver.util.RedisUtil;
|
import xyz.longicorn.driver.util.RedisUtil;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
|
import javax.mail.MessagingException;
|
||||||
import javax.servlet.ServletOutputStream;
|
import javax.servlet.ServletOutputStream;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
@ -24,6 +27,8 @@ import java.util.concurrent.TimeUnit;
|
|||||||
public class CodeController {
|
public class CodeController {
|
||||||
@Resource
|
@Resource
|
||||||
private StringRedisTemplate stringRedisTemplate;
|
private StringRedisTemplate stringRedisTemplate;
|
||||||
|
@Resource
|
||||||
|
private EmailService emailService;
|
||||||
|
|
||||||
@RequestMapping("/code-id")
|
@RequestMapping("/code-id")
|
||||||
public String createCodeId() {
|
public String createCodeId() {
|
||||||
@ -74,4 +79,22 @@ public class CodeController {
|
|||||||
return ApiResult.success(data);
|
return ApiResult.success(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Value("${application.email-content}")
|
||||||
|
private String codeTemplate;
|
||||||
|
|
||||||
|
@RequestMapping("/send-code")
|
||||||
|
public ApiResult sendVerifyCode(String email) {
|
||||||
|
RandomGenerator generator = new RandomGenerator("0123456789", 6);
|
||||||
|
String code = generator.generate(); // 产生验证码
|
||||||
|
// 发送验证码
|
||||||
|
try {
|
||||||
|
emailService.sendHTML(email, codeTemplate.replace("$code", code), "验证码");
|
||||||
|
} catch (MessagingException e) {
|
||||||
|
return ApiResult.error(1,"发送验证码失败");
|
||||||
|
}
|
||||||
|
// 记录该邮箱的验证码
|
||||||
|
stringRedisTemplate.opsForValue().set("email:code:" + email, code,
|
||||||
|
180, TimeUnit.SECONDS);
|
||||||
|
return ApiResult.success(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package xyz.longicorn.driver.controller;
|
package xyz.longicorn.driver.controller;
|
||||||
|
|
||||||
|
import cn.dev33.satoken.stp.StpUtil;
|
||||||
|
import io.swagger.models.auth.In;
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
import org.springframework.util.DigestUtils;
|
import org.springframework.util.DigestUtils;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
@ -55,6 +57,7 @@ public class UploadController {
|
|||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
@PostMapping("/upload")
|
@PostMapping("/upload")
|
||||||
public ApiResult upload(@RequestParam("parent") String parent, @RequestPart("file") MultipartFile file) {
|
public ApiResult upload(@RequestParam("parent") String parent, @RequestPart("file") MultipartFile file) {
|
||||||
|
Integer uid =(Integer) StpUtil.getLoginId();
|
||||||
//
|
//
|
||||||
// 1.计算md5
|
// 1.计算md5
|
||||||
String md5 = DigestUtils.md5DigestAsHex(file.getInputStream());
|
String md5 = DigestUtils.md5DigestAsHex(file.getInputStream());
|
||||||
@ -64,13 +67,13 @@ public class UploadController {
|
|||||||
if (parent.equals("/")) {
|
if (parent.equals("/")) {
|
||||||
fileInfo.setFolderId(0l);
|
fileInfo.setFolderId(0l);
|
||||||
} else {
|
} else {
|
||||||
FolderInfo folder = folderService.getByPath(1, parent);
|
FolderInfo folder = folderService.getByPath(uid, parent);
|
||||||
if (folder == null) {
|
if (folder == null) {
|
||||||
throw BizException.create("保存目录不存在");
|
throw BizException.create("保存目录不存在");
|
||||||
}
|
}
|
||||||
fileInfo.setFolderId(folder.getId());
|
fileInfo.setFolderId(folder.getId());
|
||||||
}
|
}
|
||||||
fileInfo.setUid(1);
|
fileInfo.setUid(uid);
|
||||||
fileInfo.setHash(md5);//
|
fileInfo.setHash(md5);//
|
||||||
fileInfo.setName(file.getOriginalFilename());
|
fileInfo.setName(file.getOriginalFilename());
|
||||||
fileInfo.setSize(file.getSize());
|
fileInfo.setSize(file.getSize());
|
||||||
|
@ -4,6 +4,7 @@ import cn.dev33.satoken.stp.StpUtil;
|
|||||||
import cn.hutool.core.util.IdUtil;
|
import cn.hutool.core.util.IdUtil;
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestBody;
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
@ -11,7 +12,9 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
import xyz.longicorn.driver.dto.ApiResult;
|
import xyz.longicorn.driver.dto.ApiResult;
|
||||||
import xyz.longicorn.driver.dto.LoginModel;
|
import xyz.longicorn.driver.dto.LoginModel;
|
||||||
|
import xyz.longicorn.driver.dto.LoginUserInfo;
|
||||||
import xyz.longicorn.driver.pojo.LoginUser;
|
import xyz.longicorn.driver.pojo.LoginUser;
|
||||||
|
import xyz.longicorn.driver.pojo.UserInfo;
|
||||||
import xyz.longicorn.driver.service.UserService;
|
import xyz.longicorn.driver.service.UserService;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
@ -31,23 +34,65 @@ public class UserController {
|
|||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
@PostMapping("/login")
|
@PostMapping("/login")
|
||||||
public ApiResult login(@Validated @RequestBody LoginModel model) {
|
public ApiResult login(@Validated @RequestBody LoginModel model) {
|
||||||
|
// 判断是否有验证码数据 有则需要判断验证码是否正确
|
||||||
|
if (StringUtils.hasLength(model.getCodeId())) {
|
||||||
String key = "code:" + model.getCodeId();
|
String key = "code:" + model.getCodeId();
|
||||||
String code1 = stringRedisTemplate.opsForValue().get(key);
|
String code1 = stringRedisTemplate.opsForValue().get(key);
|
||||||
|
|
||||||
if (code1 == null || !code1.equals(model.getCode())) {
|
if (code1 == null || !code1.equals(model.getCode())) {
|
||||||
return ApiResult.error(1,"验证码无效");
|
return ApiResult.error(1, "验证码无效");
|
||||||
}
|
}
|
||||||
// 使原始验证码失效
|
// 使原始验证码失效
|
||||||
stringRedisTemplate.delete(key);
|
stringRedisTemplate.delete(key);
|
||||||
|
}
|
||||||
|
|
||||||
Thread.sleep(2);
|
Thread.sleep(2);
|
||||||
final LoginUser user = userService.login(model.getUsername(), model.getPassword());
|
final LoginUser user = userService.login(model.getUsername(), model.getPassword());
|
||||||
|
|
||||||
return ApiResult.success(user);
|
return ApiResult.success(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PostMapping("/reg")
|
||||||
|
public ApiResult reg(@RequestBody LoginUserInfo userInfo) {
|
||||||
|
String code = stringRedisTemplate.opsForValue().get("email:code:" + userInfo.getEmail());
|
||||||
|
if(code == null || !code.equalsIgnoreCase(userInfo.getCode())){
|
||||||
|
return ApiResult.error(1,"验证码有误");
|
||||||
|
}
|
||||||
|
userService.signup(userInfo);
|
||||||
|
return ApiResult.success(userInfo);
|
||||||
|
}
|
||||||
|
|
||||||
@RequestMapping("/info")
|
@RequestMapping("/info")
|
||||||
public ApiResult loginUserInfo() {
|
public ApiResult loginUserInfo() {
|
||||||
return ApiResult.success(StpUtil.getSession().get("user"));
|
return ApiResult.success(StpUtil.getSession().get("user"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证邮箱是否已经存在 不存在返回true 存在false
|
||||||
|
*
|
||||||
|
* @param email
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@RequestMapping("/check-email")
|
||||||
|
public ApiResult checkEmail(String email) {
|
||||||
|
return ApiResult.success(
|
||||||
|
userService.getByEmail(email) == null
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequestMapping("/check-account")
|
||||||
|
public ApiResult checkAccount(String account) {
|
||||||
|
return ApiResult.success(
|
||||||
|
userService.getByAccount(account) == null
|
||||||
|
);
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
注册的邮箱验证有2中方案
|
||||||
|
1.填写验证码后才能提交注册(优点:不需要单独的激活业务 缺点:用户不一定能够及时收到正确验证码)
|
||||||
|
-- 需要单独的业务同步发
|
||||||
|
|
||||||
|
2.直接注册 后台发送一封带有验证链接的邮件 (优点: 对于激活的时效性不需要太高(用消息队列 让其他服务发送) 缺点:必须有专门的激活业务,用户可能不会激活)
|
||||||
|
-- 在注册完成后异步发送(推荐使用消息队列)
|
||||||
|
*/
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -11,8 +11,6 @@ public class LoginModel {
|
|||||||
@NotBlank(message = "密码必须填写")
|
@NotBlank(message = "密码必须填写")
|
||||||
private String password;
|
private String password;
|
||||||
// 验证码
|
// 验证码
|
||||||
@NotBlank(message = "验证码必须填写")
|
|
||||||
private String code;
|
private String code;
|
||||||
@NotBlank(message = "验证码必须填写")
|
|
||||||
private String codeId;
|
private String codeId;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,9 @@
|
|||||||
|
package xyz.longicorn.driver.dto;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import xyz.longicorn.driver.pojo.UserInfo;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class LoginUserInfo extends UserInfo {
|
||||||
|
private String code;
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
package xyz.longicorn.driver.pojo;
|
package xyz.longicorn.driver.pojo;
|
||||||
|
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.IdType;
|
||||||
import com.baomidou.mybatisplus.annotation.TableId;
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
@ -17,7 +18,7 @@ import java.util.Date;
|
|||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@Accessors(chain = true)
|
@Accessors(chain = true)
|
||||||
public class UserInfo implements Serializable {
|
public class UserInfo implements Serializable {
|
||||||
|
@TableId(type = IdType.AUTO)
|
||||||
private Integer id;
|
private Integer id;
|
||||||
private String nickname;
|
private String nickname;
|
||||||
private String email;
|
private String email;
|
||||||
|
@ -0,0 +1,46 @@
|
|||||||
|
package xyz.longicorn.driver.service;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.mail.SimpleMailMessage;
|
||||||
|
import org.springframework.mail.javamail.JavaMailSender;
|
||||||
|
import org.springframework.mail.javamail.MimeMessageHelper;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import javax.mail.MessagingException;
|
||||||
|
import javax.mail.internet.MimeMessage;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class EmailService {
|
||||||
|
@Resource
|
||||||
|
private JavaMailSender javaMailSender;
|
||||||
|
@Value("${spring.mail.from}")
|
||||||
|
private String sender;
|
||||||
|
|
||||||
|
public void sendSimple(String email, String text, String subject) {
|
||||||
|
// 简单邮件消息
|
||||||
|
SimpleMailMessage message = new SimpleMailMessage();
|
||||||
|
message.setSubject(subject); // 主题
|
||||||
|
message.setText(text); // 内容
|
||||||
|
message.setTo(email); // 收件人
|
||||||
|
message.setFrom(sender); // 发件人
|
||||||
|
javaMailSender.send(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendHTML(String email, String html, String subject) throws MessagingException {
|
||||||
|
// 创建一个有内容类型的消息
|
||||||
|
MimeMessage message = javaMailSender.createMimeMessage();
|
||||||
|
// 消息内容设置助手
|
||||||
|
MimeMessageHelper helper = new MimeMessageHelper(message, true);
|
||||||
|
// 消息内容
|
||||||
|
helper.setText(html, true); // 设置为html类型
|
||||||
|
// 消息主题
|
||||||
|
helper.setSubject(subject);
|
||||||
|
// 接收者
|
||||||
|
helper.setTo(email);
|
||||||
|
// 发送者
|
||||||
|
helper.setFrom(sender);
|
||||||
|
// 发送
|
||||||
|
javaMailSender.send(message);
|
||||||
|
}
|
||||||
|
}
|
@ -2,6 +2,8 @@ package xyz.longicorn.driver.service;
|
|||||||
|
|
||||||
import cn.dev33.satoken.stp.StpUtil;
|
import cn.dev33.satoken.stp.StpUtil;
|
||||||
import cn.hutool.core.util.IdUtil;
|
import cn.hutool.core.util.IdUtil;
|
||||||
|
import cn.hutool.core.util.RandomUtil;
|
||||||
|
import cn.hutool.crypto.digest.DigestUtil;
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
@ -21,6 +23,24 @@ public class UserService extends ServiceImpl<UserInfoMapper, UserInfo> {
|
|||||||
@Resource
|
@Resource
|
||||||
private LoginUserDao loginUserDao;
|
private LoginUserDao loginUserDao;
|
||||||
|
|
||||||
|
public UserInfo getByAccount(String account) {
|
||||||
|
// 查询数据条件
|
||||||
|
QueryWrapper q = new QueryWrapper();
|
||||||
|
// 只能根据账号查询数据
|
||||||
|
q.eq("account", account);
|
||||||
|
q.last("limit 1");
|
||||||
|
return baseMapper.selectOne(q);
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserInfo getByEmail(String email) {
|
||||||
|
// 查询数据条件
|
||||||
|
QueryWrapper q = new QueryWrapper();
|
||||||
|
// 只能根据账号查询数据
|
||||||
|
q.eq("email", email);
|
||||||
|
q.last("limit 1");
|
||||||
|
return baseMapper.selectOne(q);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 使用用户名和密码登录
|
* 使用用户名和密码登录
|
||||||
*
|
*
|
||||||
@ -29,12 +49,7 @@ public class UserService extends ServiceImpl<UserInfoMapper, UserInfo> {
|
|||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public LoginUser login(String account, String password) {
|
public LoginUser login(String account, String password) {
|
||||||
// 查询数据条件
|
UserInfo userInfo = getByAccount(account);
|
||||||
QueryWrapper q = new QueryWrapper();
|
|
||||||
// 只能根据账号查询数据
|
|
||||||
q.eq("account", account);
|
|
||||||
q.last("limit 1");
|
|
||||||
UserInfo userInfo = baseMapper.selectOne(q);
|
|
||||||
//
|
//
|
||||||
if (userInfo == null) throw BizException.create("用户名不正确");
|
if (userInfo == null) throw BizException.create("用户名不正确");
|
||||||
// 获取密码并验证
|
// 获取密码并验证
|
||||||
@ -46,7 +61,7 @@ public class UserService extends ServiceImpl<UserInfoMapper, UserInfo> {
|
|||||||
LoginUser user = new LoginUser();
|
LoginUser user = new LoginUser();
|
||||||
user.setAccount(account);
|
user.setAccount(account);
|
||||||
StpUtil.login(userInfo.getId()); // 使用用户编号在sa-token 完成登录
|
StpUtil.login(userInfo.getId()); // 使用用户编号在sa-token 完成登录
|
||||||
StpUtil.getSession().set("user",userInfo);
|
StpUtil.getSession().set("user", userInfo);
|
||||||
// 生成接口需要的token
|
// 生成接口需要的token
|
||||||
// user.setToken(IdUtil.fastSimpleUUID()); // 可以使用jwt生成token
|
// user.setToken(IdUtil.fastSimpleUUID()); // 可以使用jwt生成token
|
||||||
user.setToken(StpUtil.getTokenValue());
|
user.setToken(StpUtil.getTokenValue());
|
||||||
@ -54,4 +69,25 @@ public class UserService extends ServiceImpl<UserInfoMapper, UserInfo> {
|
|||||||
user.setUserInfo(userInfo);
|
user.setUserInfo(userInfo);
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public UserInfo signup(UserInfo userInfo) {
|
||||||
|
// 验证数据是否正确
|
||||||
|
if (getByAccount(userInfo.getAccount()) != null) {
|
||||||
|
throw BizException.create("用户名已经存在了");
|
||||||
|
}
|
||||||
|
if (getByEmail(userInfo.getEmail()) != null) {
|
||||||
|
throw BizException.create("邮箱已经存在了");
|
||||||
|
}
|
||||||
|
// 随机的加密盐
|
||||||
|
String salt = RandomUtil.randomString(6);
|
||||||
|
userInfo.setSalt(salt);
|
||||||
|
// 加密密码
|
||||||
|
userInfo.setPassword(
|
||||||
|
DigestUtil.md5Hex(userInfo.getPassword() + salt) // 1次md5加密
|
||||||
|
);
|
||||||
|
this.save(userInfo);
|
||||||
|
//
|
||||||
|
|
||||||
|
return userInfo;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,27 @@
|
|||||||
spring:
|
spring:
|
||||||
application:
|
application:
|
||||||
name: Driver
|
name: Driver
|
||||||
|
mail:
|
||||||
|
# 填写邮箱供应的SMTP地址
|
||||||
|
# smtp.yandex.com
|
||||||
|
host: smtp.qq.com
|
||||||
|
# 用户名
|
||||||
|
username: yaclty@qq.com
|
||||||
|
# 填写密码 / 授权码 Qw!212er
|
||||||
|
password: wojixltcsdlubfii
|
||||||
|
|
||||||
|
from: yaclty@qq.com
|
||||||
|
# 发送服务器的端口
|
||||||
|
port: 465
|
||||||
|
# 配置连接属性 主要是加密传输
|
||||||
|
properties:
|
||||||
|
mail:
|
||||||
|
smtp:
|
||||||
|
auth: true
|
||||||
|
starttls:
|
||||||
|
enable: true
|
||||||
|
ssl:
|
||||||
|
enable: true
|
||||||
# 数据库
|
# 数据库
|
||||||
datasource:
|
datasource:
|
||||||
name: defaultDataSource
|
name: defaultDataSource
|
||||||
@ -73,3 +94,6 @@ mybatis-plus:
|
|||||||
type-enums-package: xyz.longicorn.driver.pojo.enums
|
type-enums-package: xyz.longicorn.driver.pojo.enums
|
||||||
sa-token:
|
sa-token:
|
||||||
token-name: Authorization
|
token-name: Authorization
|
||||||
|
|
||||||
|
application:
|
||||||
|
email-content: 本次操作的验证码为:<b style="font-size:16px;color:red">$code</b>,验证码的有效期为3分钟
|
@ -96,9 +96,14 @@
|
|||||||
<el-button :loading="loginLoading" style="width: 100%" type="primary" @click="submitForm">登录
|
<el-button :loading="loginLoading" style="width: 100%" type="primary" @click="submitForm">登录
|
||||||
</el-button>
|
</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<div class="forget-tips">
|
<div class="forget-tips" style="overflow:hidden;">
|
||||||
|
<div style="float: left">
|
||||||
<router-link class="reset-link" to="/forget">密码忘记了,点我重置密码!</router-link>
|
<router-link class="reset-link" to="/forget">密码忘记了,点我重置密码!</router-link>
|
||||||
</div>
|
</div>
|
||||||
|
<div style="float: right">
|
||||||
|
<router-link class="reset-link" to="/reg">注册账号</router-link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</el-form>
|
</el-form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -34,6 +34,7 @@ export default {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
logout(){
|
logout(){
|
||||||
|
this.$store.commit('clearToken')
|
||||||
this.$router.replace('/login');
|
this.$router.replace('/login');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,245 @@
|
|||||||
|
<style scoped>
|
||||||
|
#page-login {
|
||||||
|
height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
background-color: pink;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-wrapper {
|
||||||
|
width: 800px;
|
||||||
|
margin: auto;
|
||||||
|
background-color: #fff;
|
||||||
|
padding: 30px;
|
||||||
|
border-radius: 5px;
|
||||||
|
box-shadow: 0 0 5px rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.left-picture {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
border-right: dashed 1px #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo-icon {
|
||||||
|
width: 300px;
|
||||||
|
height: 300px;
|
||||||
|
margin: auto;
|
||||||
|
fill: #999999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-form {
|
||||||
|
width: 350px;
|
||||||
|
padding: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-item {
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.forget-tips {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reset-link {
|
||||||
|
text-decoration: none;
|
||||||
|
color: #999999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-code-item {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-send {
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
<template>
|
<template>
|
||||||
<h1>Reg</h1>
|
<div id="page-login">
|
||||||
|
<div class="login-wrapper d-flex">
|
||||||
|
<div class="flex-1 left-picture">
|
||||||
|
<svg class="logo-icon" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink" width="1000" height="1000">
|
||||||
|
<path
|
||||||
|
d="M549.766 97.925c19.974 4.609 50.703 35.339 62.995 55.312 35.339-13.828 36.875 41.484 46.094 55.312 23.047-16.901 46.094-35.339 46.094-55.312 0-21.51-30.729-35.339-30.729-41.484 0-12.292 38.411-12.292 41.484-12.292 50.703 0 106.016 39.948 106.016 96.797 0 27.656-9.219 41.484-33.802 70.677h32.266c21.51 0 27.656-3.073 55.312 7.682C923.125 294.591 960 348.367 960 389.852c0 3.073 0 9.219-1.536 13.828-4.609 3.073-12.292 3.073-16.901 4.609-15.365 1.536-32.266 6.146-47.63 7.682-47.63 0-50.703 0-53.776-1.536-1.536 1.536-1.536 0-1.536 4.609 0 3.073 7.682 44.557 10.755 67.604 9.219 56.849 12.292 115.234 19.974 173.62 1.536 7.682 7.682 15.365 9.219 23.047 3.073 13.828 6.146 27.656 6.146 39.948 0 153.646-245.833 208.958-321.119 208.958h-86.042c-115.234-9.219-248.906-52.24-301.145-138.281-4.609-7.682-18.437-39.948-18.437-50.703v-36.875c3.073-16.901 7.682-33.802 21.51-50.703v-84.505l12.292-132.135c-12.292 1.536-33.802 1.536-38.411 1.536-21.51 0-38.411-3.073-61.458-6.146-7.682-1.536-18.437-3.073-24.583-6.146-4.609-1.536-3.073-12.292-3.073-13.828 0-44.557 44.557-107.552 98.333-119.844 4.609-1.536 13.828-1.536 19.974-3.073l41.484-1.536c-13.828-10.755-36.875-46.094-36.875-59.922v-29.193c13.828-58.385 62.995-82.969 106.016-82.969 1.536 0 41.484 0 41.484 12.292 0 6.146-30.729 19.974-30.729 41.484 0 1.536 6.146 32.266 16.901 32.266 3.073 0-1.536-3.073 3.073-3.073 3.073 0 32.266 10.755 36.875 10.755h12.292l3.073-3.073c-19.974-16.901-30.729-36.875-36.875-53.776 12.292 7.682 18.438 9.219 29.193 9.219 27.656 0 46.094-15.365 78.359-29.193 24.583-10.755 52.24-13.828 78.359-18.437-12.292-9.219-24.583-16.901-36.875-23.047l38.411-1.536c7.68 1.537 15.362 4.61 23.044 6.146z"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div class="login-form">
|
||||||
|
<h1 class="title">天牛网盘</h1>
|
||||||
|
<el-form
|
||||||
|
ref="form"
|
||||||
|
:model="regForm"
|
||||||
|
:rules="rules"
|
||||||
|
status-icon
|
||||||
|
size="large"
|
||||||
|
@keyup.enter="submitForm"
|
||||||
|
>
|
||||||
|
<el-form-item label="" prop="nickname" class="input-item">
|
||||||
|
<el-input v-model="regForm.nickname" placeholder="输入昵称" autocomplete="off"/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="" prop="email" class="input-item">
|
||||||
|
<el-input v-model="regForm.email" placeholder="输入邮箱" autocomplete="off"/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="" prop="account" class="input-item">
|
||||||
|
<el-input v-model="regForm.account" placeholder="输入用户名" autocomplete="off"/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="" prop="password" class="input-item">
|
||||||
|
<el-input v-model="regForm.password" placeholder="输入登录密码" type="password" autocomplete="off"/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="" prop="password2" class="input-item">
|
||||||
|
<el-input v-model="regForm.password2" placeholder="再次输入登录密码" type="password"
|
||||||
|
autocomplete="off"/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="" prop="code" class="input-item input-code-item">
|
||||||
|
<div style="display: flex;width: 100%">
|
||||||
|
<div style="flex:1">
|
||||||
|
<el-input v-model="regForm.code" placeholder="验证码" autocomplete="off"/>
|
||||||
|
</div>
|
||||||
|
<el-button :loading="sendCodeData.sending" :disabled="sendCodeData.countdown > 0" class="btn-send" @click="sendCode">
|
||||||
|
{{ sendCodeData.countdown == 0 ? '发送验证码' : ('剩余' + sendCodeData.countdown + '秒') }}
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button :loading="loginLoading" style="width: 100%" type="primary" @click="submitForm">立即注册
|
||||||
|
</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
<div class="forget-tips" style="overflow:hidden;">
|
||||||
|
<router-link class="reset-link" replace to="/login">返回登录</router-link>
|
||||||
|
</div>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
import {defineComponent, onMounted, reactive, ref} from 'vue'
|
||||||
name: "Reg.vue"
|
import {ElMessage, ElMessageBox} from "element-plus";
|
||||||
}
|
import {useRouter} from 'vue-router'
|
||||||
|
import {useStore} from "vuex";
|
||||||
|
import api from "../service/api";
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'Reg',
|
||||||
|
setup() {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {Ref<FormInstance>}
|
||||||
|
*/
|
||||||
|
const form = ref(null);
|
||||||
|
// 使用reactive将对象转换成响应对象
|
||||||
|
const regForm = reactive({
|
||||||
|
password: '',
|
||||||
|
account: '',
|
||||||
|
nickname: '',
|
||||||
|
email: '',
|
||||||
|
password2: '',
|
||||||
|
code: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
const checkPassword = (rule, value, callback) => {
|
||||||
|
if (!value) callback(Error('请填写密码')) // 验证不通过
|
||||||
|
else callback() // 验证通过
|
||||||
|
}
|
||||||
|
const emailValidator = (rule, value, callback) => {
|
||||||
|
if (!value) callback(Error('邮箱不允许为空'))
|
||||||
|
else if (!/^([a-zA-Z]|[0-9])(\w|\-)+@[a-zA-Z0-9]+\.([a-zA-Z]{2,4})$/.test(value)) callback(Error('邮箱格式不正确'))
|
||||||
|
else {
|
||||||
|
api.user.checkEmail(value).then(b => {
|
||||||
|
if (b) callback()
|
||||||
|
else callback(Error('邮箱已被占用,请换一个'))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const checkPassword2 = (rule, value, callback) => {
|
||||||
|
if (!value) callback(Error('请填写密码')) // 验证不通过
|
||||||
|
else if (value != regForm.password) callback(Error('密码不一致')) // 验证不通过
|
||||||
|
else callback() // 验证通过
|
||||||
|
}
|
||||||
|
const rules = reactive({
|
||||||
|
account: [
|
||||||
|
{required: true, message: '请填写用户名称', trigger: 'blur'}
|
||||||
|
],
|
||||||
|
password: [
|
||||||
|
{validator: checkPassword, trigger: 'blur'}
|
||||||
|
],
|
||||||
|
password2: [
|
||||||
|
{validator: checkPassword2, trigger: 'blur'}
|
||||||
|
],
|
||||||
|
email: [
|
||||||
|
{validator: emailValidator, trigger: 'blur'}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
const router = useRouter(); // 获取路由对象
|
||||||
|
const store = useStore();
|
||||||
|
const regLoading = ref(false)
|
||||||
|
const submitForm = () => {
|
||||||
|
if (regLoading.value) return;
|
||||||
|
// 验证表单
|
||||||
|
form.value.validate(async (isValid) => {
|
||||||
|
if (isValid) {
|
||||||
|
// 设置loading未true
|
||||||
|
regLoading.value = true
|
||||||
|
try {
|
||||||
|
await api.user.signup(regForm)
|
||||||
|
ElMessage.success('注册成功')
|
||||||
|
router.replace('/login').then().catch();
|
||||||
|
} catch (e) {
|
||||||
|
ElMessage.error(e.message || '登录错误')
|
||||||
|
} finally {
|
||||||
|
regLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
const sendCodeData = reactive({
|
||||||
|
sending: false, // 是否在正在发送
|
||||||
|
countdown: 0 // 倒计时
|
||||||
|
})
|
||||||
|
let timer = null
|
||||||
|
const startCountDown = (time) => {
|
||||||
|
sendCodeData.countdown = time
|
||||||
|
if (timer) {
|
||||||
|
clearInterval(timer)
|
||||||
|
}
|
||||||
|
timer = setInterval(() => {
|
||||||
|
sendCodeData.countdown--
|
||||||
|
if (sendCodeData.countdown == 0) {
|
||||||
|
// 停止倒计时
|
||||||
|
clearInterval(timer)
|
||||||
|
timer = null;
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
console.log('开始倒计时')
|
||||||
|
}
|
||||||
|
// 发送验证码
|
||||||
|
const sendCode = async () => {
|
||||||
|
if (regForm.email) {
|
||||||
|
sendCodeData.sending = true
|
||||||
|
try {
|
||||||
|
await api.sendCode(regForm.email)
|
||||||
|
// 应该禁用发送操作按钮
|
||||||
|
ElMessage.info('发送验证码成功')
|
||||||
|
startCountDown(60) // 60秒倒计时
|
||||||
|
} catch (e) {
|
||||||
|
throw e;
|
||||||
|
console.log(e)
|
||||||
|
} finally {
|
||||||
|
sendCodeData.sending = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
regForm, submitForm, rules, form, loginLoading: regLoading, sendCode, sendCodeData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
|
|
||||||
</style>
|
|
@ -5,13 +5,16 @@ import {
|
|||||||
import router from "../router";
|
import router from "../router";
|
||||||
|
|
||||||
const API_PATH = "http://localhost:8080"
|
const API_PATH = "http://localhost:8080"
|
||||||
class BizError extends Error{
|
|
||||||
|
class BizError extends Error {
|
||||||
code = -1;
|
code = -1;
|
||||||
constructor(message,code = -1) {
|
|
||||||
|
constructor(message, code = -1) {
|
||||||
super(message);
|
super(message);
|
||||||
this.code = code;
|
this.code = code;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param api
|
* @param api
|
||||||
@ -77,7 +80,7 @@ function request(api, method = 'GET', postData = {}, progressChange = null) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// 其他异常 直接抛出错误
|
// 其他异常 直接抛出错误
|
||||||
reject(new BizError(result.message,result.code))
|
reject(new BizError(result.message, result.code))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fetch(API_PATH + api, options)
|
fetch(API_PATH + api, options)
|
||||||
@ -98,8 +101,14 @@ export default {
|
|||||||
signup(params) {
|
signup(params) {
|
||||||
return request('/api/user/reg', 'POST', params);
|
return request('/api/user/reg', 'POST', params);
|
||||||
},
|
},
|
||||||
info(){
|
info() {
|
||||||
return request('/api/user/info');
|
return request('/api/user/info');
|
||||||
|
},
|
||||||
|
checkEmail(email) {
|
||||||
|
return request('/api/user/check-email', 'GET', {email});
|
||||||
|
},
|
||||||
|
checkAccount(account) {
|
||||||
|
return request('/api/user/check-account', 'GET', {account});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
folder: {
|
folder: {
|
||||||
@ -176,7 +185,10 @@ export default {
|
|||||||
return request('/api/share/create', 'POST', info)
|
return request('/api/share/create', 'POST', info)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
code(){
|
code() {
|
||||||
return request('/create');
|
return request('/create');
|
||||||
|
},
|
||||||
|
sendCode(email) {
|
||||||
|
return request('/send-code','GET',{email});
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user