Spring Boot:Web应用开发之登录与退出的实现

Spring Boot

  • 前言
  • 实现登录功能
    • 配置拦截器
  • 实现退出功能

在这里插入图片描述

前言

登录与退出功能作为 Web 应用中的基础且重要的组成部分,直接关系到用户的安全和隐私保护。通过实现登录与退出功能,可以对用户的身份进行验证和授权,确保只有合法的用户才能访问特定的资源或执行特定的操作。同时,退出功能也为用户提供了便捷的方式来结束当前会话,保护其个人信息不被他人冒用。在完成 Spring Boot 前五章的学习后,下面简单介绍使用 Spring Boot 实现登录与退出的功能。

实现登录功能

案例中选用 Spring Boot 3.0.2 作为整合、Thymeleaf 作为视图、Spring Data JPA 作为持久层

简单示例:
首先,创建新项目 SpringBootWebDemo ,勾选 Web 、MySQLThymeleafJPA 的启动器
在这里插入图片描述

然后,在 application.properties 全局配置文件中配置数据库连接信息

#数据库连接信息
spring.datasource.username=root
spring.datasource.password=0123
spring.datasource.url=jdbc:mysql://localhost:3306/springbootwebdemo
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# SpringBoot 内部使用 SpringData , SpringData 的 JPA 是使用 Hibernate 实现的
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true

接着,创建 Entity 包并在包内创建两个实体类 User 和 Address

package cn.edu.SpringBootWebDemo.Entity;

import jakarta.persistence.*;

import java.util.Date;

@Entity
@Table(name = "user")
public class User {
    private int id;
    private String username;
    private String password;
    private Date regDate; // 注册日期
    private Address address; // 地址

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY) // 自增
    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Column(name = "reg_date") // 指定字段名
    public Date getRegDate() {
        return regDate;
    }

    public void setRegDate(Date regDate) {
        this.regDate = regDate;
    }

    @ManyToOne // 假设:每一个用户对应一个地址,一个地址可以对应多个用户
    @JoinColumn(name = "address_id") // 一般在多的这边建立外键
    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", regDate=" + regDate +
                ", address=" + address +
                '}';
    }
}
package cn.edu.SpringBootWebDemo.Entity;

import jakarta.persistence.*;

@Entity
@Table(name = "address")
public class Address {
    private int id;
    private String addressInfo; // 地址详情

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY) // 自增
    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    @Column(name = "address_info") // 指定字段名
    public String getAddressInfo() {
        return addressInfo;
    }

    public void setAddressInfo(String addressInfo) {
        this.addressInfo = addressInfo;
    }

    @Override
    public String toString() {
        return "Address{" +
                "id=" + id +
                ", addressInfo='" + addressInfo + '\'' +
                '}';
    }
}

随之,创建 Dao 包并在包内创建一个 UserDao 接口,继承 JpaRepository<实体类,主键类型> ,并且声明一个默认接口中没有提供,需要根据用户名查询账号的方法

package cn.edu.SpringBootWebDemo.Dao;

import cn.edu.SpringBootWebDemo.Entity.User;
import org.springframework.data.jpa.repository.JpaRepository;

public interface UserDao extends JpaRepository<User,Integer> {

    // 默认接口中没有提供根据用户名查询账号的方法
    public User getByUsername(String username);

}

再创建 Service 包并在包内创建一个 UserService 接口,并创建该接口的实现类 UserServiceImpl

package cn.edu.SpringBootWebDemo.Service;

import cn.edu.SpringBootWebDemo.Entity.User;

public interface UserService {
    public User login(User user);
}
package cn.edu.SpringBootWebDemo.Service;

import cn.edu.SpringBootWebDemo.Dao.UserDao;
import cn.edu.SpringBootWebDemo.Entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service("userService")
public class UserServiceImpl implements UserService{

    @Autowired
    private UserDao userDao;

    @Override
    public User login(User user) {
        // 根据用户名查询有没有对应的账号
        User user1 = userDao.getByUsername(user.getUsername());
        // 判断是否为空或密码错误,登录失败返回 null
        if (user1 == null) return null;
        if(!user1.getPassword().equals(user.getPassword())) return null;
        // 返回 User 对象,登录成功
        return user1;
    }
}

其次,创建 Controller 包并在包内创建一个 UserController 类

package cn.edu.SpringBootWebDemo.Controller;

import cn.edu.SpringBootWebDemo.Entity.User;
import cn.edu.SpringBootWebDemo.Service.UserService;
import jakarta.servlet.http.HttpSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;

@Controller
public class UserController {

    @Autowired
    private UserService userService;

    @GetMapping("/Login.html")
    public String login(HttpSession httpSession){
        // 成功登录后不能再返回登录页面,而是跳转到主页
        boolean flag = httpSession.getAttribute("loginUser")==null?true:false;
        if (flag){
            return "Login";
        }else {
            return "redirect:/Main.html";
        }
    }

    @GetMapping("/Main.html")
    public String main(){
        return "Main";
    }

    // 判断登录
    @PostMapping("/Login.html")
    public String login(User user, HttpSession httpSession, Model model){
        // 控制层——接受页面的账号和密码。调用服务层的 login 方法,判断登录是否成功。
        // 成功将返回的 user 对象保存到 session 的域空间;反之返回一个错误提示。
        User user1 = userService.login(user);
        if(user1 != null) {
            // 将返回的 user 对象保存到 session 的域空间,页面跳转到主页中
            httpSession.setAttribute("loginUser",user1);
            return "redirect:/Main.html";
        }else {
            // 登录失败,返回错误提示
            model.addAttribute("loginError","用户名或密码错误!");
            return "Login";
        }
    }
}

最后,在 resources/templates 目录下创建两个页面 Login.html 和 Main.html ,再启动 Spring Boot ,打开浏览器输入 http://localhost:8080/Login.html 并按下回车键进行进行测试(启动 Spring Boot 后,数据表会自动生成,但需自行插入一条信息进行测试)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>LoginDemo</title>
</head>
<body>
    <!--  loginError 不为空才显示错误提示  -->
    <div style="color: red;" th:text="${loginError}" th:if="${not #strings.isEmpty(loginError)}"></div>

    <form th:action="@{/Login.html}" method="post">
        账号:<input type="text" name="username" placeholder="请输入账号"> <br>
        密码:<input type="password" name="password" placeholder="请输入密码"> <br>
        <input style="margin-left: 54px;" type="submit" value="登录">
    </form>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>MainDemp</title>
</head>
<body>
    登录成功!!!
    主页
</body>
</html>

结果如图:
1.登录页面
在这里插入图片描述

2.输入账号密码,进行登录
在这里插入图片描述

3.登录成功,进入主页
在这里插入图片描述

4.错误提示
在这里插入图片描述

配置拦截器

通常为了确保用户在访问需要认证的资源之前已经登录,需要在登录功能中配置拦截器。

简单示例:
首先,创建 Web 包并在包内创建一个 LoginFilter 类来自定义拦截器

package cn.edu.SpringBootWebDemo.Web;

import cn.edu.SpringBootWebDemo.Entity.User;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import org.springframework.web.servlet.HandlerInterceptor;

public class LoginFilter implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        HttpSession httpSession = request.getSession();
        // 从 session 的域空间获取 user 对象
        User user = (User) httpSession.getAttribute("loginUser");
        if(user != null){
            // 已登录,放行
            return true;
        }else {
            // 没有登录,不能访问主页面,出现提示信息
            request.setAttribute("loginError","请登录后再访问!");
            request.getRequestDispatcher("/Login.html").forward(request,response);
            return false;
        }
    }
}

然后,创建 Configuration 包并在包内创建一个配置类 LoginFilterConfiguration ,将拦截器存放在配置类中,才能注册到 Spring Boot 环境里

package cn.edu.SpringBootWebDemo.Configuration;

import cn.edu.SpringBootWebDemo.Web.LoginFilter;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class LoginFilterConfiguration implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 注册自定义的拦截器,拦截所有请求,排除登录页面
        registry.addInterceptor(new LoginFilter())
                .addPathPatterns("/**")
                .excludePathPatterns("/Login.html");

        // 在 Spring MVC 中,还需要排除静态资源( *.css 、*.js 、*.jpg 等)拦截
        // 在 Spring Boot 中,已设置排除,不需再添加
    }
}

最后,启动 Spring Boot ,打开浏览器输入 http://localhost:8080/Main.html 并按下回车键进行进行测试
结果如图:
在这里插入图片描述

实现退出功能

在成功登录后,通常用户被重定向到主页,而不是返回登录页面。同时,提供一个退出功能让用户能够主动注销并清除会话信息,也是必要的安全措施。

简单示例:
首先,在 Main.html 中重新编写

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>MainDemo</title>
</head>
<body>
    登录成功!!!<br>
    <h3>主页</h3> <br>
    <a th:href="@{/logout.html}">退出</a>
</body>
</html>

然后,在 UserController 类上添加一个退出的方法

// 退出
@GetMapping("/logout.html")
public String logout(HttpSession httpSession){
    // 通过 invalidate 方法清空 httpSession 里的内容
    httpSession.invalidate();
    return "redirect:/Login.html";
}

最后,启动 Spring Boot ,打开浏览器输入 http://localhost:8080/Login.html 并按下回车键进行进行测试
结果如图:
成功登录后,点击退出便返回登录页面
在这里插入图片描述

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

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

相关文章

吃鸡游戏msvcp140.dll丢失的解决方法

msvcp140.dll 是一个与 Microsoft Visual C Redistributable 相关的动态链接库&#xff08;DLL&#xff09;文件&#xff0c;是 Windows 操作系统中众多应用程序正常运行所必需的关键组件之一。以下是对 msvcp140.dll 文件的总体介绍和msvcp140.dll丢失的多个解决方案分享。 *…

Java项目实现Excel导出(Hutool)

官网&#xff1a; Excel生成-ExcelWriter (hutool.cn) 1.使用Hutool工具实现Excel导出&#xff08;.xlsx格式&#xff09; 业务场景&#xff1a; 使用SpringCloudmysqlmybatis-plus需要将数据库中的数据导出到Excel文件中 前端为Vue2 第零步&#xff1a;导入依赖 <!-…

NPL预训练模型-GPT-3

简介及特点 GPT-3是一个由OpenAI开发的自然语言处理&#xff08;NLP&#xff09;预训练模型&#xff0c;它是生成式预训练变换器&#xff08;Generative Pretrained Transformer&#xff09;系列的第三代模型。GPT-3以其巨大的规模和强大的语言处理能力而闻名&#xff0c;具有…

快速上手Linux核心命令

Linux 的重要性不用我多说了吧&#xff0c;大多数互联网公司&#xff0c;服务器都是采用的Linux操作系统 Linux是一个主要通过命令行来进行管理的操作系统。 只有熟练掌握Linux核心命令&#xff0c;在使用起来我们才会得心应手 这里给大家整理了Linux一些核心命令&#xff0…

游戏、app抓包

文章目录 协议app抓包游戏抓包 协议 在抓包之前&#xff0c;首先我们要对每个程序使用什么协议有个大致的了解&#xff0c;比如网页这种就是走的http协议。 在一些app中我们通过发送一个请求&#xff0c;然后服务器接受&#xff0c;响应&#xff0c;返回一个数据包&#xff0c…

数字人解决方案——EMAGE面部加肢体动画实现从音频生成数字人表情与动作

概述 AI数字人面部与肢体的驱动算法是数字人研发中至关重要的一环&#xff0c;它能够有效降低VR Chat、虚拟直播和游戏NPC等应用场景中的成本。随着技术的发展&#xff0c;基于语音的面部、肢体和手部动作生成模型已经逐步成熟并得到广泛应用。然而&#xff0c;当尝试将这些独…

反激电源——TL431及光耦反馈电路计算(不涉及环路补偿)

一、TL431及光耦反馈电路 TL431以及光耦电路是反激的副边反馈类型电路中的常见应用。 其反馈工作原理为&#xff1a;当副边的输出电压升高时&#xff0c;TL431的REF点采样电压也会升高&#xff0c;使得TL431的导通量增加&#xff0c;同时光耦内部的发光二极管流过的电流也增大&…

C++11 数据结构3 线性表的循环链式存储,实现,测试

上一节课&#xff0c;我们学了线性表 单向存储结构&#xff08;也就是单链表&#xff09;&#xff0c;这个是企业常用的技术&#xff0c;且是后面各种的基本&#xff0c;一定要牢牢掌握&#xff0c;如果没有掌握&#xff0c;下面的课程会云里雾里。 一 &#xff0c;循环链表 1…

遥测终端赋能水库泄洪监测预警,筑牢度汛安全防线!

4月10日&#xff0c;水利部召开水库安全度汛视频会议。会议要求着力强化水库防洪“四预”措施&#xff0c;加快构建雨水情监测预报“三道防线”&#xff0c;完善预警信息发布机制&#xff0c;推进数字孪生水利工程建设&#xff0c;为科学调度指挥决策提供支持。强调坚决牢牢守住…

基于3D点云的散货库存体积计算

首先&#xff0c;你需要散货库存的点云。 我将使用 IntelRealSense 捕获的散货库存的 .ply文件。 然而&#xff0c;任何其他产生点云的成像技术都同样有效。 点击这里查看本教程的 Github 上的代码。 NSDT工具推荐&#xff1a; Three.js AI纹理开发包 - YOLO合成数据生成器 - …

二叉树的中序遍历 - LeetCode 热题 36

大家好&#xff01;我是曾续缘&#x1f603; 今天是《LeetCode 热题 100》系列 发车第 36 天 二叉树第 1 题 ❤️点赞 &#x1f44d; 收藏 ⭐再看&#xff0c;养成习惯 二叉树的中序遍历 给定一个二叉树的根节点 root &#xff0c;返回 它的 中序 遍历 。 示例 1&#xff1a; 输…

爬楼梯(c)

文章目录 描述分析思路关键代码运行结果 描述 给定一个整数数组 cost &#xff0c;其中 cost[i]是从楼梯第i 个台阶向上爬需要支付的费用&#xff0c;下标从0开始。-旦你支付此费用&#xff0c;即可选择向上爬一个或者两个台阶 要求&#xff1a;请你计算并返回达到楼梯顶部的…

4.17

while(1) { HAL_ADC_Start(&hadc); adcVal HAL_ADC_GetValue(&hadc); TIM3->CCR3 adcVal-2000; } 1.总结串口的发送和接收功能使用到的函数 HAL_UART_Transmit_DMA(&huart1,"hello world",strlen("hello world")); HAL_UART_Tr…

Linux:如何删除指定时间之前修改的文件?

1、与文件有关的时间 在说明如何删除符合这种要求的文件之前&#xff0c;先来看看与文件有关的有哪些时间 简名全名中文名含义atimeaccess time访问时间文件中的数据最后被访问的时间mtimemodify time修改时间文件中的数据最后被修改的时间ctime change time变化时间文件的元…

JavaSE高阶篇-IO流

第一部分 file类 1&#xff09;File类 计算机常识: 1.名字为".jpg"的一定是图片吗? 不一定,有可能是文件夹 2.什么叫做文本文档: 用记事本打开,人能看懂的文件 比如:.txt .html .css等 .doc -> 不是 …

如何安装 IntelliJ IDEA 最新版本——详细教程

IntelliJ IDEA 简称 IDEA&#xff0c;被业界公认为最好的 Java 集成开发工具&#xff0c;尤其在智能代码助手、代码自动提示、代码重构、代码版本管理(Git、SVN、Maven)、单元测试、代码分析等方面有着亮眼的发挥。IDEA 产于捷克&#xff0c;开发人员以严谨著称的东欧程序员为主…

vscode 搭建stm32开发环境记录(eide+cortex-debug+jlink)

前言 clion使用的快过期了&#xff0c;所以就准备使用vscode 来代替clion作为代码开发环境 vscode 插件安装 创建个空白工程 添加项目相关的源文件&#xff0c;和配置宏定义和头文件目录 编译和烧录(ok) 结合cortex-debug 结果(测试ok)

数据可视化-ECharts Html项目实战(13)

在之前的文章中&#xff0c;我们深入学习ECharts动态主题切换和自定义ECharts主题。想了解的朋友可以查看这篇文章。同时&#xff0c;希望我的文章能帮助到你&#xff0c;如果觉得我的文章写的不错&#xff0c;请留下你宝贵的点赞&#xff0c;谢谢。 数据可视化-ECharts Html项…

Linux执行命令监控详细实现原理和使用教程,以及相关工具的使用

Linux执行命令监控详细实现原理和使用教程&#xff0c;以及相关工具的使用。 0x00 背景介绍 Linux上的HIDS需要实时对执行的命令进行监控&#xff0c;分析异常或入侵行为&#xff0c;有助于安全事件的发现和预防。为了获取执行命令&#xff0c;大致有如下方法&#xff1a; 遍…

MySQL-笔记-06.数据高级查询

目录 6.1 连接查询 6.1.1 交叉连接&#xff08;cross join&#xff09; 6.1.2 内连接&#xff08;inner join&#xff09; 6.1.3 外连接&#xff08;outer join&#xff09; 6.1.3.1 左外连接&#xff08;left [outer] join&#xff09; 6.1.3.2 右外连接&#xff08;rig…