vue 您所在的位置:网站首页 password电话 vue

vue

2023-09-18 03:59| 来源: 网络整理| 查看: 265

介绍

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

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

启动类

启动类

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 用户服务基本模块

拷贝配置类

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服务

创建项目: 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: /

拷贝工具类

工具类

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 执行流程图

3.0 取消登录表单校验

登录表单校验

3.1 发送验证码邮件

3.1.1 前端:显示表单 登录页面:@/views/login/index.vue

显示表单

发送 3.1.2 前端:发送邮件

编写API,发送邮件

发送邮件

export function send(user) { return axios.post('/user-service/user/sendemail',user); }

调用

调用

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交互的数据

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:发送邮件

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 监听器发送邮件

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 ,用于进行邮件接收的。

每一个邮件服务提供商,都会明确自己每一个服务器的地址

yml配置

发送邮件:

发件人:from收件人:to (扩展:cc抄送、bcc暗送/密送)主题:subject正文:content/text 3.3 后端:用户登录 3.3.0 前端登录流程

步骤一:登录页面 @/views/login/index.vue

步骤二:登录调用 vuex

步骤三:查看vuex

将用户的完整信息,传递给ajax方法

步骤四:调用ajax

步骤五:确定ajax发送位置

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 修改前端请求数据

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 前端修改

3.3.5 数据要求 admin-token

登录成功后,查询用户详情的使用

3.4 登录成功后查询权限 3.4.1 分析

登录时,返回一个固定的字符串:admin-token 或 editor-token

登录成功后,查询用户详情将携带固定字符串

根据固定字符串查询用户的权限,并返回固定的信息:

{ roles: ['admin'], //用户角色 或 [editor] 或 [admin,editor] avatar: '', //头像图片地址 name: '', //用户名 }

查询详情ajax调用时机

3.4.2 前端完善

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 总结

4. 用户注册 4.0 整体流程 示意图

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

注册 export default { }

编写路由

{ path: '/register', component: () => import('@/views/edu/user/register'), hidden: true //登录成功后,左侧菜单中不显示 }

修改登录页面

三方登录 注册

将注册连接添加到白名单

4.3.2 前端 api

export function register(user) { // 真实数据 return axios.post('/user-service/user/register',user); } 4.3.3 注册页面

注册表单 注册 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 注册功能

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 分析

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 拷贝工具类

拷贝工具类

5.1.5 生成秘钥(可选)

如果已经存在,此步省略

编写测试类,生成公钥和私钥

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

准备工作已完成

登录成功,将 EduUser 转换成token,并响应给用户

@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 基本流程

登录成功后,默认跳转到 / 页面

访问 / ,在路由中配置跳转的位置

在跳转 / 页面前,执行vuex中 user/getInfo

通过vuex执行ajax请求,查询详情

5.3.2 查询详情

修改 EduUserController 添加方法

@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相关的工具类

5.4.3 配置类

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实现,需要给用户提示

编写ajax响应的拦截器

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天不激活,激活链接失效,需要重新发送

6.2 完善用户注册

@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:编写激活页面

用户激活页面 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函数

//激活 export function active(uuid) { return axios.get(`/user-service/user/active?uuid=${uuid}`) }

步骤3:添加白名单

6.3.2 后端实现

@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 完善用户登录

@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. 退出登录

前端

后端

end


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有