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

目录

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) {
                // 得到了当前的 userinfo
                var userinfo = body.data
                if(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,session中存放的对象也发生改变?

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


  • 6
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
本门课程共有6个章节,通过4个项目,让您掌握Springboot和SpringCloud作为服务端的工作机制,Angular框架作为前端开发框架的使用方法,以及应用于网站制作和作为移动端用户界面;Spring Security+JWT作为安全框架,对网站和移动端,进行用户认证、角色访问控制;掌握Hybird开发技术在Android和iOS移动客户端中的应用;云服务器配置技术,包括java运行环境安装、Nginx作为web服务器和前后端分离的实现、SSL证书的申请与配置等内容; 第一章介绍了前后端开发环境的搭建和开发工具的安装,包括Java JDK的安装、Maven依赖管理工具的安装、NodeJS开发环境的安装、Angular开发环境的安装、Git版本管理工具的使用 第二章通过体脂计算器(BMI)项目,掌握使用Maven生成Springboot工程,Angular与Springboot的协作机制、从而理解前后端分别架构的实现方法。 第三章通过开发一个CMS系统,既网站内容管理系统,来掌握Springboot操作数据库、Angular框架与NG-BootStrap框架作为前端框架,并与Springboot服务端交互的协作机制;其中还包含了程序员的必备技能,既增、删、查、改、数据分页、文件上传、Web在线编辑器的集成与使用等内容。通过本章节的学习,你可以做出一个能自动适应各种屏幕尺寸网站项目。 第四章我们通过开发CMS系统的移动端版本,来掌握ionic框架的使用;首先我们我们将第三章的网站内容无缝衔接的显示在移动端上,并通过一个仿微信朋友圈的模块,来掌握ionic capacitor框架调用手机的摄像头,以及图片的编码转换等内容, 第五章我们通过使用Spring Security+Jwt技术来保护我们的CMS网站版和移动端,来学习Spring boot+JWT的运行机制。然后我们通过将服务端和Web端部署到云服务器上,来学习网络系统的上云步骤,其中包括域名申请、服务器采购、服务器配置,其中包括,Java运行环境的安装、Springboot运行环境部署,Nginx通过代理实现前后端分离,以及SSL证书的申请与配置等内容,服务器操作系统我们使用Cent OS,你还能学到使用XShell和XFTP进行网站管理;除此之外,我们还介绍了,android开发环境的配置以及签名发布、iOS开发环境的配置,并最终实现在真机设备上可以运行CMS移动端项目。 第六章我们通过一个网上花店电商项目作为实训项目,这个项目,服务端部分我们使用Spring Cloud,通过用户认证模块、花店模块、支付中心模块之间的相互协作,让您掌握在Spring Cloud框架中Eureka、Zuul、Feign的使用方法;客户端部分,在这个项目中我们采用运行在云服务器上的ionic作为用户界面,并通过Ionic与Android和iOS原生代码的交互过程,来学习JS Bridge技术,从而掌握Hybird移动端开发技术,这种技术可以有效地解决App上架缓慢、更新不及时的问题。同时平衡使用Web与原生开发的性能问题。花店项目是一个电商项目,自然少不了支付功能,我们在课程中可以学到如何集成微信和支付宝,并使用他们进行登录验证和支付。  为什么我要上这门课 现在的网络应用,一般都在网站、Android、iOS和微信四平台同时发布,为了适应这一趋势,我们必须把自己打造成“全栈工程师”,能独立开发“一网三平台”网络应用才能在目前的市场环境中生存。而这四个平台,以往至少需要四名程序员才能完成,面对浩如烟海的知识,本门课程采取弱水三千尺,只取一瓢饮的办法,从开发工作的实际需要出发,将工作中最常用的20%提炼出来,并在实际应用中,反复演练,让你通过,每门技术这最常用的20%,来应付80%的工作。 谁应该报名上这门课如果你是一名计算机专业的学生,那么本门课程可以作为你的实训课程,让你体验到软件企业开发工作中的实际需要,并在本门课程的学习过程中,快速度过计算机专业学生普遍存在的“断奶期”问题 如果您是一名渴望丰富自身技能的程序员,并且对Springboot、Spring Security+JWT、Spring Cloud、Angular、Ionic、Hybird移动端开发技术,有着浓厚的兴趣,但对于瀚如烟海的知识点,又让您望洋兴叹,那么在本门课程中,将会为您抛砖引玉,让您茅塞顿开。 
好的,针对您的问题,我可以提供一些思路和代码实现的参考。 首先,对于修改个人资料的功能,我们需要在后端代码中编写对应的接口,用于接收前端传来的数据并进行处理。以下是一个简单的 Java Servlet 接口示例: ```java @WebServlet("/updateProfile") public class UpdateProfileServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 获取表单数据 String username = request.getParameter("username"); String password = request.getParameter("password"); String nickname = request.getParameter("nickname"); Part avatar = request.getPart("avatar"); // 更新用户信息 // ... // 返回响应 response.setContentType("text/html; charset=UTF-8"); PrintWriter out = response.getWriter(); out.println("<script>alert('个人资料更新成功!');window.location.href='profile.jsp';</script>"); } } ``` 在这个例子中,我们通过 `request.getParameter()` 方法获取前端传来的用户名密码和昵称等数据,通过 `request.getPart()` 方法获取头像文件数据。然后,我们可以通过自己编写的服务层代码来实现更新用户信息的功能。 对于头像文件数据的处理,我们可以使用 `Part` 类来实现。以下是一个简单的处理方式: ```java Part avatar = request.getPart("avatar"); String filename = avatar.getSubmittedFileName(); String contentType = avatar.getContentType(); InputStream inputStream = avatar.getInputStream(); byte[] data = new byte[inputStream.available()]; inputStream.read(data); ``` 在这个例子中,我们通过 `getSubmittedFileName()` 获取上传的文件名,通过 `getContentType()` 获取文件类型,通过 `getInputStream()` 获取文件数据流,并通过 `read()` 方法将数据流读入字节数组中。 最后,我们需要在前端页面中编写相应的表单和 JavaScript 代码,用于向后端发送数据并处理返回的响应。以下是一个简单的示例: ```html <form action="updateProfile" method="post" enctype="multipart/form-data"> <input type="text" name="username" placeholder="用户名" required> <input type="password" name="password" placeholder="密码" required> <input type="text" name="nickname" placeholder="昵称"> <input type="file" name="avatar"> <button type="submit">更新个人资料</button> </form> <script> $("form").submit(function(event) { event.preventDefault(); var formData = new FormData(this); $.ajax({ url: $(this).attr("action"), type: "POST", data: formData, processData: false, contentType: false, success: function(response) { $("body").html(response); }, error: function(xhr, status, error) { alert("个人资料更新失败!"); } }); }); </script> ``` 在这个例子中,我们使用了 jQuery 库来处理表单的提交事件,并通过 AJAX 技术向后端发送数据。在成功更新个人资料后,我们通过 JavaScript 代码将页面重定向到个人资料展示页面。 以上是一个简单的示例,您可以根据自己的实际需求进行修改和扩展。希望对您有所帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Master_hl

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值