创新实训(五):用户管理之登录注册
登录注册部分主要通过 crsf_check() 函数检查 CSRF(跨站请求伪造)保护。
然后检查 $_POST 中是否存在必要的注册信息(用户名、密码、邮箱)
最后完成注册
在登录时要检测账号和对应的权限,展示相应的内容
一.项目展示:
二.登录代码介绍
1.PHP 部分
页面头部与页脚
php
<?php echoUOJPageHeader(UOJLocale::get('login')) ?>
...
<?php echoUOJPageFooter() ?>
echoUOJPageHeader 和 echoUOJPageFooter 分别输出页面的头部和尾部内容。
UOJLocale::get('login') 获取当前语言下的“登录”文本。
表单字段
php
<h2 class="page-header"><?= UOJLocale::get('login') ?></h2>
<form id="form-login" class="form-horizontal" method="post">
<div id="div-username" class="form-group">
<label for="input-username" class="col-sm-2 control-label"><?= UOJLocale::get('username') ?></label>
<div class="col-sm-3">
<input type="text" class="form-control" id="input-username" name="username" placeholder="<?= UOJLocale::get('enter your username') ?>" maxlength="20" />
<span class="help-block" id="help-username"></span>
</div>
</div>
<div id="div-password" class="form-group">
<label for="input-password" class="col-sm-2 control-label"><?= UOJLocale::get('password') ?></label>
<div class="col-sm-3">
<input type="password" class="form-control" id="input-password" name="password" placeholder="<?= UOJLocale::get('enter your password') ?>" maxlength="20" />
<span class="help-block" id="help-password"></span>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-3">
<button type="submit" id="button-submit" class="btn btn-secondary"><?= UOJLocale::get('submit') ?></button>
</div>
</div>
</form>
表单包含两个输入框:一个用于用户名(input-username),另一个用于密码(input-password)。
UOJLocale::get 用于获取不同语言环境下的文本,比如“用户名”、“密码”等。
maxlength="20" 限制输入的最大字符数为20。
JavaScript 部分
表单验证函数
javascript
function validateLoginPost() {
var ok = true;
ok &= getFormErrorAndShowHelp('username', validateUsername);
ok &= getFormErrorAndShowHelp('password', validatePassword);
return ok;
}
validateLoginPost 函数用于在提交前验证输入信息是否符合要求。
getFormErrorAndShowHelp 是一个辅助函数,根据字段名调用相应的验证函数(validateUsername 或 validatePassword)并显示错误信息。
表单提交函数
javascript
function submitLoginPost() {
if (!validateLoginPost()) {
return false;
}
$.post('/login', {
_token : "<?= crsf_token() ?>",
login : '',
username : $('#input-username').val(),
password : md5($('#input-password').val(), "<?= getPasswordClientSalt() ?>")
}, function(msg) {
if (msg == 'ok') {
var prevUrl = document.referrer;
if (prevUrl == '' || /.*\/login.*/.test(prevUrl) || /.*\/logout.*/.test(prevUrl) || /.*\/register.*/.test(prevUrl) || /.*\/reset-password.*/.test(prevUrl)) {
prevUrl = '/';
}
window.location.href = prevUrl;
} else if (msg == 'banned') {
$('#div-username').addClass('has-error');
$('#help-username').html('该用户已被封停,请联系管理员。');
} else if (msg == 'expired') {
$('#div-username').addClass('has-error');
$('#help-username').html('页面会话已过期。');
} else {
$('#div-username').addClass('has-error');
$('#help-username').html('用户名或密码错误。');
$('#div-password').addClass('has-error');
$('#help-password').html('用户名或密码错误。<a href="/forgot-password">忘记密码?</a>');
}
});
return true;
}
submitLoginPost 在通过验证后发送POST请求到 /login。
使用 md5 函数将密码加密,并添加客户端盐(salt),确保传输过程中的安全性。
根据服务器返回的消息(msg),处理不同的响应结果:
'ok':登录成功,跳转到先前页面(若无先前页面则跳转到主页)。
'banned':用户被封停,显示错误信息。
'expired':会话过期,显示错误信息。
其他情况:用户名或密码错误,显示错误信息及“忘记密码”链接。
表单提交事件处理
javascript
$(document).ready(function() {
$('#form-login').submit(function(e) {
e.preventDefault();
submitLoginPost();
});
});
使用 jQuery 的 $(document).ready 确保DOM完全加载后再绑定事件。
阻止默认表单提交行为,调用 submitLoginPost 函数处理登录逻辑。
三.注册代码介绍:
函数 handleRegisterPost():
php
function handleRegisterPost() {
if (!crsf_check()) {
return '页面已过期';
}
if (!isset($_POST['username'])) {
return "无效表单";
}
if (!isset($_POST['password'])) {
return "无效表单";
}
if (!isset($_POST['email'])) {
return "无效表单";
}
$username = $_POST['username'];
$password = $_POST['password'];
$email = $_POST['email'];
if (!validateUsername($username)) {
return "失败:无效用户名。";
}
if (queryUser($username)) {
return "失败:用户名已存在。";
}
if (!validatePassword($password)) {
return "失败:无效密码。";
}
if (!validateEmail($email)) {
return "失败:无效电子邮箱。";
}
$password = getPasswordToStore($password, $username);
$esc_email = DB::escape($email);
$svn_pw = uojRandString(10);
if (!DB::selectCount("SELECT COUNT(*) FROM user_info"))
DB::query("insert into user_info (username, email, password, svn_password, register_time, usergroup) values ('$username', '$esc_email', '$password', '$svn_pw', now(), 'S')");
else
DB::query("insert into user_info (username, email, password, svn_password, register_time) values ('$username', '$esc_email', '$password', '$svn_pw', now())");
return "欢迎你!" . $username . ",你已成功注册。";
}
功能:
首先通过 crsf_check() 函数检查 CSRF(跨站请求伪造)保护。
然后检查 $_POST 中是否存在必要的注册信息(用户名、密码、邮箱),如果不存在则返回 "无效表单"。
使用 validateUsername($username) 验证用户名的有效性,如果无效则返回 "失败:无效用户名。"
使用 queryUser($username) 查询数据库中是否已存在该用户名,如果存在则返回 "失败:用户名已存在。"
使用 validatePassword($password) 和 validateEmail($email) 分别验证密码和邮箱的有效性。
如果所有验证通过,则调用 getPasswordToStore($password, $username) 函数准备存储的密码。
使用 DB::escape($email) 函数对邮箱进行数据库安全转义。
生成一个随机的 svn_password,然后执行 SQL 插入语句将用户信息插入 user_info 表中。
最后返回 "欢迎你!" 加上用户名的消息,表示注册成功。
2.注册处理逻辑:
php
if (isset($_POST['register'])) {
echo handleRegisterPost();
die();
}
当前端提交注册表单时(通过 $_POST['register']),调用 handleRegisterPost() 处理注册,并输出处理结果后终止脚本执行。
3.检查用户名是否可用:
php
elseif (isset($_POST['check_username'])) {
$username = $_POST['username'];
if (validateUsername($username) && !queryUser($username)) {
echo '{"ok" : true}';
} else {
echo '{"ok" : false}';
}
die();
}
当前端通过 AJAX 发送 check_username 请求时,验证用户名是否可用。
如果 validateUsername($username) 验证通过且 queryUser($username) 返回 false(即用户名不存在),返回 {"ok" : true}。
否则返回 {"ok" : false}。
4.主体部分:
前端部分 (HTML和JavaScript)
HTML表单部分:
包含了用户输入邮箱、用户名、密码和确认密码的表单。
使用Bootstrap样式,通过form-horizontal类实现水平布局的表单。
JavaScript函数和事件绑定:
checkUsernameNotInUse():该函数使用AJAX向服务器发送请求,检查用户名是否已被使用。如果用户名已存在,返回false,否则返回true。
validateRegisterPost():验证注册表单数据的有效性。
调用validateEmail()验证邮箱格式。
调用validateUsername()验证用户名格式,并通过checkUsernameNotInUse()检查用户名是否可用。
调用validateSettingPassword()验证密码格式。
如果任何一项验证失败,返回false;否则返回true。
submitRegisterPost():提交注册表单的主函数。
首先调用validateRegisterPost()验证表单数据。
如果验证通过,使用$.post方法发送POST请求给服务器的/register路径。
发送的数据包括用户名、邮箱和经过加密的密码(使用MD5和客户端盐)。
根据服务器返回的消息,如果注册成功,显示成功对话框并跳转到之前的页面;如果失败,显示失败对话框。
事件绑定:
使用jQuery在文档准备就绪时绑定表单的提交事件。当用户点击注册按钮时,调用submitRegisterPost()函数进行处理。
后端部分 (PHP)
PHP标记:
使用包裹PHP代码,动态生成页面内容和处理表单提交。
服务器端处理:
接收POST请求,根据请求内容进行注册处理。
使用crsf_token()生成和验证CSRF令牌,确保请求的安全性。
处理注册逻辑,包括用户名是否重复、密码加密、成功或失败时返回不同的消息。
<?php
$REQUIRE_LIB['md5'] = '';
$REQUIRE_LIB['dialog'] = '';
?>
<?php echoUOJPageHeader(UOJLocale::get('register')) ?>
<h2 class="page-header"><?= UOJLocale::get('register') ?></h2>
<form id="form-register" class="form-horizontal">
<div id="div-email" class="form-group">
<label for="input-email" class="col-sm-2 control-label"><?= UOJLocale::get('email') ?></label>
<div class="col-sm-3">
<input type="email" class="form-control" id="input-email" name="email" placeholder="<?= UOJLocale::get('enter your email') ?>" maxlength="50" />
<span class="help-block" id="help-email"></span>
</div>
</div>
<div id="div-username" class="form-group">
<label for="input-username" class="col-sm-2 control-label"><?= UOJLocale::get('username') ?></label>
<div class="col-sm-3">
<input type="text" class="form-control" id="input-username" name="username" placeholder="<?= UOJLocale::get('enter your username') ?>" maxlength="20" />
<span class="help-block" id="help-username"></span>
</div>
</div>
<div id="div-password" class="form-group">
<label for="input-password" class="col-sm-2 control-label"><?= UOJLocale::get('password') ?></label>
<div class="col-sm-3">
<input type="password" class="form-control" id="input-password" name="password" placeholder="<?= UOJLocale::get('enter your password') ?>" maxlength="20" />
<input type="password" class="form-control top-buffer-sm" id="input-confirm_password" placeholder="<?= UOJLocale::get('re-enter your password') ?>" maxlength="20" />
<span class="help-block" id="help-password"></span>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-3">
<button type="submit" id="button-submit" class="btn btn-secondary"><?= UOJLocale::get('submit') ?></button>
</div>
</div>
</form>
<script type="text/javascript">
function checkUsernameNotInUse() {
var ok = false;
$.ajax({
url : '/register',
type : 'POST',
dataType : 'json',
async : false,
data : {
check_username : '',
username : $('#input-username').val()
},
success : function(data) {
ok = data.ok;
},
error : function(XMLHttpRequest, textStatus, errorThrown) {
alert(XMLHttpRequest.responseText);
ok = false;
}
});
return ok;
}
function validateRegisterPost() {
var ok = true;
ok &= getFormErrorAndShowHelp('email', validateEmail);
ok &= getFormErrorAndShowHelp('username', function(str) {
var err = validateUsername(str);
if (err)
return err;
if (!checkUsernameNotInUse())
return '该用户名已被人使用了。';
return '';
})
ok &= getFormErrorAndShowHelp('password', validateSettingPassword);
return ok;
}
function submitRegisterPost() {
if (!validateRegisterPost()) {
return;
}
$.post('/register', {
_token : "<?= crsf_token() ?>",
register : '',
username : $('#input-username').val(),
email : $('#input-email').val(),
password : md5($('#input-password').val(), "<?= getPasswordClientSalt() ?>")
}, function(msg) {
if (/^欢迎你!/.test(msg)) {
BootstrapDialog.show({
title : '注册成功',
message : msg,
type : BootstrapDialog.TYPE_SUCCESS,
buttons: [{
label: '好的',
action: function(dialog) {
dialog.close();
}
}],
onhidden : function(dialog) {
var prevUrl = document.referrer;
if (!prevUrl) {
prevUrl = '/';
};
window.location.href = prevUrl;
}
});
} else {
BootstrapDialog.show({
title : '注册失败',
message : msg,
type : BootstrapDialog.TYPE_DANGER,
buttons: [{
label: '好的',
action: function(dialog) {
dialog.close();
}
}],
});
}
});
}
$(document).ready(function() {
$('#form-register').submit(function(e) {
submitRegisterPost();
return false;
});
});
</script>
<?php echoUOJPageFooter() ?>
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步