这里是登录注册前端部分代码及思路,后端请访问:
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
的文本框—— >>直达链接
这里我使用了两种状态的文本框,分别为:
<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>
<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
,并将 的
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。
那么这个跳转应该是由后端来控制保存并跳转,前端需要做什么呢?
有些页面,我们是不能完全放行的,禁止某些页面跳转,这就是前端要做的事情。
根据项目不同要禁止的页面也不同,这里我将我所禁止的页面列出来供大家参考:
/login
,是不能加入referrer
的。referrer
的。referrer
。我的登录失败页面是/login?error
,而一旦用户登录失败,就会访问到这个URL,此时如果不禁止的话,它的referrer
里保存的URL就是登录失败的URL:/login?error
,那么当我们登录成功后,页面依然会跳回登录失败的页面,这是不可取的。至于如何来禁止页面请求携带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"> 登录span>a>li>
<li><a href="/register" class="login-content-a"><span class="am-icon-user-plus"> 注册span>a>li>
<li><a class="login-content-a doc-prompt-toggle"><span class="am-icon-bug"> 反馈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"> 个人中心span>a>li>
<li sec:authorize="hasAnyRole('ROLE_ADMIN')"><a href="/admin" class="login-content-a"><span class="am-icon-cog"> 管理中心span>a>li>
<li><a class="login-content-a doc-prompt-toggle"><span class="am-icon-bug"> 反 馈span>a>li>
<hr id="log-hr"/>
<li><a href="/logout" class="login-content-a"><span class="am-icon-sign-out"> 退出登录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}
所在节点。
而用户权限不够是看不到相应权限节点的。
相比登录,注册要考虑的地方就多了很多,各个文本框的输入检测因人而异,这里就不多赘述。
$("#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");
)并提示(提示信息由后端传入),成功则将变量 flagPhone
置 1
。
检查变量 flagPhone
是否为 1
,如果为 1
则将验证码输入框设置为不可点击并设置定时器为60秒(60秒内不可点击),并发送ajax
向后端请求发送验证码。
当点击注册时,重新进行所有文本框的输入检测。
特别地进行昵称重复检查与手机号是否注册检查(这里重复进行手机号检查的目的是当用户直接点击注册时给用户提示)
这两个检查均为ajax
请求(如果将请求设置在 blur 处会降低网站运行效率,故只在点击注册时检查)
设置两个变量标志检查成功位,如果均成功则向后台请求检查验证码与手机号的匹配。
若一切检查均成功,则提交表单,由后端处理数据并跳转到注册成功页面。
先上代码:
$("#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-success
的 class
),且名称、手机、验证码匹配也检测成功时,提交请求,注册成功!