个人中心 - 实现修改用户头像、用户名或密码

目录

1. 修改用户头像

1.1 获取原来的用户头像和用户名

1.2 实现保存头像

2. 修改用户名或密码


1. 修改用户头像

本文是针对之前的一篇项目博客 - 博客系统 做的一个扩展功能.

1.1 获取原来的用户头像和用户名

想要修改头像, 那么就得先获取数据库中原来的头像, 此处顺便将原用户名一起获取并展示出来. 原密码最好不要获取出来, 因为有可能你在修改密码的中途, 你去上厕所了, 然后你的密码被你好兄弟给修改了. 

大致效果 : 

前端展示页面相关代码

<div class="row" style="margin-top: 70px;margin-bottom: 50px;"><img id="photo" src="img/bg2.jpg"style="width: 100px;height: 115px;"><input id="file" style="font-size: 12px;width: 120px;" type="file"><button style="width: 80px;height: 50px;" onclick="savePhoto()">保存</button>
</div>
<div class="row"><span>用户名</span><input type="text" id="username">
</div>
<div class="row"><span>原密码</span><input type="password" id="old_password">
</div>
<div class="row"><span>新密码</span><input type="password" id="password">
</div>
<div class="row"><span>确认密码</span><input type="password" id="password2">
</div>
<div class="row" style="margin-top: 10px;"><button id="submit" onclick="updateUser()">修 改</button>
</div>

写前端 js 代码

// 获取用户头像和昵称
function initPage() {jQuery.ajax({url:"/user/myinfo",type:"GET",data:{},success:function(body) {if(body.code==200 && body.data!=null && body.data.id>=0) {// 得到了当前的 userinfovar userinfo = body.dataif(userinfo.photo!=null && userinfo.photo!="") {jQuery("#photo").attr("src",userinfo.photo);}jQuery("#username").val(userinfo.username);} else {alert("抱歉: 查询用户信息出错, 请刷新页面再试! " + body.msg);}}});
}
initPage();

1. 获取当前登录人的用户名和头像, 只需在后端的 session 中拿到具体的 userinfo 即可.

2. sucess 回调函数中的注意事项:  因为我们在构造数据 (userinfo) 的时候, 头像一般都是写死的本地图片, 所以新用户一般都是默认头像, 所以我们在操作 dom 树构造 photo 的 src 属性时, 一定要判断 photo 是否为 null 或者是否为 "", 如果是就不要设置 photo 的 src 属性, 否则会导致用户没有头像.

写后端代码

@RequestMapping("/myinfo")
public Object myInfo(HttpServletRequest request) {// 从 session 工具类中拿用户登录信息UserInfo userInfo = SessionUtil.getLoginUser(request);if (userInfo == null || userInfo.getId() <= 0) {return AjaxResult.fail(-2,  "当前用户未登录!");}return AjaxResult.success(userInfo);
}

因为多处代码需要拿 session , 所以将其封装成了一个公共的方法.

1.2 实现保存头像

此处我们上传新的头像后, 并点击保存按钮时, 就是修改头像成功了.

写前端 js 代码 (给保存按钮添加点击事件)

// 保存头像
function savePhoto() {// 得到图片var photo = jQuery("#file")[0].files[0];if (photo == null) {alert("请先选择要上传的头像!");return false;}// 构建一个 form 表单var formData = new FormData();formData.append("file", photo);jQuery.ajax({url:"/user/save_photo",type:"POST",data:formData,processData:false, // 告诉 jQuery 不要去加工数据contentType:false, // 告诉 jQuery 不要设置类型success:function(body) {if(body.code==200 && body.data!=null && body.data!="") {jQuery("#photo").attr("src",body.data);} else {// 图片上传失败alert("抱歉: 上传图片失败, 请重试! " + body.msg);}}});
}

1. 此处的得到图片代码比较特殊

2. 发送 ajax 时, 参数是发送一个 form 表单给后端, 所以 ajax 中需要多添加两个参数 : processData 和 contentType.

3. 表单传给后端时, 后端针对图片生成一个网络地址映射到本地保存的地址, 然后将网络地址返回给前端, 前端操作 dom 树将其设置给 photo 的 src 属性.

写后端代码

1. 配置映射图片的路径

在配置文件中 application.properties 指定保存头像的本地路径 :

imagepath=D:/image/

在添加拦截规则的类里边加上 addResourceHandlers 类 :

@Value("${imagepath}")
private String imagepath;
*** 映射图片路径* @param registry*/
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {registry.addResourceHandler("/image/**").addResourceLocations("file:" + imagepath + "/");
}

2. 保存图片到服务器, 保存图片地址到数据库

@RequestMapping("/save_photo")
public Object savePhoto(MultipartFile file, HttpServletRequest request) {// 1.保存图片到服务器// 得到图片的后缀String imageType = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf("."));// 生成图片的名称String imgName = UUID.randomUUID().toString().replace("-", "") + imageType;try {file.transferTo(new File(imagePath + imgName));} catch (IOException e) {return AjaxResult.fail(-1, "图片上传失败!");}String imgUrl = "/image/" + imgName;// 2.将图片地址保存到数据库UserInfo userInfo = SessionUtil.getLoginUser(request);if(userInfo == null || userInfo.getId() <= 0) {return AjaxResult.fail(-2, "请先登录! ");}// 修改头像int result = userService.updatePhoto(userInfo.getId(),imgUrl);if(result == 1) {// 将用户的头像更新到 session 中userInfo.setPhoto(imgUrl);HttpSession session = request.getSession();session.setAttribute(Constant.SESSION_USERINFO_KEY, userInfo);return AjaxResult.success(imgUrl);} else {return AjaxResult.fail(-3, "数据库修改失败! ");}
}

步骤分析

1. 保存图片到服务器

当项目部署云服务器时, 用户访问项目并修改头像时, 是从用户的电脑上选取了一张图片上传到后端, 那么后端需要将这张图片保存到云服务器的一个本地路径, 然后再生成图片对应的网络地址. 最后将新头像对应的网络地址返回给前端, 前端就可以通过设置头像 photo 对应的 src 属性为返回的网络地址.

2. 保存图片到数据库

修改头像不仅要更新当前页面展示的头像, 数据库中的头像对应的网络地址也要更新, 另外如果更新数据成功了, 要同时更新 session 中的 userinfo 信息. 因为博客列表页的当前用户的身份信息, 后端都是从 session 中取出来返回给前端的, 所以需要同时更新数据库和 session 中的 photo 字段(属性).

此时修改头像操作就已经大功告成了~

2. 修改用户名或密码

此处我们点击个人中心跳转到修改用户信息的页面时, 它只是将头像和用户名展示出来了, 而原密码, 新密码和确认密码需要手动输入(要么三个都不为空 - 改, 要么都为空 - 不改).

  • 如果不修改密码, 只是修改用户名或者都不修改, 然后点击修改按钮, 就提示修改成功, 并跳转到我的内容管理页面.
  • 如果修改了密码, 并点击了修改按钮, 就提示修改成功, 并强制用户重新登录.

写前端 js 代码(给修改按钮添加点击事件)

function updateUser() {var isUpdatePassword = false; // 是否修改密码// 1.非空效验var username = jQuery("#username");var oldPassword = jQuery("#old_password");var password = jQuery("#password");var password2 = jQuery("#password2");if (username.val().trim() == "") {alert("请先输入新用户名!");username.focus();return false;}if (oldPassword.val() != "" ||password.val() != "" || password2.val() != "") {// 需要修改密码isUpdatePassword = true;if (oldPassword.val().trim() == "") {alert("请先输入原密码!");oldPassword.focus();return false;}if (password.val().trim() == "") {alert("请先输入新密码!");password.focus();return false;}if (password2.val().trim() == "") {alert("请先输入确认密码!");password2.focus();return false;}// 判断新密码和确认密码是否一致if (password.val() != password2.val()) {alert("两次输入的新密码不一致,请先确认!");return false;}}// 2.将前端的数据提交给后端jQuery.ajax({url: "/user/update",type: "POST",data: {"username": username.val(),"oldPassword": oldPassword.val(),"password": password.val(),"isUpdatePassword": isUpdatePassword},success: function (res) {// 3.将返回的结果展现给用户if (res.code == 200 && res.data == 1) {// 修改成功if (isUpdatePassword) {alert("修改成功,请重新登录!");// 修改密码,重新登录location.href = "login.html";} else {alert("修改成功!");location.href = "myblog_list.html";}} else {// 修改失败alert("抱歉:修改失败,请重试!" + res.msg);}}});
}

写后端代码

@RequestMapping("/update")
public Object update(String username, String oldPassword, String password,Boolean isUpdatePassword, HttpServletRequest request) {// 1.参数效验if(!StringUtils.hasLength(username)) {return AjaxResult.fail(-1, "非法参数! ");}// 是否要修改密码if(isUpdatePassword) {// 修改原密码if(!StringUtils.hasLength(oldPassword) || !StringUtils.hasLength(password)) {return AjaxResult.fail(-1, "非法参数! ");}}// 2.组装数据 (从 session 中获取用户信息)UserInfo userInfo = SessionUtil.getLoginUser(request);if(userInfo == null || userInfo.getId() <= 0) {return AjaxResult.fail(-2,"请先登录! ");}UpdateWrapper<UserInfo> wrapper = new UpdateWrapper<>();// 判断两次密码是否一致if(isUpdatePassword) {// 验证原密码和 session 中的密码是否一致UserInfo dbUser = userService.getById(userInfo.getId());boolean checkPassword = SaltSecurityUtil.decrypt(oldPassword,dbUser.getPassword());if(!checkPassword) {return AjaxResult.fail(-3,"原密码输入错误! ");}// 修改密码password = SaltSecurityUtil.encrypt(password);wrapper.set("password",password);}// 3.修改数据库wrapper.eq("id",userInfo.getId());wrapper.set("username", username);boolean result = userService.update(wrapper);// 更新 session 中的用户名if(result) {userInfo.setUsername(username);HttpSession session = request.getSession();session.setAttribute(Constant.SESSION_USERINFO_KEY, userInfo);}// 4.将结果返回给前端return AjaxResult.success(result ? 1 : 0);
}

【步骤分析】

1. 非空效验

        前端传递了新用户名, 原密码, 新密码, 以及是否修改了密码的标志, 于是在做判断时, 如果只修改了用户名, 就可以使用 isUpdatePassword 标志位来跳过更新数据密码的操作. 否则都要进行修改.

2. 组装数据

       组装好一个新的 userinfo (新的用户名或密码), 为更新数据库操作提供数据源, 此处更新密码成功的前提是原密码和数据库密码要保持一致, 而数据库中存储的是加密后的密码, 所以需要先拿着原密码和数据库中的密码去调用解密方法, 得到一个 boolean 类型的值, 再根据这个布尔值来判断是否要进行修改操作.

【注意】session 中的对象存储机制 >>

      由于我们是可以拿到当前用户的  session, 所以想要拿数据库中存储的密码, 我就会想着去 session 中拿到 userinfo, 再去拿到对应的密码, 这确实挺方便. 但是我在实现登录页面时, 登录成功后并将 session 存储 redis, 但是在返回数据给前端之前, 我执行了将密码置为空字符串这一操作, 因为密码如果通过网络传输返回给前端, 是不安全的. 

        <问题的出处>  正因为我的这一步置空字符串操作, 就会导致 session 中的密码也变成了空字符串. 这是为什么呢 ??

因为 session 的底层是用 concurrentHashMap 来保存数据的, 而 map 中并没有直接存储新的对象, 而是存储了对象的引用, 也就是 userinfo 的引用, 虽然我是在设置密码为空之前就将 userinfo 存储 session 了, 但是这也同样影响了 session 中的 password 了. 此时 session 中的 password 已经是空字符串了.

       再回到调用解密方法这一步, 我们就不能拿着原密码和 session 中的密码去调用解密码方法了, 而是需要拿着从 session 获取到的 userinfo 中的用户 id, 去查数据库得到一个新的 userinfo, 此时这个 userinfo 的密码才不为空, 才可以拿着它的 password 去和原密码去调用解密方法.

3. 修改数据库

        经过了第二步的组装数据, 第三步就变得简单了, 只需要使用 MP 进行修改操作即可, 但是在进行修改操作时, 修改后的用户名或密码最好设置在 updatewrapper 中, 然后只传一个 wrapper 对象. 如果将修改后的用户名或密码设置给 session 中的 userinfo, 然后再给 MP 传两个参数 (userinfo, wrapper), 那么有可能造成不必要的参数覆盖问题.

       另外就是修改完数据库之后, 要及时更新 session 中的用户名, 因为如果只修改了用户名, 不修改密码, 修改完成后会跳转博客列表页, 而博客列表页的用户身份信息都是从后端的 session 中来的, 如果不及时更新 session 的话, 那么在你下次重新登录之前, 用户名都不会变.

上述的 session 中的对象存储机制是参考这篇文章得出的结论  - 为何session中存入对象后,修改对象的属性值后并没有再次存入session,session中存放的对象也发生改变?

到此为止呢, 修改头像, 修改用户名或密码就全部实现完成了~


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/18133.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

mysql进阶-修改linux服务器中MySQL的字符集

1.背景 linux中mysql8默认的字符集是latin1&#xff0c;在插入中文时会报错&#xff0c;所以一般在配置好mysql时需要修改字符集为utf8【又叫utfmb3,一般开发够用&#xff0c;一个字符用3个字节表示】或者utfmb4【一个字符用4个字节表示&#xff0c;如果存储emoji表情&#xf…

容器部署jenkins定时构建于本地时间不一致

1. Dockerfile FROM jenkins/jenkins:2.411-jdk11 USER root #以下生成密钥方式为旧格式&#xff0c;因为新格式暂不能被"Publish over SSH--->Jenkins SSH Key"功能识别 RUN ssh-keygen -q -m PEM -t rsa -b 2048 -N -f /root/.ssh/id_rsa ADD ./apache-maven…

【uniapp】实现买定离手小游戏

前言 最近玩了一个小游戏&#xff0c;感觉挺有意思&#xff0c;打算放进我的小程序【自动化小助手】里面&#xff0c;“三张押一张&#xff0c;专押花姑娘&#xff01;”&#xff0c;从三张卡牌&#xff0c;挑选一张&#xff0c;中奖后将奖励进行发放&#xff0c;并且创建下一…

减轻 PWM 的滤波要求

经典脉宽调制器 (PWM) 发出 H 个连续逻辑高电平&#xff08;1&#xff09;&#xff0c;后跟 L 个连续逻辑低电平&#xff08;0&#xff09;的重复序列。每个高电平和低电平持续一个时钟周期 T 1/F (Hz)。结果的占空比可定义为 H/N&#xff0c;其中 N HL 时钟周期。N 通常是 2…

【面试】某公司记录一次面试题

文章目录 框架类1. Spring boot与 spring 架相比&#xff0c;好在哪里?2. Spring boot以及 Spring MVC 常用注解(如requestingMapping&#xff0c;responseBody 等)3. 常用的java 设计模式&#xff0c;spring 中用到哪些设计模式4. SpringIOC是什么&#xff0c;如何理解5. AOP…

设计模式之外观模式

外观模式 本质&#xff1a; 封装交互&#xff0c;简化调用 何时使用外观模式 相关模式 外观模式经典代码&#xff1a; package com.tao.design.facade;/*** projectName: DesignMode* package: com.tao.design.facade* className: ServiceA* author: TT_Hugo* description…

替换linux的文泉驿正黑fonts-wqy-zenhei字体 替换linux默认中文字体

WSL 怎么替换 linux 的文泉驿正黑 fonts-wqy-zenhei 字体 WSL 怎么替换 linux 默认中文字体 在 wsl 中默认是没有 gnome 界面或者 xface 的&#xff0c;但是我需要使用 wsl 开发 electron 或者使用 chrome 浏览器。这个时候系统就会调用默认的系统字体了。 我使用的是 debian…

Android 9系统源码_音频管理(一)按键音效源码解析

前言 当用户点击Android智能设备的按钮的时候&#xff0c;如果伴随有按键音效的话&#xff0c;会给用户更好的交互体验。本期我们将会结合Android系统源码来具体分析一下控件是如何发出按键音效的。 一、系统加载按键音效资源 1、在TV版的Android智能设备中&#xff0c;我们…

Linux - gcc/g++工具使用

gcc/g是用于编译C/C程序的编译器 1.编译过程 1. 预处理&#xff08;头文件展开&#xff0c;条件编译&#xff0c;进行宏替换&#xff0c;去注释等) 2. 编译&#xff08;C语言汇编语言) 3. 汇编&#xff08;汇编->可重定位目标二进制文件&#xff0c;不可以被执行的&#xff…

【LLM系列之指令微调】长话短说大模型指令微调的“Prompt”

1 指令微调数据集形式“花样”太多 大家有没有分析过 prompt对模型训练或者推理的影响&#xff1f;之前推理的时候&#xff0c;发现不加训练的时候prompt&#xff0c;直接输入模型性能会变差的&#xff0c;这个倒是可以理解。假如不加prompt直接训练&#xff0c;是不是测试的时…

kafka集群搭建(Linux环境)

zookeeper搭建&#xff0c;可以搭建集群&#xff0c;也可以单机&#xff08;本地学习&#xff0c;没必要搭建zookeeper集群&#xff0c;单机完全够用了&#xff0c;主要学习的是kafka&#xff09; 1. 首先官网下载zookeeper&#xff1a;Apache ZooKeeper 2. 下载好之后上传到…

IP 监控软件

IP 监控软件可帮助管理员主动监控网络资源。随着各种设备连接到网络&#xff0c;监控设备和接口可能很复杂&#xff0c;为管理员提供这些设备的IP监控&#xff0c;了解其各种性能指标和问题。 使用有效的 IP 监控软件的优势 使用有效的 IP 监控系统和一套全面的 IP 监控工具&…

qt子进程和父进程读写数据通信

进程A&#xff08;例如主程序&#xff09;创建了一个QProcess B&#xff0c;这个B就称为A的子进程&#xff0c;而A称为B的父进程。 这也称为进程间通信&#xff0c;有多种方式&#xff1a; TCP/IPLocal Server/Socket共享内存D-Bus &#xff08;Unix库&#xff09;QProcess会…

Moonbeam:开发者的多链教科书

了解波卡的技术架构&#xff0c;只需掌握3个关键词&#xff1a; Relay Chain&#xff08;中继链&#xff09;&#xff1a;Polkadot将自身视作多核计算机&#xff0c;承载区块链底层安全架构的辐射中心。Parachain&#xff08;平行链&#xff09;&#xff1a;在“Layer 0”架构…

Postman如何导出接口的几种方法

本文主要介绍了Postman如何导出接口的几种方法&#xff0c;文中通过示例代码介绍的非常详细&#xff0c;具有一定的参考价值&#xff0c;感兴趣的小伙伴们可以参考一下 前言&#xff1a; 我的文章还是一贯的作风&#xff0c;简确用风格&#xff08;简单确实有用&#xff09;&am…

【从零开始学习JAVA | 三十四篇】IO流

目录 前言&#xff1a; IO流介绍&#xff1a; IO流的常见方法&#xff1a; 1.字节流类&#xff1a; 2.字符流类&#xff1a; 总结&#xff1a; 前言&#xff1a; IO流就是存入和读取数据的解决方案&#xff0c;并且他是一个知识点很多的章节&#xff0c;因此我们关于IO流…

Jmeter —— jmeter接口自动化测试操作流程

在企业使用jmeter开展实际的接口自动化测试工具&#xff0c;建议按如下操作流程&#xff0c; 可以使整个接口测试过程更规范&#xff0c;更有效。 接口自动化的流程&#xff1a; 1、获取到接口文档&#xff1a;swagger、word、excel ... 2、熟悉接口文档然后设计测试用例&am…

ES开启身份认证

文章目录 X-Pack简介之前的安全方案ES开启认证ES服务升级https协议开启集群节点之间的证书认证 X-Pack简介 X-Pack是Elastic Stack扩展功能&#xff0c;提供安全性&#xff0c;警报&#xff0c;监视&#xff0c;报告&#xff0c;机器学习和许多其他功能。 X-Pack的发展演变&am…

Pytest学习教程_测试报告生成pytest-html(三)

前言 pytest-html 是一个用于生成漂亮的 HTML 测试报告的 pytest 插件。它可以方便地将 pytest 运行的测试结果转换为易于阅读和理解的 HTML 报告&#xff0c;提供了丰富的测试结果展示功能和交互性。 一、安装 # 版本查看命令 pytest版本&#xff1a; pytest --version pyte…

VGN N75pro说明书

VGN N75pro说明书 1、封面和最后一页 2、第01、02、03 3、第04 4、第05