SpingBoot的项目实战--模拟电商【2.登录】

🥳🥳Welcome Huihui's Code World ! !🥳🥳

接下来看看由辉辉所写的关于SpringBoot电商项目的相关操作吧 

目录

🥳🥳Welcome Huihui's Code World ! !🥳🥳

一.功能需求

二.代码编写

1.登录功能的完成

2.全局异常的处理

3.登录密码的两次加密

(1)为什么要加密两次

(2)整体的加密流程

(3)具体流程的代码实现

①前端加密

②加密之后传到后端

③后端拿取用户信息

⑤登录测试


一.功能需求

①完成用户登录功能

②用户的各种错误操作都需要给出相应的错误提示,而不是抛出异常【全局异常处理】

③用户登录的密码的两次加密

        表单数据➡后端【加密】

        后端数据➡数据库【加密】

④用户登录成功之后,需要在首页显示出登录的用户的昵称【Redis+Cookie】

⑤用户输入表单时,前端【表单验证】和后端【JSR303】都需要有相对应的验证

二.代码编写

1.登录功能的完成

package com.wh.easyshop.service.impl;import com.baomidou.mybatisplus.extension.conditions.query.QueryChainWrapper;
import com.wh.easyshop.model.User;
import com.wh.easyshop.mapper.UserMapper;
import com.wh.easyshop.resp.JsonResponseBody;
import com.wh.easyshop.resp.JsonResponseStatus;
import com.wh.easyshop.service.IUserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.wh.easyshop.vo.UserVo;
import org.springframework.stereotype.Service;import javax.swing.*;/*** <p>* 用户信息表 服务实现类* </p>** @author wh* @since 2023-12-27*/
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {/*** 登录* @param userVo* @return*/public JsonResponseBody<?> login (UserVo userVo){//如果传过来的电话号码(登录名)为空,那么提示用户相应的信息if(userVo.getMobile()==null){return JsonResponseBody.other(JsonResponseStatus.LOGIN_MOBILE_INFO);}//如果传过来的密码为空,那么提示用户相应的信息if(userVo.getPassword()==null){return JsonResponseBody.other(JsonResponseStatus.LOGIN_PASSWORD_INFO);}User user = getOne(new QueryWrapper<User>().lambda()//判断用户名以及密码是否一致.eq(User::getId, userVo.getMobile()).eq(User::getPassword, userVo.getPassword()));//如果内容匹配不成功,那么提示用户相应的信息if(user==null){return JsonResponseBody.other(JsonResponseStatus.LOGIN_NO_EQUALS);}//如果带来的信息都一致,则提示成功return  JsonResponseBody.success();}
}

其中用到的响应类

package com.wh.easyshop.resp;import lombok.Data;@Data
public class JsonResponseBody<T> {private Integer code;private String msg;private T data;private Long total;private JsonResponseBody(JsonResponseStatus jsonResponseStatus, T data) {this.code = jsonResponseStatus.getCode();this.msg = jsonResponseStatus.getMsg();this.data = data;}private JsonResponseBody(JsonResponseStatus jsonResponseStatus, T data, Long total) {this.code = jsonResponseStatus.getCode();this.msg = jsonResponseStatus.getMsg();this.data = data;this.total = total;}public static <T> JsonResponseBody<T> success() {return new JsonResponseBody<T>(JsonResponseStatus.OK, null);}public static <T> JsonResponseBody<T> success(T data) {return new JsonResponseBody<T>(JsonResponseStatus.OK, data);}public static <T> JsonResponseBody<T> success(T data, Long total) {return new JsonResponseBody<T>(JsonResponseStatus.OK, data, total);}public static <T> JsonResponseBody<T> unknown() {return new JsonResponseBody<T>(JsonResponseStatus.UN_KNOWN, null);}public static <T> JsonResponseBody<T> other(JsonResponseStatus jsonResponseStatus) {return new JsonResponseBody<T>(jsonResponseStatus, null);}}
package com.wh.easyshop.resp;import lombok.Getter;@Getter
public enum JsonResponseStatus {OK(200, "OK"),UN_KNOWN(500, "未知错误"),LOGIN_MOBILE_INFO(5001, "未携带手机号或手机号格式有误"),LOGIN_PASSWORD_INFO(5002, "未携带密码或不满足格式"),LOGIN_NO_EQUALS(5003, "登录信息不一致"),LOGIN_MOBILE_NOT_FOUND(5004, "登录手机号未找到"),;private final Integer code;private final String msg;JsonResponseStatus(Integer code, String msg) {this.code = code;this.msg = msg;}public String getName(){return this.name();}}

我这里为了规范,还建了一个vo类,而且也考虑到后面需要做验证,为了不污染实体类与数据库的连接,还是需要建一个vo类的

VO类:

  1. 数据传输:VO类可以用于封装客户端和服务器之间的数据传输,例如RESTful API的请求和响应对象。

  2. 数据库实体映射:VO类可以用于将数据库表的记录映射为Java对象,并进行数据的读取和存储操作。

  3. 领域模型中的值对象:在领域驱动设计(DDD)中,VO类可以用于表示领域模型中的值对象,如金额、日期范围等。

总之,VO类主要用于封装和传递数据,以提高代码的可读性、可维护性和可测试性。它们通常是不可变的,并且只包含属性和访问方法

package com.wh.easyshop.vo;import lombok.Data;@Data
public class UserVo {private String mobile;private String password;}

但是上面的这个登录功能的代码只是很粗浅的,我们还需要将它进行升级

2.全局异常的处理

一个用户在输入自己的信息进行登录的时候,很可能会有一些非常规操作,一般这个时候,它会有一些错误以及异常抛出,我们可以使用全局异常进行处理

全局异常:

  1. 方便错误排查和日志记录:全局异常处理可以捕捉并记录异常信息,方便开发人员进行错误排查和系统故障分析。

  2. 提供友好的用户体验:通过合理的异常处理,可以向用户提供友好的错误提示,帮助他们理解发生的问题,并提供相应的解决方案

我先把原先的代码进行了修改,将前面有错误提示信息的地方,都换成异常【自定义异常】给它抛出

自定义异常

package com.wh.easyshop.exception;import com.wh.easyshop.resp.JsonResponseStatus;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;@EqualsAndHashCode(callSuper = true)
@AllArgsConstructor
@NoArgsConstructor
@Data
public class BusinessException extends RuntimeException {private JsonResponseStatus jsonResponseStatus;}

但是这个异常抛出了,我们需要一个类来处理它--全局异常处理类,编写全局异常处理类,需要 用到一个注解@RestControllerAdvice

@RestControllerAdvice:

 @RestControllerAdvice 是 Spring 框架中的一个注解,用于定义全局异常处理器(Global Exception Handler)。

在 Spring MVC 中,当应用程序抛出异常时,可以使用 @ExceptionHandler 注解来处理该异常。但是,如果在多个控制器中都有相同的异常处理逻辑,那么需要在每个控制器中都编写相同的代码,这样会导致代码冗余和可维护性差。

        @RestControllerAdvice 的作用就是解决这个问题,它结合了                         @ControllerAdvice@ResponseBody 两个注解的功能,用于全局处理控制器抛出的异常,并返回相应的错误信息。

        使用 @RestControllerAdvice 注解的类可以包含多个被 @ExceptionHandler 注解修饰的方法,每个方法用于处理不同类型的异常。当应用程序抛出异常时,Spring 框架会根据异常的类型自动调用对应的异常处理方法。

        @RestControllerAdvice 类中的异常处理方法可以包含自定义的逻辑,比如记录日志、返回特定的错误信息等。通常,异常处理方法会返回一个包含错误信息的响应实体,供客户端进行处理。

        总之,@RestControllerAdvice 注解用于定义全局异常处理器,通过集中处理控制器抛出的异常,提高代码的可维护性和复用性。它可以在一个类中定义多个异常处理方法,根据异常类型自动调用相应的方法,并返回相应的错误信息。【其实简而言之,就是当controller抛出异常的时候,不会往外面抛了,这个注解相当于是@Controller的增强类,把@Controller给包裹起来了】

全局异常的编写

package com.wh.easyshop.exception;import com.wh.easyshop.resp.JsonResponseBody;
import com.wh.easyshop.resp.JsonResponseStatus;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.BindException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;import java.lang.annotation.Annotation;
import java.util.Arrays;
import java.util.Objects;@RestControllerAdvice // 声明这是一个全局异常处理器类
@Slf4j // 使用log4j进行日志记录
public class GlobalExceptionHandler {// 处理业务异常@ExceptionHandler(BusinessException.class)public JsonResponseBody<?> exceptionBusinessException(BusinessException e) {JsonResponseStatus status = e.getJsonResponseStatus(); // 获取异常的状态信息log.info(status.getMsg()); // 记录日志return JsonResponseBody.other(status); // 返回状态信息}// 处理其他类型的异常@ExceptionHandler(Throwable.class)public JsonResponseBody<?> exceptionThrowable(Throwable e) {log.info(e.getMessage()); // 记录日志return JsonResponseBody.other(JsonResponseStatus.UN_KNOWN); // 返回未知状态信息}}

3.登录密码的两次加密

(1)为什么要加密两次

        第一次加密防止前端传递数据时被截取

       

 第二次加密防止数据库泄露

(2)整体的加密流程

        MD5(MD5(pass明文+固定salt)+随机salt)
        第一次固定salt写死在前端
        第二次加密采用随机的salt 并将每次生成的salt保存在数据库中

(3)具体流程的代码实现

①前端加密

对用户输入的密码进行md5加密(固定的salt)

引入md5的js

<script src="http://www.gongjuji.net/Content/files/jquery.md5.js" type="text/javascript"></script>

使用MD5加密

<script>$("#login").click(()=>{let mobile=$("#mobile").val()let password=$("#password").val()password=$.md5(password)$.post(' ${springMacroRequestContext.contextPath}/user/login',{mobile,password},resp=>{},"json")})</script>

但是我们知道MD5它的加密方式是不可逆的,也很容易被解析,所以我们可以自己加盐进去,在前后都加上字符

②加密之后传到后端

将加密后的密码传递到后端

package com.wh.easyshop.controller;import com.sun.corba.se.spi.orb.ParserImplBase;
import com.wh.easyshop.resp.JsonResponseBody;
import com.wh.easyshop.service.IUserService;
import com.wh.easyshop.vo.UserVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;/*** <p>* 用户信息表 前端控制器* </p>** @author wh* @since 2023-12-27*/
@RestController
@RequestMapping("/user")
public class UserController {@Autowiredprivate IUserService userService;@RequestMapping("/login")public JsonResponseBody<?> login(UserVo userVo){return userService.login(userVo);}}
③后端拿取用户信息

使用用户id取出用户信息

 ④后端加密

后端对前端传过来的加密后的密码在进行md5加密(取出盐),然后与数据库中存储的密码进行对比

package com.wh.easyshop.service.impl;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.QueryChainWrapper;
import com.wh.easyshop.exception.BusinessException;
import com.wh.easyshop.model.User;
import com.wh.easyshop.mapper.UserMapper;
import com.wh.easyshop.resp.JsonResponseBody;
import com.wh.easyshop.resp.JsonResponseStatus;
import com.wh.easyshop.service.IUserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.wh.easyshop.util.MD5Utils;
import com.wh.easyshop.vo.UserVo;
import org.springframework.stereotype.Service;import javax.swing.*;/*** <p>* 用户信息表 服务实现类* </p>** @author wh* @since 2023-12-27*/
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {/*** 登录* @param userVo* @return*/@Overridepublic JsonResponseBody<?> login (UserVo userVo){//通过用户名拿到用户的信息User user = getOne(new QueryWrapper<User>().lambda().eq(User::getId, userVo.getMobile()), false);//如果内容匹配不成功,那么提示用户相应的信息if(user==null){throw  new BusinessException(JsonResponseStatus.LOGIN_MOBILE_NOT_FOUND);}//把数据库的盐值与前端的密码都拿出来,再加密一次【随机盐值】String secret = MD5Utils.formPassToDbPass(userVo.getPassword(), user.getSalt());//将数据库里面的密码与上面二次加密之后的密码进行比较,如果不一致就提示相应信息if(!user.getPassword().equals(secret)){throw  new BusinessException(JsonResponseStatus.LOGIN_NO_EQUALS);}//如果带来的信息都一致,则提示成功return  JsonResponseBody.success();}}
⑤登录测试

🔺登录与注册之间的逻辑大概也是差不多的,再这里小编没有做注册了,但是没有做注册,我们的数据库中就没有数据,所以我们要使用debug将数据手动的加到数据库中,不然这个登录就永远都是失败的

把盐值拿到也放到数据库中,这里我用的是固定的盐值,大家也可以用时间戳等一些随机的不会重复的数字

package com.wh.easyshop.util;import org.springframework.stereotype.Component;
import org.springframework.util.DigestUtils;import java.nio.charset.StandardCharsets;
import java.util.UUID;@Component
public class MD5Utils {//加密盐,与前端一致private static final String salt = "f1g2h3j4";public static String md5(String src) {return DigestUtils.md5DigestAsHex(src.getBytes(StandardCharsets.UTF_8));}public static String createSalt() {return UUID.randomUUID().toString().replace("-", "");}/*** 将前端的明文密码通过MD5加密方式加密成后端服务所需密码,混淆固定盐salt,安全性更可靠*/public static String inputPassToFormPass(String inputPass) {String str = salt.charAt(1) + String.valueOf(salt.charAt(5)) + inputPass + salt.charAt(0) + salt.charAt(3);return md5(str);}/*** 将后端密文密码+随机salt生成数据库的密码,混淆固定盐salt,安全性更可靠*/public static String formPassToDbPass(String formPass, String salt) {String str = salt.charAt(7) + String.valueOf(salt.charAt(9)) + formPass + salt.charAt(1) + salt.charAt(5);return md5(str);}public static void main(String[] args) {String formPass = inputPassToFormPass("123456");System.out.println("前端加密密码:" + formPass);String salt = createSalt();System.out.println("后端加密随机盐:" + salt);String dbPass = formPassToDbPass(formPass, salt);System.out.println("后端加密密码:" + dbPass);}}

好啦,今天的分享就到这了,希望能够帮到你呢!😊😊 

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

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

相关文章

基于SpringBoot的校园生活服务平台

文章目录 项目介绍主要功能截图:部分代码展示设计总结项目获取方式🍅 作者主页:超级无敌暴龙战士塔塔开 🍅 简介:Java领域优质创作者🏆、 简历模板、学习资料、面试题库【关注我,都给你】 🍅文末获取源码联系🍅 项目介绍 基于SpringBoot的校园生活服务平台,java…

目标检测损失函数:IoU、GIoU、DIoU、CIoU、EIoU、alpha IoU、SIoU、WIoU原理及Pytorch实现

前言 损失函数是用来评价模型的预测值和真实值一致程度&#xff0c;损失函数越小&#xff0c;通常模型的性能越好。不同的模型用的损失函数一般也不一样。损失函数主要是用在模型的训练阶段&#xff0c;如果我们想让预测值无限接近于真实值&#xff0c;就需要将损失值降到最低…

uniApp中uView组件库的丰富布局方法

目录 基本使用 #分栏间隔 #混合布局 #分栏偏移 #对齐方式 API #Row Props #Col Props #Row Events #Col Events UniApp的uView组件库是一个丰富的UI组件库&#xff0c;提供了各种常用的UI组件和布局方法&#xff0c;帮助开发者快速构建美观、灵活的界面。下面给你写一…

ES6语法(四)class类 数值扩展方法对象扩展方法介绍

1. Class类 es6引入了类Class 的概念&#xff0c;作为对象的模板。可以使用Class关键字定义类&#xff0c;通过类实例化对象。   Class类可以看做是一个语法糖&#xff0c;绝大部分功能也可以通过es5做到。   Class写法只是让对象原型的写法更加清晰、更像面向对象变成的语…

通过代码启动 uiautomatorviewer

目录 一、前置说明1、总体目录2、相关回顾3、本节目标二、操作步骤1、目录设置2、代码实现1)ProcessManager 类:添加 is_running 方法2)UIAutomatorViewer类:提供 start、stop方法3、测试代码4、日志输出三、后置说明1、要点小结2、下节预告

C++设计模式 #8 抽象工厂(Abstract Factory)

抽象工厂这个名字比较难以帮助理解&#xff0c;可以把抽象工厂理解为“品牌工厂”或者“家族工厂”。 动机 在软件系统中&#xff0c;经常面临着“一系列相互依赖的对象”的创建工作&#xff1b;同时&#xff0c;由于需求的变化&#xff0c;往往存在更多系列对象的创建工作。如…

查看各个文件夹大小的linux命令

可以使用 du 命令来查看当前各个文件夹的大小。以下是一些常用的 du 命令选项&#xff1a; du&#xff1a;显示当前目录下所有文件和文件夹的大小。 du -h&#xff1a;以人类可读的方式显示文件夹大小&#xff0c;以 K、M、G 等单位表示。 du -s&#xff1a;显示每个目录的总大…

C# 常用数据类型及取值范围

1.常见数据类型和取值范围 序号数据类型占字节数取值范围1byte10 到 2552sbyte1-128 到 1273short 2-32,768 到 32,7674ushort20 到 65,5355int4-2,147,483,648 到 2,147,483,6476uint40 到 4,294,967,2957float41.5 x 10−45 至 3.4 x 10388double85.0 10−324 到 1.…

springboot(ssm游戏交易系统 游戏出售平台 Java系统

springboot(ssm游戏交易系统 账号出售平台 Java系统 开发语言&#xff1a;Java 框架&#xff1a;ssm/springboot vue JDK版本&#xff1a;JDK1.8&#xff08;或11&#xff09; 服务器&#xff1a;tomcat 数据库&#xff1a;mysql 5.7&#xff08;或8.0&#xff09; 数据库…

位运算|比特位计数、汉明距离

位运算|比特位计数、汉明距离 338 比特位计数 /** 比特位计数法一&#xff1a;Brian Kernighan 算法的原理是&#xff1a;对于任意整数 x&#xff0c;令 xx & (x−1)&#xff0c;该运算将 x 的二进制表示的最后一个 1 变成 0。因此&#xff0c;对 x 重复该操作&#xff0…

极智开发 | 解读英伟达软件生态 深度神经网络库cuDNN

欢迎关注,获取我的更多经验分享 大家好,我是极智视界,本文来介绍一下 解读英伟达软件生态 深度神经网络库cuDNN。 邀您加入我的知识星球「极智视界」,星球内有超多好玩的项目实战源码下载,链接:https://t.zsxq.com/0aiNxERDq cuDNN,全称为 NVIDIA CUDA Deep Neural Net…

Transform环境搭建与代码调试——Attention Is All Y ou Need

1、源代码 2、环境搭建 conda create -n transform python3.8 -y conda activate transform cd /media/lhy/Transforms/annotatedtransformerpip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple # # Uncomment for colabpip install -q torchdata…

一篇文章带你轻松入门Python

Python基础 1. Hello World! Python命令行 假设你已经安装好了Python, 那么在命令提示符输入: python 将直接进入python。然后在命令行提示符>>>后面输入: >>>print(Hello World!) 可以看到&#xff0c;随后在屏幕上输出: print是一个常用函数&#xf…

进击的奶牛

题目 进击的奶牛 题意 通过二分查找算法找到一个最小间距x&#xff0c;使得在数组a中选出的k个数两两之间的间距都不小于x&#xff0c;并且x尽可能大。最后输出这个最大的x值。 思路 程序通过循环依次获取了n个整数&#xff0c;存储在数组a中。.然后&#xff0c;程序对数组a进…

阿里云PolarDB数据库费用价格表

阿里云数据库PolarDB租用价格表&#xff0c;云数据库PolarDB MySQL版2核4GB&#xff08;通用&#xff09;、2个节点、60 GB存储空间55元5天&#xff0c;云数据库 PolarDB 分布式版标准版2核16G&#xff08;通用&#xff09;57.6元3天&#xff0c;阿里云百科aliyunbaike.com分享…

HPM6750开发笔记《开发环境的搭建》

目录 一&#xff0c;下载完整的HPM—SDK 二&#xff0c;安装硬件驱动 二&#xff0c;软件激活 三&#xff0c;创建工程 1.用文档中给的方法创建工程&#xff1a; 2.用sdk_env_v1.3.0中提供的工具创建工程&#xff1a; 一&#xff0c;下载完整的HPM—SDK 下载网址&#x…

Python sanic框架钉钉和第三方打卡机实现

同样还是需要开通钉钉应用这里就不错多说了 第一步:梳理逻辑流程 前提&#xff1a;打卡的机器是使用postgres数据库&#xff0c;由于因为某些原因&#xff0c;钉钉userId 我已经提前获取到了存放到数据库里。 1.用户打卡成功后&#xff0c;我们应该监听数据库进行查询&#xf…

SuperMap iClient3D for WebGL实现浮动点效果

文章目录 前言一、update方法二、创建实体点三、效果 前言 基于SuperMap iClient3D for WebGL实现浮动点效果&#xff0c;原理就是不断改变实体的高度&#xff0c;这里我们选择使用CallbackProperty来实现。 一、update方法 var cc 0 var dd truevar update function() {i…

单文件超过4GB就无法拷贝到U盘?这个你一定要知道

前言 随着现在科技发展&#xff0c;小伙伴们所使用的数据也越变越大。还记得WindowsXP流行的时候&#xff0c;XP的镜像文件仅为几百MB大小。 但是现在随便一个系统就有可能超过4GB。 如果单个文件超过4GB就有可能没办法拷贝进U盘&#xff0c;在这里就需要给小伙伴们普及一下U…

python学习14

前言&#xff1a;相信看到这篇文章的小伙伴都或多或少有一些编程基础&#xff0c;懂得一些linux的基本命令了吧&#xff0c;本篇文章将带领大家服务器如何部署一个使用django框架开发的一个网站进行云服务器端的部署。 文章使用到的的工具 Python&#xff1a;一种编程语言&…