laravel 后台添加管理员日志记录

今天抽离之前使用的 laravel 版本的 fastadmin 后台,权限系统,当时没有写 '管理员日志' 这个模块,今天实现了下,过程中,也发现几个问题,分享给大家。

可以先看下 fastadmin 源码,它使用了 tp 的 behavior 功能,在应用结束后,调用了 admin log 钩子

好久没看 tp 了,不过还稍微了解点 laravel,看代码机制,应该就是 hook 钩子之类的,还专门搜索了下 tp 的 behavioir 和 laravel 的 event 区别,可惜没找到...,不过两者应该差不多的

tp 有 behavioir 这种机制,而且在 tp 内,内置了一些系统级别的 hook,但是 laravel 好像并没有啊,这个我也简单搜了下,好像没有想要的,记得模型的一些操作好像有内置的 event,created、updated 等,laravel 系统好像没。

那我们在哪个位置需要记录日志呢,fastadmin 是在 app_end - 应用结束,记录的日志,laravel 哪里能判定应用结束,而且得是公共的地方

从 public/index.php,看过 laravel 源码的,应该知道 laravel 的最简单的执行机制是:
	
	/*
		$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);

		$response = $kernel->handle(
		    $request = Illuminate\Http\Request::capture()
		);

		$response->send();

		$kernel->terminate($request, $response);
	 */

	实例化内核(kernel),将请求(request)传递给内核,供内核处理,得到响应(response),然后发送响应

看 public/index.php 的几行代码,就是这个意思,而且,在得到相应后,调用了 terminate(),这个是所有的 middleware 的 terminate 方法调用的时机。

我们记录日志,可以在入口文件的 $reponse 后,表示已经处理了请求,得到了相应,表示其实已经结束了,然后记录日志。

但是,觉得不应该修改 laravel 的核心代码,不利于升级,而且 laravel 提供了更好的解决方法,就是上面说的 terminate():
	https://learnku.com/docs/laravel/6.x/middleware/5136#terminable-middleware

	Terminable 中间件

	它的执行时机就是:
		在准备好 HTTP 响应之后,中间件可能需要做一些工作。例如,Laravel 内置的「session」 中间件会在完全准备好响应后将会话数据写入存储。如果你在中间件上定义了一个 terminate 方法,并且你使用的是 FastCGI ,那么它将会在响应准备发送到浏览器之后自动调用。

所以,我们就确定了日志的位置。

所以,定义了一个 AdminLogMiddleware,记录下过程中的几个问题:
	1>只定义 terminate() 方法,不定义 handle() 方法,报错:
		Function name must be a string

	2>terminate() 方法,调试过程中,输出不了任何内容,这个其实就跟在 public/index.php 中,在 
		$response->send();

	  执行后面,也输出不了任何内容,而 $kernel->terminate($request, $response); 也是在这后面,所以输出不了内容

	3>输出不了内容,如何调试,在 public/index.php 中调试(这个也是我在写这边笔记时,重新看了下 public/index.php 想到的。。。)
		我的调试,就是在 AdminLogMiddleware 中间件的 handle() 方法中调试的。

		/*
			这里再强调个知识点,文档中的:
				前置 & 后置中间件

			前置中间件:
				public function handle($request, Closure $next)
				{
					// 执行内容
					return $next($request);
				}

			后置中间件:
				public function handle($request, Closure $next)
				{
					$response = $next($request);

					// 执行内容

					return $response;
				}
		 */

		发现后置中间件,也是能获取到响应的,但这里的响应,是不是经历了当前中间件处理后,然后得到的响应?还是所有请求结束后的响应内容?(应该是所有请求结束后的响应内容,因为中间件,并非执行请求,只是处理请求前的,一道道过滤机制,或其他逻辑处理吧)

		还是文档、源码、整个机制不清楚。。。有时间我也得再好好啃,先把自己理解的记录下来

	4>后置中间件 和 terminate() 方法,都可以收到 $response,但2者的区别到底是什么,我还不是很清楚,但是目前看有一点是清楚的:
		后置中间件,我们得到 $response,可以输出,而 terminate() 不行

	5>我们可以更优化下日志处理,将其作为一个 event 事件,解耦,逻辑还清晰。因为可能以后还可能定义其他操作
		如果那样的话,我们也不能定义为 AdminLogMiddleware,但我们目前只想让部分路由使用,中间件好像更好点。

		不过,我们也可以定义一个全局的日志中间件,在中间件里,通过路由匹配,来指定哪些路由想要记录日志

		关于这点发现好像自己都被绕进去了,是不是这么架构合理...(架构知识很欠缺...)

临时写的笔记,有点乱,另外自身实力有限,勿怪,最后,记录下代码:
	数据库迁移:
        Schema::create('admin_logs', function (Blueprint $table) {
            $table->increments('id');
            $table->integer('admin_id')->unsigned()->comment('管理员ID');
            $table->string('url', 255)->comment('请求地址');
            $table->text('params')->comment('请求参数');
            $table->string('ip', 255)->comment('IP地址');
            $table->string('user_agent', 255)->comment('用户代理');
            $table->string('content', 255)->comment('日志描述');

            $table->timestamps();

            $table->index('admin_id');
            $table->index('ip');
        });

        DB::statement("ALTER TABLE `app_versions` comment '后台管理日志表'");

   	中间件 terminate() 方法: 
    public function terminate($request, $response)
    {
        $admin = Auth::guard('admin')->user();

        // 数据处理
        $admin_id = $admin ? $admin->id : 0;
        $method = $request->method();
        $uri = $request->path();

        // 1.过滤 uris(不记录日志的 uri)
        $guarded_uris = [
            'admin/ajax/lang',
        ];
        if(in_array($uri, $guarded_uris)){
            return true;
        }

        // 2.过滤 uris + method(某些 uri 的 get 和 post 一致,也需要过滤)
        $guards_get_method_uris = [
            'admin/account/login',
        ];
        if(in_array($uri, $guards_get_method_uris)){
            return true;
        }

        // 2.过滤 params(日志中不记录密码等私密信息)
        $guarded_params = [
            'password',
        ];
        $params = $request->all();
        $params = array_filter($params, function ($key) use ($guarded_params) {
            return !in_array($key, $guarded_params);
        }, ARRAY_FILTER_USE_KEY);
        $params = json_encode($params, JSON_UNESCAPED_UNICODE);

        $ip = $request->getClientIp();
        $user_agent = Agent::getUserAgent();

        /*
        	这里结合的是我系统里的,记录内容会根据权限菜单名来匹配
         */
        // 4.得到日志内容(我们通过 uri 匹配相应的 '权限菜单 identifier',来获取)
        // 1>后台权限路由
        $admin_permission = AdminPermission::where('identifier', substr($uri, 6))->first();
        if($admin_permission){
            $id_chain = $admin_permission->id_chain;
            $names = AdminPermission::whereIn('id', explode('-', $id_chain))->pluck('name');
            $content = $names->join('-');

        // 2>后台其他路由
        }else{
            switch($uri){
                case 'admin/account/login':
                    $content = '登录';
                    break;

                case 'admin/account/logout':
                    $content = '退出登录';
                    break;

                default:
                    $content = '其他操作';
                    break;
            }
        }

        $admin_log_data = [
            'admin_id' => $admin_id,
            'method' => $method,
            'uri' => $uri,
            'params' => $params,
            'ip' => $ip,
            'user_agent' => $user_agent,
            'content' => $content,
        ];
        AdminLog::create($admin_log_data);
    }

    最终的记录结果:
		(`id`, `admin_id`, `uri`, `params`, `ip`, `user_agent`, `content`, `created_at`, `updated_at`)

		(26,1,'admin/auth/permission/index','{\"sort\":\"sortid\",\"order\":\"desc\",\"offset\":\"0\",\"limit\":\"10\",\"_\":\"1569680908796\"}','127.0.0.1','Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36','权限管理-菜单管理-列表','2019-09-28 14:40:43','2019-09-28 14:40:43');

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值