day24_头像上传 密码加密 验证码

day24_头像上传 密码加密 验证码

1头像上传

1上传要求(文件上传)

image-20250825094742545

2基础上传代码

页面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <input id="myfile" type="file" onchange="uploadFile()" >
</body>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script >

    // axios.post('/xxxx',xxx,{headers:{
    //        contentType:'multi-part/formdata'
    //     }})

    // var formData = new FormData();
    // formData.set("myfile",文件)
    // axios.post("/xxxx",formData)

    const uploadFile = ()=>{
        //找到文件数据
        let upfile =  document.getElementById("myfile").files[0]
        console.log(upfile)
        //表单对象
        var formData = new FormData();
        //把文件放入表单对象(axios检测到有文件数据 会自动写请求头 multi-part/formdata)
        formData.append("myfile",upfile)
        formData.append("name",'jack')
        //发送请求 把表单数据放入axios请求
        axios.post("/baseProj/upload",formData)
    }

</script>
</html>

服务端

package com.javasm.controller;

import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
import java.io.IOException;

/**
 * @className: UploadServlet
 * @description:
 * @author: gfs
 * @date: 2025/8/25 9:59
 * @version: 0.1
 * @since: jdk17
 */
@WebServlet("/upload")
@MultipartConfig
public class UploadServlet extends HttpServlet {

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        /*
        * 注意: 1 需要增加 @MultipartConfig注解 让tomcat解析读取请求体中的流数据
        *      2  文件对象 需要通过特殊api读取   Part myfile = req.getPart("myfile");
        *      3  本地io操作 可以通过Part对象的write方法 指定路径和文件名 自动写文件
        * */

        System.out.println(req.getParameter("name"));
        System.out.println(req.getParameter("myfile"));
        //对应multipart中的 一段文件
        Part myfile = req.getPart("myfile");
        //写保存的路径 自动写入
        myfile.write("D:\\test.png");
    }
}

3.文件上传接口

1接收上传的流数据

2控制文件存储的地址(通常需要通过url访问)

还需要考虑是否允许覆盖 改变文件名 或者存储目录

3把url访问的实际路径 回传给页面 方便页面的后续数据操作

package com.javasm.controller;

import com.alibaba.fastjson.JSON;
import com.javasm.entity.Result;
import com.javasm.entity.ReturnCode;

import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.UUID;

/**
 * @className: UploadServlet
 * @description:
 * @author: gfs
 * @date: 2025/8/25 9:59
 * @version: 0.1
 * @since: jdk17
 */
@WebServlet("/upload")
@MultipartConfig
public class UploadServlet extends HttpServlet {

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        /*
        * 注意: 1 需要增加 @MultipartConfig注解 让tomcat解析读取请求体中的流数据
        *      2  文件对象 需要通过特殊api读取   Part myfile = req.getPart("myfile");
        *      3  本地io操作 可以通过Part对象的write方法 指定路径和文件名 自动写文件
        * */

        System.out.println(req.getParameter("name"));
        System.out.println(req.getParameter("myfile"));
        //对应multipart中的 一段文件
        Part myfile = req.getPart("myfile");
        //2 控制文件在服务端存储位置  文件名与目录
        /*
          存储文件的位置
        * 1存储的文件地址
        *     需要能通过网络请求访问
        * 2找到运行的代码的路径
        *     req.getServletContext().getRealPath("/")
        * 3存储的文件名
        *     myfile.getSubmittedFileName()

        注意:如何避免文件互相覆盖
          1.原文件名(不重要 可以改文件名)
          2.原文件名(重要 可以改目录)
             按登录的用户名 按日期 按公司信息  组合使用

        * */
        String realPath = req.getServletContext().getRealPath("/");
        System.out.println(realPath);
        //改路径
        String folderPath = "imgs/";
        //原文件名
        String fileName = myfile.getSubmittedFileName();
        //修改原文件名 组合UUID
        String newFileName = UUID.randomUUID()+myfile.getSubmittedFileName().substring(myfile.getSubmittedFileName().lastIndexOf("."));

        //写保存的路径 自动写入
        myfile.write(realPath+folderPath+newFileName);

        //3把文件查看使用的路径url 回传给界面
        Result result = new Result(ReturnCode.OPERATION_DATA_SUCCESS.getCode(),ReturnCode.OPERATION_DATA_SUCCESS.getMsg(),
                "http://localhost:8080/baseProj/"+folderPath+newFileName);
        resp.setContentType("application/json;charset=utf-8");
        PrintWriter writer = resp.getWriter();
        writer.print(JSON.toJSONString(result));
        writer.close();
    }
}

4使用上传组件 配合上传接口

地址输入框 换成上传组件

上传组件的img标签 绑定图片地址

上传框 需要设置样式

            <el-form-item label="头像地址" prop="headImg">
                <!-- <el-input v-model="insertForm.headImg" /> -->
                <el-upload
                    class="avatar-uploader"
                    action=""
                    :show-file-list="false"
                    :http-request="addUpload"
                >
                    <img v-if="insertForm.headImg" :src="insertForm.headImg" class="avatar" />
                    <el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon>
                </el-upload>
            </el-form-item>
1


<style scoped>
    img{
        width: 50px;
        height: 50px;
    }


.avatar-uploader .avatar {
  width: 178px;
  height: 178px;
  display: block;
}
</style>

<style>
.avatar-uploader .el-upload {
  border: 1px dashed var(--el-border-color);
  border-radius: 6px;
  cursor: pointer;
  position: relative;
  overflow: hidden;
  transition: var(--el-transition-duration-fast);
}

.avatar-uploader .el-upload:hover {
  border-color: var(--el-color-primary);
}

.el-icon.avatar-uploader-icon {
  font-size: 28px;
  color: #8c939d;
  width: 178px;
  height: 178px;
  text-align: center;
}
</style>


通过 :http-request="addUpload" 自定义上传的js

/**上传函数 */
const addUpload = async (rawFile)=>{
    //1找到文件
    let curretFile = rawFile.file
    //2创建formData 把文件放进去
    let formData = new FormData()
    formData.append("myfile",curretFile)
    let resp = await axios.post("http://localhost:8080/baseProj/upload",formData)
    //给上传组件 配置图片显示的路径
    insertForm.headImg =  resp.data.returnData
}

2密码加密

通过加密算法 把用户数据的数据 转换成密文 再与数据库中的密文对比 如果相同 表示原文相同

image-20250825113802345

image-20250825113815398

package com.javasm.utils;

import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/**
 * @className: MyUtils
 * @description:
 * @author: gfs
 * @date: 2025/8/25 11:31
 * @version: 0.1
 * @since: jdk17
 */
public class MyUtils {

    public static String transPwd(String pwd){
        String transPwd;
        try {
            MessageDigest md5 = MessageDigest.getInstance("MD5");
            md5.update(pwd.getBytes());
            //32位16进制字符
            transPwd = new BigInteger(1, md5.digest()).toString(16);

        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
        return transPwd.toUpperCase();
    }

}

image-20250825115740338

3验证码

操作流程中 增加流程的复杂度

1人机检测 2降低一定程度的瞬时流量 (前台系统中用的多 后台管理系统一般不用)

vue3-puzzle-vcode

安装

pnpm install vue3-puzzle-vcode --save

配置代码

<template>
    <button @click="onShow">开始验证</button>
    <Vcode :show="isShow" @success="onSuccess" @close="onClose" />
</template>

<script setup>
  import { ref } from "vue";
  import Vcode from "vue3-puzzle-vcode";

  const isShow = ref(false);

  const onShow = () => {
    isShow.value = true;
  };

  const onClose = () => {
    isShow.value = false;
  };

  const onSuccess = () => {
    onClose(); // 验证成功,需要手动关闭模态框
  };
</script>

改造到登录流程中

验证完输入框 直接发请求

验证完输入框 弹出验证码 验证码通过发请求

<template>
    <el-card class="loginPanel" style="max-width: 399px">
        <template #header>
            <div class="card-header">
                <img src="../assets/login.png"/>
            </div>
        </template>

        <el-form ref="formRef" hide-required-asterisk :rules="rules" :model="loginForm" label-width="60" style="max-width: 380px">
            <el-form-item label="用户名" prop="username">
                <el-input v-model="loginForm.username" :suffix-icon="User"/>
            </el-form-item>
            <el-form-item label="密码" prop="password">
                <el-input type="password" show-password v-model="loginForm.password" :suffix-icon="Lock" />
            </el-form-item>

            <el-form-item>
                <el-button plain color="#626aef" @click="loginSubmit">登录</el-button>
                <el-button plain color="#626aef" @click="loginReset">重置</el-button>
            </el-form-item>
        </el-form>


    </el-card>


    <Vcode :show="isShow" @success="onSuccess" @close="onClose" />


</template>

<script setup>
import { ref, reactive, onMounted } from 'vue'
import { Check, Delete, Edit, Message, Search, Star, Share,User,Lock } from '@element-plus/icons-vue'
import router from '@/router'
import { ElMessage,ElMessageBox  } from 'element-plus'

import {myGet,myPost} from '@/myaxios'


import Vcode from "vue3-puzzle-vcode";

/** 测试验证码... */
//控制验证码弹出框显示.隐藏
const isShow = ref(false);




//关框
const onClose = () => {
  isShow.value = false;
};

//拖动成功
const onSuccess = () => {
            onClose(); // 验证成功,需要手动关闭模态框
            console.log("提交表单数据",loginForm);
            myGet("/login",loginForm)
            .then(resp=>{
                
                console.log(resp.data);
                
                if(resp.data.code == 10010){
                    ElMessage.success(resp.data.msg)
                    //在前端缓存数据
                    sessionStorage.setItem("loginUser",JSON.stringify(resp.data.returnData) )
                    //跳转到主页面
                    router.push("/main")
                }else if(resp.data.code == 10011){
                    ElMessage.error(resp.data.msg)
                }else if(resp.data.code == 10012){
                    ElMessage.warning(resp.data.msg)
                }else if(resp.data.code == 10013){
                    ElMessage.info(resp.data.msg)
                }
                
            })
           
};




const loginForm = reactive({
    username:'',
    password:''
})

const formRef = ref()

const loginReset = ()=>{
    formRef.value.resetFields()
}
const loginSubmit = ()=>{
    formRef.value.validate((valid,fields)=>{
        if(valid){
            //验证通过开框
            isShow.value = true

        }
    })
}

const rules = reactive({
    username: [
        { required: true, message: '请输入用户名', trigger: 'blur' },
    ],
    password:[
        { required: true, message: '请输入密码', trigger: 'blur' },
        { min: 4, max: 8, message: '长度4-8位之间', trigger: 'blur' },
    ]
})



/*
 跨域(domain)请求
 当产生了 从一个网站 访问另一个网站数据 会出现跨域请求 CORS 会通知浏览器做访问限制
 默认不然产生跨域数据访问
 符合CORS 在服务端 设置允许访问

 浏览器对跨域的限定
    http:// 127.0.0.1 :  8080
    协议     地址        端口号
    浏览器自动发送预检请求 method options 检测响应头 是否符合CORS要求 
    通过后会缓存一定事件 




*/


// onMounted(()=>{
//     myGet("/login",{username:'aa',password:'bbb'})
    
// })



</script>

<style scoped>
/* scoped只对当前vue文件生效  */

.loginPanel {
    margin: 300px auto;
    background-color: rgba(219, 219, 219, 0.541);
}
</style>

4监听器

JAVAEE中监听器

域对象

request

session

servletContext 服务器对象

围绕对象的生命中后期监控

ServletContextListener 一般用在服务器启动时 加载第三方工具和启动资源上

package com.javasm.listener;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
import java.io.InputStream;

/**
 * @className: MyListener1
 * @description:
 * @author: gfs
 * @date: 2025/8/25 15:05
 * @version: 0.1
 * @since: jdk17
 */
@WebListener
public class MyBatisHealperPro implements ServletContextListener {

    private static SqlSessionFactory sqlSessionFactory;
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        //服务器对象创建了
        System.out.println("contextInitialized.......");

        try{
            //创建mybatis公共对象
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            //SqlSession            sql会话
            //SqlSessionFactory     sql会话 工厂  mybatis全局对象
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

        } catch (Exception e) {
            throw new RuntimeException(e);
        }


    }

    //获得连接
    public static SqlSession getSqlSession(){
        SqlSession sqlSession = sqlSessionFactory.openSession();
        return sqlSession;
    }
    //归还连接
    public static void backSqlSession(SqlSession sqlSession){
        sqlSession.close();
    }
    //提交事务 归还连接
    public static void backAndSaveSqlSession(SqlSession sqlSession){
        sqlSession.commit();
        sqlSession.close();
    }



    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        //服务器对象销毁了
        System.out.println("contextDestroyed.......");
    }
}

5版本控制工具

image-20250825155638533

操作说明

1.配置idea中 版本控制插件

image-20250825155830732

image-20250825155903392

注意 检查有没有svn.exe

如果没有 重新安装 勾选命令行 继续安装

image-20250825155959489

image-20250825160026531

2导出项目

服务器先放一份项目代码

image-20250825160458907

通过idea导出

image-20250825160547588

image-20250825160803800

3设置忽略列表

image-20250825161106733

image-20250825161401340

image-20250825161637946

注意: 忽略时 尽量不要用扩展名 指定目录 或指定文件

4文件操作过程 提交更新 处理冲突

image-20250825162051681

image-20250825162441011

注意: 删除文件操作 要提交 其他要更新

​ 目录名 文件名 提交定好 不要随便改 删除+新增

修改了代码 目录结构 新增了文件 删除了文件 都需要提交

更新代码

image-20250825162905048

一般以目录 更新

冲突情况

image-20250825163805545

处理冲突 先更新

image-20250825163848546

merge界面

image-20250825163919582

确定后 改好新版本 可以提交了

posted @ 2025-09-27 21:44  小胡coding  阅读(8)  评论(0)    收藏  举报