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

news2024/11/27 16:26:33

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 并按下回车键进行进行测试
结果如图:
成功登录后,点击退出便返回登录页面
在这里插入图片描述

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

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

相关文章

数据链路层(上):以太网、二层交换机和网络风暴

目录 数据链路层知识概览 数据链路层设备 1、二层交换机 2、拓展&#xff1a;二层交换机与三层交换机有啥区别&#xff1f; 3、广播风暴 4、交换机以太网接口的工作模式 数据链路层的功能 数据链路层--以太网 1、以太网是什么&#xff1f; 2、以太网地址 数据链路层知…

MediaStream使用webRtc多窗口传递

最近在做音视频通话&#xff0c;有个需求是把当前会话弄到另一个窗口单独展示&#xff0c;但是会话是属于主窗口的&#xff0c;多窗口通信目前不能直接传递对象&#xff0c;所以想着使用webRtc在主窗口和兄弟窗口建立连接&#xff0c;把主窗口建立会话得到的MediaStream传递给兄…

系统稳定性建设

说到系统稳定性&#xff0c;不知道大家会想起什么&#xff1f;大多数人会觉得这个词挺虚的&#xff0c;不知道系统稳定性指的是什么。 一年前看到这个词&#xff0c;也是类似于这样的感受&#xff0c;大概只知道要消除单点、做好监控报警&#xff0c;但却并没有一个体系化的方…

ChatGLM-6B的部署步骤

2022年8月&#xff0c;清华背景的智谱AI基于GLM框架&#xff0c;正式推出拥有1300亿参数的中英双语稠密模型 GLM-130B(论文地址、代码地址&#xff0c;论文解读之一&#xff0c;GLM-130B is trained on a cluster of 96 DGX-A100 GPU (840G) servers with a 60-day&#xff0c;…

【Excel如何在表格中筛选重复的值之条件格式】

在使用excel进行统计时经常会遇到&#xff0c;数据统计出现重复的现象&#xff0c;为了确保数据的唯一性&#xff0c;可以用到条件格式筛选出重复值&#xff0c;以确保数据的正确性。 筛选重复值&#xff1a; 选中要筛选的范围&#xff0c;行或列或整个表选中【开始】-【条件…

记录一次k8s pod之间ip无法访问,问题排查与定位

记录一次k8s pod之间ip无法访问&#xff0c;问题排查与定位 问题展现现象 node之间通信正常 部分node上的pod无法通信 排查有问题node 使用启动网络测试工具 环境准备 docker 数据库mysql 使用有状态副本集合 --- apiVersion: apps/v1 kind: StatefulSet metadata:anno…

MATLAB实现图片栅格化

MATLAB实现图片栅格化 1.读取图片&#xff1a;首先&#xff0c;你需要使用imread函数读取要栅格化的图片。 2.设置栅格大小&#xff1a;确定你希望将图片划分成的栅格大小&#xff0c;即每个栅格的宽度和高度。 3.计算栅格数量&#xff1a;根据图片的总尺寸和栅格大小&#…

pycharm 更换Eclipse 的按键模式 keymap

流程 整体来说比较简单&#xff0c;其实只要下载一个eclipse keymap插件就可以完成 首先 ctrl alt s 打开设置页面&#xff0c;找到 plugin 安装完成后还是在 settings 下切换到 keymap即可以看到eclipse 的按键设置出现了&#xff0c;应用后ok 即可完成 再去试试&#x…

绿色自适应网址发布页源码

源码介绍 绿色自适应网址发布页源码&#xff0c;源码由HTMLCSSJS组成&#xff0c;记事本打开源码文件可以进行内容文字之类的修改&#xff0c;双击html文件可以本地运行效果&#xff0c;也可以上传到服务器里面&#xff0c;重定向这个界面 效果截图 源码下载 绿色自适应网址…

Adobe Bridge 2024 v14.0.3 (macOS, Windows) - 集中管理创意资源

Adobe Bridge 2024 v14.0.3 (macOS, Windows) - 集中管理创意资源 Acrobat、After Effects、Animate、Audition、Bridge、Character Animator、Dimension、Dreamweaver、Illustrator、InCopy、InDesign、Lightroom Classic、Media Encoder、Photoshop、Premiere Pro、Adobe XD…

51单片机入门_江协科技_31~32_OB记录的自学笔记_LCD1602液晶显示屏

31. LCD1602 31.1. LCD1602介绍 •LCD1602&#xff08;Liquid Crystal Display&#xff09;液晶显示屏是一种字符型液晶显示模块&#xff0c;可以显示ASCII码的标准字符和其它的一些内置特殊字符&#xff0c;还可以有8个自定义字符 •显示容量&#xff1a;162个字符&#xff0c…

【VIC水文模型】准备工作:平台软件安装

VIC水文模型所需平台软件安装 1 Arcgis安装2 Cygwin安装&#xff08;Linux系统&#xff09;3 Matlab/R/Fortran的安装Notepad 4 VIC模型程序代码获取参考 由于VIC模型的编程语言为C语言&#xff0c;交互方式为控制台输指令&#xff0c;需要在Linux系统上运行。Windows 上使用 …

Linux--进程间的通信-命名管道

前文&#xff1a; Linux–进程间的通信-匿名管道 Linux–进程间的通信–进程池 命名管道的概念 命名管道是一种进程间通信&#xff08;IPC&#xff09;机制&#xff0c;运行不同进程之间进行可靠的、单向或双向的数据通信。 特点和作用&#xff1a; 跨平台性&#xff1a;在W…

Vanna-ai 大模型开源项目 基于RAG的TextToSql框架 安装和使用教程

github项目地址&#xff1a;vanna-ai/vanna: &#x1f916; 与 SQL 数据库聊天&#x1f4ca;。通过 LLMs使用RAG进行准确的TextToSQL的生成工作 &#x1f504; 。 Vanna 是 MIT 许可的开源 Python RAG&#xff08;检索增强生成&#xff09;框架&#xff0c;用于 SQL 生成和相关…

锐捷云桌面的安装

按下 <DEL> 键进入 BIOS setup 界面&#xff08;初始密码为 admin &#xff09;。 输入密码之后就进入 BIOS 的 Main 界面 设置服务器 BMC IP 地址。 a 云服务器启动后&#xff0c;在 BIOS 的主页面&#xff0c;把光标移到 [Server Mgmt] 项。 b 选择 [BMC Network C…

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

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

腾讯AI Lab:“自我对抗”提升大模型的推理能力

本文介绍了一种名为“对抗性禁忌”&#xff08;Adversarial Taboo&#xff09;的双人对抗语言游戏&#xff0c;用于通过自我对弈提升大型语言模型的推理能力。 &#x1f449; 具体的流程 1️⃣ 游戏设计&#xff1a;在这个游戏中&#xff0c;有两个角色&#xff1a;攻击者和防守…

【七 (2)FineBI FCP模拟试卷-平台新增用户留存分析】

目录 文章导航一、字段解释1、用户平台登录信息表格2、用户平台激活信息表格 二、需求三、操作步骤1、建立用户平台登录信息表格和用户平台激活信息表格的关联关系2、将用户平台激活信息表格的激活日期添加到用户平台登录信息表格3、新增公式列&#xff0c;计算激活时间和登录时…

SpringCloud(一)

微服务框架 一、分布式架构 分布式架构︰根据业务功能对系统进行拆分&#xff0c;每个业务模块作为独立项目开发&#xff0c;称为一个服务。 优点: 降低服务耦合有利于服务升级拓展 微服务是一种经过良好架构设计的分布式架构方案&#xff0c;微服务架构特征: 单一职责:微…

源码解读——SplitFed: When Federated Learning Meets Split Learning

源码地址 1. 源码概述 源码里一共包含了5个py文件 单机模型&#xff08;Normal_ResNet_HAM10000.py&#xff09;联邦模型&#xff08;FL_ResNet_HAM10000.py&#xff09;本地模拟的SFLV1&#xff08;SFLV1_ResNet_HAM10000.py&#xff09;网络socket下的SFLV2&#xff08;SF…