介绍
vue-element-admin 是一个后台前端解决方案,它基于 vue 和 element-ui实现。它使用了最新的前端技术栈,内置了 i18 国际化解决方案,动态路由,权限验证,提炼了典型的业务模型,提供了丰富的功能组件,它可以帮助你快速搭建企业级中后台产品原型。相信不管你的需求是什么,本项目都能帮助到你。
目录
介绍1. 后端用户服务1.1 数据库1.2 创建JavaBean1.3 用户服务环境1.4 用户服务基本模块
2. MQ服务3. 用户登录3.0 取消登录表单校验3.1 发送验证码邮件3.1.1 前端:显示表单3.1.2 前端:发送邮件3.1.3 完善 EduUser3.1.4 用于封装与MQ交互的数据3.1.5 编写Rabbit配置类3.1.6 后端实现
3.2 编写MQ:发送邮件3.2.1 拷贝配置类3.2.2 监听器发送邮件3.2.3 邮件总结
3.3 后端:用户登录3.3.0 前端登录流程3.3.1 修改前端api3.3.2 修改前端请求数据3.3.3 后端实现3.3.4 前端修改3.3.5 数据要求 admin-token
3.4 登录成功后查询权限3.4.1 分析3.4.2 前端完善3.4.3 后端实现
3.5 总结
4. 用户注册4.0 整体流程4.1 图片验证码4.2 后端实现4.2.1 完善EduUser4.2.2 service实现4.3.3 controller
4.3 前端实现4.3.1 显示页面4.3.2 前端 api4.3.3 注册页面4.3.4 注册功能
5 整合JWT5.0 概述5.0 分析5.1 搭建环境5.1.1 拷贝坐标(已有)5.1.2 复制yml配置5.1.3 拷贝封装类5.1.4 拷贝工具类5.1.5 生成秘钥(可选)
5.2 登录成功:生成token5.3 查询详情:获得token5.3.1 基本流程5.3.2 查询详情
5.4 过滤器5.4.1 配置yml5.4.2 pom和工具类5.4.3 配置类5.4.4 过滤器
5.5 token 无效
6 激活6.1 分析6.2 完善用户注册6.3 用户激活6.3.1 前端实现6.3.2 后端实现
6.4 完善用户登录
7. 退出登录end
1. 后端用户服务
1.1 数据库
CREATE DATABASE zx_edu_user;
USE zx_edu_user;
CREATE TABLE `edu_user` (
`id` INT(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
`username` VARCHAR(50) DEFAULT NULL COMMENT '用户名',
`password` VARCHAR(255) DEFAULT NULL COMMENT '用户密码',
`phone` CHAR(11) DEFAULT NULL COMMENT '电话',
`email` VARCHAR(50) DEFAULT NULL COMMENT '邮箱',
`roles` VARCHAR(30) DEFAULT NULL COMMENT '角色,多个值使用逗号分隔,例如:admin,editor',
`created` DATE DEFAULT NULL
) ;
INSERT INTO `edu_user` VALUES (1, 'jack', '1234', '13699282444', '[email protected]', 'admin', '2015-10-20');
INSERT INTO `edu_user` VALUES (2, 'rose', '1234', '13377776666', '[email protected]', 'editor', NULL);
INSERT INTO `edu_user` VALUES (3, 'tom', '1234', '15533336666', '[email protected]', 'admin,editor', '2020-02-14');
1.2 创建JavaBean
![JavaBean](https://img-blog.csdnimg.cn/e166701d68434c4289ac8c759b22a54c.png)
package com.czxy.zx.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Date;
/**
* @author txt
* @email [email protected]
*/
@Data
//@TableName("edu_user")
@ApiModel(value = "EduUser对象",description = "用户")
public class EduUser {
@TableId(value="id" , type = IdType.AUTO)
private Integer id;
//用户名
@ApiModelProperty("用户名")
private String username;
//用户密码
@ApiModelProperty("用户密码")
private String password;
//电话
@ApiModelProperty("电话")
private String phone;
//邮箱
@ApiModelProperty("邮箱")
private String email;
//角色,多个值使用逗号分隔,例如:admin,editor
@ApiModelProperty("角色,多个值使用逗号分隔,例如:admin,editor")
private String roles;
//创建时间
@ApiModelProperty("创建时间")
private Date created;
//状态:0 未激活、1已激活
@ApiModelProperty("状态:0 未激活、1已激活")
private String status;
@TableField(exist = false)
@ApiModelProperty("验证码")
private String verifycode;
@TableField(exist = false)
@ApiModelProperty("重复密码")
private String repassword;
}
1.3 用户服务环境
创建项目:zx-service-user31 修改pom文件,添加坐标
zx-parent31
com.czxy.zx
1.0-SNAPSHOT
4.0.0
zx-service-user31
org.springframework.boot
spring-boot-starter-web
com.alibaba.nacos
nacos-client
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-discovery
io.springfox
springfox-swagger2
io.springfox
springfox-swagger-ui
org.springframework.cloud
spring-cloud-starter-openfeign
org.springframework.boot
spring-boot-starter-test
com.baomidou
mybatis-plus-boot-starter
${mybatis.plus.version}
mysql
mysql-connector-java
com.czxy.zx
zx-common31
com.czxy.zx
zx-domain31
org.springframework.boot
spring-boot-starter-data-redis
org.springframework.boot
spring-boot-starter-mail
org.springframework.boot
spring-boot-starter-amqp
com.alibaba
fastjson
org.springframework.boot
spring-boot-devtools
true
创建yml文件 # 服务端口号
server:
port: 9010
# 服务名
spring:
application:
name: user-service
datasource:
driverClassName: com.mysql.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/zx_edu_user?useUnicode=true&characterEncoding=utf8
username: root
password: 1234
druid: #druid 连接池配置
initial-size: 1 #初始化连接池大小
min-idle: 1 #最小连接数
max-active: 20 #最大连接数
test-on-borrow: true #获取连接时候验证,会影响性能
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848 #nacos服务地址
redis:
database: 0 #数据库索引,取值0-15,表示16个库可选择
host: 127.0.0.1 #服务器地址
port: 6379 #服务器连接端口号
mail:
host: smtp.126.com #发送邮件服务器
username: [email protected] #账号
password: 1qaz2wsx #密码
default-encoding: UTF-8 #默认编码时
rabbitmq:
host: 127.0.0.1
port: 5672
username: guest
password: guest
virtualHost: /
devtools:
restart:
enabled: true #设置开启热部署
additional-paths: src/main/java #重启目录
exclude: WEB-INF/**
freemarker:
cache: false #页面不加载缓存,修改即时生效
#开启log4j打印SQL语句
logging:
level:
com:
czxy:
zx:
user:
mapper: debug
# mp日志打印
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
db-config:
logic-delete-value: 1
logic-not-delete-value: 0
启动类
![启动类](https://img-blog.csdnimg.cn/9621accd6787482e8a36218fba82a38e.png)
package com.czxy.cz;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
/**
* @author txt
* @email [email protected]
*/
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class UserServiceApplication {
public static void main(String[] args) {
SpringApplication.run(UserServiceApplication.class,args);
}
}
1.4 用户服务基本模块
![](https://img-blog.csdnimg.cn/cad79860fe564d009dc87c623513426a.png)
拷贝配置类
MyBatisPlusConfig.javaSwagger2ConfigurationV3.java 编写mapper package com.czxy.zx.user.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.czxy.zx.domain.EduUser;
import org.apache.ibatis.annotations.Mapper;
/**
* @author txt
* @email [email protected]
*/
@Mapper
public interface EduUserMapper extends BaseMapper {
}
编写service接口 package com.czxy.zx.user.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.czxy.zx.domain.EduUser;
/**
* @author txt
* @email [email protected]
*/
public interface EduUserService extends IService {
}
编写service实现类 package com.czxy.zx.user.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.czxy.zx.domain.EduUser;
import com.czxy.zx.user.mapper.EduUserMapper;
import com.czxy.zx.user.service.EduUserService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
* @author txt
* @email [email protected]
*/
@Service
@Transactional
public class EduUserServiceImpl extends ServiceImpl implements EduUserService {
}
编写controller package com.czxy.zx.user.controller;
import com.czxy.zx.user.service.EduUserService;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
/**
* @author txt
* @email [email protected]
*/
@RestController
@RequestMapping("/user")
public class EduUserController {
@Resource
private EduUserService eduUserService;
}
2. MQ服务
![](https://img-blog.csdnimg.cn/c8faf0d45d654067a01c70cf42457ed9.png)
创建项目: zx-mq31 编写pom文件
org.springframework.boot
spring-boot-starter-amqp
org.springframework.boot
spring-boot-starter-mail
com.alibaba
fastjson
com.czxy.zx
zx-common31
编写yml文件 # 服务端口号
server:
port: 8900
# 服务名
spring:
application:
name: mq-service
mail:
host: smtp.126.com #发送邮件服务器
username: [email protected] #账号
password: 1qaz2wsx #密码
default-encoding: UTF-8 #默认编码时
rabbitmq:
host: 127.0.0.1
port: 5672
username: guest
password: guest
virtualHost: /
拷贝工具类 ![工具类](https://img-blog.csdnimg.cn/037c018ac0b94945b2927d76d295a5ad.png) package com.czxy.zx.utils;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import javax.mail.internet.MimeMessage;
/**
* @author txt
* @email [email protected]
*/
public class EmailUtils {
/**
* 发送邮件
* @param javaMailSender
* @param title 标题
* @param email
* @param text
*/
public static void sendEmail(JavaMailSender javaMailSender, String title , String email, String text) {
try {
MimeMessage mimeMessage = javaMailSender.createMimeMessage();
MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(mimeMessage,true);
// a. 发件人
mimeMessageHelper.setFrom("[email protected]");
// b. 收件人
mimeMessageHelper.setTo(email);
// c. 主题
mimeMessageHelper.setSubject(title);
// d. 正文
mimeMessageHelper.setText(text, true);
javaMailSender.send(mimeMessage);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e.getMessage());
}
}
}
编写启动类 package com.czxy.zx;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @author txt
* @email [email protected]
*/
@SpringBootApplication
public class MQApplication {
public static void main(String[] args) {
SpringApplication.run(MQApplication.class,args);
}
}
3. 用户登录
element ui admin 执行流程图
![](https://img-blog.csdnimg.cn/289a694dd70f4f458312e59c10b6cbc7.png)
3.0 取消登录表单校验
![登录表单校验](https://img-blog.csdnimg.cn/fab66af7c33541c69fe4e732e9902366.png)
3.1 发送验证码邮件
![](https://img-blog.csdnimg.cn/15ba705a2ede4ea2a77fc671c65b7b00.png)
3.1.1 前端:显示表单
登录页面:@/views/login/index.vue
![显示表单](https://img-blog.csdnimg.cn/0e88885f012c4736abd82b336c39056f.png)
发送
3.1.2 前端:发送邮件
编写API,发送邮件 ![发送邮件](https://img-blog.csdnimg.cn/280037e55f024c4c956cd04ba760cb8c.png) export function send(user) {
return axios.post('/user-service/user/sendemail',user);
}
调用 ![调用](https://img-blog.csdnimg.cn/87bb395c3d544aeab873adce27481c51.png) async sendEmail() {
let { message } = await send( this.loginForm)
this.$message.success( message )
}
3.1.3 完善 EduUser
用于封装表单提交的用户数据
package com.czxy.zx.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel;
import lombok.Data;
import java.util.Date;
/**
* @author txt
* @email [email protected]
*/
@Data
//@TableName("edu_user")
@ApiModel(value = "EduUser对象",description = "用户")
public class EduUser {
@TableId(value="id" , type = IdType.AUTO)
private Integer id;
//用户名
private String username;
//用户密码
private String password;
//电话
private String phone;
//邮箱
private String email;
//角色,多个值使用逗号分隔,例如:admin,editor
private String roles;
//创建时间
private Date created;
//状态:0 未激活、1已激活
private String status;
@TableField(exist = false)
private String verifycode;
}
3.1.4 用于封装与MQ交互的数据
![](https://img-blog.csdnimg.cn/541a7e566113439f8e484d913cb2f0d7.png)
package com.czxy.zx.vo;
import lombok.Data;
/**
* @author txt
* @email [email protected]
*/
@Data
public class UserEmail {
private String username;
private String email;
private String text;
}
3.1.5 编写Rabbit配置类
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Fy7yeYPN-1664327744645)(assets/image-20220425235913800.png)]
package com.czxy.zx.user.config;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
/**
* @author txt
* @email [email protected]
*/
@Component
public class RabbitEmailConfig {
// 队列的名称
public static final String QUEUE_NAME = "zx-email";
@Bean
public Queue queue() {
return new Queue(QUEUE_NAME);
}
}
3.1.6 后端实现
/**
* @author txt
* @email [email protected]
*/
@RestController
@RequestMapping("/user")
public class EduUserController {
@Resource
private EduUserService eduUserService;
@Resource
private StringRedisTemplate stringRedisTemplate;
@Resource
private RabbitTemplate rabbitTemplate;
@PostMapping("/sendemail")
public BaseResult sendemail(@RequestBody EduUser eduUser) {
//1 随机字符串
Random random = new Random();
// [0,8999) --> [1000,9999)
int randomNumber = random.nextInt(8999) + 1000;
//2 发送redis一份
stringRedisTemplate.opsForValue().set("login" + eduUser.getUsername(), randomNumber + "");
//3 mq存放
UserEmail userEmail = new UserEmail();
userEmail.setUsername(eduUser.getUsername());
userEmail.setEmail(eduUser.getEmail());
userEmail.setText("登录验证码是:" + randomNumber);
String userEmailStr = JSONObject.toJSONString(userEmail);
rabbitTemplate.convertAndSend(RabbitEmailConfig.QUEUE_NAME , userEmailStr);
return BaseResult.ok("发送中,请查收");
}
3.2 编写MQ:发送邮件
![](https://img-blog.csdnimg.cn/0cc11cb6e5e749eb83448c79c563f4f4.png)
3.2.1 拷贝配置类
与登录模块使用的配置类相同
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PvpLZghg-1664327744646)(assets/image-20220425235944490.png)]
package com.czxy.zx.config;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
/**
* @author txt
* @email [email protected]
*/
@Component
public class RabbitEmailConfig {
// 队列的名称
public static final String QUEUE_NAME = "zx-email";
@Bean
public Queue queue() {
return new Queue(QUEUE_NAME);
}
}
3.2.2 监听器发送邮件
![](https://img-blog.csdnimg.cn/7755d382c43d4189906eee914aeb2229.png)
package com.czxy.zx.listener;
import com.alibaba.fastjson.JSONObject;
import com.czxy.zx.config.RabbitEmailConfig;
import com.czxy.zx.utils.EmailUtils;
import com.czxy.zx.vo.UserEmail;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* @author txt
* @email [email protected]
*/
@Component
public class RabbitEmailListener {
@Resource
private JavaMailSender javaMailSender;
@RabbitListener(queues = RabbitEmailConfig.QUEUE_NAME)
public void sendEmail(String message) {
//1 将消息转换成 UserEmail
UserEmail userEmail = JSONObject.parseObject(message, UserEmail.class);
//2 发送邮件,如果邮箱为空,将出现循环异常
if(userEmail.getEmail() != null) { EmailUtils.sendEmail(javaMailSender,userEmail.getEmail(),userEmail.getText());
}
}
}
3.2.3 邮件总结
邮件操作常见的协议:
SMTP ( Simple Mail Transfer Protocol )简单邮件传输协议 ,用于进行邮件发送的协议。POP3 ( Post Office Protocol - Version 3 ) 邮局协议版本3 ,用于进行邮件接收的。 ![](https://img-blog.csdnimg.cn/40d91ad3b8244c44bd8501285c2c53cd.png) 每一个邮件服务提供商,都会明确自己每一个服务器的地址 ![](https://img-blog.csdnimg.cn/4cf95ed3ea644f43b9cef9fb3519a6f1.png) yml配置 ![](https://img-blog.csdnimg.cn/366d36153896458c8efb18fcf7c75bc6.png) 发送邮件:
发件人:from收件人:to (扩展:cc抄送、bcc暗送/密送)主题:subject正文:content/text
3.3 后端:用户登录
3.3.0 前端登录流程
步骤一:登录页面 @/views/login/index.vue ![](https://img-blog.csdnimg.cn/c9e3b56fb72f490f88e2cebb89ee3d6f.png) 步骤二:登录调用 vuex ![](https://img-blog.csdnimg.cn/34f9093f24ed4f36bca0fedb7dc2c233.png) 步骤三:查看vuex
将用户的完整信息,传递给ajax方法 ![](https://img-blog.csdnimg.cn/b65914358c6f49ca926a82739790cb37.png) 步骤四:调用ajax ![](https://img-blog.csdnimg.cn/2ab9c318259f48cd9acac0ee1c8ca282.png) 步骤五:确定ajax发送位置 ![](https://img-blog.csdnimg.cn/4cd6d694918d44c8a3e2a1323859861d.png)
3.3.1 修改前端api
export function login(user) {
// 真实数据
return axios.post('/user-service/user/login',user);
// 临时模拟
// return axios.post('/teacher-service/user/login',user);
}
3.3.2 修改前端请求数据
![](https://img-blog.csdnimg.cn/2ca0b674c32041a4a544916623cf1707.png)
3.3.3 后端实现
EduUserController 添加 login 方法
package com.czxy.zx.user.controller;
import com.alibaba.fastjson.JSON;
import com.czxy.zx.domain.EduUser;
import com.czxy.zx.user.config.RabbitEmailConfig;
import com.czxy.zx.user.service.EduUserService;
import com.czxy.zx.vo.BaseResult;
import com.czxy.zx.vo.UserEmail;
import org.apache.commons.lang3.RandomUtils;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
/**
* @author txt
* @email [email protected]
*/
@RestController
@RequestMapping("/user")
public class EduUserController {
@Resource
private EduUserService eduUserService;
@Resource
private RabbitTemplate rabbitTemplate;
@Resource
private StringRedisTemplate stringRedisTemplate;
@PostMapping("/send")
public BaseResult send(@RequestBody EduUser eduUser) {
//1 随机生成验证码
int num = RandomUtils.nextInt(1000, 10000);
//2 封装发送邮件的数据--
UserEmail userEmail = new UserEmail();
userEmail.setEmail(eduUser.getEmail());
userEmail.setTitle("用户登录验证码");
userEmail.setText(eduUser.getUsername() + "您好,本次验证码:" + num);
//3 发送邮件-将邮件信息存放mq
String jsonStr = JSON.toJSONString(userEmail);
rabbitTemplate.convertAndSend("", RabbitEmailConfig.QUEUE_NAME, jsonStr);
//4 将验证码存放redis
String redisName = "login_verify_code_" + eduUser.getUsername();
stringRedisTemplate.opsForValue().set(redisName, num + "");
//5 提示
return BaseResult.ok("验证码发送成功!");
}
@PostMapping("/login")
public BaseResult login(@RequestBody EduUser eduUser) {
//1 校验验证码
// 1.1 获得redis
String redisName = "login_verify_code_" + eduUser.getUsername();
String redisVerifyCode = stringRedisTemplate.opsForValue().get(redisName);
// 1.2 删除redis
stringRedisTemplate.delete(redisName);
// 1.3 校验:无效
if(redisVerifyCode == null) {
return BaseResult.error("验证码无效");
}
// 1.4 校验:错误
if(!redisVerifyCode.equalsIgnoreCase(eduUser.getVerifycode())) {
return BaseResult.error("验证码错误");
}
//2 通过service用户登录
EduUser loginUser = eduUserService.login(eduUser);
//3 提示
if(loginUser != null) {
// 需要设置token
String token = "admin-token";
return BaseResult.ok("登录成功").append("token", token);
}
return BaseResult.error("用户名或密码不匹配");
}
}
3.3.4 前端修改
![](https://img-blog.csdnimg.cn/bbfc6a0887504e96bf580e4b35821e85.png)
3.3.5 数据要求 admin-token
![](https://img-blog.csdnimg.cn/d1628b4208ea469186cae39d4a246f70.png)
登录成功后,查询用户详情的使用 ![](https://img-blog.csdnimg.cn/4e8ad5c27fa448f494f0668ad32627d9.png)
3.4 登录成功后查询权限
3.4.1 分析
登录时,返回一个固定的字符串:admin-token 或 editor-token 登录成功后,查询用户详情将携带固定字符串 ![](https://img-blog.csdnimg.cn/388243b4b9644107962b13a6b3dea77c.png) 根据固定字符串查询用户的权限,并返回固定的信息: {
roles: ['admin'], //用户角色 或 [editor] 或 [admin,editor]
avatar: '', //头像图片地址
name: '', //用户名
}
![](https://img-blog.csdnimg.cn/cd1fb95c366445f98ddaccac1cd17eea.png)
查询详情ajax调用时机 ![](https://img-blog.csdnimg.cn/4999c0ab53fe4a2f924ade2abd76cdda.png)
3.4.2 前端完善
![](https://img-blog.csdnimg.cn/f6fae04738e64e20827f9f8fc4252195.png)
3.4.3 后端实现
完善 EduUserController,添加查询详情功能 /**
* 查询详情
* @param token
* @return
*/
@GetMapping("/info")
public BaseResult info(String token) {
System.out.println(token);
Map map = new HashMap();
// 根据固定字符串拼凑数据
if("admin-token".equals(token)) {
map.put("roles", Arrays.asList("admin"));
map.put("avatar","https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif");
map.put("name","Super Admin");
} else {
map.put("roles", Arrays.asList("editor"));
map.put("avatar","https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif");
map.put("name","Normal Editor");
}
return BaseResult.ok("成功", map);
}
3.5 总结
![](https://img-blog.csdnimg.cn/797e9d80700b4b739b4bbd78fbb2a8ad.png)
4. 用户注册
4.0 整体流程
示意图
![](https://img-blog.csdnimg.cn/58f508d8a1f442c1946b02c30259dd19.png)
4.1 图片验证码
package com.czxy.zx.user.controller;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.annotation.Resource;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;
import java.util.concurrent.TimeUnit;
/**
* Created by tanxintong.
*/
@Controller
@RequestMapping("/verifycode")
public class VerifyCodeController {
@Resource
private StringRedisTemplate stringRedisTemplate;
// /verifycode/jack
@GetMapping("/{username}")
public void verifyCode(@PathVariable("username") String username , HttpServletResponse response ) throws IOException {
//字体只显示大写,去掉了1,0,i,o几个容易混淆的字符
String VERIFY_CODES = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ";
int IMG_WIDTH = 72;
int IMG_HEIGTH = 27;
Random random = new Random();
//创建图片
BufferedImage image = new BufferedImage(IMG_WIDTH, IMG_HEIGTH, BufferedImage.TYPE_INT_RGB);
//画板
Graphics g = image.getGraphics();
//填充背景
g.setColor(Color.WHITE);
g.fillRect(1,1,IMG_WIDTH-2,IMG_HEIGTH-2);
g.setFont(new Font("楷体",Font.BOLD,25));
StringBuilder sb = new StringBuilder();
//写字
for(int i = 1 ; i
//随机颜色
g.setColor(new Color(random.nextInt(255),random.nextInt(255),random.nextInt(255)));
int x = random.nextInt(IMG_WIDTH - 1);
int y = random.nextInt(IMG_HEIGTH - 1);
int x1 = random.nextInt(12) + 1;
int y1 = random.nextInt(6) + 1;
g.drawLine(x, y, x - x1, y - y1);
}
//响应到浏览器
ImageIO.write(image,"jpeg", response.getOutputStream());
}
}
4.2 后端实现
4.2.1 完善EduUser
package com.czxy.zx.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel;
import lombok.Data;
import java.util.Date;
/**
* @author txt
* @email [email protected]
*/
@Data
//@TableName("edu_user")
@ApiModel(value = "EduUser对象",description = "用户")
public class EduUser {
@TableId(value="id" , type = IdType.AUTO)
private Integer id;
//用户名
private String username;
//用户密码
private String password;
//电话
private String phone;
//邮箱
private String email;
//角色,多个值使用逗号分隔,例如:admin,editor
private String roles;
//创建时间
private Date created;
//状态:0 未激活、1已激活
private String status;
@TableField(exist = false)
private String verifycode;
@TableField(exist = false)
private String repassword;
}
4.2.2 service实现
service 接口 package com.czxy.zx.user.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.czxy.zx.domain.EduUser;
/**
* @author txt
* @email [email protected]
*/
public interface EduUserService extends IService {
boolean register(EduUser eduUser);
}
service实现类 package com.czxy.zx.user.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.czxy.zx.domain.EduUser;
import com.czxy.zx.user.mapper.EduUserMapper;
import com.czxy.zx.user.service.EduUserService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Date;
/**
* @author txt
* @email [email protected]
*/
@Service
@Transactional
public class EduUserServiceImpl extends ServiceImpl implements EduUserService {
@Override
public boolean register(EduUser eduUser) {
//1 校验,用户名存在不允许注册
QueryWrapper queryWrapper = new QueryWrapper();
queryWrapper.eq("username", eduUser.getUsername());
EduUser findUser = this.baseMapper.selectOne(queryWrapper);
if(findUser != null) {
throw new EduException("用户名已存在");
}
//2 自动生成数据
eduUser.setCreated(new Date()); //创建时间
eduUser.setStatus("0"); //登录状态
//3 保存
int insert = this.baseMapper.insert(eduUser);
//4 提示
return insert == 1;
}
}
4.3.3 controller
/**
* 注册功能
* @param eduUser
* @return
*/
@PostMapping("/register")
public BaseResult register(@RequestBody EduUser eduUser) {
//1.1 校验验证码
String redisVerifyCode = stringRedisTemplate.opsForValue().get("register" + eduUser.getUsername());
stringRedisTemplate.delete("register" + eduUser.getUsername());
if(redisVerifyCode == null) {
return BaseResult.error("验证码无效");
}
if(! redisVerifyCode.equalsIgnoreCase(eduUser.getVerifycode())) {
return BaseResult.error("验证码错误");
}
//1.2 密码校验
if(eduUser.getPassword() == null) {
return BaseResult.error("密码不能为空");
}
if(! eduUser.getPassword().equals(eduUser.getRepassword())) {
return BaseResult.error("密码和确认密码不一致");
}
//2 注册
boolean result = eduUserService.register(eduUser);
//3 处理结果
if(result) {
// 成功
// 3.1 生成UUID
String uuid = UUID.randomUUID().toString().replace("-","");
// 3.2 生成激活路由
String url = "http://localhost:8080/active/"+eduUser.getUsername()+"/" + uuid; //访问前端
// 3.3 发送激活邮件
String text = eduUser.getUsername() + "您好:您使用本网站的激活程序,请点击激活";
// 3.4 发送邮件
UserEmail userEmail = new UserEmail();
userEmail.setUsername(eduUser.getUsername());
userEmail.setEmail(eduUser.getEmail());
userEmail.setText(text);
String userEmailStr = JSONObject.toJSONString(userEmail);
rabbitTemplate.convertAndSend(RabbitEmailConfig.QUEUE_NAME , userEmailStr);
// 3.5 保存激活状态码
stringRedisTemplate.opsForValue().set("active" + eduUser.getUsername() , uuid , 5 , TimeUnit.MINUTES);
return BaseResult.ok("注册成功");
}
return BaseResult.ok("注册失败");
}
4.3 前端实现
4.3.1 显示页面
创建页面 @/views/edu/user/register.vue ![](https://img-blog.csdnimg.cn/39abde844ed24c2f91c0f5da6cb54e50.png)
注册
export default {
}
编写路由 ![](https://img-blog.csdnimg.cn/2b092871051e465495d21bc69559c2e8.png) {
path: '/register',
component: () => import('@/views/edu/user/register'),
hidden: true //登录成功后,左侧菜单中不显示
}
修改登录页面 ![](https://img-blog.csdnimg.cn/4b53dc88c78a4fd78a6222989917cf34.png)
三方登录
注册
将注册连接添加到白名单 ![](https://img-blog.csdnimg.cn/d9b6cc3280dd47fcb670d2a3d5f234a7.png)
4.3.2 前端 api
![](https://img-blog.csdnimg.cn/2a6adcd38797477bb1021b68ba1dc1d7.png)
export function register(user) {
// 真实数据
return axios.post('/user-service/user/register',user);
}
4.3.3 注册页面
![](https://img-blog.csdnimg.cn/bca380e7f9cd47bfb410c5e7babec605.png)
注册表单
注册
export default {
data() {
return {
loginForm: {
},
capsTooltip: false,
passwordType: 'password',
recapsTooltip: false,
repasswordType: 'password',
verifycodeImg: '',
}
},
methods: {
showPwd() {
if (this.passwordType === 'password') {
this.passwordType = ''
} else {
this.passwordType = 'password'
}
this.$nextTick(() => {
this.$refs.password.focus()
})
},
checkCapslock(e) {
const { key } = e
this.capsTooltip = key && key.length === 1 && (key >= 'A' && key {
this.$refs.repassword.focus()
})
},
recheckCapslock(e) {
const { key } = e
this.recapsTooltip = key && key.length === 1 && (key >= 'A' && key process.env.VUE_APP_BASE_API}/user-service/verifycode/${this.loginForm.username}?t=${new Date().getTime()}`
}
4.3.4 注册功能
![](https://img-blog.csdnimg.cn/f3ca3366b3b340a8b3bf83e4527edda6.png)
async userRegister() {
let {message} = await register(this.loginForm)
this.$message.success(message)
//跳转到登录
this.$router.push('/login')
}
5 整合JWT
5.0 概述
什么是JWT?
JWT( json-web-token ): 现在比较火的token中的一种,为了解决HTTP协议无状态的问题。 采用的方法:提供一个有效的标识数据
是否有标识数据?标识数据是否有效?
5.0 分析
![](https://img-blog.csdnimg.cn/a2eee2be0d7748c68510f60d6b82f625.png)
5.1 搭建环境
5.1.1 拷贝坐标(已有)
在common项目中已经添加
commons-beanutils
commons-beanutils
io.jsonwebtoken
jjwt
joda-time
joda-time
5.1.2 复制yml配置
给user服务的yml文件中,添加如下配置:
sc:
jwt:
secret: sc@Login(Auth}*^31)&czxy% # 登录校验的密钥
pubKeyPath: D:/rsa/rsa.pub # 公钥地址
priKeyPath: D:/rsa/rsa.pri # 私钥地址
expire: 360 # 过期时间,单位分钟
5.1.3 拷贝封装类
拷贝:JwtProperties
package com.czxy.zx.user.config;
import com.czxy.zx.user.utils.RsaUtils;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import javax.annotation.PostConstruct;
import java.io.File;
import java.security.PrivateKey;
import java.security.PublicKey;
/**
* @author txt
* @email [email protected]
*/
@Configuration
@ConfigurationProperties(prefix = "sc.jwt")
@Data
public class JwtProperties {
private String secret;
private String pubKeyPath;
private String priKeyPath;
private Integer expire;
private PublicKey publicKey;
private PrivateKey privateKey;
@PostConstruct //初始化方法注解
public void init() {
try {
File pubFile = new File(pubKeyPath);
File priFile = new File(priKeyPath);
if(!pubFile.exists() || ! priFile.exists()) {
RsaUtils.generateKey(pubKeyPath,priKeyPath,secret);
}
// 获得公钥和私钥对象
this.publicKey = RsaUtils.getPublicKey(pubKeyPath);
this.privateKey = RsaUtils.getPrivateKey(priKeyPath);
} catch (Exception e) {
e.printStackTrace();
}
}
}
5.1.4 拷贝工具类
拷贝工具类 ![](https://img-blog.csdnimg.cn/93838f6e19844696be47aa82847f88f5.png)
5.1.5 生成秘钥(可选)
如果已经存在,此步省略 ![](https://img-blog.csdnimg.cn/84b3376f36d24d2eb548045034e30ab1.png) 编写测试类,生成公钥和私钥 package com.czxy.zx.utils;
/**
* @author txt
* @email [email protected]
*/
public class TestRsa {
//公钥的位置
private static final String pubKeyPath = "D:\\rsa\\rsa.pub";
//私钥的位置
private static final String priKeyPath = "D:\\rsa\\rsa.pri";
public static void main(String[] args) throws Exception {
RsaUtils.generateKey(pubKeyPath,priKeyPath,"1234");
}
}
5.2 登录成功:生成token
准备工作已完成 ![](https://img-blog.csdnimg.cn/85a6aa6d24d34189af9e96a858cde45e.png) 登录成功,将 EduUser 转换成token,并响应给用户
![](https://img-blog.csdnimg.cn/bc170f0cf2f14dd7ae0594bdfc2c3a93.png)
@PostMapping("/login")
public BaseResult login(@RequestBody EduUser eduUser) {
//1 校验验证码
// 1.1 获得redis
String redisName = "login_verify_code_" + eduUser.getUsername();
String redisVerifyCode = stringRedisTemplate.opsForValue().get(redisName);
// 1.2 删除redis
stringRedisTemplate.delete(redisName);
// 1.3 校验:无效
if(redisVerifyCode == null) {
return BaseResult.error("验证码无效");
}
// 1.4 校验:错误
if(!redisVerifyCode.equalsIgnoreCase(eduUser.getVerifycode())) {
return BaseResult.error("验证码错误");
}
//2 通过service用户登录
EduUser loginUser = eduUserService.login(eduUser);
//3 提示
if(loginUser != null) {
if("0".equals(loginUser.getStatus())) {
return BaseResult.error("用户未激活,请先激活");
}
if("2".equals(loginUser.getStatus())) {
return BaseResult.error("临时冻结,36小时");
}
if("3".equals(loginUser.getStatus())) {
return BaseResult.error("该账号已冻结");
}
// 需要设置token
//String token = "admin-token";
String token = JwtUtils.generateToken(loginUser, jwtProperties.getExpire(), jwtProperties.getPrivateKey());
return BaseResult.ok("登录成功").append("token", token);
}
return BaseResult.error("用户名或密码不匹配");
}
5.3 查询详情:获得token
5.3.1 基本流程
登录成功后,默认跳转到 / 页面 ![](https://img-blog.csdnimg.cn/7c1ab71d588c4509a07aaac417d8d208.png) 访问 / ,在路由中配置跳转的位置 ![](https://img-blog.csdnimg.cn/5751ce8ccb1d445f923a4c5f7fe5b783.png) 在跳转 / 页面前,执行vuex中 user/getInfo ![](https://img-blog.csdnimg.cn/491d73b9368e49bb896713ddc0e3b11d.png) 通过vuex执行ajax请求,查询详情 ![](https://img-blog.csdnimg.cn/1acb698ea3c74be995ba725dbb79f7c5.png)
5.3.2 查询详情
修改 EduUserController 添加方法 ![](https://img-blog.csdnimg.cn/f9073c47c893454f8f96d196071e893c.png)
@GetMapping("/info")
public BaseResult info( String token) {
try {
//1 通过token 获得用户信息
EduUser eduUser = JwtUtils.getObjectFromToken(token, jwtProperties.getPublicKey(), EduUser.class);
//2 模拟数据
/* Map map = new HashMap();
if("admin-token".equalsIgnoreCase(token)) {
map.put("roles", Arrays.asList("admin")); //角色的值必须是数组
map.put("avatar","https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif");
map.put("name","张三三");
} else {
// 非管理员的权限
}*/
//3 真实数据
Map map = new HashMap();
if(eduUser.getRoles() != null) {
map.put("roles", eduUser.getRoles().split(",")); //角色的值必须是数组
} else {
map.put("roles", Arrays.asList("editor")); //没有权限的固定:editor
}
map.put("avatar","https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif"); //需要完善用户头像
map.put("name",eduUser.getUsername());
// 数据返回 baseResult.data --> Map
return BaseResult.ok("获得权限成功", map);
} catch (Exception e) {
return BaseResult.error("获得权限失败");
}
}
5.4 过滤器
5.4.1 配置yml
在gateway 网关中,添加如下配置:
sc:
jwt:
secret: sc@Login(Auth}*^31)&czxy% # 登录校验的密钥
pubKeyPath: D:/rsa/rsa.pub # 公钥地址
priKeyPath: D:/rsa/rsa.pri # 私钥地址
expire: 360 # 过期时间,单位分钟
filter:
allowPaths:
- swagger
- /api-docs
- /user/login
- /user/info
- /user/register
- /user/sendemail
- /user/active
- /verifycode
5.4.2 pom和工具类
添加坐标
com.czxy.zx
zx-domain-java12
io.jsonwebtoken
jjwt
joda-time
joda-time
commons-beanutils
commons-beanutils
jwt相关的工具类 ![](https://img-blog.csdnimg.cn/f8d57d22e960481abd607d6d06d043f5.png)
5.4.3 配置类
![](https://img-blog.csdnimg.cn/927d349075c643d98945f17409cd9b92.png)
FilterProperties package com.czxy.zx.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import java.util.List;
/**
* @author txt
* @email [email protected]
*/
@Data
@Configuration
@ConfigurationProperties(prefix = "sc.filter")
public class FilterProperties {
private List allowPaths; //允许访问的路径(白名单)
}
JwtProperties package com.czxy.zx.config;
import com.czxy.zx.utils.RsaUtils;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import javax.annotation.PostConstruct;
import java.io.File;
import java.security.PrivateKey;
import java.security.PublicKey;
/**
* @author txt
* @email [email protected]
*/
@Configuration
@ConfigurationProperties(prefix = "sc.jwt")
@Data
public class JwtProperties {
private String secret;
private String pubKeyPath;
private String priKeyPath;
private Integer expire;
private PublicKey publicKey;
private PrivateKey privateKey;
@PostConstruct //初始化方法注解
public void init() {
try {
File pubFile = new File(pubKeyPath);
File priFile = new File(priKeyPath);
if(!pubFile.exists() || ! priFile.exists()) {
RsaUtils.generateKey(pubKeyPath,priKeyPath,secret);
}
// 获得公钥和私钥对象
this.publicKey = RsaUtils.getPublicKey(pubKeyPath);
this.privateKey = RsaUtils.getPrivateKey(priKeyPath);
} catch (Exception e) {
e.printStackTrace();
}
}
}
5.4.4 过滤器
package com.czxy.zx.filter;
import com.czxy.zx.config.FilterProperties;
import com.czxy.zx.config.JwtProperties;
import com.czxy.zx.domain.EduUser;
import com.czxy.zx.utils.JwtUtils;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.util.List;
/**
* @author txt
* @email [email protected]
*/
@Component
@EnableConfigurationProperties(FilterProperties.class)
public class LoginFilter implements GlobalFilter, Ordered {
@Resource
private FilterProperties filterProperties;
@Resource
private JwtProperties jwtProperties;
@Override
public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
try {
//1 获得请求路径
ServerHttpRequest request = exchange.getRequest();
String path = request.getURI().getPath();
System.out.println(path);
//2 白名单
List allowPaths = filterProperties.getAllowPaths();
for (String allowPath : allowPaths) {
if(path.contains(allowPath)) {
// 放行
return chain.filter(exchange);
}
}
//3 获得token
String token = request.getHeaders().getFirst("X-Token");
//4 校验token
JwtUtils.getObjectFromToken(token, jwtProperties.getPublicKey(), EduUser.class);
//5.1 成功,放行
return chain.filter(exchange);
} catch (Exception e) {
e.printStackTrace();
//5.2 失败,返回提示`token失效`
ServerHttpResponse response = exchange.getResponse();
// 响应状态 401 没有权限
response.setStatusCode(HttpStatus.UNAUTHORIZED);
// 响应数据的编码
response.getHeaders().add("Content-Type","application/json;charset=UTF-8");
// 响应“没有权限”提示
DataBuffer wrap = response.bufferFactory().wrap("没有权限".getBytes(StandardCharsets.UTF_8));
return exchange.getResponse().writeWith(Flux.just(wrap));
}
}
@Override
public int getOrder() {
return 1;
}
}
5.5 token 无效
如果token实现,需要给用户提示
![](https://img-blog.csdnimg.cn/d355054f2cfd43f5a07d80dcaa3c0400.png)
编写ajax响应的拦截器 ![](https://img-blog.csdnimg.cn/8bfdf56adebd4fd39eedfe9fbaeb33d4.png)
if(error.response.status == 401) {
// 401 提示信息,重新登录
MessageBox.confirm('Token失效,请重新登录', '确认退出', {
confirmButtonText: '重新登录',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
store.dispatch('user/resetToken').then(() => {
location.reload()
})
})
} else {
// 普通提示信息
Message({
message: error.message,
type: 'error',
duration: 5 * 1000
})
}
6 激活
6.1 分析
需求:
用户点击链接后可以进行账号激活,将用户的状态0改成1用户重复点击,提示“账号已经激活,无需重复激活”需要防止其他人帮着激活激活成功了,跳转到登录页面,“账号已激活,请登录”1天不激活,激活链接失效,需要重新发送
![](https://img-blog.csdnimg.cn/33fae466d7b64e7099166d12ddc000d0.png)
6.2 完善用户注册
![](https://img-blog.csdnimg.cn/b1155800f5c4489dac519422aa4c496d.png)
@PostMapping("/register")
public BaseResult register(@RequestBody EduUser eduUser) {
//1 校验
// 1.1 密码
if(StringUtils.isBlank(eduUser.getPassword())) {
ExceptionUtils.cast("密码不能为空");
}
if(! eduUser.getPassword().equals(eduUser.getRepassword())) {
ExceptionUtils.cast("确认密码和密码不一致");
}
// 1.2 验证码
// 1) 获得redis验证码
String key = "register" + eduUser.getUsername() ;
String redisVerifyCode = stringRedisTemplate.opsForValue().get(key);
// 2) 删除redis验证码
stringRedisTemplate.delete(key);
// 3) 无效
if(redisVerifyCode == null) {
ExceptionUtils.cast("验证码无效");
}
// 4) 不对
if(! redisVerifyCode.equalsIgnoreCase(eduUser.getVerifycode())) {
ExceptionUtils.cast("验证码错误");
}
//2 注册
boolean register = eduUserService.register(eduUser);
//3 成功,发送激活邮件
if(register) {
// 成功
// 获得uuid值
String uuidStr = UUID.randomUUID().toString().replace("-", "");
// 将uuid存放到redis中,设置有效时间
stringRedisTemplate.opsForValue().set(uuidStr,eduUser.getUsername(), 24, TimeUnit.HOURS);
// 发送激活邮件
UserEmail userEmail = new UserEmail();
userEmail.setEmail(eduUser.getEmail());
userEmail.setTitle("XXX平台激活邮件");
// TODO 作业:用户激活
//String url = "http://localhost:10010/user-service/user/active?username=" + eduUser.getUsername();
String url = "http://localhost:9527/#/active?uuid=" + uuidStr;
String msg = eduUser.getUsername() + ",你好:" +
"点击链接进行账号激活。" +
"如果不能点击,请复制下面的连接:" + url;
userEmail.setText(msg);
System.out.println(eduUser.getId());
//3 发送邮件-将邮件信息存放mq
String jsonStr = JSON.toJSONString(userEmail);
rabbitTemplate.convertAndSend("", RabbitEmailConfig.QUEUE_NAME, jsonStr);
return BaseResult.ok("注册成功,请进行账号激活");
}
return BaseResult.error("注册失败");
}
6.3 用户激活
6.3.1 前端实现
步骤:
步骤1:编写激活页面步骤2:编写ajax函数步骤3:添加白名单 步骤1:编写激活页面 ![](https://img-blog.csdnimg.cn/e0ab271f9d5f4a96a77082615f6844b2.png)
用户激活页面
import { active } from "@/api/user";
export default {
methods: {
async activeUserFn(uuid) {
let baseResult = await active(uuid);
this.$message.success(baseResulssage);
this.$router.push('/login')
}
},
mounted() {
// 获得参数uuid
let uuid = this.$route.query.uuid
// 如果没有调整到登录页面
if(! uuid) {
this.$message.error('激活链接无效,请重新访问');
this.$router.push('/login')
} else {
// 发送ajax进行激活
this.activeUserFn(uuid);
}
},
}
步骤2:编写ajax函数
![](https://img-blog.csdnimg.cn/b34825c07cb84d4e8d4f95dece18c345.png)
//激活
export function active(uuid) {
return axios.get(`/user-service/user/active?uuid=${uuid}`)
}
步骤3:添加白名单 ![](https://img-blog.csdnimg.cn/3769504ea4984ae2a0d4eb3bbfe151fa.png)
6.3.2 后端实现
![](https://img-blog.csdnimg.cn/465586942e824402a8a100c807128361.png)
@GetMapping("/active")
public BaseResult active(String uuid) {
//1 使用uuid从redis获得信息
String username = stringRedisTemplate.opsForValue().get(uuid);
//2 判断,如果不存在给出提示
if(username == null) {
return BaseResult.ok("链接不完整or不需要激活or链接已超时");
}
//3 通过用户名查询用户
QueryWrapper queryWrapper = new QueryWrapper();
queryWrapper.eq("username", username);
EduUser eduUser = eduUserService.getOne(queryWrapper);
if(eduUser == null) {
return BaseResult.ok("激活账号不存在");
}
//4 修改用户的状态 0 --> 1
if(! "0".equals(eduUser.getStatus()) ) {
return BaseResult.ok("账号不需要激活");
}
eduUser.setStatus("1");
eduUserService.updateById(eduUser);
//5 删除redis信息
stringRedisTemplate.delete(uuid);
//6 激活成功的提示
return BaseResult.ok("激活成功");
}
6.4 完善用户登录
![](https://img-blog.csdnimg.cn/7d0ae02a4f064c46aac8ee24683a8eae.png)
@PostMapping("/login")
public BaseResult login(@RequestBody EduUser eduUser) {
//1 校验验证码
// 1.1 获得redis
String redisName = "login_verify_code_" + eduUser.getUsername();
String redisVerifyCode = stringRedisTemplate.opsForValue().get(redisName);
// 1.2 删除redis
stringRedisTemplate.delete(redisName);
// 1.3 校验:无效
if(redisVerifyCode == null) {
return BaseResult.error("验证码无效");
}
// 1.4 校验:错误
if(!redisVerifyCode.equalsIgnoreCase(eduUser.getVerifycode())) {
return BaseResult.error("验证码错误");
}
//2 通过service用户登录
EduUser loginUser = eduUserService.login(eduUser);
//3 提示
if(loginUser != null) {
if("0".equals(loginUser.getStatus())) {
return BaseResult.error("用户未激活,请先激活");
}
if("2".equals(loginUser.getStatus())) {
return BaseResult.error("临时冻结,36小时");
}
if("3".equals(loginUser.getStatus())) {
return BaseResult.error("该账号已冻结");
}
// 需要设置token
String token = "admin-token";
return BaseResult.ok("登录成功").append("token", token);
}
return BaseResult.error("用户名或密码不匹配");
}
7. 退出登录
前端 ![](https://img-blog.csdnimg.cn/6c1dc759445f42c8b39f7a70172ce65a.png) 后端 ![](https://img-blog.csdnimg.cn/d2ce198549d3411aad5b6b8bbff6426f.png)
end
|