員工管理系統 - 登錄與註冊功能
創建項目
利用 Spring Initializr 快速創建一個項目:
這裏填寫一些項目名、描述、組織、包結構等;
這裏主要是選擇要引入的依賴,不選也可以,後面複製我的 pom.xml 代碼即可。
創建完項目以後,刪除一些用不上的文件(比如 test 目錄下的文件),項目結構如下:
pom.xml
然後檢查一下 pom.xml 中的依賴:按上面的選擇應該和我的 pom.xml 差不多,但是我做了一些刪減,把用不上的去掉了。可以直接複製我的 pom.xml,複製後一定要刷新 Maven 項目!
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.yusael</groupId>
<artifactId>ems</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>ems</name>
<description>springboot + mybatis + thymeleaf 的 ems</description>
<properties>
<java.version>1.8</java.version>
</properties>
<!--繼承springboot父項目-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.7.RELEASE</version>
</parent>
<dependencies>
<!--引入springboot的web支持-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--thymeleaf-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!--mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.2</version>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.43</version>
<scope>runtime</scope>
</dependency>
<!--druid-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.12</version>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
數據庫表設計和環境準備
需求分析:
- 既然做的員工管理系統,那肯定需要一個員工表(t_emp)來存儲員工的信息
表的具體內容需要根據業務需求來決定,我們這裏就做一個簡易的管理系統,設的較爲簡單。 - 由於我們要做登錄功能,因此我們需要一個用戶表(t_user)來存儲用戶信息。
建表SQL
員工表的SQL:
CREATE TABLE `t_emp` (
`id` varchar(40) NOT NULL,
`name` varchar(60) DEFAULT NULL,
`salary` double(7,2) DEFAULT NULL,
`age` int(3) DEFAULT NULL,
`bir` date DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
用戶表的SQL:
CREATE TABLE `t_user` (
`id` varchar(40) NOT NULL,
`username` varchar(40) DEFAULT NULL,
`realname` varchar(40) DEFAULT NULL,
`password` varchar(40) DEFAULT NULL,
`sex` varchar(8) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
application.properties
數據庫建完表以後,還需要在 application.properties 中配置:
server.port=8989
server.servlet.context-path=/ems
##mysql配置
#指定連接池類型
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
#指定驅動
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
#指定url
spring.datasource.url=jdbc:mysql://localhost:3306/ems?characterEncoding=UTF-8
#指定用戶名
spring.datasource.username=root
#指定密碼
spring.datasource.password=1234
##配置thymleaf(下面註釋的是默認配置, 可以不設置)
#spring.thymeleaf.prefix=classpath:/templates/
#spring.thymeleaf.suffix=.html
#spring.thymeleaf.encoding=UTF-8
#spring.thymeleaf.cache=false # 想讓熱部署生效必須配置這個
#spring.thymeleaf.servlet.content-type=text/html
# 默認無法直接訪問templates下的頁面, 需要設置
spring.resources.static-locations=classpath:/templates/, classpath:/static/
#指定mapper配置文件位置
mybatis.mapper-locations=classpath:/com/yusael/mapper/*.xml
#指定起別名了的類
mybatis.type-aliases-package=com.yusael.entity
由於配置了mapper文件的位置,在啓動類中加上 @MapperScan("com.yusael.dao")
掃描該目錄。
package com.yusael;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("com.yusael.dao")
public class EmsApplication {
public static void main(String[] args) {
SpringApplication.run(EmsApplication.class, args);
}
}
用戶註冊與登錄功能
MVC 的模式開發項目,包的結構如下:
entity
先在 com.yusael.entity
包下創建與數據庫表對應的實體類 User.java
:
package com.yusael.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import java.util.Date;
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class User {
private String id;
private String username;
private String realname;
private Double password;
private Date sex;
}
dao
在 com.yusael.dao
包下開發 UserDAO.java
:
package com.yusael.dao;
import com.yusael.entity.User;
import org.apache.ibatis.annotations.Param;
public interface UserDAO {
void save(User user);
User login(@Param("username") String name, @Param("password")String password);
}
在 resources/com/yusael/mapper
下創建 UserDAOMapper.xml
,這裏的路徑要與 application.properties 中配置的一致。
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.yusael.dao.UserDAO">
<!--註冊-->
<insert id="save" parameterType="User">
insert into t_user values (#{id}, #{username}, #{realname}, #{password}, #{sex})
</insert>
<!--登錄-->
<select id="login" parameterType="User" resultType="User">
select select id, username, realname, password, sex from t_user
where username=#{username} and password=#{password}
</select>
</mapper>
service
在 com.yusael.service
包下開發接口 UserService.java
:
package com.yusael.service;
import com.yusael.entity.User;
public interface UserService {
void register(User user);
User login(String username, String password);
}
在 com.yusael.service
包下開發 UserServiceImpl.java
:
package com.yusael.service;
import com.yusael.dao.UserDAO;
import com.yusael.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@Transactional
public class UserServiceImpl implements UserService {
@Autowired
private UserDAO userDAO;
@Override
public void register(User user) {
user.setId(UUID.randomUUID().toString());
userDAO.save(user);
}
@Override
public User login(String username, String password) {
return userDAO.login(username, password);
}
}
生成驗證碼的工具
在開發 com.yusael.controller
包的內容前,我們需要引入一個驗證碼功能的代碼,將它放到 com.yusael.utils
下作爲一個工具類:這個不需要我們自己寫,直接拿過來用就可以了。
package com.yusael.utils;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.Random;
public class ValidateImageCodeUtils {
/**
* 驗證碼難度級別 Simple-數字 Medium-數字和小寫字母 Hard-數字和大小寫字母
*/
public enum SecurityCodeLevel {
Simple, Medium, Hard
};
/**
* 產生默認驗證碼,4位中等難度
*
* @return
*/
public static String getSecurityCode() {
return getSecurityCode(4, SecurityCodeLevel.Medium, false);
}
/**
* 產生長度和難度任意的驗證碼
*
* @param length
* @param level
* @param isCanRepeat
* @return
*/
public static String getSecurityCode(int length, SecurityCodeLevel level, boolean isCanRepeat) {
// 隨機抽取len個字符
int len = length;
// 字符集合(--除去易混淆的數字0,1,字母l,o,O)
char[] codes = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'
};
// 根據不同難度截取字符串
if (level == SecurityCodeLevel.Simple) {
codes = Arrays.copyOfRange(codes, 0, 10);
} else if (level == SecurityCodeLevel.Medium) {
codes = Arrays.copyOfRange(codes, 0, 36);
}
// 字符集和長度
int n = codes.length;
// 拋出運行時異常
if (len > n && isCanRepeat == false) {
throw new RuntimeException(String.format("調用SecurityCode.getSecurityCode(%1$s,%2$s,%3$s)出現異常," + "當isCanRepeat爲%3$s時,傳入參數%1$s不能大於%4$s", len, level, isCanRepeat, n));
}
// 存放抽取出來的字符
char[] result = new char[len];
// 判斷能否出現重複字符
if (isCanRepeat) {
for (int i = 0; i < result.length; i++) {
// 索引0 and n-1
int r = (int) (Math.random() * n);
// 將result中的第i個元素設置爲code[r]存放的數值
result[i] = codes[r];
}
} else {
for (int i = 0; i < result.length; i++) {
// 索引0 and n-1
int r = (int) (Math.random() * n);
// 將result中的第i個元素設置爲code[r]存放的數值
result[i] = codes[r];
// 必須確保不會再次抽取到那個字符,這裏用數組中最後一個字符改寫code[r],並將n-1
codes[r] = codes[n - 1];
n--;
}
}
return String.valueOf(result);
}
/**
* 生成驗證碼圖片
* @param securityCode
* @return
*/
public static BufferedImage createImage(String securityCode){
int codeLength = securityCode.length();//驗證碼長度
int fontSize = 18;//字體大小
int fontWidth = fontSize+1;
//圖片寬高
int width = codeLength*fontWidth+6;
int height = fontSize*2+1;
//圖片
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics2D g = image.createGraphics();
g.setColor(Color.WHITE);//設置背景色
g.fillRect(0, 0, width, height);//填充背景
g.setColor(Color.LIGHT_GRAY);//設置邊框顏色
g.setFont(new Font("Arial", Font.BOLD, height-2));//邊框字體樣式
g.drawRect(0, 0, width-1, height-1);//繪製邊框
//繪製噪點
Random rand = new Random();
g.setColor(Color.LIGHT_GRAY);
for (int i = 0; i < codeLength*6; i++) {
int x = rand.nextInt(width);
int y = rand.nextInt(height);
g.drawRect(x, y, 1, 1);//繪製1*1大小的矩形
}
//繪製驗證碼
int codeY = height-10;
g.setColor(new Color(19,148,246));
g.setFont(new Font("Georgia", Font.BOLD, fontSize));
for(int i=0;i<codeLength;i++){
double deg=new Random().nextDouble()*20;
g.rotate(Math.toRadians(deg), i*16+13,codeY-7.5);
g.drawString(String.valueOf(securityCode.charAt(i)), i*16+5, codeY);
g.rotate(Math.toRadians(-deg), i*16+13,codeY-7.5);
}
g.dispose();//關閉資源
return image;
}
public static void main(String[] args) throws IOException {
String securityCode = ValidateImageCodeUtils.getSecurityCode();
System.out.println(securityCode);
BufferedImage image = ValidateImageCodeUtils.createImage(securityCode);
ImageIO.write(image,"png",new FileOutputStream("aa.png"));
}
}
controller
IndexController
我們知道,resources/templates
下面放的是我們的頁面文件(html),如果我們直接訪問 templates 下的靜態頁面是無法獲取 static 中的樣式的。
我們需要用控制器進行去訪問,該控制器沒有其他作用,只是爲了訪問界面而已。
在 com.yusael.controller
下創建一個 IndexController.java
:
package com.yusael.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
// 直接訪問templates下的靜態頁面是無法獲取static中的樣式的
// 用該控制器進行去訪問, 該控制器沒有其他作用, 只是爲了訪問界面而已
@Controller
public class IndexController {
@GetMapping("index")
public String toIndex() {
return "ems/login"; // ems下的login.html
}
@GetMapping("toRegister")
public String toRgsiter() {
return "ems/regist"; // ems下的regist.html
}
}
@GetMapping("toSave")
public String toSaave() {
return "ems/addEmp"; // ems下的addEmp.html
}
}
UserController
在 com.yusael.controller
下開發 UserController.java
:
package com.yusael.controller;
import com.yusael.entity.User;
import com.yusael.service.UserService;
import com.yusael.utils.ValidateImageCodeUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.awt.image.BufferedImage;
import java.io.IOException;
@Controller
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
// 登錄方法
@PostMapping("/login")
public String login(String username, String password, HttpSession session) {
User login = userService.login(username, password);
if (login != null) {
session.setAttribute("user", login);
return "redirect:/emp/findAll"; // 跳轉到查詢所有
} else {
return "redirect:/index"; // 跳轉回到登錄
}
}
@PostMapping("/register")
public String register(User user, String code, HttpSession session) {
String sessionCode = (String)session.getAttribute("code"); // 生成的驗證碼
// 忽略大小寫, 比較用戶輸入的驗證碼與生成的驗證碼
if (sessionCode.equalsIgnoreCase(code)) { // 輸入正確
userService.register(user); // 註冊
return "redirect:/index"; // 註冊成功跳轉到登錄界面
} else { // 輸入錯誤
return "redirect:/toRegister"; // 註冊失敗跳轉到註冊界面
}
}
@GetMapping("/code")
public void getImage(HttpSession session, HttpServletResponse response) throws IOException {
// 生成驗證碼
String securityCode = ValidateImageCodeUtils.getSecurityCode();
BufferedImage image = ValidateImageCodeUtils.createImage(securityCode);
// 存入session作用域中
session.setAttribute("code", securityCode);
// 響應圖片
ServletOutputStream os = response.getOutputStream();
ImageIO.write(image, "png", os);
}
}
前端頁面
這裏就把 登陸頁面login.html
和 註冊頁面regist.html
的文件放出來(能體會到後端效果即可),完整項目可以去 https://github.com/szluyu99/ems_thymeleaf。
將 login.html
和 regist.html
放到 resources/templates/ems
下:(css、img這些請去GitHub獲取,沒有這些不影響項目功能)
login.html:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<title>login</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<link rel="stylesheet" type="text/css" th:href="@{/css/style.css}"/>
</head>
<body>
<div id="wrap">
<div id="top_content">
<div id="header">
<div id="rightheader">
<p>
2009/11/20
<br/>
</p>
</div>
<div id="topheader">
<h1 id="title">
<a href="#">main</a>
</h1>
</div>
<div id="navigation">
</div>
</div>
<div id="content">
<p id="whereami">
</p>
<h1>
login
</h1>
<form th:action="@{/user/login}" method="post">
<table cellpadding="0" cellspacing="0" border="0"
class="form_table">
<tr>
<td valign="middle" align="right">
username:
</td>
<td valign="middle" align="left">
<input type="text" class="inputgri" name="username"/>
</td>
</tr>
<tr>
<td valign="middle" align="right">
password:
</td>
<td valign="middle" align="left">
<input type="password" class="inputgri" name="password"/>
</td>
</tr>
</table>
<p>
<input type="submit" class="button" value="Submit »"/>
<input type="button" class="button" onclick="location.href='/ems/toRegister'" value="Regist »"/>
</p>
</form>
</div>
</div>
<div id="footer">
<div id="footer_bg">
[email protected]
</div>
</div>
</div>
</body>
</html>
regist.html:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>regist</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<link rel="stylesheet" type="text/css" href="css/style.css" />
</head>
<body>
<div id="wrap">
<div id="top_content">
<div id="header">
<div id="rightheader">
<p>
2009/11/20
<br />
</p>
</div>
<div id="topheader">
<h1 id="title">
<a href="#">main</a>
</h1>
</div>
<div id="navigation">
</div>
</div>
<div id="content">
<p id="whereami">
</p>
<h1>
註冊
</h1>
<form th:action="@{/user/register}" method="post">
<table cellpadding="0" cellspacing="0" border="0"
class="form_table">
<tr>
<td valign="middle" align="right">
用戶名:
</td>
<td valign="middle" align="left">
<input type="text" class="inputgri" name="username" />
</td>
</tr>
<tr>
<td valign="middle" align="right">
真實姓名:
</td>
<td valign="middle" align="left">
<input type="text" class="inputgri" name="realname" />
</td>
</tr>
<tr>
<td valign="middle" align="right">
密碼:
</td>
<td valign="middle" align="left">
<input type="password" class="inputgri" name="password" />
</td>
</tr>
<tr>
<td valign="middle" align="right">
性別:
</td>
<td valign="middle" align="left">
男
<input type="radio" class="inputgri" name="sex" value="男" checked="checked"/>
女
<input type="radio" class="inputgri" name="sex" value="女"/>
</td>
</tr>
<tr>
<td valign="middle" align="right">
驗證碼:
<img id="num" th:src="@{/user/code}" />
<a href="javascript:;" onclick="document.getElementById('num').src = '/ems/user/code?'+(new Date()).getTime()">換一張</a>
</td>
<td valign="middle" align="left">
<input type="text" class="inputgri" name="code" />
</td>
</tr>
</table>
<p>
<input type="submit" class="button" value="Submit »" />
</p>
</form>
</div>
</div>
<div id="footer">
<div id="footer_bg">
[email protected]
</div>
</div>
</div>
</body>
</html>
啓動項目
運行 EmsApplcation.java
,瀏覽器中輸入:http://localhost:8989/ems/index
,進入登陸界面:
進入登陸界面,但是此時我們數據庫中還沒有信息,我們點擊 Regist,先去註冊一個賬號。
點擊 Regist,可以發現網址變爲:http://localhost:8989/ems/toRegister
,來到了註冊界面。
我們輸入要註冊的賬號的信息,點擊 Submit。
我們在 UserController.java
中設置的是註冊成功跳回登陸界面,成功跳回登陸界面則說明註冊成功。
數據庫中也成功插入了數據。
此時我們就可以登錄了,登錄後應當跳到後臺管理界面,但是我們還沒有做,因此會報錯。
我們可以在 UserController.java
的 register
和 login
方法中分別加一句輸出,可以驗證我們註冊和登錄成功。