基于vue.js+thymeleaf模板引擎+ajax的注册登陆简洁模板(含从零到一详细介绍)

文章目录

前言

大多数的网页都离不开注册登陆这两个功能,所以我想结合所学知识,使用vue.jsthymeleafAjax做出一个简易通用的模板,该模板应具有如下功能:

  1. 用户名和密码是否按照一定格式输入
  2. 提示用户名是否已存在
  3. 两次输入密码是否一致
  4. 密码进行md5加密处理
  5. 验证码点击刷新
  6. 符合条件的输入框显示对勾,否则提示具体错误信息
  7. 只有全部显示正确才可以进行注册或者登陆

1、数据库准备

确保mysql正确安装配置,使用可视化工具如Navicat操作数据库,创建数据库名称如atweb,建立t_user表格,语句如下(新建查询后直接粘贴运行即可):

sql 复制代码
-- 创建数据库
create database atweb;
--创建表格
use atweb;
create table if not exists t_user
(
	u_id int primary key auto_increment comment '主键且自增',
	u_name varchar(40) not null,
	u_pwd varchar(100) comment '考虑到加密,长度尽量多给',
	u_describe varchar(50) comment '后续字段有需要自行添加'
)

成功创建的提示为:

2、工具类与相关基类使用

我们通常会把一些需要经常调用且很少变化的代码 封装 起来作为方法或者类,以提高开发效率和减少出错的可能行。

2.1、工具类

  1. JDBCTools工具类:

    • 主要用来读取数据库配置文件,具有连接数据库和释放连接的功能
    • 代码如下:
    java 复制代码
    package com.qb.util;
    import com.alibaba.druid.pool.DruidDataSourceFactory;
    import javax.sql.DataSource;
    import java.sql.Connection;
    import java.sql.SQLException;
    import java.util.Properties;
    
    public class JDBCTools {
        // 1、创建数据源,即连接池
        private static DataSource dataSource;
    
        // 2、创建ThreadLocal对象
        private static ThreadLocal<Connection> threadLocal;
    
        static {
            try {
                //1、读取druid.properties文件
                Properties pro = new Properties();
                pro.load(JDBCTools.class.getClassLoader().getResourceAsStream("druid.properties"));
    
                //2、连接连接池
                dataSource = DruidDataSourceFactory.createDataSource(pro);
    
                //3、创建线程池
                threadLocal = new ThreadLocal<>();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 获取连接的方法
         * 后续知识: 数据库事务操作  必须使用同一个数据库连接
         * @return
         * @throws SQLException
         */
        public static Connection getConnection() {
            // 从线程中获取连接  threadLocal可以保证线程安全
            Connection connection = threadLocal.get();
            if (connection == null) {
                // 从连接池中获取一个连接
                try {
                    connection = dataSource.getConnection();
                    // 将连接与当前线程绑定
                    threadLocal.set(connection);
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            return connection;
        }
    
        
         //释放连接的方法
        public static void releaseConnection() {
            // 获取当前线程中的连接
            Connection connection = threadLocal.get();
            if (connection != null) {
                try {
                    connection.close();
                    // 将已经关闭的连接从当前线程中移除
                    threadLocal.remove();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }
  2. MD5Util工具类

    • 针对明文字符串执行MD5加密
    • 代码如下:
    java 复制代码
     package com.qb.util;
     import java.math.BigInteger;
     import java.security.MessageDigest;
     import java.security.NoSuchAlgorithmException;
     
     
     public class MD5Util {
       
         public static String encode(String source) {
     
             // 1.判断明文字符串是否有效
             if (source == null || "".equals(source)) {
                 throw new RuntimeException("用于加密的明文不可为空");
             }
     
             // 2.声明算法名称
             String algorithm = "md5";
     
             // 3.获取MessageDigest对象
             MessageDigest messageDigest = null;
             try {
                 messageDigest = MessageDigest.getInstance(algorithm);
             } catch (NoSuchAlgorithmException e) {
                 e.printStackTrace();
             }
     
             // 4.获取明文字符串对应的字节数组
             byte[] input = source.getBytes();
     
             // 5.执行加密
             byte[] output = messageDigest.digest(input);
     
             // 6.创建BigInteger对象
             int signum = 1;
             BigInteger bigInteger = new BigInteger(signum, output);
     
             // 7.按照16进制将bigInteger的值转换为字符串
             int radix = 16;
             String encoded = bigInteger.toString(radix).toUpperCase();
     
             return encoded;
         }
     }
  3. ThreadLocalUtil工具类

    • 代码如下:
    java 复制代码
    package com.qb.util;
    public class ThreadLocalUtil {
    
        //1.指定ThreadLocal对象
        private static ThreadLocal<String> threadLocal = new ThreadLocal<>();
    
        public static void setStr(String name){
            threadLocal.set(name);
            //添加数据
        }
    
        public static String getStr(){
            //获取数据
            return threadLocal.get();
        }
    
        public static  void remove(){
            //移除数据
            threadLocal.remove();
        }
    }

2.2、相关基类

  1. BaseServlet类

    • 该类继承HttpServlet类,封装模板引擎,方便跳转请求的编写
    • 封装doGetdoPost方法,可通过传递方法名称调用指定方法(利用反射
    • 代码如下:
    java 复制代码
    public class BaseServlet extends HttpServlet {
    
        private TemplateEngine templateEngine;
    
        //使用模版引擎需要实例化模版引擎对象  并且设定前缀和后缀
        @Override
        public void init() throws ServletException {
    
            // 1.获取ServletContext对象
            ServletContext servletContext = this.getServletContext();
    
            // 2.创建Thymeleaf解析器对象
            ServletContextTemplateResolver templateResolver = new ServletContextTemplateResolver(servletContext);
    
            // 3.给解析器对象设置参数
            // ①HTML是默认模式,明确设置是为了代码更容易理解
            templateResolver.setTemplateMode(TemplateMode.HTML);
    
            // ②设置前缀
            String viewPrefix = servletContext.getInitParameter("view-prefix");
    
            templateResolver.setPrefix(viewPrefix);
    
            // ③设置后缀
            String viewSuffix = servletContext.getInitParameter("view-suffix");
    
            templateResolver.setSuffix(viewSuffix);
    
            // ④设置缓存过期时间(毫秒)
            templateResolver.setCacheTTLMs(60000L);
    
            // ⑤设置是否缓存
            templateResolver.setCacheable(true);
    
            // ⑥设置服务器端编码方式
            templateResolver.setCharacterEncoding("utf-8");
    
            // 4.创建模板引擎对象
            templateEngine = new TemplateEngine();
    
            // 5.给模板引擎对象设置模板解析器
            templateEngine.setTemplateResolver(templateResolver);
    
        }
    
        //使用模版引擎 转发到页面 并且渲染数据
        protected void processTemplate(String templateName, HttpServletRequest req, HttpServletResponse resp) throws IOException {
            // 1.设置响应体内容类型和字符集
            resp.setContentType("text/html;charset=UTF-8");
    
            // 2.创建WebContext对象
            WebContext webContext = new WebContext(req, resp, getServletContext());
    
            // 3.处理模板数据
            templateEngine.process(templateName, webContext, resp.getWriter());
        }
    
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            doPost(req,resp);
        }
    
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            req.setCharacterEncoding("utf-8");
            resp.setContentType("text/html;charset=utf-8");
    
            String methodName = req.getParameter("method");
            try {
                Method method = this.getClass().getDeclaredMethod(methodName,HttpServletRequest.class,HttpServletResponse.class);
                method.setAccessible(true);
                method.invoke(this,req,resp);
            } catch (Exception e) {
                System.out.println("反射方法有误,请检查:"+methodName);
                e.printStackTrace();
                throw  new RuntimeException(e);
            }
        }
    }
  2. BaseDaoImpl类

    • 该类为数据库操作的基类
    • 封装了常用的数据库增删该查操作
    • 代码如下:
    java 复制代码
    public abstract class BaseDaoImpl {
        private QueryRunner queryRunner = new QueryRunner();
    
        /**
         * 通用的增删改的方法
         * @param sql String sql
         * @param args Object... 如果sql中有?,就传入对应个数的?
         * @return int 受影响行数
         */
        protected int update(String sql,Object... args) {
            try {
                return queryRunner.update(JDBCTools.getConnection(),sql,args);
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
    
        /**
         * 查询单个对象的方法
         * @param clazz Class 记录对应的类类型
         * @param sql String 查询语句
         * @param args Object... 如果sql中有?,即根据条件查询
         * @param <T> 泛型方法声明的泛型类型
         * @return  T 一个对象
         */
        protected <T> T getBean(Class<T> clazz, String sql, Object... args){
            List<T> list = getList(clazz, sql, args);
            if(list != null && list.size()>0) {
                //return getList(clazz, sql, args).get(0);
                return list.get(0);
            }
            return null;
        }
    
        /**
         * 通用查询多个对象的方法
         * @param clazz Class 记录对应的类类型
         * @param sql String 查询语句
         * @param args Object... 如果sql中有?,即根据条件查询,可以设置?的值
         * @param <T> 泛型方法声明的泛型类型
         * @return List<T> 把多个对象放到了List集合
         */
        protected <T> List<T> getList(Class<T> clazz, String sql, Object... args){
            try {
                return queryRunner.query(JDBCTools.getConnection(),sql,new BeanListHandler<T>(clazz),args);
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
    
        protected Object getValue(String sql,Object... args){
            try {
                return queryRunner.query(JDBCTools.getConnection(),sql,new ScalarHandler(),args);
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
    
        protected void batch(String sql,Object[][] args){
            try {
                queryRunner.batch(JDBCTools.getConnection(),sql,args);
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
    
    }
  3. SysResult类

    • 用于封装系统操作结果,并提供了一些静态工厂方法来方便创建不同类型的响应对象
    • 代码如下:
    java 复制代码
    package com.qb.vo;
    import java.io.Serializable;
    public class SysResult implements Serializable {
    
        private Boolean flag;
        private String msg;
        private Object data;
    
        public static SysResult success(String msg,Object data){
    
            return new SysResult(true,msg,data);
        }
    
        public static SysResult success(Object data){
    
            return new SysResult(true,"业务调用成功",data);
        }
    
        public static SysResult success(){
    
            return new SysResult(true,"业务调用成功",null);
        }
    
        public static SysResult fail(){
    
            return new SysResult(false,"业务调用失败",null);
        }
        
        public Boolean getFlag() {
            return flag;
        }
    
        public void setFlag(Boolean flag) {
            this.flag = flag;
        }
    
        public String getMsg() {
            return msg;
        }
    
        public void setMsg(String msg) {
            this.msg = msg;
        }
    
        public Object getData() {
            return data;
        }
    
        public void setData(Object data) {
            this.data = data;
        }
    
        public SysResult() {}
    
        public SysResult(Boolean flag, String msg, Object data) {
            this.flag = flag;
            this.msg = msg;
            this.data = data;
        }
    }

3、web包目录说明

  1. 首先需要把所需的jar包引入到lib下,并执行Add as Library操作

  2. 创建static 包,并在里面创建script 包,引入axios.jsvue.js脚本

  3. 创建pages包和user包,新建注册和登陆的html网页

  4. 配置web.xml文件(配置了验证码生成的servlet),内容如下:

    xml 复制代码
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
             version="4.0">
        
        <context-param>
            <param-name>view-prefix</param-name>
            <param-value>/WEB-INF/pages/</param-value>
        </context-param>
        <context-param>
            <param-name>view-suffix</param-name>
            <param-value>.html</param-value>
        </context-param>
        
        <servlet>
            <servlet-name>KaptchaServlet</servlet-name>
            <servlet-class>com.google.code.kaptcha.servlet.KaptchaServlet</servlet-class>
        </servlet>
        <servlet-mapping>
            <servlet-name>KaptchaServlet</servlet-name>
            <url-pattern>/kaptcha</url-pattern>
        </servlet-mapping>
    </web-app>

4、注册功能设计(本文核心部分)

注意:位于WEB-INF下的页面不可以通过地址栏直接访问,只能通过服务器内部跳转,因此需要一个IndexServlet来访问首页,它的功能就是简单的跳转,代码如下:

java 复制代码
@WebServlet("/index.html")
public class IndexServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req,resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.getRequestDispatcher("/WEB-INF/pages/index.html").forward(req,resp);
    }
}

首页的设计很简单,提供两个超链接,负责 前往注册前往登陆,代码如下:

html 复制代码
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <base th:href="@{/}">
    <title>首页</title>
</head>
<body>
<a href="user?method=toRegister">前往注册</a>
<a href="user?method=toLogin">前往登陆</a>
</body>
</html>

上面的 xmlns:th="http://www.thymeleaf.org"提供了thymeleaf模板引擎内容,
<base th:href="@{/}">将路径提升到根部的url,因此下面的超链接可以不用写前缀

4.1、注册页面设计

由于页面代码片段切分起来麻烦,我会整体展示页面源代码,并在代码间做详细注释

页面源代码展示:

html 复制代码
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <base th:href="@{/}">
    <title>用户注册</title>
    //以下两行代码用来引入js脚本
    <script src="static/script/vue.js"></script>
    <script src="static/script/axios.js"></script>
</head>
<body>
// 这里的 app 将会在下面Vue对象的属性中进行绑定
<div id="app">
    <table border="1" cellspacing="0" cellpadding="0" align="center" width="48%">
    // 每对tr标签代表一行数据,v-model用于属性绑定,会随着vue对象属性的改变而改变
    // @blur代表离焦事件,也就是失去鼠标的光标后自动触发,属性和方法都在vue对象中定义
        <tr align="center">
            <td>用户名</td>
            <td><input type="text" name="username" v-model="user.username" @blur="checkUsername"></td>
            <td><span v-text="msg.usernameErrorMsg"></span></td>
        </tr>
        <tr align="center">
            <td>密码</td>
            <td><input type="password" name="password" v-model="user.password" @blur="checkUserPwd"></td>
            <td><span v-text="msg.passwordErrorMsg"></span></td>
        </tr>
        <tr align="center">
            <td>确认密码</td>
            <td><input type="password" name="rePassword" v-model="user.rePassword" @blur="checkUserRePwd"></td>
            <td><span v-text="msg.passwordErrorMsg2"> </span></td>
        </tr>

        <tr align="center">
            <td>个人描述</td>
            <td><input type="text" name="describe" v-model="user.describe" @blur="checkDescription"></td>
            <td><span v-text="msg.describeMsg"></span></td>
        </tr>
        <tr align="center">
            <td>验证码</td>
            <td><input type="text" name="code" v-model="code" @blur="checkCode"></td>
            <td><span v-text="msg.codeMsg"></span></td>
        </tr>
        // 验证码生成是利用jar包里的servlet,而该servlet已在web.xml中配置
        // changeImage用来点击刷新图片,具体逻辑见下面的注释
        <tr>
            <td colspan="3"><img :src="kaptchaImg" alt="" width="100px" height="50px" @click="changeImage"></td>
        </tr>
        <tr align="center">
            <td colspan="3">
                <button @click="register">注册</button>
            </td>
        </tr>
    </table>

    <script type="text/javascript">
        // vue对象
        const app = new Vue({
            el: "#app", // 绑定上面的div id
            data: { // data代表属性,里面可以定义各种对象,并无限嵌套
                msg: {// 消息对象
                    usernameErrorMsg: "用户名应为6~16位数字和字母组成",
                    passwordErrorMsg: "密码的长度至少为8位",
                    passwordErrorMsg2: "密码两次输入不一致",
                    describeMsg:"请输入个人描述",
                    codeMsg: "请输入正确的验证码"

                },
                // 全局变量,上面user.xx 就是和这里的user属性保持一致
                user: {
                    username: '',
                    password: '',
                    rePassword:'',
                    describe:'',
                },
                // 标志数组,只有用户输入数据全部正确,才会发起注册请求
                flagArray:[0,0,0,0,0],
                // 验证码
                code:'',
                // 表示生成验证码的servlet
                kaptchaImg:"kaptcha"
            },
            methods: {
                // 异步请求,用来检查验证码是否输入正确,sysResult代表响应的对象,页面根据对象属性做不同操作
                async checkCode() {
                    // axios.get这行代码,代表使用get请求,访问业务逻辑的user并携带method和code两个参数
                    let {data:sysResult} = await axios.get("user?method=checkCode&code=" + this.code)
                    if (sysResult.flag) {
                        this.msg.codeMsg = "√"
                        this.flagArray[4] = 1
                    }else{
                        this.msg.codeMsg = "请输入正确的验证码!"
                        this.flagArray[4] = 0
                    }
                },
                // 异步请求,用来规定用户名形式和检查用户名是否已存在
                async checkUsername() {
                    // 正则表达式:用户名应为6~16个字母或数字组成
                    let usernameRege = /^[a-zA-Z0-9]{6,16}$/
                    let usernameFlag = usernameRege.test(this.user.username)
                    // 符合用户名正则的继续判断用户是否已存在,否则直接提示用户名格式错误的信息
                    if (usernameFlag) {
                    // 这里同上,使用get请求,访问user并携带两个参数,根据响应对象属性做出特定操作
                        let {data:sysResult} = await axios.get("user?method=checkNameUnique&username="+this.user.username)
                        if (sysResult.flag) {
                            this.msg.usernameErrorMsg = "√"
                            this.flagArray[0] = 1
                        }else{
                            this.msg.usernameErrorMsg = "用户名已存在!"
                            this.flagArray[0] = 0
                        }
                    }else{
                        this.msg.usernameErrorMsg = "用户名应为6~16位数字和字母组成"
                        this.flagArray[0] = 0
                    }
                },
                // 正则判断用户密码是否符合要求
                checkUserPwd() {
                    // 描述至少为8个字符
                    let pwdRege = /^.{8,}$/
                    let pwdFlag = pwdRege.test(this.user.password)
                    if(pwdFlag){
                        this.msg.passwordErrorMsg = "√"
                        this.flagArray[1] = 1
                    }else{
                        this.msg.passwordErrorMsg = "密码的长度至少为8位"
                        this.flagArray[1] = 0
                    }
                },
                // 判断两次密码输入是否一致,注意判断使用三个连续等号
                checkUserRePwd() {
                    if (this.user.password === this.user.rePassword) {
                        this.msg.passwordErrorMsg2 = "√"
                        this.flagArray[2] = 1
                    }else{
                        this.msg.passwordErrorMsg2="两次输入的密码不一致!"
                        this.flagArray[2] = 0
                    }
                },
                checkDescription() {
                    // 描述至少为两个字符
                    let desRege = /^.{2,}$/
                    let desFlag = desRege.test(this.user.describe)
                    if (desFlag) {
                        this.msg.describeMsg = "√";
                        this.flagArray[3] = 1
                    } else {
                        this.msg.describeMsg = "请输入个人描述"
                        this.flagArray[3] = 0
                    }
                },
                // 数组.join(",")可以将数组转化为字符串,元素使用逗号拼接
                async register() {
                    if (this.flagArray.join(",") === "1,1,1,1,1") {
                        alert("表单校验成功!")
                        let p = this.user
                        // 这里将页面输入的数据全部以参数的形式传递到业务逻辑层
                        let {data:sysResult} = await axios.get(`user?method=register&uName=${p.username}&uPwd=${p.password}&uDescribe=${p.describe}`)
                        if(sysResult.flag){
                            // 注册成功应往登陆页面跳转,因此data属性保存的是重定向前往登陆的重定向路径
                            window.location.href = sysResult.data
                        }
                    }else{
                        // 阻止跳转
                        event.preventDefault();
                        alert("表单校验不成功!!!");
                    }
                },
                // 验证码图片的刷新逻辑,只需要改变kaptcha方法的参数即可,这样浏览器检测到变化后,重新发起请求,从而改变验证码图片
                changeImage() {
                    //浏览器解析到src的属性变化 则会重新发起请求,new Date充当随机数的作用
                    this.kaptchaImg="kaptcha?date="+new Date()
                },

            }
        })

    </script>
</div>
</body>
</html>

4.2、注册逻辑设计

下面是业务逻辑层源码与重要注释:

java 复制代码
@WebServlet("/user")
public class UserServlet extends BaseServlet {
    private  final UserService us = new UserServiceImpl();
    // 该实例化对象用于调用writeValueAsString方法将对象转化为json串
    private final ObjectMapper MAPPER = new ObjectMapper();
    // 跳转到注册页面
    protected void toRegister(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.processTemplate("user/register", req, resp);
    }
    // 跳转到登陆页面
    protected void toLogin(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.processTemplate("user/login", req, resp);
    }
    // 注册功能,添加user对象
    protected void register(HttpServletRequest req, HttpServletResponse resp) throws Exception {
        User user = new User();
        // 这行代码可以接收前端传来的数据,并将其作为对象属性封装到对象中
        // 但是需要注意,形参的键需要和类的属性名保持一致
        BeanUtils.populate(user,req.getParameterMap());
        // 调用service层的方法(三层大家都了解过吧,后续再调用dao层,不做解释)
        us.registerByUser(user);
        /* 下面代码不生效,使用axios进行处理
        简单理解为:通过ajax发起请求,那么这次请求就不能再使用resp的重定向,而是
        将重定向数据作为数据存入到响应体中,最后在axios里完成重定向跳转
         resp.sendRedirect(req.getContextPath()+"/user?method=toLogin");*/
        String realHref = req.getContextPath()+"/user?method=toLogin";
        // 将路径存入到SysResult(响应体对象)
        String json = MAPPER.writeValueAsString(SysResult.success(realHref));
        // 将json数据返回到原请求
        resp.getWriter().write(json);
    }
    // 检查用户名是否已存在
    protected void checkNameUnique(HttpServletRequest req, HttpServletResponse resp) throws Exception {
        String username = req.getParameter("username");
        // System.out.println(username);
        // 逻辑:通过查询数据库中该用户名的数量来判断用户是否存在
        long res = us.selectUserByCount(username);
        String json = "";
        if (res > 0) {
            // 数量大于零,用户已存在,响应fail方法
            json = MAPPER.writeValueAsString(SysResult.fail());
        } else {
            // 不存在则响应success方法
            json = MAPPER.writeValueAsString(SysResult.success());
        }
        resp.getWriter().write(json);
    }
    // 检查验证码是否正确,代码上面方法类似,不做解释
    protected void checkCode(HttpServletRequest req, HttpServletResponse resp) throws Exception {
        String code = req.getParameter("code");
        // realCode存储在session中
        String realCode = (String) req.getSession().getAttribute("KAPTCHA_SESSION_KEY");
        String json = "";
        if (code.equals(realCode)) {
            json = MAPPER.writeValueAsString(SysResult.success());
        } else {
            json = MAPPER.writeValueAsString(SysResult.fail());
        }
        resp.getWriter().write(json);
    }

}

5、登陆功能设计

登陆的功能与注册类似,比起注册,登陆的代码量要少很多,而且完全可以复用注册的代码。

5.1、登陆页面设计

源代码展示(相关解释会在注释中出现):

html 复制代码
<!DOCTYPE html>
<html lang="en"  xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <base th:href="@{/}">
    <title>用户登陆</title>
    <script src="static/script/vue.js"></script>
    <script src="static/script/axios.js"></script>
</head>
<body>
<div id="app">
    <table border="1" cellspacing="0" cellpadding="0" align="center" width="48%">
        <tr align="center">
            <td>用户名</td>
            <td><input type="text" name="username" v-model="user.username" @blur="checkUsername"></td>
            <td><span v-text="msg.usernameMsg"></span></td>
        </tr>
        <tr align="center">
            <td>密码</td>
            <td><input type="password" name="password" v-model="user.password" @blur="checkPwd"></td>
            <td><span v-text="msg.passwordMsg"></span></td>
        </tr>

        <tr align="center">
            <td>验证码</td>
            <td><input type="text" name="code" v-model="user.code" @blur="checkCode"></td>
            <td><span v-text="msg.verifyMsg"></span></td>
        </tr>
        <tr>
            <td colspan="3"><img :src="kapImg" alt="" width="100px" height="50px" @click="changeImg"></td>
        </tr>
        <tr align="center">
            <td colspan="3">
                <button @click="login">登陆</button>
            </td>
        </tr>
    </table>

    <script type="text/javascript">
        const app = new Vue({
            el: "#app",
            data: {
                msg: {
                    usernameMsg: "请输入用户名",
                    passwordMsg: "请输入密码",
                    verifyMsg: "请输入验证码"
                },
                // 全局变量
                user: {
                    username: '',
                    password: '',
                    code:''
                },
                flagArray:[0,0,0],
                kapImg:"kaptcha"
            },
            methods: {
                changeImg() {
                    this.kapImg="kaptcha?date=" + new Date()
                },
                async checkCode() {
                    let {data:sysResult} = await axios.get("user?method=checkCode&code=" + this.user.code)
                    if (sysResult.flag) {
                        this.msg.verifyMsg = "√"
                        this.flagArray[2] = 1
                    }else{
                        this.msg.verifyMsg = "请输入正确的验证码!"
                        this.flagArray[2] = 0
                    }
                },
                async checkUsername() {
                    // 用户名应为6~16个字母或数字组成
                    let usernameRege = /^[a-zA-Z0-9]{6,16}$/
                    let usernameFlag = usernameRege.test(this.user.username)
                    if (usernameFlag) {
                        this.msg.usernameMsg = "√"
                        this.flagArray[0] = 1
                    }else{
                        this.msg.usernameMsg = "用户名格式错误,请根据注册账号进行输入"
                        this.flagArray[0] = 0
                    }
                },
                checkPwd() {
                    let pwdRege = /^.{8,}$/
                    let pwdFlag = pwdRege.test(this.user.password)
                    if(pwdFlag){
                        this.msg.passwordMsg = "√"
                        this.flagArray[1] = 1
                    }else{
                        this.msg.passwordMsg = "密码的长度至少为8位"
                        this.flagArray[1] = 0
                    }
                },
                // 异步请求登陆,传递账号密码两个参数
                async login() {
                    if (this.flagArray.join(",") === "1,1,1") {
                        let p = this.user
                        let {data:sysResult} = await axios.get(`user?method=login&uName=${p.username}&uPwd=${p.password}`)
                        if(sysResult.flag){
                            alert("表单校验成功!")
                            // 这里的data是首页地址,逻辑就是登陆成功跳转首页
                            window.location.href = sysResult.data
                        }else{
                            alert("用户名或密码错误")
                            event.preventDefault()
                        }
                    }else{
                        // 阻止跳转
                        event.preventDefault()
                        alert("表单校验不成功!!!")
                    }
                }

            }
        })

    </script>
</div>
</body>
</html>

5.2、登陆逻辑设计

与注册逻辑设计相比,登陆只有下面这段代码是核心:

java 复制代码
protected void login(HttpServletRequest req, HttpServletResponse resp) throws Exception {
        User user_login = new User();
        BeanUtils.populate(user_login,req.getParameterMap());

        String json = "";
        boolean flag = us.selectByUser(user_login);
        if (flag) {
            String realHref = req.getContextPath()+"/index.html";
            json = MAPPER.writeValueAsString(SysResult.success(realHref));
        }else{
            json = MAPPER.writeValueAsString(SysResult.fail());
        }
        resp.getWriter().write(json);
    }

这段代码的意思就是创建User对象并通过工具类给对象的账号和密码属性赋值,随后根据账号密码来查找数据库中是否存在账号密码对应的用户,若有就响应success和首页地址,若没有就响应fail,最后通过resp将json数据返回前端ajax

6、运行效果图

  1. 首页

  2. 注册

  3. 正确输入后的注册

  4. 登陆

  5. 正确输入后的登陆


码文不易,如有帮助请留下免费的大拇指,想要源码的评论区留言即可,我会上传资源在本平台。

相关推荐
救救孩子把14 分钟前
深入理解 Java 对象的内存布局
java
落落落sss16 分钟前
MybatisPlus
android·java·开发语言·spring·tomcat·rabbitmq·mybatis
看到请催我学习21 分钟前
如何实现两个标签页之间的通信
javascript·css·typescript·node.js·html5
万物皆字节22 分钟前
maven指定模块快速打包idea插件Quick Maven Package
java
夜雨翦春韭29 分钟前
【代码随想录Day30】贪心算法Part04
java·数据结构·算法·leetcode·贪心算法
我行我素,向往自由35 分钟前
速成java记录(上)
java·速成
twins352041 分钟前
解决Vue应用中遇到路由刷新后出现 404 错误
前端·javascript·vue.js
一直学习永不止步41 分钟前
LeetCode题练习与总结:H 指数--274
java·数据结构·算法·leetcode·数组·排序·计数排序
邵泽明42 分钟前
面试知识储备-多线程
java·面试·职场和发展
程序员是干活的1 小时前
私家车开车回家过节会发生什么事情
java·开发语言·软件构建·1024程序员节