整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          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


          主站蜘蛛池模板: 国产福利一区二区三区在线观看| 色噜噜一区二区三区| 激情内射亚洲一区二区三区爱妻| 天堂一区人妻无码| 亚洲一区二区三区香蕉| 日韩电影在线观看第一区| 国产精品久久久久久一区二区三区 | 中文字幕aⅴ人妻一区二区| 亚洲午夜日韩高清一区 | 国产av天堂一区二区三区| 美女免费视频一区二区三区| 日本一区二区三区中文字幕| 国产av成人一区二区三区| 成人精品视频一区二区三区| 成人一区二区免费视频| 91视频一区二区| 内射一区二区精品视频在线观看| 日本一区二区三区在线看| 一区二区福利视频| 亚洲视频在线一区| 日韩精品无码久久一区二区三| 中文字幕一区二区三区在线不卡| 国产一区二区三区乱码| 亚洲一区二区三区高清在线观看| 国产精品久久久久一区二区三区 | 久久久久久一区国产精品| 亚洲国产成人一区二区精品区| 91久久精品一区二区| 亚洲国产成人久久一区WWW | 亚洲色精品三区二区一区| 国产在线视频一区二区三区98| 黑人一区二区三区中文字幕| 无码乱码av天堂一区二区| 2018高清国产一区二区三区 | 国产aⅴ精品一区二区三区久久| 国产成人精品久久一区二区三区av| 91精品一区二区| 色噜噜AV亚洲色一区二区| 国产SUV精品一区二区88L| 精品国产一区二区三区久久久狼 | 日本在线观看一区二区三区|