整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

Java Web轻松学61 - 实现用户注册功能

系列文章旨在记录和总结自己在Java Web开发之路上的知识点、经验、问题和思考,希望能帮助更多(Java)码农和想成为(Java)码农的人。


目录

  1. 介绍
  2. 四大架构(技术、业务、应用、数据)
  3. 思路
  4. 展示层 - 注册页面 register.html
  5. User实体类
  6. DAO层 - UserMapper接口和元数据
  7. 服务层 - UserService
  8. 控制器层 - 添加Handler
  9. 总结

介绍

到现在为止,我们的租房网应用只是实现了简单的用户登录(实际上仅有前端页面,后台还没有实现登录验证的业务逻辑)、查看自己感兴趣的房源(也是仅有一个接口)、查看某个房源的详情、编辑某个房源的信息等功能。

本篇文章将为我们的租房网应用实现一个简单的用户注册功能。

四大架构(技术、业务、应用、数据)

前面的工作只是一直不断的在使用新技术改造我们的项目,从最开始的Servlet,经过使用JSP和JSTL、Spring MVC和Spring IoC、关系数据库H2Database和JDBC、Maven、Spring JDBC,到目前采用ORM框架MyBatis、连接池框架Druid等技术,我们的项目在开发效率、可维护性、性能等方面不敢说达到完美,但也算很不错了。

所以,既然我们的技术架构搭建的也还够用了,现在暂时让我们的重心转移到业务架构上来。当然,还有应用架构数据架构

应用架构我们就暂时采取独立的单块应用架构吧,就是说把所有业务功能都放在一个应用中。目前很流行微服务架构,但那适合于业务功能很复杂,需要拆分为成百上千个应用,数据库也动辄上百个库,上万张表,我们的租房网应用现在还早着呢,全部功能放在一起又快又简单。

数据架构上目前也仅仅使用了房源数据,而房源数据里面也只是模拟了若干个字符串类型的数据,并没有涉及到文档、图片、视频、音频等其他类型的数据,我们就先用关系数据库吧。

业务架构我们也不作深入分析和设计,我们就怎么简单怎么来吧,现在缺用户注册功能,那我们就实现一个用户注册功能,还是赶紧把租房网应用改造成至少像模像样要紧。

思路

首先从租房网平台的最终用户的角度来看,我们应该有一个注册页面。

这个注册页面跟登录页面类似,不涉及任何其他业务的数据,因此可以采用静态页面的方式来实现。

有了这个注册页面,我们就可以直接打开这个页面进行注册。当然,也可以从其他页面比如登录页面链接到注册页面。

用户填写注册信息完毕之后,需要将它们提交到我们的租房网应用的后台。首先到达的是我们的控制器。因此,我们需要为控制器设计一个Handler,采用POST方法映射到 register.action 。

注册信息至少应该包含用户名和密码,当然实际应用中还应该包含更多用户信息,所以,我们应该设计一个用户实体类,类名就叫 User 吧。

根据分层的思维,控制器的Handler需要调用一个服务来实现用户注册的业务逻辑,我们就设计一个 UserService 吧,它专门用来处理用户相关的业务,比如注册、登录等等。

现在我们已经使用了MyBatis作为DAO层,所以还需要设计一个 UserMapper 。

最后,我们需要把用户数据保存在数据库中,所以要建立一个 user 表。

展示层 - 注册页面 register.html

注册页面的内容很简单,主要是使用表单元素<form>(HTML的基础知识大家可以参考这篇文章):

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>租房网 - 注册</title>
</head>
<body>
	<form action="register.action" method="post">
		<h2>用户注册</h2>
		<label for="user_name">请输入用户名</label><input type="text" id="user_name" name="userName" />
		<label for="password">请输入密码</label><input type="password" id="password" name="password" />
		<label for="password_confirmed">请再次输入密码</label><input type="password" id="password_confirmed" name="passwordConfirmed" />
		<input type="submit" value="注册•" />
	</form>
	<p><a href="login.html">已经注册,直接登录!</a></p>
</body>
</html>

这里重点关注的就是:

  • 表单元素的action属性和method属性;
  • <input>元素的name属性;

它们的值都需要与后台代码一致。


为了方便用户使用注册功能,一般的Web应用都会在登录页面设计一个链接跳转到注册页面,于是我们的登录页面 login.html 变为:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>租房网 - 登录•</title>
</head>
<body>
	<form action="login.action" method="post">
		<label for="user_name">用户名</label><input type="text" id="user_name" name="userName" />
		<label for="password">密码</label><input type="password" id="password" name="password" />
		<input type="submit" value="登录•" />
	</form>
	<p><a href="register.html">还没有注册?</a></p>
</body>
</html>

主要是在表单元素<form>之后添加了一个<a>元素。

现在我们可以发布一下应用并启动Tomcat,验证一下我们的页面是否有错误,除了最后提交注册请求时会出现404的错误之外,显示上应该没什么问题,登录页面变成这样:


注册页面是这样的:


看到上面的用户注册页面,我们很容易想到这里还有一个要考虑的问题,就是用户一旦点击注册按钮提交了注册请求之后,如何知道自己是否注册成功呢?如果不成功,那是因为什么导致不成功呢?即我们的系统应该给用户返回何种响应呢?

我这里的设计是提供一个注册结果的响应页面,它显然是动态的,它要么提示注册成功,要么提示导致注册失败的原因。所以,我设计了两个页面,一个是静态页面,一个是JSP页面。

register-success.html:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>租房网 - 注册成功!</title>
</head>
<body>
	<h1>注册成功!请<a href="login.html">登录</a>!</h1>
</body>
</html>

register-failure.jsp:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>租房网 - 注册失败!</title>
</head>
<body>
<h2>注册失败!请重新<a href="register.html">注册</a>!</h2>
<h3>失败原因:${errorMessage}</h3>
</body>
</html>

注意,这里使用EL表达式来访问数据,所以转发给此页面的时候必须附加上数据 errorMessage

User实体类

虽然我们的思路中是按照前端页面、控制器层、服务层、数据访问层(DAO层)来分析的,但是由于我们的控制器层依赖于服务层,而服务层又依赖于数据访问层,所以开发的时候我们可以自下而上,这样的话代码层面不会出现错误提示。

我们先来考虑User实体类,前面已经提到过用户提交的注册信息至少包含用户名和密码,所以User实体类也必须有这两项。

难道这样就够了吗?我们再以用户的角度思考一下,假如用户有一天突然觉得自己的用户名不好,希望修改为另外一个用户名怎么办?是不是需要将关联到该用户名的其他记录都需要修改为新的用户名?这种方案实际上也可以,不过就会产生牵一发而动全身的不良效果。

所以,为了灵活考虑,我们应该为每个用户赋予一个全局唯一的且不可变的用户ID,然后跟该用户有关的记录都关联到这个用户ID。从这里我们可以看出,这个用户ID是由我们的系统自动生成的(那该如何生成呢?下面介绍),对用户是不可见的。

然后,需要考虑用户ID的数据类型(实际上它与如何生成也有一定关系),我们这里仍然选择字符串。

综合起来我们的User实体类有如下特点:

  • 用户ID由系统自动生成,唯一且不可变;
  • 用户名也唯一,但是可以被用户修改;
  • 用户密码的安全性问题,暂且不考虑。
package houserenter.entity;

public class User {

	private String id;
	private String name;
	private String password;
	public String getId() {
		return id;
	}
	public void setId(String id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
	@Override
	public String toString() {
		return "User [name=" + name + ", password=" + password + "]";
	}
	
}

实体类中的getter方法、setter方法、toString()方法可以使用IDE提供的功能快速添加。

DAO层 - UserMapper接口和元数据

我们很容易根据业务功能设计出 UserMapper 包含哪些接口。

因为用户注册功能相当于是新增一个用户,所以需要一个插入用户的接口。

又因为用户注册时需要判断用户名是否已经被注册,所以需要一个根据用户名查找用户的接口。

不过,由于我们采用的是H2Database的嵌入式模式,所以必须由应用自己来创建用户表(实际生产环境中一般是由DBA来建表),所以还需要一个创建用户表的接口。但是用户表不能每次都重复创建,所以使用了 if not exists 语法。

UserMapper.java:

package houserenter.mapper;

import houserenter.entity.User;

public interface UserMapper {

	int cteateTable();
	
	int insert(User user);
	
	User selectByName(String name);
}

UserMapper.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="houserenter.mapper.UserMapper">

	<update id="cteateTable">
		create table if not exists user(id varchar(36) primary key, name varchar(32), password varchar(16))
	</update>

	<insert id="insert" parameterType="houserenter.entity.User">
		insert into user(id, name, password) values(#{id}, #{name}, #{password})
	</insert>
	
	<select id="selectByName" parameterType="java.lang.String" resultType="houserenter.entity.User">
		select id,name,password from user where name = #{name}
	</select>
</mapper>

服务层 - UserService

服务层实现业务逻辑,所以就目前来说 UserService 最主要的一个方法是处理用户的注册请求。

用户注册的业务逻辑我这里设计的比较简单,仅仅包括判断两次密码是否一致和用户名是否已经被注册,实际上还有用户名和密码的长度、字符要求等限制:



另外,由于我们采用的是H2Database的嵌入式模式,所以必须由应用自己来创建用户表(实际生产环境中一般是由DBA来建表),所以在UserService组件实例化之后首先需要判断用户表是否存在,如果不存在则创建。

package houserenter.service;

import java.util.UUID;

import javax.annotation.PostConstruct;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import houserenter.entity.User;
import houserenter.mapper.UserMapper;

@Service
public class UserService {

	@Autowired
	private UserMapper userMapper;
	
	@PostConstruct
	public void init() {
		userMapper.cteateTable();
	}
	
	public User register(String userName, String password, String passwordConfirmed) throws Exception {
		
		if (!passwordConfirmed.equals(password)) {
			throw new Exception("两次输入的密码不一致,请重新输入!");
		}
		
		User user = userMapper.selectByName(userName);
		if (user != null) {
			throw new Exception("用户名 " + userName + " 已经注册过,请选择其他用户名!");
		}
		
		user = new User();
		user.setId(UUID.randomUUID().toString());
		user.setName(userName);
		user.setPassword(password);
		userMapper.insert(user);
		
		return user;
	}
}

这个UserService其实也没有太多可说的,它依赖于 UserMapper,然后依照业务流程来编写代码即可。

需要重点关注的是,我在这里使用了Java异常(可以参考这篇文章),一旦两次密码不一致,或者用户名已经被注册过,就抛出异常。

还有一点是,我使用了 java.util.UUID 类来生成全局唯一的用户ID。

最后要提醒的是,register() 方法的返回值类型是 User 。这是因为在此业务逻辑中我们生成了用户ID,而此用户ID有可能被上层组件用到。一般情况下,方法一旦生成了新数据,则需要将该新数据返回给调用者。

还有一个比较容易忽略的问题是数据库操作的事务。因为我们的这段业务逻辑中访问数据库的地方有两处,一处是判断用户名是否已经被注册过,一处是插入新用户。如果有两个用户注册的请求到来,且它们要注册的用户名相同,那么很有可能一个请求刚刚判断完用户名不存在(尚未插入到数据库),另一个请求接着也判断该用户名是否已经被注册过(显然是没有),最后导致该用户名被注册两次。我们可以在数据库层面为user表的name列加上唯一性约束,也可以在应用层面将若干操作封装为事务。我们这里暂且忽略这个问题。

控制器层 - 添加Handler

控制器类 HouseRenterController 首先要注入 UserService :

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>租房网 - 注册</title>
</head>
<body>
	<form action="register.action" method="post">
		<h2>用户注册</h2>
		<label for="user_name">请输入用户名</label><input type="text" id="user_name" name="userName" />
		<label for="password">请输入密码</label><input type="password" id="password" name="password" />
		<label for="password_confirmed">请再次输入密码</label><input type="password" id="password_confirmed" name="passwordConfirmed" />
		<input type="submit" value="注册•" />
	</form>
	<p><a href="login.html">已经注册,直接登录!</a></p>
</body>
</html>

添加处理注册请求的Handler也很简单:

	@PostMapping("/register.action")
	public ModelAndView postRegister(String userName, String password, String passwordConfirmed) {
		System.out.println("userName: " + userName + ", password: " + password + ", passwordConfirmed: " + passwordConfirmed);
		ModelAndView mv = new ModelAndView();
		try {
			userService.register(userName, password, passwordConfirmed);
			mv.setViewName("register-success.html");
		} catch (Exception e) {
			mv.addObject("errorMessage", e.getMessage());
			mv.setViewName("register-failure.jsp");
		}
		return mv;
	}

唯一要关注的是,注册成功和注册失败分别转发到了不同的页面。

总结

本篇文章简单实现了用户注册的功能,还有很多可以优化改进的地方:

  • 注册请求的参数绑定和校验还可以进一步简化,目前即便用户名为空也可以注册成功、长度和字符也没有限制等;
  • 注册结果的展示还不够友好,用户还需要点击一次才能继续登录或注册;
  • 不够安全,密码是明文存储、没有验证码等;
  • 用户ID的生成可以采用更好的方案;
  • 具有原子性的业务,访问数据库需要放到一个事务中;
  • 异常处理还不够完善;
  • 等等。

不管怎样,我们还是实现了一个基本可用的用户注册功能,而且开发起来还是相当快、相当清晰的,因为我们之前已经搭建好了整个技术框架啊,正所谓磨刀不误砍柴工!

一、写在前面

二、效果图

三、实现思路

四、实现代码

1、login总界面

2、registercheck总代码

3、logoutcheck总代码

4、amendcheck总代码


相关文章

一、写在前面

哈喽~大家好,这篇呢我们来看看用 JSP 连接 MySQL 登入注册项目实践,这里就可能有人问了,唉?追桑~前些天不是写了 jsp 登入注册的项目吗?怎么这次还在写呢?哈哈,您别担心,这次呢,肯定和上次不同,我们先来看看效果吧!

二、效果图

数据库界面

感觉是不是不一样了,哈哈哈,那么接下来我们来看看是怎么实现的。

三、实现思路

首先我们这里很明显,有四个总页面分别是 login(登入界面)、logout(注销界面)、amend(修改界面)、register(注册界面),这四个总页面分别对应着检查页面(check)、成功页面(success)、失败页面(fail)。建立之好后,通过 from 的 action 来进行跳转,我们先来看看 MySQL (数据库)表名叫 login。

我们这里数据库共三列,第一列叫 name (用户名)、pass(密码)、time(注册时间),name 与 pass 都是 int(整型) 类型的,time 是 varchar (可变长类型),如图。

四、实现代码

1、login总界面

首先我们先有个页面,有基本的用户名框,密码框,两按钮,一个注册,一个注销,通过 from进行跳转,代码如下

 <form method="post" action="check.jsp">
        <input type="text" name="user" style="width: 300px;height: 50px" placeholder="请输入用户名:"
        > <br>
        <input type="password" name="pass" style="width: 300px;height: 50px" placeholder="请输入密码:" > <br>
        <button type="submit" style="width:80px;height:40px; font-size: 20px" class="clear">登录</button>
        <button type="reset" style="width:80px;height:40px; font-size: 20px" class="clear">重置</button>
        <br>
        没有账号?<a href="register.jsp">点击注册</a><br>
        不想用了?<a href="logout.jsp">点击注销</a>
    </form>

用 check 连接数据库(如何连接数据库,前面文章已经给出了,有兴趣的小伙伴可以看看前面写的文章,也放在前面了) 同样的道理,还是那五个步骤(这里就不过多的解释,可以看看上面表格给出的文章),先来看看代码。

String user = request.getParameter("user"); // getParameter  与 getAttribute  区别
        String pass = request.getParameter("pass");
        // String getParameter(String name):根据参数名称获取参数值
        // getAttribute()获取的是服务器设置的数据。getAttribute() 方法返回指定属性名的属性值。
 
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
            String url = "jdbc:mysql://localhost:3306/db1?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC";
            String user1 = "root";
            String pass1 = "123456";
            Connection connection = DriverManager.getConnection(url,user1,pass1);
            String sql = "select * from login where name=? and pass=?";
 
            PreparedStatement ps = connection.prepareStatement(sql);
            ps.setString(1,user);
            ps.setString(2,pass);
            ResultSet re = ps.executeQuery();
 
            if (re.next()){
                response.sendRedirect("loginsuccess.jsp");
                session.setAttribute("user",user);
            }else {
                response.sendRedirect("loginfail.jsp");
            }
 
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        }

这里 response.sendRedirect 跳转了两个页面一个 loginsuccess 和 loginfail 的两个界面,下面我们来看看,这两个文件(其实很简单)

loginsuccess 代码

 <div class="form">
                <h2> <h22>登录成功</h22><br>
                </h2>
                <fon>恭喜您成功登入 <br>    欢迎使用 <br>
                    <a class="a1" href="login.jsp">返回登入界面</a>
                </fon>
            </div>

loginfail 代码:

<h2> <h22>登录失败</h22><br>
                </h2>
                <fon>宝~是不是账号或密码记错惹? <br>
                    <a class="a1" href="login.jsp">返回登入界面</a><br>
                    <p1><a href="amend.jsp">点击修改密码</a></p1>
                </fon>

这里我们点击运行看看效果

这里我们用两个升级 大装备(html)(css) 来美化一下我们的页面,这里我们页面美化的,用的是这位大佬的页面(博主名为键盘奏鸣曲),大家可以来看看,点击链接

HTML 代码

<%@ page contentType="text/html;charset=UTF-8" language="java" %>

<head>

<meta charset="UTF-8">

<meta name="viewport" content="width=device-width, initial-scale=1.0">

<link rel="stylesheet" href="css.css">

<title>123</title>



</head>

<body>


<section>

<div class="color"></div>

<div class="color"></div>

<div class="color"></div>

<div class="box">

<div class="circle" style="--x:0"></div>

<div class="circle" style="--x:1"></div>

<div class="circle" style="--x:2"></div>

<div class="circle" style="--x:3"></div>

<div class="circle" style="--x:4"></div>

<div class="container">

<div class="form">

<h2>登录</h2>

<form method="post" action="check.jsp">

<div class="inputBox">

<input type="text" placeholder="姓名" name="user">


</div>

<div class="inputBox">

<input type="password" placeholder="密码" name="pass">


</div>

<div class="inputBox">

<input type="submit" value="登录">


</div>

<p class="forget">不想用了?<a href="logout.jsp">

点击这里

</a></p>

<p class="forget">没有账户?<a href="register.jsp">

注册

</a></p>

</form>

</div>

</div>

</div>

</section>

</body>


</html>

CSS 代码

/*.center{*/
/*    text-align:center;*/
/*    margin-top: 50px;*/
/*}*/
.fon{
    font-size: 40px;
}
/*body{*/
/*    background: url("images/image-2.jpg") no-repeat 0 0;*/
/*    background-size: 100% 100%;*/
/*    text-decoration:none;*/
/*}*/
 
/*input {*/
/*    background-color: transparent;*/
/*    outline: none;*/
/*    color: black;*/
/*}*/
/*.clear{*/
/*    opacity:0.3;*/
/*}*/
 
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}
 
/* 使用flex布局,让内容垂直和水平居中 */
 
section {
    /* 相对定位 */
    position: relative;
    overflow: hidden;
    display: flex;
    justify-content: center;
    align-items: center;
    min-height: 100vh;
    /* linear-gradient() 函数用于创建一个表示两种或多种颜色线性渐变的图片 */
    background: linear-gradient(to bottom, #f1f4f9, #dff1ff);
}
 
/* 背景颜色 */
 
section .color {
    /* 绝对定位 */
    position: absolute;
    /* 使用filter(滤镜) 属性,给图像设置高斯模糊*/
    filter: blur(200px);
}
 
/* :nth-child(n) 选择器匹配父元素中的第 n 个子元素 */
 
section .color:nth-child(1) {
    top: -350px;
    width: 600px;
    height: 600px;
    background: #ff359b;
}
 
section .color:nth-child(2) {
    bottom: -150px;
    left: 100px;
    width: 500px;
    height: 500px;
    background: #fffd87;
}
 
section .color:nth-child(3) {
    bottom: 50px;
    right: 100px;
    width: 500px;
    height: 500px;
    background: #00d2ff;
}
 
.box {
    position: relative;
}
 
/* 背景圆样式 */
 
.box .circle {
    position: absolute;
    background: rgba(255, 255, 255, 0.1);
    /* backdrop-filter属性为一个元素后面区域添加模糊效果 */
    backdrop-filter: blur(5px);
    box-shadow: 0 25px 45px rgba(0, 0, 0, 0.1);
    border: 1px solid rgba(255, 255, 255, 0.5);
    border-right: 1px solid rgba(255, 255, 255, 0.2);
    border-bottom: 1px solid rgba(255, 255, 255, 0.2);
    border-radius: 50%;
    /* 使用filter(滤镜) 属性,改变颜色。
    hue-rotate(deg)  给图像应用色相旋转
    calc() 函数用于动态计算长度值
    var() 函数调用自定义的CSS属性值x*/
    filter: hue-rotate(calc(var(--x) * 70deg));
    /* 调用动画animate,需要10s完成动画,
    linear表示动画从头到尾的速度是相同的,
    infinite指定动画应该循环播放无限次*/
    animation: animate 10s linear infinite;
    /* 动态计算动画延迟几秒播放 */
    animation-delay: calc(var(--x) * -1s);
}
 
/* 背景圆动画 */
 
@keyframes animate {
    0%, 100% {
        transform: translateY(-50px);
    }
    50% {
        transform: translateY(50px);
    }
}
 
.box .circle:nth-child(1) {
    top: -50px;
    right: -60px;
    width: 100px;
    height: 100px;
}
 
.box .circle:nth-child(2) {
    top: 150px;
    left: -100px;
    width: 120px;
    height: 120px;
    z-index: 2;
}
 
.box .circle:nth-child(3) {
    bottom: 50px;
    right: -60px;
    width: 80px;
    height: 80px;
    z-index: 2;
}
 
.box .circle:nth-child(4) {
    bottom: -80px;
    left: 100px;
    width: 60px;
    height: 60px;
}
 
.box .circle:nth-child(5) {
    top: -80px;
    left: 140px;
    width: 60px;
    height: 60px;
}
 
/* 登录框样式 */
 
.container {
    position: relative;
    width: 400px;
    min-height: 400px;
    background: rgba(255, 255, 255, 0.1);
    display: flex;
    justify-content: center;
    align-items: center;
    backdrop-filter: blur(5px);
    box-shadow: 0 25px 45px rgba(0, 0, 0, 0.1);
    border: 1px solid rgba(255, 255, 255, 0.5);
    border-right: 1px solid rgba(255, 255, 255, 0.2);
    border-bottom: 1px solid rgba(255, 255, 255, 0.2);
}
 
.form {
    position: relative;
    width: 100%;
    height: 100%;
    padding: 50px;
}
 
/* 登录标题样式 */
 
.form h2 {
    text-align: center;
    position: relative;
    color: #fff;
    font-size: 40px;
    font-weight: 600;
    letter-spacing: 5px;
    margin-bottom: 30px;
    cursor: pointer;
}
 
.form h2 h22 {
    top: -40px;
    text-align: center;
    position: relative;
    color: #fff;
    font-size: 40px;
    font-weight: 600;
    letter-spacing: 5px;
    margin-bottom: 30px;
    cursor: pointer;
}
 
.form .a1, .form p1 {
    bottom: -90px;
    left: 50px;
    position: relative;
    color: #fff;
    font-size: 18px;
    font-weight: 600;
    letter-spacing: 5px;
    /*margin-bottom: 10px;*/
    cursor: pointer;
    text-decoration: none;
}
 
.form p1 a{
 
    position: relative;
    color: #fff;
    font-size: 18px;
    font-weight: 600;
    letter-spacing: 5px;
    /*margin-bottom: 10px;*/
    cursor: pointer;
    text-decoration: none;
}
 
.form fon {
    top: -30px;
    left: 30px;
    position: relative;
    color: #fff;
    font-size: 28px;
    font-weight: 600;
    letter-spacing: 5px;
    margin-bottom: 30px;
    cursor: pointer;
}
/* 登录标题的下划线样式 */
 
.form h2::before {
    content: "";
    position: absolute;
    left: 0;
    bottom: -10px;
    width: 0px;
    height: 3px;
    background: #fff;
    transition: 0.5s;
}
 
.form h2 h22::before {
    content: "";
    position: absolute;
    /*left: 0;*/
    /*bottom: -10px;*/
    /*width: 0px;*/
    /*height: 3px;*/
    /*background: #fff;*/
    /*transition: 0.5s;*/
}
 
.form h2:hover:before {
    width: 53px;
}
 
.form .inputBox {
    width: 100%;
    margin-top: 20px;
}
 
/* 输入框样式 */
 
.form .inputBox input {
    width: 100%;
    padding: 10px 20px;
    background: rgba(255, 255, 255, 0.2);
    outline: none;
    border: none;
    border-radius: 30px;
    border: 1px solid rgba(255, 255, 255, 0.5);
    border-right: 1px solid rgba(255, 255, 255, 0.2);
    border-bottom: 1px solid rgba(255, 255, 255, 0.2);
    font-size: 16px;
    letter-spacing: 1px;
    color: #fff;
    box-shadow: 0 5px 15px rgba(0, 0, 0, 0.05);
}
 
.form .inputBox input::placeholder {
    color: #fff;
}
 
/* 登录按钮样式 */
 
.form .inputBox input[type="submit"],.form .inputBox input[type="reset"]  {
    background: #fff;
    color: #666;
    max-width: 100%;
    margin-bottom: 20px;
    font-weight: 600;
    cursor: pointer;
}
 
.forget {
    margin-top: 6px;
    color: #fff;
    letter-spacing: 1px;
}
 
.forget a {
    color: #fff;
    font-weight: 600;
    text-decoration: none;
}

同样的道理我们来升级一下 loginsuccess 与 loginfail 。

loginsuccess 代码

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>登入成功界面</title>
    <link rel="stylesheet" href="css.css" type="text/css">
</head>
<body>
<%--<div class="center">--%>
<%--    <p class="fon">登入成功界面</p>--%>
<%--    <p class="fon1">恭喜您成功登入,欢迎使用</p>--%>
<%--    <a href="login.jsp">点击退出,返回登入界面</a>--%>
<%--</div>--%>
 
<section>
    <div class="color"></div>
    <div class="color"></div>
    <div class="color"></div>
    <div class="box">
        <div class="circle" style="--x:0"></div>
        <div class="circle" style="--x:1"></div>
        <div class="circle" style="--x:2"></div>
        <div class="circle" style="--x:3"></div>
        <div class="circle" style="--x:4"></div>
        <div class="container">
            <div class="form">
                <h2> <h22>登录成功</h22><br>
                </h2>
                <fon>恭喜您成功登入 <br>    欢迎使用 <br>
                    <a class="a1" href="login.jsp">返回登入界面</a>
                </fon>
            </div>
        </div>
    </div>
</section>
 
</body>
</html>

loginfail 代码

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>登入失败界面</title>
    <link rel="stylesheet" href="css.css" type="text/css">
</head>
<body>
<%--<div class="center">--%>
<%--    <p class="fon">登入失败界面</p>--%>
<%--    <p class="fon1">对不起,您账号或密码有误,请返回登入界面</p>--%>
<%--    <a href="login.jsp">返回登入界面</a><br>--%>
<%--    忘记密码?<a href="amend.jsp">点击修改密码</a>--%>
<%--</div>--%>
 
<section>
    <div class="color"></div>
    <div class="color"></div>
    <div class="color"></div>
    <div class="box">
        <div class="circle" style="--x:0"></div>
        <div class="circle" style="--x:1"></div>
        <div class="circle" style="--x:2"></div>
        <div class="circle" style="--x:3"></div>
        <div class="circle" style="--x:4"></div>
        <div class="container">
            <div class="form">
                <h2> <h22>登录失败</h22><br>
                </h2>
                <fon>宝~是不是账号或密码记错惹? <br>
                    <a class="a1" href="login.jsp">返回登入界面</a><br>
                    <p1><a href="amend.jsp">点击修改密码</a></p1>
                </fon>
                
            </div>
        </div>
    </div>
</section>
 
</body>
</html>

点击运行,我们来看看效果

那么这里我们是完成了,login总界面的效果,同样的道理,代码都差不多,我们直接 cv 大法,这里就给出重点要改的代码。

2、registercheck总代码

里面要重点改的代码,一个是 sql 语句插入,另一个是时间格式转换。

tring sql = "insert into login(name, pass,time)VALUES(?,?,?)";

SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");// 时间转换,要不然就会是国际时间格式

String data = formatter.format(new Date());// 记录的是当前的时间

ps.setString(3,data);

3、logoutcheck总代码

3、logoutcheck总代码

里面要重点改的代码,是 sql 语句删除。

String sql = "DELETE FROM login WHERE name =? and pass =?";

4、amendcheck总代码

里面要重点改的代码,是 sql 语句更新。

String sql = "update login set pass='"+pass+"'";

然后分别是各个总页面的 success 与 fail 页面来实现好,这里有一个小细节,我们在作抛出异常,这里可以 out.println 来打印出信息来测试,可以输出在网页上,这样可以方便知道那里有异常。


catch (ClassNotFoundException e) {
            e.printStackTrace();
            out.println("1");
            // response.sendRedirect("registerfail.jsp");
        } catch (SQLException e) {
            e.printStackTrace();
            out.println("2");
            // response.sendRedirect("registerfail.jsp");

好了,点击运行,完成总效果。



































作者:一个名叫追的程序猿

原文出处:https://blog.csdn.net/aasd23/article/details/124458396?spm=1001.2100.3001.7377&utm_medium=distribute.pc_feed_blog_category.none-task-blog-classify_tag-16-124458396-null-null.nonecase&depth_1-utm_source=distribute.pc_feed_blog_category.none-task-blog-classify_tag-16-124458396-null-null.nonecase

文章主要是为了整理之前学习项目中的知识点,并进行一定程度的理解。

技术列表:

  1. SpringBoot
  2. MySQL
  3. redis
  4. JWT

用户登录逻辑:

首先打开前端登录页面,F12进行网页URL抓取:

随便输入信息,查看请求后端的地址:

地址:

请求 URL: http://localhost:8888/login
请求方法: POST
状态代码: 200 
远程地址: [::1]:8888
引用站点策略: strict-origin-when-cross-origin

很明显,前端用户输入账户,通过post请求传递给后端接收:

@RequestMapping("/login")
public class LoginController {
    @Autowired
    private LoginService loginService;
    @PostMapping
    public Result login(@RequestBody LoginParam loginParam){
        return loginService.login(loginParam);
    }

}

这里为了方便传输,把前端参数封装成对象传入.

后端验证查询账户的逻辑操作:

  1. 获取账户密码
  2. 判断账户密码是否为空
  3. 密码加密,采用MD5加密+盐的操作
  4. 通过处理的账户密码(加密)进行数据库查询
String pwd = DigestUtils.md5Hex(password + salt);
//根据账号和加密的密码进行数据库的查找
SysUser sysUser = sysUserService.findUser(account, pwd);
  1. 查找成功,使用JWT工具包生成token,保存到redis中

  1. 向前端返回生成的token,检查token,进行登录
{
    "code": 200,
    "success": true,
    "msg": "success",
    "data": "eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2MzQ4OTY1MDIsInVzZXJJZCI6MSwiaWF0IjoxNjM0MDA3NDcwfQ.9elJSiGa-QARLqKGLTeFW9go7ujsArd0QV_HihHfEm0"
}

这里就实现了JWT和redis实现简单的登录验证功能。

用户注册逻辑:

前端请求:

前端传递账户名、密码、昵称,调用http://localhost:8888/register地址进行post传参,后端接收参数

  1. 获取前端参数,判断参数是否合法(是否为空)
  2. 判断账户是否存在,存在,返回账户已经被注册了的json数据
  3. 不存在、注册账户(生成User对象)调用保存接口,保存的时候需要将密码进行MD5+盐 加密
  4. 生成token令牌String token = JWTUtils.createToken(sysUser.getId());
  5. 存入redis 并返回这里存入redis是因为,注册成功后会跳转到前端页面,前端页面会去redis中找user的信息进行对比,如果存在,显示登录,反之登录失败。
  6. 注意加上事务,一旦中间的任何过程出现问题,注册的用户需要回滚(防止脏数据)

将生成得token传递给前端:

{
    "code": 200,
    "success": true,
    "msg": "success",
    "data": "eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2MzQ4NDc0MzQsInVzZXJJZCI6MTQ0NzU1MjU3MDYzMDQzNDgxNywiaWF0IjoxNjMzOTU4NDAxfQ.zn5meG_lUWROuz7TmkUGS0MTjO1-TDQa42uM_-uhXqs"
}

前端调用该接口,:

http://localhost:8888/users/currentUser

前端将获得token传递给后端接口currentUser,在redis中找到user信息,校验信息,解析数据,返回用户基本信息,前端解析

前端index.js部分代码段:

login({commit}, user) {
      return new Promise((resolve, reject) => {
        login(user.account, user.password).then(data => {
          if(data.success){
            commit('SET_TOKEN', data.data)
            setToken(data.data)
            resolve()
          }else{
            reject(data.msg)
          }
        }).catch(error => {
          reject(error)
        })
      })

到这里就是简单的登录注册的功能,上述内容都是部分代码段,如果有需要学习的可以评论留言。

参考文献:

【码神之路】博客开发;

结束:

如果你看到这里或者正好对你有所帮助,希望能点个或者⭐感谢;

有错误的地方,欢迎在评论指出,作者看到会进行修改。

原文链接:https://www.cnblogs.com/xbhog/p/15397167.html