前端Vue

前端使用vue3,vite构建,UI使用elementplus

后端SpringBoot

创建多模块的SpringBoot项目,下面是层级结构:

  • cloud
    • cloud-admin
    • (cloud-admin)pom.xml (子)
    • cloud-common
    • (cloud-common)pom.xml (子)
  • pom.xml (父)

(父)pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.juncloud</groupId>
<artifactId>juncloud</artifactId>
<version>1.0</version>

<name>juncloud</name>
<description>智能云盘</description>
<properties>
<java.version>1.8</java.version>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>

<!-- 依赖声明 -->
<dependencyManagement>
<dependencies>
<!-- Spring Boot 依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.7.14</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.7.13</version>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
<version>8.0.33</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
<version>1.18.28</version>
</dependency>
<!-- Redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>2.7.13</version>
</dependency>
<!-- 加密算法 -->
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId>
<version>2.1.0</version>
</dependency>
<!-- mybatis-plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.1</version>
</dependency>
<!-- 代码生成器 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.1</version>
</dependency>
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity</artifactId>
<version>1.7</version>
</dependency>
<!-- 邮件发送-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
<version>2.7.13</version>
</dependency>
<!--切面-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.7</version>
</dependency>
<!-- 阿里JSON解析器 -->
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>2.0.35</version>
</dependency>
<!--常用工具类 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
<!-- io常用工具类 -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.13.0</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.11</version>
</dependency>
<!-- 自定义 -->
<dependency>
<groupId>com.juncloud</groupId>
<artifactId>juncloud-admin</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>com.juncloud</groupId>
<artifactId>juncloud-common</artifactId>
<version>1.0</version>
</dependency>
</dependencies>
</dependencyManagement>

<modules>
<module>juncloud-admin</module>
<module>juncloud-common</module>
</modules>
<packaging>pom</packaging>

<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.7.14</version>
<configuration>
<mainClass>com.juncloud.admin.JuncloudApplication</mainClass>
</configuration>
</plugin>
</plugins>
</build>
</project>

(cloud-admin)pom.xml (子)

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>com.juncloud</groupId>
<artifactId>juncloud</artifactId>
<version>1.0</version>
</parent>

<modelVersion>4.0.0</modelVersion>
<packaging>jar</packaging>
<artifactId>juncloud-admin</artifactId>
<version>1.0</version>
<name>juncloud-admin</name>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- Redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<!-- 加密算法 -->
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId>
</dependency>
<!-- mybatis-plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.juncloud</groupId>
<artifactId>juncloud-common</artifactId>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.7.14</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

(cloud-common)pom.xml (子)

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.juncloud</groupId>
<artifactId>juncloud</artifactId>
<version>1.0</version>
</parent>
<groupId>com.juncloud</groupId>
<artifactId>juncloud-common</artifactId>
<version>1.0</version>
<name>juncloud-common</name>
<properties>
<java.version>1.8</java.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<!-- mybatis-plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
<!-- 代码生成器 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
</dependency>
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<!-- 邮件发送-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<!--切面-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
</dependency>
<!--常用工具类 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<!-- io常用工具类 -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
</dependency>
<!-- 加密算法 -->
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

验证码

创建CreateImageCode.java,进行绘制图片

public class CreateImageCode {
// 图片的宽度。
private int width = 160;
// 图片的高度。
private int height = 40;
// 验证码字符个数
private int codeCount = 5;
// 验证码干扰线数
private int lineCount = 150;
// 验证码
private String code = null;
// 验证码图片Buffer
private BufferedImage bufferImg = 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;

// 图像buffer
bufferImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics g = bufferImg.getGraphics();
// 设置背景色
g.setColor(getRandColor(200, 250));
g.fillRect(0, 0, width, height);
// 设置字体
// Font font = 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.10f;// 噪声率
int area = (int) (yawpRate * width * height);
for (int i = 0; i < area; i++) {
int x = random.nextInt(width);
int y = random.nextInt(height);
bufferImg.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为字的左下角的x坐标,y为字的左下角的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 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 < h1; 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(bufferImg, "png", sos);
sos.close();
}

public BufferedImage getBuffImg() {
return bufferImg;
}

public String getCode() {
return code.toLowerCase();
}
}

使用时:

@GetMapping("/checkCode")
public void checkCode(HttpServletResponse response, HttpSession session, Integer type) throws IOException {
CreateImageCode createImageCode = new CreateImageCode(130, 38, 5, 20);
response.setHeader("Pragma", "no-cache");
response.setHeader("Cache-Control", "no-cache");
response.setDateHeader("Expires", 0);
response.setContentType("image/jpeg");
String code = createImageCode.getCode();
if (type == null || type == 0) {
session.setAttribute(Constants.CHECK_CODE_KEY, code);
} else {
session.setAttribute(Constants.CHECK_CODE_KEY_EMAIL, code);
}
createImageCode.write(response.getOutputStream());
}

当我们需要验证用户传回的验证码是否正确时:(下面是一个示例)

@PostMapping("/sendEmailCode")
@GlobalInterceptor(checkParams = true)
public AjaxResult sendEmailCode(HttpSession session,
@VerifyParam(required = true, regex = VeriyfRegexEnum.EMAIL, max = 150) String email,
@VerifyParam(required = true) String checkCode,
@VerifyParam(required = true) Integer type) {
try {
if (!checkCode.equalsIgnoreCase((String) session.getAttribute(Constants.CHECK_CODE_KEY_EMAIL))) {
return AjaxResult.error("图片验证码不正确");
}
userInfoService.sendEmailCode(email, type);
} finally {
session.removeAttribute(Constants.CHECK_CODE_KEY_EMAIL);
}
return AjaxResult.success("发送成功");
}

aop实现参数校验

引入切片依赖

<!--切面-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
</dependency>

新建包annotation,在annotation包中,创建GlobalInterceptor.java

@Target({ElementType.METHOD}) // 作用在方法上
@Retention(RetentionPolicy.RUNTIME) // 生命周期
@Documented // 生成文档
@Mapping
public @interface GlobalInterceptor {
/**
* 是否需要登录
* @return
*/
boolean checkLogin() default false;

/**
* 校验参数
* @return
*/
boolean checkParams() default false;

/**
* 校验频次
*/
}

新建包aspect,在aspect包中,新建GlobalOperationAspect.java


使用只需要在需要校验的接口添加@GlobalInterceptor即可,如下示例:

@PostMapping("/sendEmailCode")
@GlobalInterceptor(checkParams = true)
public AjaxResult sendEmailCode(HttpSession session, String email, String checkCode, Integer type) {
try {
if (!checkCode.equalsIgnoreCase((String) session.getAttribute(Constants.CHECK_CODE_KEY_EMAIL))) {
return AjaxResult.error("图片验证码不正确");
}
emailCodeService.sendEmailCode(email, type);
} finally {
session.removeAttribute(Constants.CHECK_CODE_KEY_EMAIL);
}
return AjaxResult.success("发送成功");
}

文件上传

  • 分片上传
@PostMapping("/uploadFile")
@GlobalInterceptor(checkParams = true)
public AjaxResult uploadFile(HttpSession session,
Integer fileId,
MultipartFile file,
@VerifyParam(required = true) String fileName,
@VerifyParam(required = true) String filePid,
@VerifyParam(required = true) String fileMd5,
@VerifyParam(required = true) Integer chunkIndex,
@VerifyParam(required = true) Integer chunks){
SessionWebUserDto webUserDto = getUserInfoFromSession(session);
UploadResultDto resultDto = fileInfoService.uploadFile(webUserDto, fileId, file, fileName, filePid, fileMd5, chunkIndex, chunks);
return AjaxResult.success(resultDto);
}

服务器部署

前端nginx

nginx配置

server
{
listen 80;
server_name junyyds.top;
index index.php index.html index.htm default.php default.htm default.html;
root /workspace/JunCloud/juncloud-ui/dist;

#SSL-START SSL相关配置,请勿删除或修改下一行带注释的404规则
#error_page 404/404.html;
#SSL-END

#ERROR-PAGE-START 错误页配置,可以注释、删除或修改
#error_page 404 /404.html;
#error_page 502 /502.html;
#ERROR-PAGE-END

#PHP-INFO-START PHP引用配置,可以注释或修改
include enable-php-00.conf;
#PHP-INFO-END

#REWRITE-START URL重写规则引用,修改后将导致面板设置的伪静态规则失效
include /www/server/panel/vhost/rewrite/junyyds.top.conf;
#REWRITE-END

#禁止访问的文件或目录
location ~ ^/(\.user.ini|\.htaccess|\.git|\.env|\.svn|\.project|LICENSE|README.md)
{
return 404;
}

#一键申请SSL证书验证目录相关设置
location ~ \.well-known{
allow all;
}

#禁止在证书验证目录放入敏感文件
if ( $uri ~ "^/\.well-known/.*\.(php|jsp|py|js|css|lua|ts|go|zip|tar\.gz|rar|7z|sql|bak)$" ) {
return 403;
}

location / {
index /data/dist/index.html;
try_files $uri $uri/ /index.html;
}

location /api {
proxy_pass http://localhost:9090/api;
proxy_set_header x-forwarded-for $remote_addr;
}

location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$
{
expires 30d;
error_log /dev/null;
access_log /dev/null;
}

location ~ .*\.(js|css)?$
{
expires 12h;
error_log /dev/null;
access_log /dev/null;
}
access_log /www/wwwlogs/junyyds.top.log;
error_log /www/wwwlogs/junyyds.top.error.log;
}