laravel网站开发实例----5
  TEZNKK3IfmPf 2023年11月12日 23 0

接口安全很重要,我们需要有合理的节流机制,调用频率的限制,限制每个 ip 的调用次数。可以在设置路由的时候这样设置

$api->version('v1', [
'namespace' => 'App\Http\Controllers\Api'
], function($api) {

$api->group([
'middleware' => 'api.throttle',
'limit' => 60,
'expires' => 60,
],function ($api){
//短信验证码
$api->post('registerCodes', 'UsersController@store')
->name('api.registerCodes.store');
//用户注册
$api->post('users','UsersRegisterController@store')
->name('api.users.store');
});

});

api.throttle 是 dingoapi提供的接口调用限制的方法。

前面的教程实现了用户的注册,用户管理系统接下来还有以下功能:
1、用户登录
2、找回密码
3、用户成为会员
4、用户报名比赛(这部分需要与比赛系统合起来)
4、修改密码
5、修改用户个人信息

用户注册时加入登录状态初始化

用户登录方面我准备给每个玩家记录一个目前你是第多少次登录的数值,用于记录用户活跃度和避免用户异地登录的风险。
1、使用 artisan 创建数据库迁移文件
创建一个叫userlogins的表,用于存放每个玩家的登录状况

php artisan make:migration create_userlogins_table –create=userlogins

public function up()
{
Schema::create('userlogins', function (Blueprint $table) {
$table->increments('id');
$table->bigInteger('schoolid')->unique();
$table->integer('ci');
$table->integer('status');
$table->timestamps();
});
}

迁移数据库

php artisan migrate

创建对应的模型文件,一般模型名 x = 数据库名首字母大写去掉后面的s。所以数据库名一定要取为以-s结尾的复数形式

php artisan make:model App\Models\Userlogin

namespace App\Models;
use Illuminate\Notifications\Notifiable;
use Illuminate\Database\Eloquent\Model;

class Userlogin extends Model
{
//
use Notifiable;

protected $table = "userlogins";

protected $fillable = [
'schoolid', 'ci','status'
];

protected $hidden = [
'ci'
];
}

修改注册时的控制器的store方法

public function store(Request $request){
$this->validate($request,[
'schoolid' => 'required|unique:users|max:10',
'email' => 'required|unique:users',
'password' => 'required|min:8|max:16',
]);
$verifyData = \Cache::get($request->verification_key);
if (!$verifyData) {
return response('验证码失效',200);
}
$code = $request->verification_code;
if (hash_equals($verifyData['code'],$code)){
//如果验证成功,就将所有信息存入数据库
$user = User::create([
'name' => $request->name,
'schoolid' =>$request->schoolid,
'email' => $verifyData['email'],
'password' => bcrypt($request->password),
'sex' =>$request->sex,
'xueyuan'=>$request->xueyuan,
'zhuanye'=>$request->zhuanye,
'shifouhuiyuan' => 0,
]);
$userlogin = Userlogin::create([
'schoolid' => $request->schoolid,
'ci' => 0,
'status' => 0,
]);
\Cache::forget($request->verification_key);
return response('注册成功');
}else{
$rs['info'] = '验证码错误';
$rs['status_code'] = '400';
return response($rs,200);
}
}
用户登录

1、创建路由

$api->post('login','UsersController@home')
->name('api.login.store');

2、在控制器中写逻辑
之前用 bcrypt 对用户密码进行了加密,而这种加密方法跟加密的时间有关,也就是说,密文是不可逆的,下面是一些使用的方法

//对 A 密码使用Bcrypt 加密
$password = Hash::make('secret');

//你也可直接使用 bcrypt 的 function
$password = bcrypt('secret');

//对加密的 A 密码进行验证
if (Hash::check('secret', $hashedPassword))
{
// The passwords match...
}
//检查 A 密码是否需要重新加密
if (Hash::needsRehash($hashed))
{
$hashed = Hash::make('secret');
}

于是我们的逻辑可以这样写

namespace App\Http\Controllers\Api;

use App\Models\User;
use App\Models\Userlogin;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Mail;
class UsersController extends Controller
{
public function home(Request $request){
$this->validate($request,[
'schoolid' => 'required',
'password' => 'required',
]);
$password = $request->password;
$schoolid = $request->schoolid;
$user = User::where('schoolid',$schoolid)->first();
if($user!=null){
if(Hash::check($password,$user->password)){
$userlogin = Userlogin::where('schoolid',$schoolid)->first();
$userlogin->ci = $userlogin->ci + 1;
$userlogin->save();
$rs['info'] = '登录成功';
$rs['status_code'] = 200;
return response($rs,200);
}else{
$rs['info'] = '密码错误';
$rs['status_code'] = 400;
return response($rs,200);
}
}else{
$rs['info'] = '您还没有注册';
$rs['status_code'] = 400;
return response($rs,200);
}
}
...

用户成功登陆之后,为了使用户可以做接下来的操作,需要给用户设置session
a、使用mysql数据库作为session的驱动,所以需要创建一个名为 sessions 的数据库

php artisan make:migration create_sessions_table –create=sessions

b、迁移文件的内容

public function up()
{
Schema::create('sessions', function (Blueprint $table) {
$table->string('id')->unique();
$table->unsignedInteger('user_id')->nullable();
$table->string('ip_address', 45)->nullable();
$table->text('user_agent')->nullable();
$table->text('payload');
$table->integer('last_activity');
});
}

c、执行迁移文件

php artisan migrate

d、使用session

//新增一个session
$request->session()->put('key','value');
//删除一个session
$request->session()->forget('key');
//判断是否存在某个session
$request->session()->has('key');
//查找一个session
$request->session()->get('key');
//清空session
$request->session()->flush();

e、在Kernel.php中的 protected $middleware 加上 session 的中间件

\Illuminate\Session\Middleware\StartSession::class,

f、登录代码示例

public function home(Request $request){
$this->validate($request,[
'schoolid' => 'required',
'password' => 'required',
]);
// $request->session()->flush();
$schoolid = $request->schoolid;
$schoolidkey = 'login'.$schoolid;
$value = $request->session()->has($schoolidkey);//判断session中是否有当前用户
if($value == false){//如果没有,就登陆
$password = $request->password;
$user = User::where('schoolid',$schoolid)->first();//从数据库中拿到当前用户模型
if($user!=null){//如果用户存在的话
if(Hash::check($password,$user->password)){//判断密码是否正确
$userlogin = Userlogin::where('schoolid',$schoolid)->first();
$userlogin->ci = $userlogin->ci + 1;//累计登录次数
$userlogin->save();//保存对数据模型的修改
$key = str_random(15);//随机生成的值,用于用户保存,并在调用接口的时候传递
$keykey = 'login'.$schoolid;
$request->session()->put($keykey,$key);//这个session用于登录时判断
$request->session()->put($key,$schoolid);//这个session用于登录后其他接口的判断
$rs['info'] = $schoolid.',这是您第'.$userlogin->ci.'登录,欢迎回来';
$rs['key'] = $key;
$rs['status_code'] = 200;
return response($rs,200);
}else{
$rs['info'] = '密码错误';
$rs['status_code'] = 400;
return response($rs,200);
}
}else{
$rs['info'] = '您还没有注册';
$rs['status_code'] = 400;
return response($rs,200);
}
}else{//如果有,就去掉session,重新登录
$rs['info'] = $schoolid.'同学你好,登录失败,请重试';
$key = $request->session()->get($schoolid);
$request->session()->forget($schoolidkey);//清除session,这样就不需要写退出接口了
$request->session()->forget($key);
$rs['status_code'] = 400;
return response($rs,200);
}
}

注意:session的key只能为字符串,如果用纯数字的话,会出现key失效的问题
3、测试
用postman进行接口测试,结果如下
登录的接口:

{
"schoolid":int
"password":string
}

登录成功:

{
"info": "2015211795,这是您第23登录,欢迎回来",
"key": "JlCQGDDIwBFz3Zs",
"status_code": 200
}

登录失败:

{
"info": "您还没有注册",
"status_code": 400
}
////
{
"info": "密码错误",
"status_code": 400
}
////
{
"info": "2015211795同学你好,登录失败,请重试",
"status_code": 400
}
找回密码

方法总结一下
1、定制路由
2、在控制器的方法中写逻辑
3、如果涉及修改数据库或者增加数据库,还要写迁移文件和模型文件
4、测试

1、定制路由

//找回密码验证码
//找回密码验证码
$api->post('findpassCodes','UsersController@findpasstore')
->name('api.findpassCodes.findpasstore');
//找回密码
$api->post('findpass','UsersController@findpass')
->name('api.findpass.findpass');

2、在控制器中写逻辑
首先判断玩家是否在线,如果在线,不能找回密码。
找回密码需要发送验证码,所以需要两个方法
将验证码存到缓存中

//找回密码时邮件发验证码
public function findpasstore(Request $request){
$this->validate($request,[
'schoolid' => 'required|max:10',
]);
$schoolid = $request->schoolid;
$schoolidkey = 'login'.$schoolid;
$value = $request->session()->has($schoolidkey);
if ($value == false){
//发邮件
$user = User::where('schoolid',$schoolid)->first();
$email = $user->email;
$code = str_pad(random_int(1, 9999), 4, 0, STR_PAD_LEFT);
$name = $user->name;
Mail::send('findpass',['name'=>$name,'code'=>$code],function($message) use ($email){
$to = $email;
$message ->to($to)->subject('找回密码验证码');
});
$info = "邮件已发送,如长时间没收到邮件,请重试";
$key = 'findpassCode'.str_random(15);
$expiredAt = now()->addMinutes(10);
// 缓存验证码 10分钟过期。
\Cache::put($key, ['email' => $request->email, 'code' => $code], $expiredAt);
return $this->response->array([
'key' => $key,
'expired_at' => $expiredAt->toDateTimeString(),
'info' => $info,
])->setStatusCode(201);
}else{
$rs['status_code'] = 400;
$rs['info'] = '您已经登录,不能重置密码';
return $this->response->accepted($rs);
}
}
//找回密码逻辑
public function findpass(Request $request){
$this->validate($request,[
'schoolid' => 'required|max:10',
'password' => 'required|min:8',
'verification_code' => 'required|max:4',
]);
$schoolid = $request->schoolid;
$schoolidkey = 'login'.$schoolid;
$value = $request->session()->has($schoolidkey);
if ($value == false){
$verifyData = \Cache::get($request->verification_key);
if (!$verifyData) {
return response('验证码失效',200);
}
$code = $request->verification_code;
if (hash_equals($verifyData['code'],$code)){
$user = User::where('schoolid',$schoolid)->first();
$user->password = bcrypt($request->password);
$user->save();
return $this->response->array([
'status_code' => 200,
'info' => '重置成功',
])->setStatusCode(200);
}else{
return $this->response->array([
'status_code' => 400,
'info' => '验证码错误',
])->setStatusCode(200);
}
}else{
$rs['status_code'] = 400;
$rs['info'] = '您已经登录,不能重置密码';
return $this->response->accepted($rs);
}
}

3、测试
重置成功

{
"status_code": 200,
"info": "重置成功"
}

重置失败

{
"status_code": 400,
"info": "您已经登录,不能重置密码"
}

目前只需要这几个功能即可,用户在线报名比赛需要与比赛系统结合。
下一讲:比赛报名系统

【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月12日 0

暂无评论

推荐阅读
  TEZNKK3IfmPf   2024年04月26日   56   0   0 java数据库sql
  TEZNKK3IfmPf   2024年05月31日   27   0   0 sqlite数据库
  TEZNKK3IfmPf   2024年05月31日   31   0   0 数据库mysql
  TEZNKK3IfmPf   2024年05月31日   27   0   0 数据库mysql
TEZNKK3IfmPf