IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> 手把手教你使用Spring Boot从零开始制作用户注册登录功能 -> 正文阅读

[Java知识库]手把手教你使用Spring Boot从零开始制作用户注册登录功能

用户登录是网站基本上最常见的功能了。当然,使用Spring Boot实现用户登录也不是难事,今天我将分享,从零开始制作一个用户登录功能的过程。

当然,大家还是需要掌握Spring Boot基本使用以及MyBatis的配置(SSM基本操作),再来看这篇文章比较好。

在最开头我们先约定:主类所在的项目包路径:com.example.userlogin后面其余新建的软件包都在这个包路径之下

文章最末尾我也会给出示例的仓库地址。

1,基本知识-用户登录的实现原理

对于用户登录,我们一定都听说过cookie这个词。其实,cookie是网络编程中使用最广泛的技术之一,它用于储存用户的登录信息。它储存在用户本地,每次随着网络请求发送给服务端,服务端就用这个判断用户是否登录。可以看看这个图:

cookie

可见,用户未登录之前,http请求是不带cookie的,登录后,客户端会将登录信息放在请求中给服务端,服务端进行验证,登录成功后,服务端会把用户信息放在cookie里面,随着响应返回给客户端,客户端就会储存这个cookie。下次再访问网站,cookie会和客户端的请求一起发给服务端,服务端验证信息正确,判断这个用户是登录状态。

cookie里面也是以key-value形式储存数据,并且cookie有它自己的属性例如生命周期、生效域名等等。

但是实际上,cookie里面由于存放着用户信息,很容易存在安全隐患,cookie可以被拦截甚至伪造。

因此现在网站登录都使用session机制,它和cookie机制最大的区别就是用户信息不再放在客户端而是服务端,交互过程如图:

image.png

可见session机制就是以cookie作为载体,cookie里面只储存一个session id,与服务端通信。每个客户端每一次登录请求都会和服务端会生成唯一的session id,相当于客户端每次只要告诉服务端session id,服务端就可以找到相对应的客户端数据。

因此下面示例我们都使用session机制进行。使用Spring Boot可以很轻松的实现session读写。

2,开始-进行配置

我们需要配置如下依赖:

  • jackson-annotations json配置注释
<!-- Jackson -->
<dependency>
   <groupId>com.fasterxml.jackson.core</groupId>
   <artifactId>jackson-annotations</artifactId>
   <version>2.12.4</version>
</dependency>
  • codec 编码加密,用于加密储存用户密码
<!-- 加密 -->
<dependency>
   <groupId>commons-codec</groupId>
   <artifactId>commons-codec</artifactId>
</dependency>
  • common-lang3 实用工具集
<!-- 实用工具 -->
<dependency>
   <groupId>org.apache.commons</groupId>
   <artifactId>commons-lang3</artifactId>
</dependency>
  • Spring Web 最基本的Spring Boot网络支持
<!-- Spring Web -->
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-web</artifactId>
</dependency>
  • Validation 用于信息校验
<!-- Spring Validation -->
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-validation</artifactId>
</dependency>
  • Spring Session Spring Boot的session操作支持
<!-- Spring Session -->
<dependency>
   <groupId>org.springframework.session</groupId>
   <artifactId>spring-session-core</artifactId>
</dependency>
  • MyBatis 大名鼎鼎的数据库持久框架,操作数据库
<!-- MyBatis -->
<dependency>
   <groupId>org.mybatis.spring.boot</groupId>
   <artifactId>mybatis-spring-boot-starter</artifactId>
   <version>2.2.0</version>
</dependency>

<!-- MySQL -->
<dependency>
   <groupId>mysql</groupId>
   <artifactId>mysql-connector-java</artifactId>
   <scope>runtime</scope>
</dependency>
  • Lombok 代码简化,不多说
<!-- Lombok -->
<dependency>
   <groupId>org.projectlombok</groupId>
   <artifactId>lombok</artifactId>
   <optional>true</optional>
</dependency>
  • Spring Test 内置测试功能,不多说
<!-- Spring Test -->
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-test</artifactId>
   <scope>test</scope>
</dependency>

依赖可以根据自己实际情况进行增删,这里我主要用这些,大多数在创建Spring Boot工程时也可以进行选择。

然后配置项目配置文件application.properties

# 网站端口配置
server.port=8802

# JSON配置,设定不对未知字段和空值字段进行序列化节省流量
spring.jackson.deserialization.fail-on-unknown-properties=false
spring.jackson.default-property-inclusion=non_null

# MySQL数据库地址和账户配置(根据自己实际情况进行填写)
spring.datasource.url=jdbc:mysql://localhost:3306/miyakogame?serverTimezone=GMT%2B8
spring.datasource.username=swsk33
spring.datasource.password=dev-2333

对于Mybatis的Mapper XML文件,默认位于src/main/resources/com/{xxx}/{xxx}/dao/之下,即和你的dao包位置对应。例如我们项目的Mapper类一般会放在com.example.userlogin.dao下,那么Spring Boot就默认去这个地方扫描Mapper XML:src/main/resources/com/example/userlogin/dao,目录需手动创建。当然我们也可以进行指定:

mybatis.mapper-locations=file:Resources/mybatisMapper/*.xml

这里我们的项目就不配置MyBatis的XML位置了,就使用默认位置。配置全部根据自己实际情况进行填写。

3,封装一个请求结果类Result<T>

为了方便起见,我们通常封装一个结果类,里面主要是请求结果代码、消息、是否操作成功和数据体,可以根据自己需要进行修改。

新建软件包model,并在其中新建Result<T>内容如下:

package com.example.userlogin.model;

import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import java.io.Serializable;

/**
 * 请求结果类
 *
 * @param <T> 数据体类型
 */
@Setter
@Getter
@NoArgsConstructor
public class Result<T> implements Serializable {

   /**
    * 消息
    */
   private String message;

   /**
    * 是否操作成功
    */
   private boolean success;

   /**
    * 返回的数据主体(返回的内容)
    */
   private T data;

   /**
    * 设定结果为成功
    *
    * @param msg  消息
    * @param data 数据体
    */
   public void setResultSuccess(String msg, T data) {
      this.message = msg;
      this.success = true;
      this.data = data;
   }

   /**
    * 设定结果为失败
    *
    * @param msg 消息
    */
   public void setResultFailed(String msg) {
      this.message = msg;
      this.success = false;
      this.data = null;
   }

}

一般都会返回给前端这个结果对象,更加便捷的传递是否成功以及操作消息等等。

注意前后端传递交互的数据都需要实现序列化接口并且要有无参构造器,这里使用了Lombok的注解,下面也一样。

4,建立用户类,并初始化数据库表

创建软件包dataobject,并在其中建立用户类User,实际根据业务需要不同,用户类可能有很多属性,这里我们只建立最简单的如下:

package com.example.userlogin.dataobject;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Size;
import java.io.Serializable;

/**
 * 用户类
 */
@Setter
@Getter
@NoArgsConstructor
@JsonIgnoreProperties(value = {"password"}, allowSetters = true)
public class User implements Serializable {

   /**
    * 用户id
    */
   private Integer id;

   /**
    * 用户名
    */
   @NotEmpty(message = "用户名不能为空!")
   private String username;

   /**
    * 密码
    */
   @NotEmpty(message = "密码不能为空!")
   @Size(min = 8, message = "密码长度不能小于8!")
   private String password;

}

我们设定了用户的最基本属性,并对其设定了校验规则,密码为敏感信息,因此我们使用Jackson注解设定密码字段为不允许序列化(不会传给前端)。

创建了用户类,我们数据库表也需要对应起来,初始化一个用户表,我这里sql如下:

drop table if exists `user`;
create table `user`
(
   `id`       int unsigned auto_increment,
   `username` varchar(16) not null,
   `password` varchar(32) not null,
   primary key (`id`)
) engine = InnoDB
  default charset = utf8mb4;

连接MySQL并use相应数据库,将这个sql文件使用source命令执行即可。

每个对象都有主键id,且一般设为自增无符号整数,这样有最高的数据库读写效率。密码一般使用MD5加密储存,因此固定32位长度。一般每个数据库表都有创建时间gmt_created和修改时间gmt_modified字段,这里简单起见省略。

5,创建数据服务层-DAO并配置Mapper XML

DAO层主要是Java的对于数据库操作的接口和实现类。MyBatis的强大之处,就是只需定义接口,就可以实现操作数据库。

创建软件包dao,新建接口UserDAO

package com.example.userlogin.dao;

import com.example.userlogin.dataobject.User;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface UserDAO {

   /**
    * 新增用户
    *
    * @param user 用户对象
    * @return 新增成功记录条数
    */
   int add(User user);

   /**
    * 修改用户信息
    *
    * @param user 用户对象
    * @return 修改成功记录条数
    */
   int update(User user);

   /**
    * 根据id获取用户
    *
    * @param id 用户id
    * @return 用户对象
    */
   User getById(Integer id);

   /**
    * 根据用户名获取用户
    *
    * @param username 用户名
    * @return 用户对象
    */
   User getByUsername(String username);

}

根据实际需要定义数据库增删改查方法,这里就定义这些。注意这里接口上面要打上@Mapper注解表示它是个数据持久层接口。且一般来说,增删改方法的返回值都是int,表示操作成功记录条数,方法一般是返回相应对象或者对象的List。

然后编写Mapper XML文件,我们在项目文件夹下的src/main/resources目录下创建多级目录:com/example/userlogin/dao,在这个目录下存放XML文件。

创建UserDAO.xml内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.userlogin.dao.UserDAO">
   <resultMap id="userResultMap" type="com.example.userlogin.dataobject.User">
      <id column="id" property="id"/>
      <result column="username" property="username"/>
      <result column="password" property="password"/>
   </resultMap>

   <insert id="add" parameterType="com.example.userlogin.dataobject.User">
      insert into `user` (username, password)
      values (#{username}, #{password})
   </insert>

   <update id="update" parameterType="com.example.userlogin.dataobject.User">
      update `user`
      set password=#{password}
      where id = #{id}
   </update>

   <select id="getById" resultMap="userResultMap">
      select *
      from `user`
      where id = #{id}
   </select>

   <select id="getByUsername" resultMap="userResultMap">
      select *
      from `user`
      where username = #{username}
   </select>
</mapper>

这样,数据库操作层就完成了!

.一般约定某一个对象(xxx)的数据库操作层接口一般命名为xxxDAO(有的企业也命名为xxxMapper),一个xxxDAO接口对应一个xxxDAO.xml文件。

6,创建用户服务层

现在就要进行正式的服务层逻辑了,完成我们的主要功能:用户注册、登录、信息修改。

其实,用户注册就是前端发送用户注册信息(封装为User对象),后端检验然后往数据库增加一条用户记录的过程;登录也是前端发送用户登录信息,同样封装为User对象,后端根据这个对象的username字段从数据库取出用户、进行比对最后设定session的过程;修改用户也是前端发送修改后的用户信息的User对象,后端进行比对,然后修改数据库相应记录的过程。

先新建包service,在其中添加用户服务接口UserService

package com.example.userlogin.service;

import com.example.userlogin.dataobject.User;
import com.example.userlogin.model.Result;
import org.springframework.stereotype.Service;

@Service
public interface UserService {

   /**
    * 用户注册
    *
    * @param user 用户对象
    * @return 注册结果
    */
   Result<User> register(User user);

   /**
    * 用户登录
    *
    * @param user 用户对象
    * @return 登录结果
    */
   Result<User> login(User user);

   /**
    * 修改用户信息
    *
    * @param user 用户对象
    * @return 修改结果
    */
   Result<User> update(User user);
   
}

然后再在包service下建立包impl,然后在里面新建用户服务实现类UserServiceImpl

package com.example.userlogin.service.impl;

import com.example.userlogin.dao.UserDAO;
import com.example.userlogin.dataobject.User;
import com.example.userlogin.model.Result;
import com.example.userlogin.service.UserService;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class UserServiceImpl implements UserService {

   @Autowired
   private UserDAO userDAO;

   @Override
   public Result<User> register(User user) {
      Result<User> result = new Result<>();
      //先去数据库找用户名是否存在
      User getUser = null;
      try {
         getUser = userDAO.getByUsername(user.getUsername());
      } catch (Exception e) {
         e.printStackTrace();
      }
      if (getUser != null) {
         result.setResultFailed("该用户名已存在!");
         return result;
      }
      //加密储存用户的密码
      user.setPassword(DigestUtils.md5Hex(user.getPassword()));
      //存入数据库
      userDAO.add(user);
      //返回用户数据,成功消息
      result.setResultSuccess("注册用户成功!", user);
      return result;
   }

   @Override
   public Result<User> login(User user) {
      Result<User> result = new Result<>();
      //去数据库查找用户
      User getUser = null;
      try {
         getUser = userDAO.getByUsername(user.getUsername());
      } catch (Exception e) {
         e.printStackTrace();
      }
      if (getUser == null) {
         result.setResultFailed("用户不存在!");
         return result;
      }
      //比对密码(数据库取出用户的密码是加密的,因此要把前端传来的用户密码加密再比对)
      if (!getUser.getPassword().equals(DigestUtils.md5Hex(user.getPassword()))) {
         result.setResultFailed("用户名或者密码错误!");
         return result;
      }
      //设定登录成功消息以及用户信息
      result.setResultSuccess("登录成功!", getUser);
      return result;
   }

   @Override
   public Result<User> update(User user) {
      Result<User> result = new Result<>();
      if (user.getId() == null) {
         result.setResultFailed("用户id不可为空!");
         return result;
      }
      //去数据库查找用户
      User getUser = null;
      try {
         getUser = userDAO.getById(user.getId());
      } catch (Exception e) {
         e.printStackTrace();
      }
      if (getUser == null) {
         result.setResultFailed("用户不存在!");
         return result;
      }
      //检测传来的对象里面字段值是否为空,若是就用数据库里面的对象相应字段值补上
      if (StringUtils.isEmpty(user.getPassword())) {
         user.setPassword(getUser.getPassword());
      } else {
         //加密储存
         user.setPassword(DigestUtils.md5Hex(user.getPassword()));
      }
      //存入数据库
      userDAO.update(user);
      result.setResultSuccess("修改用户成功!", user);
      return result;
   }

}

需要注意的是服务接口需要有@Service注解,接口实现类要有@Component注解,还自动注入了DAO的实例进行数据库操作。

7,创建用户登录API

服务层写完了,现在就是前后端交互的桥梁需要打通了-编写API,这样前端才能发送请求调用我们后端的服务。

我们的API要实现用户登录注册判断用户是否登录修改用户信息用户登出这几个功能。

新建包api,然后在里面新建类UserAPI

package com.example.userlogin.api;

import com.example.userlogin.dao.UserDAO;
import com.example.userlogin.dataobject.User;
import com.example.userlogin.model.Result;
import com.example.userlogin.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import javax.validation.Valid;

@RestController
public class UserAPI {

   /**
    * session的字段名
    */
   public static final String SESSION_NAME = "userInfo";

   @Autowired
   private UserService userService;

   @Autowired
   private UserDAO userDAO;

   /**
    * 用户注册
    *
    * @param user    传入注册用户信息
    * @param errors  Validation的校验错误存放对象
    * @param request 请求对象,用于操作session
    * @return 注册结果
    */
   @PostMapping("/register")
   public Result<User> register(@RequestBody @Valid User user, BindingResult errors, HttpServletRequest request) {
      Result<User> result;
      //如果校验有错,返回注册失败以及错误信息
      if (errors.hasErrors()) {
         result = new Result<>();
         result.setResultFailed(errors.getFieldError().getDefaultMessage());
         return result;
      }
      //调用注册服务
      result = userService.register(user);
      //如果注册成功,则设定session
      if (result.isSuccess()) {
         request.getSession().setAttribute(SESSION_NAME, result.getData());
      }
      return result;
   }

   /**
    * 用户登录
    *
    * @param user    传入登录用户信息
    * @param errors  Validation的校验错误存放对象
    * @param request 请求对象,用于操作session
    * @return 登录结果
    */
   @PostMapping("/login")
   public Result<User> login(@RequestBody @Valid User user, BindingResult errors, HttpServletRequest request) {
      Result<User> result;
      //如果校验有错,返回登录失败以及错误信息
      if (errors.hasErrors()) {
         result = new Result<>();
         result.setResultFailed(errors.getFieldError().getDefaultMessage());
         return result;
      }
      //调用登录服务
      result = userService.login(user);
      //如果登录成功,则设定session
      if (result.isSuccess()) {
         request.getSession().setAttribute(SESSION_NAME, result.getData());
      }
      return result;
   }

   /**
    * 判断用户是否登录
    *
    * @param request 请求对象,从中获取session里面的用户信息以判断用户是否登录
    * @return 结果对象,已经登录则结果为成功,且数据体为用户信息;否则结果为失败,数据体为空
    */
   @GetMapping("/islogin")
   public Result<User> isLogin(HttpServletRequest request) {
      HttpSession session = request.getSession();
      Result<User> result = new Result<>();
      //从session里面获取用户信息
      User sessionUser = (User) session.getAttribute(SESSION_NAME);
      //如果从session中获取用户信息为空,则说明没有登录
      if (sessionUser == null) {
         result.setResultFailed("用户未登录!");
         return result;
      }
      //若用户登录,利用里面的信息去数据库查找并进行比对,保证信息正确性
      User getUser = null;
      try {
         getUser = userDAO.getByUsername(sessionUser.getUsername());
      } catch (Exception e) {
         e.printStackTrace();
      }
      if (getUser == null || !getUser.getPassword().equals(sessionUser.getPassword())) {
         result.setResultFailed("用户信息无效!");
         return result;
      }
      result.setResultSuccess("用户已登录!", getUser);
      return result;
   }

   /**
    * 用户信息修改
    *
    * @param user    修改后用户信息对象
    * @param request 请求对象,用于操作session
    * @return 修改结果
    */
   @PostMapping("/update")
   public Result<User> update(@RequestBody User user, HttpServletRequest request) {
      Result<User> result = new Result<>();
      HttpSession session = request.getSession();
      //检查session中的用户是否和当前被修改用户一致
      User sessionUser = (User) session.getAttribute(SESSION_NAME);
      if (sessionUser.getId() != user.getId()) {
         result.setResultFailed("当前登录用户和被修改用户不一致,终止!");
         return result;
      }
      result = userService.update(user);
      //修改成功则刷新session信息
      if (result.isSuccess()) {
         session.setAttribute(SESSION_NAME, result.getData());
      }
      return result;
   }

   /**
    * 用户登出
    *
    * @param request 请求,用于操作session
    * @return 结果对象
    */
   @GetMapping("/logout")
   public Result logout(HttpServletRequest request) {
      Result result = new Result();
      //用户登出很简单,就是把session里面的用户信息设为null即可
      request.getSession().setAttribute(SESSION_NAME, null);
      result.setResultSuccess("用户退出登录成功!", null);
      return result;
   }

}

因为是API,所以使用@RestController标注类,可见注册登录都是通过前端POST请求后端接收,且在各个方法中加入参数HttpServletRequest request,就可以通过request对象对session进行读写。每一个不同的请求都会有一个唯一的session,每个session中的信息都是key-value形式储存,这里我们只在session里面储存用户信息,因此我们把用户信息的key设为一个固定的名字userInfo,通过建立个常量SESSION_NAME

每个不同机器的请求都会生成独一无二的session,上面这段代码操作,我们可以理解为:登录/注册用户后,取得了用户信息,我们将每个不同机器的用户信息储存在了与它们相对应的session里面,并在其中设定用户信息的keyuserInfo

通过HttpServletRequestgetSession方法,即可获取这个请求的session对象,为HttpSession类型,其中setAttribute方法用于设定session中的键值对,getAttribute方法用于获取session中信息。

8,配置cookie属性

其实到上面第7步,我们的功能基本上完整了,但是还有一些重要配置需要进行。

我们还要开启session功能,并配置cookie属性例如过期时间等等。

新建包config,在其中建立配置类SessionConfig

package com.example.userlogin.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.session.MapSessionRepository;
import org.springframework.session.config.annotation.web.http.EnableSpringHttpSession;
import org.springframework.session.web.http.CookieSerializer;
import org.springframework.session.web.http.DefaultCookieSerializer;

import java.util.concurrent.ConcurrentHashMap;

/**
 * session配置类
 */
@Configuration
@EnableSpringHttpSession
public class SessionConfig {

   /**
    * 设定cookie序列化器的属性
    */
   @Bean
   public CookieSerializer cookieSerializer() {
      DefaultCookieSerializer serializer = new DefaultCookieSerializer();
      serializer.setCookieName("JSESSIONID");
      // 用正则表达式配置匹配的域名,可以兼容 localhost、127.0.0.1 等各种场景
      serializer.setDomainNamePattern("^.+?\.(\w+\.[a-z]+)$");
      // cookie生效路径
      serializer.setCookiePath("/");
      // 设置是否只能服务器修改,浏览器端不能修改
      serializer.setUseHttpOnlyCookie(false);
      // 最大生命周期的单位是分钟
      serializer.setCookieMaxAge(24 * 60 * 60);
      return serializer;
   }

   /**
    * 注册序列化器
    */
   @Bean
   public MapSessionRepository sessionRepository() {
      return new MapSessionRepository(new ConcurrentHashMap<>());
   }

}

注意配置类打上@Configuration注解,@EnableSpringHttpSession开启session。

9,配置拦截器

虽然我们有判断用户登录的API,但是如果我们页面很多,每一个都要判断登录,就会很麻烦。通过拦截器即可对指定的路径设定拦截点。

继续在config包下创建拦截器类UserInterceptor

package com.example.userlogin.config;

import com.example.userlogin.api.UserAPI;
import com.example.userlogin.dao.UserDAO;
import com.example.userlogin.dataobject.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

/**
 * 拦截器
 */
public class UserInterceptor implements HandlerInterceptor {

   @Autowired
   private UserDAO userDAO;

   // Controller方法执行之前
   @Override
   public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
      HttpSession session = request.getSession();
      //获取session中用户信息,并比对
      User sessionUser = (User) session.getAttribute(UserAPI.SESSION_NAME);
      //如果用户未登录,则重定向至主页
      if (sessionUser == null) {
         response.sendRedirect("/");
         return false;
      }
      User getUser = null;
      try {
         getUser = userDAO.getById(sessionUser.getId());
      } catch (Exception e) {
         e.printStackTrace();
      }
      //如果session中用户信息无效,则重定向至主页
      if (getUser == null || !getUser.getPassword().equals(sessionUser.getPassword())) {
         response.sendRedirect("/");
         return false;
      }
      return true;
   }

   // Controller方法执行之后
   @Override
   public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

   }

   // 整个请求完成后(包括Thymeleaf渲染完毕)
   @Override
   public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

   }

}

可见拦截器有三个方法,分别对应三个切入点。一般我们只需要修改在Controller执行之前的那个,它可以像API一样操作session,返回true时表示允许继续访问这个Controller,否则终止访问。通过HttpServletResponsesendRedirect方法可以发送重定向。

拦截器类写好了,接下来就是注册拦截器了。在config类中创建配置类InterceptorRegister

package com.example.userlogin.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.ArrayList;
import java.util.List;

/**
 * 拦截器注册
 */
@Configuration
public class InterceptorRegister implements WebMvcConfigurer {

   /**
    * 把我们定义的拦截器类注册为Bean
    */
   @Bean
   public HandlerInterceptor getInterceptor() {
      return new UserInterceptor();
   }

   /**
    * 添加拦截器,并配置拦截地址
    */
   @Override
   public void addInterceptors(InterceptorRegistry registry) {
      List<String> pathPatterns = new ArrayList<>();
      pathPatterns.add("/update");
      registry.addInterceptor(getInterceptor()).addPathPatterns(pathPatterns);
   }

}

addInterceptors方法里面,我们进行拦截器注册,并配置拦截地址。上述例子只添加拦截/update这个路径,其余不拦截。

还可以设定拦截全部,只排除/login,如下写addInterceptors方法:

@Override
public void addInterceptors(InterceptorRegistry registry) {
   List<String> pathPatterns = new ArrayList<>();
   pathPatterns.add("/login");
   registry.addInterceptor(getInterceptor()).excludePathPatterns(pathPatterns);
}

在此,一个比较简易但完整的用户注册登录就做完了!

10,总结

用户登录注册看起来要写的东西很多,但实际上流程很清晰,也不难理解。

我们发现,DAO层就是单纯操作数据库,返回用户对象;Service层基本上就是用于验证信息正确性,返回封装的Result对象;Controller层进一步设定session,也是返回封装Result对象。每个不同的部分各司其职,完成了我们整个业务逻辑。


?

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2021-08-06 09:29:41  更:2021-08-06 09:30:15 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/11 12:12:38-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码