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

 

      🧸安清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 {
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

51单片机看门狗系统

在 STC89C52 单片机中&#xff0c;看门狗控制寄存器的固定地址为 0xE1。此地址由芯片厂商在硬件设计时确定&#xff0c;但是它在头文件中并未给出&#xff0c;因此在使用看门狗系统时需要声明下这个特殊功能寄存器 sfr WDT_CONTR 0xE1; 本案将用一个小灯的工作状况来展示看门…

【C语言入门】解锁核心关键字的终极奥秘与实战应用(三)

目录 一、auto 1.1. 作用 1.2. 特性 1.3. 代码示例 二、register 2.1. 作用 2.2. 特性 2.3. 代码示例 三、static 3.1. 修饰局部变量 3.2. 修饰全局变量 3.3. 修饰函数 四、extern 4.1. 作用 4.2. 特性 4.3. 代码示例 五、volatile 5.1. 作用 5.2. 代码示例…

springCload快速入门

原作者&#xff1a;3. SpringCloud - 快速通关 前置知识&#xff1a; Java17及以上、MavenSpringBoot、SpringMVC、MyBatisLinux、Docker 1. 分布式基础 1.1. 微服务 微服务架构风格&#xff0c;就像是把一个单独的应用程序开发为一套小服务&#xff0c;每个小服务运行在自…

Unity Shader Graph 2D - 跳动的火焰

在游戏中&#xff0c;火焰是一种常见的特效。通常来讲火焰特效通过粒子系统的方式实现的相对较多&#xff0c;本文将通过Shader Graph的方式来实现一种不同的火焰效果。 那么怎么实现呢 首先创建一个名为Fire的Shader Graph文件&#xff0c;然后创建一个名为M_Fire的材质球。 …

【工欲善其事】利用 DeepSeek 实现复杂 Git 操作:从原项目剥离出子版本树并同步到新的代码库中

文章目录 利用 DeepSeek 实现复杂 Git 操作1 背景介绍2 需求描述3 思路分析4 实现过程4.1 第一次需求确认4.2 第二次需求确认4.3 第三次需求确认4.4 V3 模型&#xff1a;中间结果的处理4.5 方案验证&#xff0c;首战告捷 5 总结复盘 利用 DeepSeek 实现复杂 Git 操作 1 背景介绍…

CommonJS

CommonJS 是由 JavaScript 社区于 2oo9 年提出的包含模块、文件、IO、控制台在内的一系列标准。Node.js 的实现中采用了 CommonJS 标准的一部分&#xff0c;并在其基础上进行了一些调整。我们所说的 CommonJS 模块和 Node.js 中的实现并不完全一样&#xff0c;现在一般谈到 Com…

AP单类平均准确率

P_true N_true P_pred TP Fp N_pred FN TNP NTP&#xff08;真正样本&#xff0c;与真实框IoU大于阈值的框&#xff09; FP&#xff08;假正样本&#xff0c;与真实框IoU小于阈值的框&#xff09; TN&#xff08;真负样本&#xff0c;背景&#xff09;…

MQTT实战之在vue和java中使用

在VUE中使用MQTT 1、创建vue项目&#xff08;使用node版本为20.12.0&#xff09; >>npm create vitelatest Need to install the following packages: create-vite6.1.1 Ok to proceed? (y) y √ Project name: ... mqtt-vue √ Select a framework: Vue √ Select a v…

oracle: 表分区>>范围分区,列表分区,散列分区/哈希分区,间隔分区,参考分区,组合分区,子分区/复合分区/组合分区

分区表 是将一个逻辑上的大表按照特定的规则划分为多个物理上的子表&#xff0c;这些子表称为分区。 分区可以基于不同的维度&#xff0c;如时间、数值范围、字符串值等&#xff0c;将数据分散存储在不同的分区 中&#xff0c;以提高数据管理的效率和查询性能&#xff0c;同时…

数据分析系列--⑥RapidMiner构建决策树(泰坦尼克号案例含数据)

一、资源下载 二、数据处理 1.导入数据 2.数据预处理 三、构建模型 1.构建决策树 2.划分训练集和测试集 3.应用模型 4.结果分析 一、资源下载 点击下载数据集 二、数据处理 1.导入数据 2.数据预处理 三、构建模型 1.构建决策树 虽然决策树已经构建,但对于大多数初学者或…

Unity游戏(Assault空对地打击)开发(4) 碰撞体和刚体的添加

前言 飞机和世界的大小关系不太对&#xff0c;我稍微缩小了一下飞机。 详细步骤 选中所有地形对象&#xff0c;如果没有圈起的部分&#xff0c;点击Add Component搜索添加。 接着选中Player对象&#xff0c;添加这两个组件&#xff0c;最好&#xff08;仅对于本项目开发&#x…

【Windows7和Windows10下从零搭建Qt+Leaflet开发环境】

Windows7和Windows10下从零搭建QtLeaflet开发环境 本文开始编写于2025年1月27日星期一&#xff08;农历&#xff1a;腊月二十八&#xff0c;苦逼的人&#xff0c;过年了还在忙工作&#xff09;。 第一章 概述 整个开发环境搭建需要的资源&#xff1a; 操作系统 Windows7_x6…

【游戏设计原理】97 - 空间感知

一、游戏空间的类型 将游戏设计中的空间设计单独提取出来&#xff0c;可以根据其结构、功能和玩家的交互方式划分为以下几种主要类型。这些类型可以单独存在&#xff0c;也可以组合使用&#xff0c;以创造更加复杂和有趣的游戏体验。 1. 线性空间 定义&#xff1a;空间设计是…

基于云计算、大数据与YOLO设计的火灾/火焰目标检测

摘要&#xff1a;本研究针对火灾早期预警检测需求&#xff0c;采用在Kaggle平台获取数据、采用云计算部署的方式&#xff0c;以YOLO11构建模型&#xff0c;使用云计算服务器训练模型。经训练&#xff0c;box loss从约3.5降至1.0&#xff0c;cls loss从约4.0降至1.0&#xff0c;…

【大数据技术】教程03:本机PyCharm远程连接虚拟机Python

本机PyCharm远程连接虚拟机Python 注意:本文需要使用PyCharm专业版。 pycharm-professional-2024.1.4VMware Workstation Pro 16CentOS-Stream-10-latest-x86_64-dvd1.iso写在前面 本文主要介绍如何使用本地PyCharm远程连接虚拟机,运行Python脚本,提高编程效率。 注意: …

bypass hcaptcha、hcaptcha逆向

可以过steam&#xff0c;已支持并发&#xff0c;欢迎询问&#xff01; 有事危&#xff0c;ProfessorLuoMing

Mac本地部署DeekSeek-R1下载太慢怎么办?

Ubuntu 24 本地安装DeekSeek-R1 在命令行先安装ollama curl -fsSL https://ollama.com/install.sh | sh 下载太慢&#xff0c;使用讯雷&#xff0c;mac版下载链接 https://ollama.com/download/Ollama-darwin.zip 进入网站 deepseek-r1:8b&#xff0c;看内存大小4G就8B模型 …

VSCode设置内容字体大小

1、打开VSCode软件&#xff0c;点击左下角的“图标”&#xff0c;选择“Setting”。 在命令面板中的Font Size处选择适合自己的字体大小。 2、对比Font Size值为14与20下的字体大小。

嵌入式学习---蜂鸣器篇

1. 蜂鸣器分类 蜂鸣器是一种电子发声器件&#xff0c;采用直流电压供电&#xff0c;能够发出声音。广泛应用于计算机、打印机、报警器、电子玩具等电子产品中作为发声部件。一般仅从外形不易分辨蜂鸣器的种类。但是有些蜂鸣器使用广泛&#xff0c;见得多了就很容易分辨。例如常…

解析PHP文件路径相关常量

PHP文件路径相关常量包括以下几个常量&#xff1a; __FILE__&#xff1a;表示当前文件的绝对路径&#xff0c;包括文件名。 __DIR__&#xff1a;表示当前文件所在的目录的绝对路径&#xff0c;不包括文件名。 dirname(__FILE__)&#xff1a;等同于__DIR__&#xff0c;表示当前…