前端部分
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; |
| |
| 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; |
| |
| 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(){ |
| |
| 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(); |
| |
| 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(){ |
| |
| |
| 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),防止重复。所以后端的注册没有判断用户名重复