Spring boot + Mybatis 从零开始搭建个人博客系统(三)——登录注册(前端)

这里是登录注册前端部分代码及思路,后端请访问:
Spring boot + Mybatis 从零开始搭建个人博客系统(四)——登录注册(后端)

页面设计

注册

注册
注册成功

登录登录

前端

前端样式部分建立在 amazeUI 基础上,如使用请先引入相关CSS与JS
CSS与JS的引入我使用的是 BootCDN 作为CDN加速:

<link href="https://cdn.bootcss.com/amazeui/2.7.2/css/amazeui.min.css" rel="stylesheet">

<script src="https://cdn.bootcss.com/amazeui/2.7.2/js/amazeui.min.js"></script>

输入检查

关于输入检查我使用的是 amazeUI 的文本框—— >>直达链接
这里我使用了两种状态的文本框,分别为:

  1. 验证成功状态:
    验证成功
<div class="am-form-group am-form-success am-form-icon am-form-feedback">
    <label class="am-form-label" for="doc-ipt-success">验证成功</label>
    <input type="text" id="doc-ipt-success" class="am-form-field">
    <span class="am-icon-check"></span>
</div>
  1. 验证警告状态:
    验证警告
<div class="am-form-group am-form-warning">
    <label class="am-form-label" for="doc-ipt-warning">验证警告</label>
    <input type="text" id="doc-ipt-warning" class="am-form-field">
</div>

从这里我们可以看出来,我们只需要在验证成功时加入 class am-form-success,并将<span class="am-icon-check"></span>display设置为block (显示对勾)
并在验证失败时将对勾的 display设置为none ,加入 class am-form-warning 即可。

注意:在添加class之前要将所有class移除,因为你并不清楚该文本框之前是什么状态,移除后加入 am-form-group 与对应状态 class 即可

这里我将状态检查封装为一个函数,便于以后的操作。
放出精简后的部分示例代码:

function phoneCheck(formText) {
        if (formText == "") {
            $("#form-phone").removeClass();
            $("#form-phone").addClass("am-form-group");
            $("#form-phone").addClass("am-form-warning");

            $("#form-text-error-phone").html("哼!竟然不给我联系方式!");
            
            $("#icon-phone-success").css("display", "none");
        } else if (!formText.match(/^(13[0-9]|14[579]|15[0-3,5-9]|16[6]|17[0135678]|18[0-9]|19[89])\d{8}$/)) {
            $("#form-phone").removeClass();
            $("#form-phone").addClass("am-form-group");
            $("#form-phone").addClass("am-form-warning");

            $("#form-text-error-phone").html("这是什么!不要乱写鸭!");

            $("#icon-phone-success").css("display", "none");
        } else {
            $("#form-phone").removeClass();
            $("#form-phone").addClass("am-form-group");
            $("#form-phone").addClass("am-form-success");

            $("#form-text-error-phone").html("");
            $("#form-text-error-phone").css("display", "none");

            $("#icon-phone-success").css("display", "block");
        }
}

$("#form-text-error-phone").html("哼!竟然不给我联系方式!");
关于这句代码是我设立了一个 div 紧跟在文本框后面,当出现警告时提示哪部分出错。

当文本框失去焦点时调用即可:

$("#doc-ipt-3").blur(function () {
    var formText = $("#doc-ipt-3").val();
    phoneCheck(formText);
});

关于登录:

禁止跳转

因为我设计的是当用户访问登录页面,也就是/login时,会保存用户访问之前的链接,当用户登录成功后跳转回之前访问的页面。

这个我用到了请求头中的referrer报文头,当发出请求时一般会带上这个报文头,告诉服务器用户从哪个页面链接过来的,也就是说里面储存了用户跳转到登录页面时所在页面的URL。

那么这个跳转应该是由后端来控制保存并跳转,前端需要做什么呢?

有些页面,我们是不能完全放行的,禁止某些页面跳转,这就是前端要做的事情。
根据项目不同要禁止的页面也不同,这里我将我所禁止的页面列出来供大家参考:

  1. 所有成功页面。
    如注册成功页面、发表成功页面等等,这些成功页面跳转到/login,是不能加入referrer的。
  2. “忘记密码”页面
    我所想的是如果登录成功,自然不需要再找回密码,所以“忘记密码”页面是不需要referrer的。
  3. 登录失败页面
    这是最重要的一个,一定不能加入referrer。我的登录失败页面是/login?error,而一旦用户登录失败,就会访问到这个URL,此时如果不禁止的话,它的referrer里保存的URL就是登录失败的URL:/login?error,那么当我们登录成功后,页面依然会跳回登录失败的页面,这是不可取的。

至于如何来禁止页面请求携带referrer?很简单,只需要在页面上加一个标签即可:

<!-- 从该页面发出的请求不会包含referrer -->
<meta name="referrer" content="never">
登录失败处理

当登录失败后,后端会自动将链接跳转到 /login?error
没错,我们要获取的就是检查是否页面有那个 error
这里我使用的是 Thymeleaf 作为模板来进行前后端页面层次的数据交互。
(其实这个应该后端管的,但是毕竟要交互了解一下麻油坏处)
首先,添加 Thymeleaf 的使用域:

<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:http="http://www.w3.org/1999/xhtml">

之后,设立一个节点标识登录失败:

<div class="am-alert am-alert-warning error-alert" data-am-alert  th:if="${param.error}">
            用户名或密码错误!
</div>

其中,最主要的就是:th:if="${param.error}"
th:if 表判断,如果存在字段 param.error ,则显示,否则不显示
(有兴趣的可以去看Themeleaf 详细语法介绍)

这样我们就可以在用户失败的时候提示他啦!

但是仅仅这样我就会满足了吗?哼哼,怎么可能!
为了页面的美观,我还设置了一个小步骤,就是当文本框获得焦点时,将错误提示框隐藏:

$("#doc-ipt-3").focus(function () {
    $(".error-alert").css("display", "none");
});

这样就完美啦!大功告成!

登录成功处理

当用户登录成功后,我需要将用户的昵称在标题栏上显示出来,毕竟你不能让用户一直登录下去。
这里,我们同样用Themeleaf来处理。
首先,使用域要多添加一个 security 的使用域进行权限管理:

<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:http="http://www.w3.org/1999/xhtml"
      xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4">

之后,我们只需要写好两种样式(登录前与登录后)的节点就好:

<div class="am-btn-group login-button-head"  th:unless="${#httpServletRequest.remoteUser}">
                        <div class="am-dropdown" data-am-dropdown>
                            <button class="am-btn am-dropdown-toggle login-button" data-am-dropdown-toggle> <span class="am-icon-user login-icon"></span></button>
                            <ul class="am-dropdown-content login-content">
                                <li><a href="/login" class="login-content-a"><span class="am-icon-leaf">&nbsp;&nbsp;&nbsp;登录</span></a></li>
                                <li><a href="/register" class="login-content-a"><span class="am-icon-user-plus">&nbsp;&nbsp;&nbsp;注册</span></a></li>
                                <li><a class="login-content-a doc-prompt-toggle"><span class="am-icon-bug">&nbsp;&nbsp;&nbsp;反馈</span></a></li>
                            </ul>
                        </div>
                    </div>

                    <div class="am-btn-group login-button-head"  th:if="${#httpServletRequest.remoteUser}">
                        <div class="am-dropdown" data-am-dropdown id="login-sign-drop">
                            <button class="am-btn am-btn-secondary am-topbar-btn am-btn-sm am-dropdown-toggle" data-am-dropdown-toggle id="login-sign-name">
                                <span id="login-user-name"></span>
                                <span class="am-icon-caret-down"></span>
                            </button>
                            <ul class="am-dropdown-content login-content login-content-b" id="login-content-ul">
                                <li><a href="/user" class="login-content-a"><span class="am-icon-leaf">&nbsp;&nbsp;&nbsp;个人中心</span></a></li>
                                <li sec:authorize="hasAnyRole('ROLE_ADMIN')"><a href="/admin" class="login-content-a"><span class="am-icon-cog">&nbsp;&nbsp;&nbsp;管理中心</span></a></li>
                                <li><a class="login-content-a doc-prompt-toggle"><span class="am-icon-bug">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></a></li>
                                <hr id="log-hr"/>
                                <li><a href="/logout" class="login-content-a"><span class="am-icon-sign-out">&nbsp;&nbsp;&nbsp;退出登录</span></a></li>
                            </ul>
                        </div>
                        <button class="am-btn" id="login-button-circle" sec:authorize="hasAnyRole('ROLE_ADMIN')"><span id="login-button-write"></span></button>
</div>

th:unless="${#httpServletRequest.remoteUser} 代表未登录,其中httpServletRequest.remoteUser其实就是登录后用户的用户名(ID),而 th:unless 就是 th:if 的反例,如果值为空,才显示。

sec:authorize="hasAnyRole('ROLE_ADMIN') 为权限检测,语句代表如果登录的用户具有ROLE_ADMIN权限时才会显示节点。

之后我们只要在主JS中加入ajax请求用户昵称即可:

/* 登录成功后加入名称 */
var username = "";
$.ajax(
    {
        type:"get",
        url:"/getUserName",
        dataType:"json",
        async:false,
        success:function(data){
            if(data['msg']=="success") {
                console.log("已登录");
                username = data['username'];
            }
        },
        error:function(){
            console.log("未登录");
        }
    });
if(username != "") {
    $("#login-user-name").html(username);
}

此时,如果用户未登录,则会显示 th:unless="${#httpServletRequest.remoteUser} 所在节点
已登录则会显示 th:if="${#httpServletRequest.remoteUser} 所在节点。
而用户权限不够是看不到相应权限节点的。

关于注册:

相比登录,注册要考虑的地方就多了很多,各个文本框的输入检测因人而异,这里就不多赘述。

注册流程:
  1. 对每个文本框进行输入检测(blur),出错时提示。
  2. 当用户点击“获取验证码”时,对用户所输入的手机号进行输入检测,如果手机号输入正确($("#form-phone").hasClass("am-form-success")),使用ajax对后台进行非异步请求(async: false),设立变量 flagPhone=0
    具体检查内容:手机号是否已被注册,如果已被注册则将手机号文本框置错(
$("#form-phone").removeClass();
$("#form-phone").addClass("am-form-group");
$("#form-phone").addClass("am-form-warning");

$("#form-text-error-phone").html(data['msg']);

$("#icon-phone-success").css("display", "none");

)并提示(提示信息由后端传入),成功则将变量 flagPhone1
检查变量 flagPhone 是否为 1 ,如果为 1 则将验证码输入框设置为不可点击并设置定时器为60秒(60秒内不可点击),并发送ajax向后端请求发送验证码。

  1. 当点击注册时,重新进行所有文本框的输入检测。
    特别地进行昵称重复检查手机号是否注册检查(这里重复进行手机号检查的目的是当用户直接点击注册时给用户提示)
    这两个检查均为ajax请求(如果将请求设置在 blur 处会降低网站运行效率,故只在点击注册时检查)
    设置两个变量标志检查成功位,如果均成功则向后台请求检查验证码与手机号的匹配。

  2. 若一切检查均成功,则提交表单,由后端处理数据并跳转到注册成功页面。

验证码点击:

先上代码:

$("#msg_btn").click(function () {
    if($("#form-phone").hasClass("am-form-success")) {
        var flagPhone = 0;

        var phone = $("input[name=phone]").val();
        console.log(phone);
        $.ajax(
            {
                type:"post",
                url:"/phoneCheck",
                dataType:"json",
                async:false,
                data:{
                    phone: phone
                },
                success:function(data){
                    if(data['msg']=="success") {
                        flagPhone = 1;
                    } else {
                        $("#form-phone").removeClass();
                        $("#form-phone").addClass("am-form-group");
                        $("#form-phone").addClass("am-form-warning");

                        $("#form-text-error-phone").html(data['msg']);
                        $("#form-text-error-phone").css("margin-left", "170px");
                        $("#form-text-error-phone").css("display", "block");

                        $("#icon-phone-success").css("display", "none");
                    }
                },
                error:function(){
                    console.log("请求失败");
                }
            });

        if(flagPhone == 1) {
            settime(this);
            $.ajax(
                {
                    type:"post",
                    url:"/sendIdentifyCode",
                    dataType:"json",
                    data:{
                        phone: phone
                    },
                    success:function(data){
                        if(data['msg']=="success") {
                            alert("手机验证码成功发送!(๑•ㅂ•́)و✧");
                        } else {
                            alert("手机验证码发送失败!w(゜Д゜)w ");
                        }
                    },
                    error:function(){
                        alert("手机验证码发送失败!w(゜Д゜)w ");
                    }
                });
        }
    } else {
        alert("请正确填写手机号哟_(xз」∠)_~");
    }
});

具体流程与讲解上面说的差不多了,附上验证码点击后设置函数:

/* 验证码 */
var countdown=60;
function settime(val) {
    if (countdown == 0) {
        val.removeAttribute("disabled");
        $("#msg_btn").html("获取验证码");
        countdown = 60;
        return false;
    } else {
        val.setAttribute("disabled", true);
        $("#msg_btn").html("重新发送(" + countdown + ")");
        countdown--;
    }
    setTimeout(function() {
        settime(val);
    },1000);
}

成功后调用,即可设置验证码按钮1分钟点击一次的效果。

注册点击检查:
/* 注册点击检验 */
function toVaild() {
    var formText = $("#doc-ipt-name-1").val();
    nameCheck(formText);

    formText = $("#doc-ipt-pwd-1").val();
    passwordCheck(formText);

    formText = $("#doc-ipt-phone-1").val();
    phoneCheck(formText);

    formText = $("#doc-ipt-captcha-1").val();
    captchaCheck(formText);

    var flag = 0;
    var flagP = 0;
    var flagC = 0;
    $.ajax(
        {
            type:"post",
            url:"/nameCheck",
            dataType:"json",
            async: false,
            data:{
                name: $("#doc-ipt-name-1").val()
            },
            success:function(data){
                if(data['msg']=="success") {
                    flag = 1;
                } else {
                    $("#form-name").removeClass();
                    $("#form-name").addClass("am-form-group");
                    $("#form-name").addClass("am-form-warning");

                    $("#form-text-error-name").html(data['msg']);
                    $("#form-text-error-name").css("margin-left", "260px");
                    $("#form-text-error-name").css("display", "block");

                    $("#icon-name-success").css("display", "none");
                }
            },
            error:function(){
                console.log("请求失败");
            }
        });
    $.ajax(
        {
            type:"post",
            url:"/phoneCheck",
            dataType:"json",
            async: false,
            data:{
                phone: $("#doc-ipt-phone-1").val()
            },
            success:function(data){
                if(data['msg']=="success") {
                    flagP = 1;
                } else {
                    $("#form-phone").removeClass();
                    $("#form-phone").addClass("am-form-group");
                    $("#form-phone").addClass("am-form-warning");

                    $("#form-text-error-phone").html(data['msg']);
                    $("#form-text-error-phone").css("margin-left", "170px");
                    $("#form-text-error-phone").css("display", "block");

                    $("#icon-phone-success").css("display", "none");
                }
            },
            error:function(){
                console.log("请求失败");
            }
        });

    if($("#form-phone").hasClass("am-form-success") && $("#doc-ipt-captcha-1").val()!="") {
        $.ajax(
            {
                type:"post",
                url:"/captchaCheck",
                dataType:"json",
                async: false,
                data:{
                    phone: $("#doc-ipt-phone-1").val(),
                    captcha: $("#doc-ipt-captcha-1").val()
                },
                success:function(data){
                    if(data['msg']=="success") {
                        flagC = 1;
                    } else {
                        $("#form-captcha").removeClass();
                        $("#form-captcha").addClass("am-form-group");
                        $("#form-captcha").addClass("am-form-warning");

                        $("#form-text-error-captcha").html(data['msg']);
                        $("#form-text-error-captcha").css("margin-left", "-70px");
                        $("#form-text-error-captcha").css("margin-top", "10px");
                        $("#form-text-error-captcha").css("display", "block");

                        $("#icon-captcha-success").css("display", "none");
                    }
                },
                error:function(){
                    console.log("请求失败");
                }
            });

    }

    if($("#form-name").hasClass("am-form-success") &&
       $("#form-password").hasClass("am-form-success") &&
       $("#form-phone").hasClass("am-form-success") &&
       $("#form-captcha").hasClass("am-form-success") && flag == 1 && flagP == 1 && flagC == 1) {
        return true;
    } else {
        return false;
    }
}

注意:以上几个 ajax 一定要设置非异步请求 async: false ,否则可能没有请求完毕就会执行请求下面的代码,出现未知错误。
调用函数方法:
form 表单属性中加入: onsubmit="return toVaild()" ,return 后面的就是你的检查函数,这样当点击 submit 按钮时,会先执行你的检查函数,当你的检查函数返回 true 才会提交。
如上述代码所示,当所有输入节点均检测成功(含有 am-form-successclass),且名称、手机、验证码匹配也检测成功时,提交请求,注册成功!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值