前端部分
HTML+CSS部分
引入JQuery包和JQuery.cookie包,前者封装了DOM操作的一些方法,后者封装了对cookie的操作
我们使用cookie主要是为了做登录后页面的跳转时,能存下来这个用户是谁,这样后面可以对单一用户进行操作,比如根据用户id查询等
代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>用户登录</title>
<link rel="stylesheet" href="css/login.css">
<script src="js/dependency/jquery-3.7.1.min.js" defer></script>
<script src="js/dependency/jquery.cookie.js" defer></script>
<script src="js/login.js" defer></script>
</head>
<body>
<div class="login">
<div class="inputArea">
<ul>
<li>用户名:<input type="text" class="username" id="userName"></input></li>
<li>密码:<input type="password" class="password" id="passWord"></input></li>
</ul>
</div>
<div class="clickArea">
<input type="button" value="登录" id="loginBtn" />
<input type="button" value="注册" id="registerBtn" />
</div>
</div>
<div class="register">
<div class="registerInput">
<ul>
<li>用户名:<input type="text" class="username" id="regiestUserName"></input></li>
<li>密码:<input type="password" class="password" id="resistPassWord"></input></li>
<li>确认密码:<input type="password" class="password" id="confirmPassWord"></input></li>
</ul>
</div>
<div class="registerArea">
<input type="button" value="注册" id="doRegister" />
<input type="button" value="取消" id="cancleRegister" />
</div>
</div>
</body>
</html>
body{
margin:0;
padding:0;
}
/*登录样式*/
.login{
height: 30vh;
width: 20vw;
/*background: red;*/
margin-top: 10vh;
margin-left: auto;
margin-right: auto;
border: 3px solid red;
border-radius: 30px;
}
.login .inputArea ul{
display: flex;
flex-direction: column;
margin: 12vh 0 0 0;
justify-content: center;
align-items: center;
}
.inputArea ul li{
margin: 0 auto;
display: flex;
line-style-type:none;
}
.clickArea{
display: flex;
margin: 30px auto;
justify-content: center;
align-items: center;
}
/*注册弹框样式*/
.register{
height: 30vh;
width: 20vw;
/*background: red;*/
margin: 50px auto;
border: 3px solid blue;
border-radius: 30px;
display:none;
}
.registerInput ul{
display: flex;
flex-direction: column;
margin: 12vh 0 0 0;
justify-content: center;
align-items: center;
}
.registerInput ul li{
margin: 0 auto;
display: flex;
line-style-type:none;
}
.registerArea{
display: flex;
margin: 30px auto;
justify-content: center;
align-items: center;
}
代码要点
- 这里使用的是无序列表,但是实际使用发现很难对齐文字和输入框,就像这样
推荐修改为两个无边框表格,会好很多
- 注册部分(蓝色边框部分),要设置为display:none,用户点击注册按钮的时候弹出
- 弹性盒子模型,我们希望输入框竖着排列,按钮横着排列,注意设置
JS代码部分
代码
let checkInputAndTrim = function(param,value){
if(!value||value==""){
alert(param + "为空!");
return -1;
}
return value.trim();
}
// 单击登录按钮的单击事件触发
$("#loginBtn").on("click", function(){
// trim + 校验用户输入非空
let username = checkInputAndTrim("用户名", $("#userName").val());
let password = checkInputAndTrim("密码", $("#passWord").val());
if(username!=-1 || password!=-1){
$.ajax({
url: "login",
type: "post",
data:{username, password},
success: function (value) {
console.log(value);
let code = value.code;
console.log(code);
if(code!=0){
let msg = value.msg;
alert(msg);
}else{
alert("登录成功!");
let userId = value.data[0].id;
// 获取当前时间
let expirationTime = new Date();
// 在当前时间的基础上增加2小时
expirationTime.setTime(expirationTime.getTime() + 120 * 60 * 1000);
$.cookie("userId",userId,{expires: expirationTime, path: "/CommodityManage" });
// 跳转其他页面
self.location.href='CommodityManage.html';
}
},
error: function () {
alert("登录失败!");
}
})
}
})
// 单击注册按钮的单击事件触发
$("#registerBtn").on("click", function(){
$(".register").css("display","block");
})
// 确认注册
$("#doRegister").on("click", function(){
// trim + 校验用户输入非空
let username = checkInputAndTrim("用户名", $("#regiestUserName").val());
let password1 = checkInputAndTrim("密码", $("#resistPassWord").val());
let password2 = checkInputAndTrim("确认密码", $("#confirmPassWord").val());
if(username!=-1||password1!=-1||password2!=-1){
if(password1!=password2){
alert("两次输入的密码不一致!");
return;
}
$.ajax({
url: "register",
type: "post",
data:{username, password1,password2},
success: function (value) {
console.log(value);
let code = value.code;
console.log(code);
if(code!=0){
let msg = value.msg;
alert(msg);
}else{
alert("注册成功!");
// 清空输入的数值
$(".registerInput ul li input").val("");
// 隐藏注册框
$(".register").css("display","none");
}
},
error: function () {
alert("注册失败!");
}
})
}
})
// 取消注册
$("#cancleRegister").on("click", function(){
// 清空输入的数值
$(".registerInput ul li input").val("");
// 隐藏注册框
$(".register").css("display","none");
});
代码要点
- 经常需要用到的代码块可以用函数单独拿出来,代码复用可以使代码的逻辑更清晰
// 校验非空+去除用户输入的字符串的前后空格,重复使用,所以提出来
let checkInputAndTrim = function(param,value){
if(!value||value==""){
alert(param + "为空!");
return -1;
}
return value.trim();
}
- 这里的cookie是后端传给前端的用户id
cookie最好设置过期时间,并设置保存路径,否则可能会导致无关页面修改/删除本页面的cookie
expirationTime.setTime(expirationTime.getTime() + 120 * 60 * 1000);
$.cookie("userId",userId,{expires: expirationTime, path: "/CommodityManage" });
后端部分
Java代码
这里关于JDBC的部分是经过封装的,不过是无侵入的封装,不改变原有的JDBC,所以具体使用起来没什么特别大的差异,读者可以原地修改为JDBC
登录Servlet部分
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.qcby.db.MysqlUtil;
public class LoginServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
resp.setCharacterEncoding("UTF-8");
String userName = req.getParameter("username");
String passWord = req.getParameter("password");
String[] queryParams = { "id", "user_name", "password" };
String loginSql = "select id, user_name, password from t_entity_user where user_name= \"" + userName + "\"";
ArrayList<String[]> userList = MysqlUtil.showUtil(loginSql, queryParams);
for (String[] user : userList) {
System.out.println("=============查询用户的结果:" + Arrays.toString(user));
}
resp.setContentType("application/json;charset=UTF-8");
PrintWriter pw = resp.getWriter();
if (userList.size() == 0) {
pw.write(MysqlUtil.listToFreedomJson("-1", "未找到用户名为" + userName + "的用户!", null, null));
return;
} else if (!passWord.equals(userList.get(0)[2])) {
pw.write(MysqlUtil.listToFreedomJson("-2", "密码错误", null, null));
return;
}
ArrayList<String[]> resultList = new ArrayList<>();
resultList.add(new String[] { userList.get(0)[0] });
String[] resultParams = { "id" };
pw.write(MysqlUtil.listToFreedomJson("0", "success", resultList, resultParams));
}
}
如果不使用@WebServlet注解的话,就需要在WEB-INF文件夹下的web.xml当中注册Servlet,并配置访问路径
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
id="WebApp_ID" version="3.1" metadata-complete="true">
<display-name>MyFirstServlet</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
<!--登录 -->
<servlet>
<servlet-name>login</servlet-name>
<servlet-class>com.qcby.servlet.LoginServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>login</servlet-name>
<url-pattern>/login</url-pattern>
</servlet-mapping>
<!--注册 -->
<servlet>
<servlet-name>register</servlet-name>
<servlet-class>com.qcby.servlet.RegisterServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>register</servlet-name>
<url-pattern>/register</url-pattern>
</servlet-mapping>
</web-app>
登录要点
- 首先校验是否存在用户名对应的用户,然后再判断是否密码错误
- 自己封装的错误信息尽量完整
- String用equals判断相等时,应该将一定不为null的放在equals的前面,防止出现空指针异常
在这里,用户输入的password经过了前端的非空检验,所以一定不是null,而从数据库中查出来的密码,可能为null
if (!passWord.equals(userList.get(0)[2])) {
pw.write(MysqlUtil.listToFreedomJson("-2", "密码错误", null, null));
return;
}
注册Servlet部分
import java.io.IOException;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.qcby.db.MysqlUtil;
public class RegisterServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
req.setCharacterEncoding("UTF-8");
resp.setCharacterEncoding("UTF-8");
String userName = req.getParameter("username");
String passWord1 = req.getParameter("password1");
String passWord2 = req.getParameter("password2");
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSS");
String remark = formatter.format(Calendar.getInstance().getTime());
resp.setContentType("application/json;charset=UTF-8");
PrintWriter pw = resp.getWriter();
if (passWord1 != null && !passWord1.equals(passWord2)) {
pw.write(MysqlUtil.listToFreedomJson("-3", "两次密码输入不一致", null, null));
return;
}
String insertSql = "insert into t_entity_user(user_name, password, remark) value(\"" + userName + "\",\""
+ passWord1 + "\",\"" + remark + "\")";
System.out.println(insertSql);
try {
int result = MysqlUtil.add(insertSql);
System.out.println("注册用户插入的行数" + result);
} catch (Exception e) {
pw.write(MysqlUtil.listToFreedomJson("-4", e.getMessage(), null, null));
return;
}
pw.write(MysqlUtil.listToFreedomJson("0", "注册成功", null, null));
}
}
注册要点
- 前端已经校验了两个密码是否相等了,后端为什么还要再校验一次?这是因为用户有可能是用postman或者swagger等接口测试工具发的请求,所以需要重新校验一次
至于为什么非空没校验,是因为懒,所以没写
但是真实企业场景下,除非做一些请求来源的验证,否则前端有的校验,后端有;前端没有的校验,后端一样有!后端负责整个项目的兜底
- sql的字符串一定要注意转移引号,否则sql会执行出错
- 这里在数据库的user表中,给user_name字段加了一个唯一索引(UNIQUE),防止重复。所以后端的注册没有判断用户名重复