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

news2024/11/22 18:52:47

文章目录

    • 前言
    • 1、数据库准备
    • 2、工具类与相关基类使用
      • 2.1、工具类
      • 2.2、相关基类
    • 3、web包目录说明
    • 4、注册功能设计(本文核心部分)
      • 4.1、注册页面设计
      • 4.2、注册逻辑设计
    • 5、登陆功能设计
      • 5.1、登陆页面设计
      • 5.2、登陆逻辑设计
    • 6、运行效果图

前言

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

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

1、数据库准备

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

-- 创建数据库
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工具类:

    • 主要用来读取数据库配置文件,具有连接数据库和释放连接的功能
    • 代码如下:
    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加密
    • 代码如下:
     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工具类

    • 代码如下:
    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方法,可通过传递方法名称调用指定方法(利用反射
    • 代码如下:
    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类

    • 该类为数据库操作的基类
    • 封装了常用的数据库增删该查操作
    • 代码如下:
    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类

    • 用于封装系统操作结果,并提供了一些静态工厂方法来方便创建不同类型的响应对象
    • 代码如下:
    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 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来访问首页,它的功能就是简单的跳转,代码如下:

@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);
    }
}

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

<!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、注册页面设计

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

页面源代码展示:

<!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、注册逻辑设计

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

@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、登陆页面设计

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

<!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、登陆逻辑设计

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

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. 正确输入后的登陆
    在这里插入图片描述


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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1649698.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

分布式锁-快速入门

文章目录 前言一、基础概念1.1 什么是锁1.2 什么是分布式锁1.3 锁和事务的区别二、分布式锁基础理论2.1 为什么要使用分布式锁2.2 分布式锁特性2.3 分布式锁的实现方式总结前言 由于在平时的工作中,线上服务器是分布式多台部署的,经常会面临解决分布式场景下数据一致性的问题…

OpenCV 入门(三)—— 车牌筛选

OpenCV 入门系列&#xff1a; OpenCV 入门&#xff08;一&#xff09;—— OpenCV 基础 OpenCV 入门&#xff08;二&#xff09;—— 车牌定位 OpenCV 入门&#xff08;三&#xff09;—— 车牌筛选 OpenCV 入门&#xff08;四&#xff09;—— 车牌号识别 OpenCV 入门&#xf…

编译适配纯鸿蒙系统的ijkplayer中的ffmpeg库

目前bilibili官方的ijkplayer播放器&#xff0c;是只适配Android和IOS系统的。而华为接下来即将发布纯harmony系统&#xff0c;是否有基于harmony系统的ijkplayer可以使用呢&#xff1f; 鸿蒙版ijkplayer播放器是哪个&#xff0c;如何使用&#xff0c;这个问题&#xff0c;大家…

Linux 第二十二章

&#x1f436;博主主页&#xff1a;ᰔᩚ. 一怀明月ꦿ ❤️‍&#x1f525;专栏系列&#xff1a;线性代数&#xff0c;C初学者入门训练&#xff0c;题解C&#xff0c;C的使用文章&#xff0c;「初学」C&#xff0c;linux &#x1f525;座右铭&#xff1a;“不要等到什么都没有了…

基于FPGA的累加器及数码管显示VHDL代码Quartus仿真

名称&#xff1a;基于FPGA的累加器及数码管显示VHDL代码Quartus仿真&#xff08;文末获取&#xff09; 软件&#xff1a;Quartus 语言&#xff1a;VHDL 代码功能&#xff1a; 累加器及数码管显示 1、可以通过按键输入1~9 2、数字输入后进行累加&#xff0c;将累加结果显示…

Day 26 数据库日志管理

数据库日志管理 一&#xff1a;日志管理 1.日志分类 ​ 错误日志 &#xff1a;启动&#xff0c;停止&#xff0c;关闭失败报错。rpm安装日志位置 /var/log/mysqld.log ​ 通用查询日志&#xff1a;所有的查询都记下来 ​ 二进制日志&#xff1a;实现备份&#xff0c;增量备份…

rockchip sensors da215s适配

一 、 RK3568 da215s适配 ,增加一个新的 sensor 驱动需做一些适配工作。 SOC&#xff1a;RK3568 KERNEL&#xff1a;Android 12 二、 Android sensors 架构 三、 Sensors hal 与 kernel driver 的通信框图 四、 Rockchip sensors hal 介绍 代码路径&#xff1a; hardw…

RapidJSON介绍

1.简介 RapidJSON 是一个 C 的 JSON 解析库&#xff0c;由腾讯开源。 支持 SAX 和 DOM 风格的 API&#xff0c;并且可以解析、生成和查询 JSON 数据。RapidJSON 快。它的性能可与strlen() 相比。可支持 SSE2/SSE4.2 加速。RapidJSON 独立。它不依赖于 BOOST 等外部库。它甚至…

上位机图像处理和嵌入式模块部署(树莓派4b镜像烧录经验总结)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 陆陆续续也烧录了好多次树莓派的镜像了&#xff0c;这里面有的时候很快&#xff0c;有的时候很慢。特别是烧录慢的时候&#xff0c;也不知道是自己…

crossover怎么打开软件 mac怎么下载steam crossover下载的软件怎么运行

CrossOver是一款Mac和Linux平台上的类虚拟机软件&#xff0c;通过CrossOver可以运行Windows的可执行文件。如果你是Mac用户且需要使用CrossOver&#xff0c;但是不知道CrossOver怎么打开软件&#xff0c;如果你想在Mac电脑上玩Windows游戏&#xff0c;但不知道怎么下载Steam&am…

大模型改变了哪些工作方式?

大模型的崛起深刻改变了我们的工作方式。如今&#xff0c;许多行业已广泛应用大型机器学习模型&#xff0c;实现了自动化数据处理、智能决策和高效分析。这一变革不仅释放了大量人力资源&#xff0c;使得人们能够专注于更具创造性的任务&#xff0c;还大幅提升了工作效率和准确…

【mobx-入门与思考】

介绍 mobx 是 nodejs生态中的框架&#xff0c; 主要用于做状态管理&#xff0c;可以监控变量状态的变化。 nodejs中除了mobx&#xff0c;还有个redux&#xff0c;也是做状态管理的&#xff0c;都是比较成熟的框架&#xff0c;二者的选择可以参考 【nodejs状态管理: Redux VS M…

录屏软件哪个好用?这4款不容错过!

在现代社会中&#xff0c;信息的传递和分享变得越来越重要。一个好的录屏软件能够帮助我们将想要分享的信息快速直观地展示给他人。 通过下文推荐的4款录屏软件&#xff0c;我们可以轻松地分享自己的知识、经验和见解&#xff0c;让更多的人受益。 方法一&#xff1a;QQ软件进…

服务器2080ti驱动的卸载与安装

服务器2080ti驱动的卸载与安装 前言1、下载驱动2、驱动卸载与安装2.1 卸载原来驱动2.2 安装新驱动 3、查看安装情况 前言 安装transformers库&#xff0c;运行bert模型时出错&#xff0c;显示torch版本太低&#xff0c;要2.0以上的&#xff0c;所以更新显卡驱动&#xff0c;重…

基于FPGA的数字电子钟VHDL代码Quartus仿真

名称&#xff1a;基于FPGA的数字电子钟VHDL代码Quartus仿真&#xff08;文末获取&#xff09; 软件&#xff1a;Quartus 语言&#xff1a;VHDL 代码功能&#xff1a; 数字电子钟 1)设计一个能显示秒、分、时的24小时数字钟 2)用数码管显示出时&#xff0c;分&#xff0c;…

MFC列表控件用ADO添加数据实例

1、本程序基于前期我的博客文章《MFC用ADO连接ACESS数据库实例(免费源码下载)》 程序功能通过编辑框、组合框实时将数据写入ACESS数据库并在列表控件上显示。 2、在主界面资源视图上加上一个按钮控件、两个静态文本、一个编辑框IDC_EDIT1变量名name、一个组合框IDC_COMBO1变量名…

网络机顶盒哪个好?2024畅销网络机顶盒排行榜

因买网络机顶盒踩雷的人不在少数&#xff0c;许多不懂网络机顶盒哪个好的消费者在挑选时会参考网络机顶盒排行榜&#xff0c;这次小编带来了业内最新发布的热销网络机顶盒排行榜&#xff0c;想买网络机顶盒可以看看入围的以下品牌&#xff0c;是目前最受消费者欢迎的品牌。 一&…

参数配置不生效导致海思1151芯片TPC功率超大,引起性能恶化。

• 【Wi-Fi领域】【现网案例4】参数配置不生效导致海思1151芯片TPC功率超大&#xff0c;引起性能恶化。 【问题描述】XXX客户反馈OLT-HG8245W5-6T–Wi-Fi–WA8021V5-LAN-PC组网概率出现近距离测速只有20Mbps 【问题单】DTS2022101410914 【问题分析】 在客户反馈此问题后&#…

面试集中营—JVM篇

一、JVM内存模型 线程独占&#xff1a;栈&#xff0c;本地方法栈&#xff0c;程序计数器; 线程共享&#xff1a;堆&#xff0c;方法区 虚拟机栈&#xff1a;线程私有的&#xff0c;线程执行方法是会创建一个栈阵&#xff0c;用来存储局部变量表&#xff0c;操作栈&#xff0c;…

W801学习笔记二十二:英语背单词学习应用——下

续上篇&#xff1a; W801学习笔记二十一&#xff1a;英语背单词学习应用——上 五、处理用户交互 由于英语也是采用了和唐诗一样的《三分钟限时挑战》《五十题竞速挑战》《零错误闯关挑战》&#xff0c;所以用户交互的逻辑和唐诗是一样的。所以&#xff0c;我们抽一个基类&a…