【基于SprintBoot+Mybatis+Mysql】电脑商城项目之用户登录

news2025/2/5 3:55:00

 

      🧸安清h:个人主页 

   🎥个人专栏:【Spring篇】【计算机网络】【Mybatis篇】

🚦作者简介:一个有趣爱睡觉的intp,期待和更多人分享自己所学知识的真诚大学生。


目录

 🎯1.登录-持久层

🚦1.1规划需要执行的SQL语句

🚦1.2接口设计和抽象方法

🎯2.登录-业务层

🚦2.1规划异常

✨1.密码匹配失败异常

✨2.用户名没有被找到

✨3.异常的编写

🚦2.2设计业务层接口和抽象方法

🚦2.3抽象方法实现 

🎯3.登录-控制层

🚦3.1处理异常

🚦3.2设计请求

🚦3.3处理请求

🎯4.登录-前端页面

🎯用户会话Session

🎯拦截器


当用户输入用户名和密码将数据提交给后台数据库进行查询,如果存在对应的用户名和密码则表示登录成功,登录成功之后跳转到系统的主页,就是index.html页面,跳转在前端使用jquery来完成。

 🎯1.登录-持久层

🚦1.1规划需要执行的SQL语句

依据用户提交的用户名和密码做select查询。密码的比较在业务层执行。

select * from t_user where username=?

注意:如果在分析过程中发现某个功能模块已经被开发完成,所以就可以省略当前的开发步骤,这个分析过程不能够省略。 这个功能模块在用户注册部分已经实现过,所以在此无需重复进行了。

🚦1.2接口设计和抽象方法

 不用重复开发。单元测试也无需单独执行了。

🎯2.登录-业务层

🚦2.1规划异常

✨1.密码匹配失败异常

用户名对应的密码错误:PasswordNotMatchExcepption异常,运行时异常,业务层异常。

//密码验证失败的异常
public class PasswordNotMatchException extends ServiceException{
    public PasswordNotMatchException() {
        super();
    }

    public PasswordNotMatchException(String message) {
        super(message);
    }

    public PasswordNotMatchException(String message, Throwable cause) {
        super(message, cause);
    }

    public PasswordNotMatchException(Throwable cause) {
        super(cause);
    }

    protected PasswordNotMatchException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
        super(message, cause, enableSuppression, writableStackTrace);
    }
}

✨2.用户名没有被找到

抛出异常:UsernameNotFoundException异常,运行时异常,业务层异常。

//用户数据不存在的异常
public class UserNotFoundException extends ServiceException{
    public UserNotFoundException() {
        super();
    }

    public UserNotFoundException(String message) {
        super(message);
    }

    public UserNotFoundException(String message, Throwable cause) {
        super(message, cause);
    }

    public UserNotFoundException(Throwable cause) {
        super(cause);
    }

    protected UserNotFoundException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
        super(message, cause, enableSuppression, writableStackTrace);
    }
}

✨3.异常的编写

  • 业务层异常需要继承ServiceException异常类。
  • 在具体的异常类中定义构造方法(可以使用快捷键来生成,有5个构造方法)。 

🚦2.2设计业务层接口和抽象方法

1.直接在IUserService接口中编写抽象方法,login(String username,String password)。将当前登录成功的用户数据以当前用户对象的形式进行返回。状态管理:可以将数据保存在cookie或者session中,可以避免重复度很高的数据多次频繁操作数据进行获取(用户名、用户id-存放在session中,用户的头像保存在cookie中,而cookie存储在了客户端上)。

//用户模块业务层接口
public interface IUserService {
    void reg(User user);
//    用户登录功能
//    @param username 用户名
//    @param password 用户的密码
//    @return 当前匹配的用户数据,如果没有则返回null值
    User login(String username,String password);
}

🚦2.3抽象方法实现 

1.需要在实现类中实现父接口的抽象方法。

 @Override
    public User login(String username, String password) {
//根据用户名称查询用户的数据是否存在,如果不在则抛出异常
        User result=userMapper.findByUsername(username);
        if(result == null){
            throw new UserNotFoundException("用户数据不存在");
        }
//        检测用户密码是否匹配
//        1.先获取到数据库中的加密之后的密码
        String oldPassword = result.getPassword();
//        2.和用户传递过来的密码进行比较
//        2.1先获取盐值:上一次注册时自动生成的盐值
        String salt=result.getSalt();
//        2.2将用户的密码按照相同的md5算法的规则进行加密
        String newMd5Password = getMD5Password(password,salt);
//        3.将密码进行比较
        if(!newMd5Password.equals(oldPassword)){
            throw new PasswordNotMatchException("用户密码错误");
        }
        
//        判断is_delete字段值是否为1,为1表示被标记删除
        if(result.getIsDelete()==1){
            throw new UserNotFoundException("用户数据不存在");
        }
        
//        调用mapper层的findByUsername来查询用户的数据,提升了系统的性能
        User user = new User();
        user.setUid(result.getUid());
        user.setUsername(result.getUsername());
        user.setAvatar(result.getAvatar());
//        将当前的用户数据返回,返回的数据是为了辅助其他页面做数据展示使用的(uid,username,avatar)
        return user;
    }

2.在测试类中测试业务层登录的方法是否可以执行通过。 在test-service-UserServiceTests中编写如下代码:

    @Test
    public void login(){
        User user = userService.login("test01","123");
        System.out.println(user);
    }

3.如果一个类没有手动创建,直接将这个类复制到项目,idea找不到这个类。之前的缓存导致不能够正常的找到这类的符号。重新构建项目即可。Build->rebuild。 

🎯3.登录-控制层

🚦3.1处理异常

业务层抛出的异常是什么,需要在统一的异常处理类中进行统一的捕获和处理,如果也曾抛出的异常已经在统一异常处理类中曾经处理过,则不需要重复添加。在BaseController类中添加如下代码:

else if(e instanceof UserNotFoundException){
            result.setState(5001);
            result.setMessage("用户数据不存在的异常");
        }else if(e instanceof PasswordNotMatchException){
            result.setState(5002);
            result.setMessage("用户密码错误的异常");
       }

🚦3.2设计请求

请求路径:/users/login

请求方式:POST

请求数据:String username,String password,HttpSession session

响应结果:JsonResult<User>

🚦3.3处理请求

在UserController类中编写处理请求的方法。


//    约定大于配置:开发思想来完成,省略大量的配置甚至注解编写

//    1.接收数据方式:请求处理方法的参数列表设置为pojo类型来接受前端的数据,
//    SpringBoot会将前端的url地址中的参数名和pojo类的属性名进行比较,
//    如果这两个名称相同,则将值注入到pojo类中对应的属性上


//    2.接收数据方式:请求处理方法的参数列表设置为非pojo类型(本例中为String类型),
//    SpringBoot会直接将请求的参数名和方法的参数名直接进行比较,
//    如果名称相同则自动完成值的依赖注入

    @RequestMapping("login")
    public JsonResult<User> login(String username,String password){
        User data = userService.login(username,password);
        return new JsonResult<User>(OK,data);
    }
}

🎯4.登录-前端页面

1.在login.html页面中依据前面所设置的请求来发送ajax请求。

<script>
		$("#btn-login").click(function (){
			$.ajax({
				url:"/users/login",
				type:"POST",
				data:$("#form-login").serialize(),
				dataType:"JSON",
				success:function (json){
					if(json.state==200){
						alert("登录成功");
					// 	跳转到系统主页
					// 	相对路径来确定跳转的页面
						location.href="index.html";
					}else{
						alert("登录失败");
					}
				},
				error:function (xhr){
					alert("登录时产生未知的异常"+xhr.message);
				}
			});
		});
	</script>

 2.如果跳转不了,rebuild项目。

🎯用户会话Session

session对象主要存在服务器端,可以用于保存服务器的临时数据的对象,所保存的数据可以在整个项目中都可以通过访问来获取,把session中的数据看做一个共享的数据。首次登录的时候所获取到的用户数据,转移到session对象即可。session.getAttrbute("key")可以将获取session中的数据这种行为进行封装,封装在BaseController类中。

1.封装session对象中数据的获取(封装在父类中),数据的设置(当用户登录成功后进行数据的设置,设置到全局的session对象中)。

2.在父类中封装两个数据:获取uid和获取username对应的两个方法。用户头像暂时不考虑,将来封装在cookie中来使用。

//    方法不需要被修改,用final来修饰
//    获取session对象中的uid
//    @param session session对象
//    @return 当前登录的用户uid的值
    protected final Integer getuidFromSession(HttpSession session){
        return Integer.valueOf(session.getAttribute("uid").toString());
    }

    //    获取当前登录用户的username
//    @param session session对象
//    @return 当前登录用户的用户名
    protected final String getUsernameFromSession(HttpSession session){
//        getAttribute返回的类型是object,所以加上toString转换成字符串
        return session.getAttribute("username").toString();
    }

3.在登录的方法中将数据封装在session对象中。服务本身自动创建有session对象,已经是一个全局的session对象。SpringBoot直接使用session对象,直接将HttpSession类型的对象做为请求处理方法的参数,会自动将全局的session对象注入到请求处理方法的session形参上。

    @RequestMapping("login")
    public JsonResult<User> login(String username,String password,HttpSession session){
        User data = userService.login(username,password);
//        向session对象中完成数据的绑定(session全局的)
        session.setAttribute("uid",data.getUid());
        session.setAttribute("username",data.getUsername());
//        获取session中绑定的数据
        System.out.println(getuidFromSession(session));
        System.out.println(getUsernameFromSession(session));
        return new JsonResult<User>(OK,data);
    }

🎯拦截器

首先将所有的请求统一拦截到拦截器中,可以在拦截器中来定义过滤的规则,如果不满足系统的设置的过滤规则,统一的处理是重新去打开login.html页面(重定向和转发),推荐使用重定向。

在SpringBoot项目中拦截器的定义和使用。SpringBoot是依靠SPringMVC来完成的。SpringMVC提供了一个HandlerInterceptor接口,用于表示定义一个拦截器。受限制自定义个类,在这个类实现这个接口。

1.首先自定义一个类,在这个类实现HandlerInterceptor接口。

2.注册过滤器:添加白名单(哪些资源可以在不登录的情况下访问:login.html/register.html/login/reg/index.html/product.html),添加黑名单(在用户登录状态下才可以访问的页面资源)。

3.注册过滤器的技术:借助WebMvcConfigure接口,可以将用户定义的拦截器进行注册,注册后才可以保证拦截器能够生效和使用。定义一个类,然后让这个类实现WebMvcConfigure接口。配置信息,建议存放在项目的config包结构下。

//处理器拦截器的注册
@Configuration  //加载当前的拦截器并进行注册
public class LoginInterceptorConfigurer implements WebMvcConfigurer {
    //    配置拦截器
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //    创建自定义拦截器对象
        HandlerInterceptor interceptor = new LoginInterceptor();
//    配置白名单;存放在List集合中
        List<String> patterns = new ArrayList<>();
        patterns.add("/bootstrap3/**");
        patterns.add("/css/**");
        patterns.add("/images/**");
        patterns.add("/js/**");
        patterns.add("/web/register.html");
        patterns.add("/web/login.html");
        patterns.add("/web/index.html");
        patterns.add("/web/product.html");
        patterns.add("/users/reg");
        patterns.add("/users/login");


//        拦截器的注册
        registry.addInterceptor(interceptor)
                .addPathPatterns("/**")  //表示要拦截的url是什么
                .excludePathPatterns(patterns);  //除了哪些路径之外
    }
}

4.如果短时间内多次访问,会提示重定向次数过多,login.html页面无法打开。将浏览器cookie请求,再将浏览器设置为初始设置。

解释:

//将自定义拦截器进行注册   
 default void addInterceptors(InterceptorRegistry registry) {
    }

源码解析:

public interface HandlerInterceptor {
//在调用所有处理请求的方法之前被自动调用执行的方法
    default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        return true;
    }
//在ModelAndView对象返回之后被调用的方法
    default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
    }
//在整个请求所有关联的资源被执行完毕最后所执行的方法
    default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
    }
}

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

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

相关文章

【Deep Seek本地化部署】模型实测:规划求解python代码

目录 前言 一、实测 1、整数规划问题 2、非线性规划问题 二、代码正确性验证 1、整数规划问题代码验证 2、非线性规划问题代码验证 三、结果正确性验证 1、整数规划问题结果正确性验证 2、非线性规划问题正确性验证 四、整数规划问题示例 后记 前言 模型&#xff…

【游戏设计原理】98 - 时间膨胀

从上文中&#xff0c;我们可以得到以下几个启示&#xff1a; 游戏设计的核心目标是让玩家感到“时间飞逝” 游戏的成功与否&#xff0c;往往取决于玩家的沉浸感。如果玩家能够完全投入游戏并感受到时间飞逝&#xff0c;说明游戏设计在玩法、挑战、叙事等方面达到了吸引人的平衡…

C语言基础系列【1】第一个C程序:Hello, World!

C语言的历史与特点 历史背景 C语言起源于20世纪70年代&#xff0c;最初是由美国贝尔实验室的Dennis Ritchie和Ken Thompson为了开发UNIX操作系统而设计的一种编程语言。在UNIX系统的开发过程中&#xff0c;他们发现原有的B语言&#xff08;由Thompson设计&#xff09;在功能和…

【LLM】DeepSeek-R1-Distill-Qwen-7B部署和open webui

note DeepSeek-R1-Distill-Qwen-7B 的测试效果很惊艳&#xff0c;CoT 过程可圈可点&#xff0c;25 年应该值得探索更多端侧的硬件机会。 文章目录 note一、下载 Ollama二、下载 Docker三、下载模型四、部署 open webui 一、下载 Ollama 访问 Ollama 的官方网站 https://ollam…

go-zero学习笔记(三)

利用goctl生成rpc服务 编写proto文件 // 声明 proto 使用的语法版本 syntax "proto3";// proto 包名 package demoRpc;// golang 包名(可选) option go_package "./demo";// 如需为 .proto 文件添加注释&#xff0c;请使用 C/C 样式的 // 和 /* ... */…

C# 修改项目类型 应用程序程序改类库

初级代码游戏的专栏介绍与文章目录-CSDN博客 我的github&#xff1a;codetoys&#xff0c;所有代码都将会位于ctfc库中。已经放入库中我会指出在库中的位置。 这些代码大部分以Linux为目标但部分代码是纯C的&#xff0c;可以在任何平台上使用。 源码指引&#xff1a;github源…

地址查询API接口:高效查询地址信息,提升数据处理效率

地址查询各省市区API接口 地址查询是我们日常生活中经常遇到的一个需求&#xff0c;无论是在物流配送、地图导航还是社交网络等应用中&#xff0c;都需要通过地址来获取地理位置信息。为了满足这个需求&#xff0c;我们可以使用地址查询API接口来高效查询地址信息&#xff0c;提…

图 、图的存储

图的基本概念&#xff1a; 图g由顶点集v和边集e组成&#xff0c;记为g&#xff08;v&#xff0c;e&#xff09; 用|v|表示图g中顶点的个数&#xff0c;也称图g的阶&#xff0c;用|e|表示图g中边的条数 线性表可以是空表&#xff0c;树可以是空树&#xff0c;但图不可以是空&…

【数据结构】(4) 线性表 List

一、什么是线性表 线性表就是 n 个相同类型元素的有限序列&#xff0c;每一个元素只有一个前驱和后继&#xff08;除了第一个和最后一个元素&#xff09;。 数据结构中&#xff0c;常见的线性表有&#xff1a;顺序表、链表、栈、队列。 二、什么是 List List 是 Java 中的线性…

YOLO11/ultralytics:环境搭建

前言 人工智能物体识别行业应该已经饱和了吧&#xff1f;或许现在并不是一个好的入行时候。 最近看到了各种各样相关的扩展应用&#xff0c;为了理解它&#xff0c;我不得不去尝试了解一下。 我选择了git里非常受欢迎的yolo系列&#xff0c;并尝试了最新版本YOLO11或者叫它ultr…

Spring Boot 2 快速教程:WebFlux优缺点及性能分析(四)

WebFlux优缺点 【来源DeepSeek】 Spring WebFlux 是 Spring 框架提供的响应式编程模型&#xff0c;旨在支持非阻塞、异步和高并发的应用场景。其优缺点如下&#xff1a; 优点 高并发与低资源消耗 非阻塞 I/O&#xff1a;基于事件循环模型&#xff08;如 Netty&#xff09;&am…

《OpenCV》——图像透视转换

图像透视转换简介 在 OpenCV 里&#xff0c;图像透视转换属于重要的几何变换&#xff0c;也被叫做投影变换。下面从原理、实现步骤、相关函数和应用场景几个方面为你详细介绍。 原理 实现步骤 选取对应点&#xff1a;要在源图像和目标图像上分别找出至少四个对应的点。这些对…

20250202在Ubuntu22.04下使用Guvcview录像的时候降噪

20250202在Ubuntu22.04下使用Guvcview录像的时候降噪 2025/2/2 21:25 声卡&#xff1a;笔记本电脑的摄像头自带的【USB接口的】麦克风。没有外接3.5mm接口的耳机。 缘起&#xff1a;在安装Ubuntu18.04/20.04系统的笔记本电脑中直接使用Guvcview录像的时候底噪很大&#xff01; …

The Simulation技术浅析(四):随机数生成

随机数生成技术 是 The Simulation 中的核心组成部分,广泛应用于蒙特卡洛模拟、密码学、统计建模等领域。随机数生成技术主要分为 伪随机数生成器(PRNG,Pseudo-Random Number Generator) 和 真随机数生成器(TRNG,True Random Number Generator)。 1. 伪随机数生成器(PR…

结构体DMA串口接收比特错位

发送&#xff1a; 显示&#xff1a; uint16_t接收时候会比特错位。

如何在Intellij IDEA中识别一个文件夹下的多个Maven module?

目录 问题描述 理想情况 手动添加Module&#xff0c;配置Intellij IDEA的Project Structure 问题描述 一个文件夹下有多个Maven项目&#xff0c;一个一个开窗口打开可行但是太麻烦。直接open整个文件夹会发现Intellij IDEA默认可能就识别一个或者几个Maven项目&#xff0c;如…

基于UKF-IMM无迹卡尔曼滤波与交互式多模型的轨迹跟踪算法matlab仿真,对比EKF-IMM和UKF

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 5.完整程序 1.程序功能描述 基于UKF-IMM无迹卡尔曼滤波与交互式多模型的轨迹跟踪算法matlab仿真,对比EKF-IMM和UKF。 2.测试软件版本以及运行结果展示 MATLAB2022A版本运行 3.核心程序 .…

YOLOV11-1:YoloV11-安装和CLI方式训练模型

YoloV11-安装和CLI方式训练模型 1.安装和运行1.1安装的基础环境1.2安装yolo相关组件1.3命令行方式使用1.3.1 训练1.3.2 预测 本文介绍yoloV11的安装和命令行接口 1.安装和运行 1.1安装的基础环境 GPU环境&#xff0c;其中CUDA是12.4版本 1.2安装yolo相关组件 # 克隆github…

Pluto固件编译笔记

前段时间我已经做到在电脑上交叉编译一个简单的c/c程序&#xff0c;然后复制到pluto上运行。 要做到这一点&#xff0c;其实参考adi pluto官网的wiki就能做到了。 但这样有几个问题&#xff0c;只能做到简易程序&#xff0c;如果程序复杂&#xff0c;要调用更多库而SYSROOT里…

弄懂Runable,Callable,Future之间的关系

JDK1.5之前&#xff0c;我们创建线程有这样两种方式 1.继承Thread类 2.连接实现Runnable接口 但是这两个方法我们都没有返回值&#xff0c;如果需要获取任务返回结果怎么办&#xff1f; 然后在JDK1.5之后&#xff0c;官方就提供了Callable和Future&#xff0c;有获取任务返…