网盘登录业务记录

embedded/2024/9/18 4:31:03/ 标签: 个人开发, 后端

一、获取验证码

本项目中利用文件流写回验证码图片。

工具类核心:利用jdk内部的BufferedImage类:这个类生成一个图片存到内存缓冲区,方便我们对图片进行操作。

package com.easypan.utils;import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Random;public class CreateImageCode {// 图片的宽度。private int width = 160;// 图片的高度。private int height = 40;// 验证码字符个数private int codeCount = 4;// 验证码干扰线数private int lineCount = 20;// 验证码private String code = null;// 验证码图片Bufferprivate BufferedImage buffImg = null;Random random = new Random();public CreateImageCode() {creatImage();}public CreateImageCode(int width, int height) {this.width = width;this.height = height;creatImage();}public CreateImageCode(int width, int height, int codeCount) {this.width = width;this.height = height;this.codeCount = codeCount;creatImage();}public CreateImageCode(int width, int height, int codeCount, int lineCount) {this.width = width;this.height = height;this.codeCount = codeCount;this.lineCount = lineCount;creatImage();}// 生成图片private void creatImage() {int fontWidth = width / codeCount;// 字体的宽度int fontHeight = height - 5;// 字体的高度int codeY = height - 8;// 图像bufferbuffImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);Graphics g = buffImg.getGraphics();//Graphics2D g = buffImg.createGraphics();// 设置背景色g.setColor(getRandColor(200, 250));g.fillRect(0, 0, width, height);// 设置字体//Font font1 = getFont(fontHeight);Font font = new Font("Fixedsys", Font.BOLD, fontHeight);g.setFont(font);// 设置干扰线for (int i = 0; i < lineCount; i++) {int xs = random.nextInt(width);int ys = random.nextInt(height);int xe = xs + random.nextInt(width);int ye = ys + random.nextInt(height);g.setColor(getRandColor(1, 255));g.drawLine(xs, ys, xe, ye);}// 添加噪点float yawpRate = 0.01f;// 噪声率int area = (int) (yawpRate * width * height);for (int i = 0; i < area; i++) {int x = random.nextInt(width);int y = random.nextInt(height);buffImg.setRGB(x, y, random.nextInt(255));}String str1 = randomStr(codeCount);// 得到随机字符this.code = str1;for (int i = 0; i < codeCount; i++) {String strRand = str1.substring(i, i + 1);g.setColor(getRandColor(1, 255));// g.drawString(a,x,y);// a为要画出来的东西,x和y表示要画的东西最左侧字符的基线位于此图形上下文坐标系的 (x, y) 位置处g.drawString(strRand, i * fontWidth + 3, codeY);}}// 得到随机字符private String randomStr(int n) {String str1 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890";String str2 = "";int len = str1.length() - 1;double r;for (int i = 0; i < n; i++) {r = (Math.random()) * len;str2 = str2 + str1.charAt((int) r);}return str2;}// 得到随机颜色private Color getRandColor(int fc, int bc) {// 给定范围获得随机颜色if (fc > 255) fc = 255;if (bc > 255) bc = 255;int r = fc + random.nextInt(bc - fc);int g = fc + random.nextInt(bc - fc);int b = fc + random.nextInt(bc - fc);return new Color(r, g, b);}/*** 产生随机字体*/private Font getFont(int size) {Random random = new Random();Font font[] = new Font[5];font[0] = new Font("Ravie", Font.PLAIN, size);font[1] = new Font("Antique Olive Compact", Font.PLAIN, size);font[2] = new Font("Fixedsys", Font.PLAIN, size);font[3] = new Font("Wide Latin", Font.PLAIN, size);font[4] = new Font("Gill Sans Ultra Bold", Font.PLAIN, size);return font[random.nextInt(5)];}// 扭曲方法private void shear(Graphics g, int w1, int h1, Color color) {shearX(g, w1, h1, color);shearY(g, w1, h1, color);}private void shearX(Graphics g, int w1, int h1, Color color) {int period = random.nextInt(2);boolean borderGap = true;int frames = 1;int phase = random.nextInt(2);for (int i = 0; i < h1; i++) {double d = (double) (period >> 1) * Math.sin((double) i / (double) period + (6.2831853071795862D * (double) phase) / (double) frames);g.copyArea(0, i, w1, 1, (int) d, 0);if (borderGap) {g.setColor(color);g.drawLine((int) d, i, 0, i);g.drawLine((int) d + w1, i, w1, i);}}}private void shearY(Graphics g, int w1, int h1, Color color) {int period = random.nextInt(40) + 10; // 50;boolean borderGap = true;int frames = 20;int phase = 7;for (int i = 0; i < w1; i++) {double d = (double) (period >> 1) * Math.sin((double) i / (double) period + (6.2831853071795862D * (double) phase) / (double) frames);g.copyArea(i, 0, 1, h1, 0, (int) d);if (borderGap) {g.setColor(color);g.drawLine(i, (int) d, i, 0);g.drawLine(i, (int) d + h1, i, h1);}}}public void write(OutputStream sos) throws IOException {ImageIO.write(buffImg, "png", sos);sos.close();}public BufferedImage getBuffImg() {return buffImg;}public String getCode() {return code.toLowerCase();}
}

controller层: 

 @RequestMapping("/checkCode")public void checkCode(HttpServletResponse response, HttpSession session,Integer type) throws IOException {//验证码的高度、宽度、字符的数量、干扰线的数量CreateImageCode vCode = new CreateImageCode(130, 38, 4, 10);response.setHeader("Pragma", "no-cache");response.setHeader("Cache-Control", "no-cache");response.setDateHeader("Expires", 0);//上面三行。禁止缓存是为了确保验证码图片每次都是新生成的,而不是从缓存中获取旧的验证码图片。response.setContentType("image/jpeg");//将验证码存到sessionString code = vCode.getCode();//将生成的数据存到sessionif (type == null || type == 0) {//0是登录session.setAttribute(Constants.CHECK_CODE_KEY, code);} else {//注册session.setAttribute(Constants.CHECK_CODE_KEY_EMAIL, code);}

 解释:每次访问页面显示的验证码都要刷新,下面的代码是在服务端禁用jsp缓存。 Cache-Control头域用来指定请求和响应遵循的缓存机制。

response.setHeader("Pragma", "no-cache");
response.setHeader("Cache-Control", "no-cache");
response.setDateHeader("Expires", 0);

设置返回值类型:字节流

response.setContentType("image/jpeg");

  前端接收 字节流对象。

业务中有好几处需要用到验证码:

1.为什么要将这验证码分成两类存储,(session中对应不同的key)???

注册业务需要同时用到两处的验证码。普通验证码和邮箱验证码,进行比对时都要比对。

补充:也可以后端返回前端base64编码,前端接收展示图片。验证码校验可以存到session中也可以存到redis中。 

二、邮箱发送验证码(注册和忘记密码找回时使用)

利用MimeMessage    javaMailSender  MimeMessageHelper发送邮件

参数type:0表示注册,1表示找回密码

逻辑:1.如果type是0,在发送邮件之前判断该邮箱是否被占用。2.发送生成的随机数

3.将发送的验证码中其它的设置成失效。4.将刚才发送的验证码和email信息存到数据库。

这个数据库表存在的意义??

如果有两个程序并发执行,输入同一个email,如果不存到数据库中,两个都会通过,引发异常。

相关依赖 :

  <!--邮件发送--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-mail</artifactId><version>${springboot.version}</version></dependency>

配置文件: 

# 配置邮件服务器的地址 smtp.qq.com
spring.mail.host=smtp.qq.com
# 配置邮件服务器的端口(465或587)
spring.mail.port=465
# 配置用户的账号
spring.mail.username=xxxxxxxxxxx@qq.com
# 配置用户的密码
spring.mail.password=xxxxxxxxxxx
# 配置默认编码
spring.mail.default-encoding=UTF-8
# SSL 连接配置
spring.mail.properties.mail.smtp.socketFactory.class=javax.net.ssl.SSLSocketFactory
# 开启 debug,这样方便开发者查看邮件发送日志
spring.mail.properties.mail.debug=true

 Spring Boot的starter模块提供了自动化配置,所以在引入了spring-boot-starter-mail依赖之后,会根据配置文件中的内容去创建JavaMailSender实例 

可以查看一下JavaMailSender的视线类:

    private void sendCode(String ToEmail, String s) {try {MimeMessage message= javaMailSender.createMimeMessage();MimeMessageHelper helper=new MimeMessageHelper(message,true);helper.setFrom(appConfig.getSendUserName());helper.setTo(ToEmail);SysSettingsDto sysSettingsDto = redisComponent.getSysSettingsDto();helper.setSubject(sysSettingsDto.getRegisterEmailTitle());//标题helper.setText(String.format(sysSettingsDto.getRegisterEmailContent(),s));//内容helper.setSentDate(new Date());//日期javaMailSender.send(message);//发送}catch (Exception e){}}

事务 

@Transactional(rollbackFor = Exception.class)注解,spring提供的声明式事务管理注解。

发送邮箱验证码需要做事务管理的原因是确保邮件发送的可靠性和一致性。在发送邮件的过程中,涉及到多个步骤,如创建邮件、设置邮件内容、发送邮件等。如果其中某个步骤出现异常或失败,事务管理可以确保在出现问题时回滚所有操作,避免发送不完整或重复的邮件。事务管理可以保证邮件发送的可靠性,确保邮件要么完全发送成功,要么完全失败,从而维护系统的数据一致性。

aop做参数校验

业务中不做参数校验可以出现空指针的严重错误

1.定义校验注解

2.创建切面类,编写校验逻辑。

发送邮件

MimeMessage message= javaMailSender.createMimeMessage();
MimeMessageHelper helper=new MimeMessageHelper(message,true);
helper.setFrom(appConfig.getSendUserName());
helper.setTo(ToEmail);
SysSettingsDto sysSettingsDto = redisComponent.getSysSettingsDto();
helper.setSubject(sysSettingsDto.getRegisterEmailTitle());
helper.setText(String.format(sysSettingsDto.getRegisterEmailContent(),s));
helper.setSentDate(new Date());
javaMailSender.send(message);

MimeMessage表示一个mail对象。

MimeMessageHelper类辅助设置发送邮件的属性。

为什么将发送邮件的模版存到redis中?

由于发送邮件涉及到第三方服务、响应时间可能会过长。这是个耗时操作、如果放在用户请求进程中同步处理,响应时间过长。为了提升用户体验,存入redis中。(也可以使用消息队列)


http://www.ppmy.cn/embedded/23351.html

相关文章

【Spring AI】01. 概述

文章目录 Spring Ai 概述 Spring Ai 概述 项目旨在简化人工智能功能的应用程序的开发&#xff0c;避免不必要的复杂性。 该项目从著名的 Python 项目&#xff08;例如 LangChain 和 LlamaIndex&#xff09;中汲取灵感&#xff0c;但 Spring AI 并不是这些项目的直接移植。该项…

CSS--选择器

选择器 学习链接报错汇总document.querySelector(#rdpmPrjApproval|rdpmPrjApprovalInfoGroup)获取失败二级目录三级目录 学习链接 CSS6大种选择器(超详细!!!) 报错汇总 document.querySelector(‘#rdpmPrjApproval|rdpmPrjApprovalInfoGroup’)获取失败 document.getEleme…

linux驱动-CCF-3-consumer 接口

clk consumer常用接口 #获取句柄 struct clk *clk_get(struct device *dev, const char *id); struct clk *devm_clk_get(struct device *dev, const char *id);#prepare 与enable int clk_enable(struct clk *clk);//使能时钟&#xff0c;不会睡眠 void clk_disable(struct cl…

鸿蒙应用开发-初见:入门知识、应用模型

基础知识 Stage模型应用程序包结构 开发并打包完成后的App的程序包结构如图 开发者通过DevEco Studio把应用程序编译为一个或者多个.hap后缀的文件&#xff0c;即HAP一个应用中的.hap文件合在一起称为一个Bundle&#xff0c;bundleName是应用的唯一标识 需要特别说明的是&…

Kubernetes 声明式语言 YAML

什么是 YAML YAML&#xff08;YAML Ain’t Markup Language&#xff09;是一种可读的数据序列化语言&#xff0c;通常用于配置文件、数据序列化和交换格式。YAML 的设计目标是易读易写&#xff0c;并且能够映射到动态语言中的数据结构 YA加粗样式ML 是 JSON 的超集&#xff0…

Trello与Notion的开源替代项目管理利器Focalboard本地安装与远程访问

本篇文章将介绍如何使用 Docker 本地部署 Focalboard 项目管理工具&#xff0c;并且结合 cpolar 内网穿透进行公网访问&#xff0c;实现团队协作&#xff0c;提高工作效率&#xff01; Focalboard 是一个开源项目管理工具&#xff0c;可以替代 Asana、Trello 和 Notion 等软件…

AI手机,走入小径分岔的花园

博尔赫斯在他的成名作《小径分岔的花园》里&#xff0c;描述了一种奇妙的世界观&#xff1a;一个可能性被选择之后&#xff0c;出现了许多不同的后世&#xff0c;许多不同的时间。 在现实世界中&#xff0c;选择不会如此神奇。但站在岔路口的抉择&#xff0c;也一定会带来结果的…

Redis基本數據結構 ― List

Redis基本數據結構 ― List 介紹常用命令範例1. 將元素推入List中2. 取得List內容3. 彈出元素 介紹 Redis中的List結構是一個雙向鏈表。 LPUSH LPOP StackLPUSH RPOP QueueLPUSH BRPOP Queue(消息隊列) 常用命令 命令功能LPUSH將元素推入列表左端RPUSH將元素推入列表右…

【ARMv9 DSU-120 系列 9 -- DSU-120 Debug block】

请阅读【Arm DynamIQ™ Shared Unit-120 专栏 】 请阅读【ARM Coresight SoC-400/SoC-600 专栏导读】 文章目录 ARM DSU-120 Debug BlockCluster debug componentsCache DebugDSU-120 Cache Debug 特性应用场景Terminology处理元素(PE)

Day43 JDK1.8新特性 下 接口的类优先原则和接口冲突,日期时间组件,重复注解

Day43 JDK1.8新特性 1.接口的默认方法与静态方法 从JDK1.8开始&#xff0c;接口中可以有默认方法&#xff0c;既default修饰的方法&#xff0c;此方法可以让接口的实现类所调用&#xff0c;而接 口中的静态方法直接用接口名调用即可 public class Test1 {Testpublic void test…

没想到在vscode里启用vue项目调试这么简单

在 .vscode/lanch.json 中建个Chrome: Lanch配置 然后把url改成项目运行地址 用 npm run dev 把项目启动起来 再按F5启动调试就可以 欢迎关注公众号&#xff1a;清晰编程&#xff0c;获取更多精彩内容

【c++】cpp运算符重载

目录 &#xff08;1&#xff09;什么是运算符重载 &#xff08;2&#xff09;运算符重载的本质是函数调用 &#xff08;3&#xff09;可以与不可以重载的运算符 &#xff08;4&#xff09;单目运算符与双目符重载区别 &#xff08;5&#xff09;双目运算符重载举例 重载操…

改ip地址软件手机怎么弄?分享操作指南与注意事项

随着移动互联网的普及&#xff0c;手机已成为我们日常生活中不可或缺的工具。在某些情况下&#xff0c;我们可能需要更改手机的IP地址&#xff0c;以满足特定的网络需求或实现某些功能。然而&#xff0c;对于许多用户来说&#xff0c;如何在手机上更改IP地址可能是一个相对陌生…

C++里的new

C里的new&#xff1a; new开辟的空间在堆上&#xff0c;而一般声明的变量存放在栈上。当在局部函数中new出一段新的空间&#xff0c;该段空间在局部函数调用结束后仍然能够使用&#xff0c;可以用来向主函数传递参数。new出来的是一段空间的首地址。所以一般需要用指针来存放这…

基于ssm+vue开放式教学评价管理系统【ppt·代码·文档报告】

项目演示视频 ssmvue开放式教学评价管理系统 项目名称&#xff1a;开放式教学评价管理系统 系统介绍&#xff1a;本系统是通过java的SSM框架来实现的&#xff0c;前端采用vue框架进行实现 管理员通过登录进入到系统操作界面&#xff0c;结合需求可以对个人信息进行在线修改维护…

【QT教程】QT6图形渲染与OpenGL编程

QT6图形渲染与OpenGL编程 使用AI技术辅助生成 QT界面美化视频课程 QT性能优化视频课程 QT原理与源码分析视频课程 QT QML C扩展开发视频课程 免费QT视频课程 您可以看免费1000个QT技术视频 免费QT视频课程 QT统计图和QT数据可视化视频免费看 免费QT视频课程 QT性能优化视频免…

四信数字孪生水库解决方案,加快构建现代化水库运行管理矩阵

近年&#xff0c;水利部先后出台《关于加快构建现代化水库运行管理矩阵的指导意见》与《构建现代化水库运行管理矩阵先行先试工作方案》等文件&#xff0c;明确总体要求及试点水库、先行区域建设技术要求等&#xff0c;为全面推进现代化水库运行管理矩阵建设工作提供依据。 《2…

怎么理解JavaScript原型、原型链

原型 JavaScript被描述为一种基于原型的语言&#xff0c;也就是每一个对象都拥有一个原型对象。 当我们访问一个对象的属性时&#xff0c;它不仅仅该改对象上查找&#xff0c;还会到这个对象的原型上查找&#xff0c;甚至是到这个对象的原型的原型上查找&#xff0c;这样一层…

cpp程序优化

cpp程序优化 原 数值计算优化方法C/C C编程优化——让你的代码飞起来 RGB格式的彩色图像先转换成黑白图像 C/C代码优化具体方案 c 性能优化策略 1.关于继承&#xff1a;尽量少使用多重继承不可否认良好的抽象设计可以让程序更清晰&#xff0c;代码更看起来更好&#xff…

stable diffusion本地部署顺序

在部署Stable Diffusion模型到本地环境时&#xff0c;通常需要以下步骤&#xff1a; 安装Python和必要的库&#xff1a;确保你的环境中安装了Python&#xff0c;并安装了如PyTorch、transformers等Stable Diffusion所需的库。 下载预训练模型&#xff1a;从Hugging Face库或其…