Correcting teacher:天蓬老师
Correction status:qualified
Teacher's comments:学得时候 会感觉很多的, 实际工作中用得并不是太多的, 不必太担心
管理员列表
admin
外, 还需要获取用户组表 admin_group
的”角色名称”字段值. 暂时先用循环管理员查询结果的方式, 从 admin_group
表中查询对应的角色名称.新增/修改管理员
layui.open()
弹出iframe层的方式打开新增/修改界面. _token
(@csrf). password_hash(待加密数据, 加密方式)
来给管理员密码加密. 如: $admin['password'] = password_hash($admin['password'], PASSWORD_DEFAULT);
.jQuery
获取checkbox的选中情况: $('input[name="status"]').prop('checked');
, 返回 true
/ false
; 设置checkbox的选中情况: $('input[name="status"]').prop('checked', true/false)
.弹出iframe层的代码片段:
function add() {
// 打开iframe弹出层
layer.open({
// 弹出层的类型, 2 表示已iframe的方式弹出.
type: 2,
// 弹出界面标题
title: '添加新管理员',
// 点击弹出窗口外的阴影区域是否关闭弹出窗口
shadeClose: false,
// 阴影区域的透明度
shade: 0.8,
// 弹出界面的大小, 可以设置像素值和百分比.
area: ['400px', '55%'],
// 弹出界面的iframe区域的url地址, 即iframe中要渲染的页面地址
content: '/admin/admin/add' //iframe的url
});
}
权限中间件业务实现中的一些知识点
Auth::user()
可以从 session
中获取保存在其中的登录用户信息.haldler()
方法中, 使用传入的 $request
参数获取请求的控制器名称和方法名称: $request->route()->action['controller']
. namespace\Controller@action
, 可以用字符串函数分离出控制器名和方法名.$request
对象的 ajax()
方法, 返回当前请求是否是ajax请求. 当权限验证没有通过时, 可以利用它判断当前请求类型, ajax请求则返回json格式字符串信息, 普通get/post请求返回原始的response相应信息.$routeMiddleware
属性组.路由中, 使用 namespace()
方法给路由加上命名空间, 简化给每条路由添加验证中间件的步骤:
// 使用namespace()方法给路由添加命名空间
Route::namespace('admins')->middleware(['auth', 'right.check'])->group(function() {
// 用户管理
/* 因为加了命名空间, 所以Admin控制前相对Controllers的命名空间前缀admins就可以不写了. 对比上面的'admins\Home@index'... */
Route::get('/admin/admin/index', 'Admin@index');
Route::get('/admin/admin/add', 'Admin@add');
Route::post('/admin/admin/save', 'Admin@save');
// 其他路由...
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>账号列表</title>
<link rel="stylesheet" href="/static/plugin/layui/css/layui.css">
<script src="/static/plugin/layui/layui.js"></script>
</head>
<body>
<div style="text-align: center; color: #666;">
<h2>管理员列表</h2>
</div>
<div style="float: right; height: 50px; line-height: 50px; padding: 0 10px;">
<button class="layui-btn layui-btn-success layui-btn-sm" onclick="add()">新增</button>
</div>
<table class="layui-table">
<thead>
<tr style="text-align: center">
<th>ID</th>
<th>用户名</th>
<th>分组</th>
<th>真实姓名</th>
<th>最后登录时间</th>
<th>用户状态</th>
<th>操作</th>
</tr>
</thead>
<tbody>
@csrf
@foreach($admins as $admin)
<tr>
<td>{{$admin['id']}}</td>
<td>{{$admin['username']}}</td>
<!-- 如果后端的keyval()方法没有指定value列名,则使用这个调用取值 -->
<!-- <td>大括号 大括号 $groups【$admin【'gid'】】【'title'】反大括号 反大括号</td> -->
<!-- 如果后端的keyval()方法指定了value列名,则使用这个调用取值 -->
<td>{{$groups[$admin['gid']]}}</td>
<td>{{$admin['real_name']}}</td>
<td>{{$admin['lastlogin'] ? date('Y-m-d H:i:s', $admin['lastlogin']) : ''}}</td>
<td>{{$admin['status'] == 0 ? '正常' : '禁用'}}</td>
<td>
<button class="layui-btn layui-btn-xs" onclick="edit({{$admin['id']}})">修改</button>
@if($admin['status'] == 0)
<button class="layui-btn layui-btn-danger layui-btn-xs" onclick="delOrResume(1, {{$admin['id']}}, '{{$admin['username']}}')">删除</button>
@else
<button class="layui-btn layui-btn-normal layui-btn-xs" onclick="delOrResume(0, {{$admin['id']}}, '{{$admin['username']}}')">恢复</button>
@endif
</td>
</tr>
@endforeach
</tbody>
</table>
</body>
<script>
layui.use(['layer'], function() {
layer = layui.layer;
$ = layui.jquery;
});
function edit(id) {
layer.open({
type: 2,
title: '修改管理员信息',
shadeClose: false,
// 应该是阴影区域的透明度
shade: 0.8,
// 分别是宽,高
area: ['400px', '55%'],
content: '/admin/admin/edit?id=' + id
});
}
function add() {
layer.open({
type: 2,
title: '添加新管理员',
// 点击弹出窗口外的阴影区域是否关闭弹出窗口
shadeClose: false,
shade: 0.8,
area: ['400px', '55%'],
content: '/admin/admin/add' //iframe的url
});
}
function delOrResume(status, id, username) {
var msg = status == 1 ? '删除' : '恢复';
//询问框
layer.confirm('确定要' + msg + username + '吗?', {
btn: ['确定','取消'] //按钮
}, function(){
var _token = $('input[name="_token"]').val();
$.post('/admin/admin/del_resume', {id: id, resume: status, _token: _token}, function(res) {
if(res.status != undefined && res.status == "0") {
layer.msg(res.message, {icon: 1});
setTimeout(() => {
window.location.reload();
}, 1000);
} else if(res.status != undefined) {
layer.alert(res.message, {icon: 2});
} else {
layer.alert('操作失败', {icon: 2});
}
// layer.alert(res);
}, 'json');
}, function() {
});
}
</script>
</html>
2-添加管理员视图
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>添加新管理员</title>
<link rel="stylesheet" href="/static/plugin/layui/css/layui.css">
<script src="/static/plugin/layui/layui.js"></script>
<style>
body {
padding: 10px;
}
.cms-label {
text-align-last: justify;
}
</style>
</head>
<body>
<form action="" class="layui-form">
@csrf
<div class="layui-form-item">
<label for="username" class="layui-form-label cms-label">用户名</label>
<div class="layui-input-inline">
<input type="text" class="layui-input" name="username" id="username">
</div>
</div>
<div class="layui-form-item">
<label for="gid" class="layui-form-label cms-label">角色</label>
<div class="layui-input-inline">
<select name="gid" id="gid">
<option value=""></option>
@foreach($groups as $group)
<option value="{{$group['gid']}}">{{$group['title']}}</option>
@endforeach
</select>
</div>
</div>
<div class="layui-form-item">
<label for="password" class="layui-form-label cms-label">密码</label>
<div class="layui-input-inline">
<input type="password" name="password" id="password" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label for="real_name" class="layui-form-label cms-label">姓名</label>
<div class="layui-input-inline">
<input type="text" name="real_name" id="real_name" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label for="status" class="layui-form-label cms-label">状态</label>
<div class="layui-input-inline">
<input type="checkbox" name="status" id="status" class="layui-input" title="禁用">
</div>
</div>
<div class="layui-form-item">
<div class="layui-input-block">
<!-- 设置form中的button是普通button, 给button加type="button" -->
<button type="button" class="layui-btn" onclick="save()">保存</button>
</div>
</div>
</form>
</body>
<script>
layui.use(['layer', 'form'], function() {
layer = layui.layer;
form = layui.form;
$ = layui.jquery;
});
/* 提交保存 */
function save() {
var username = $.trim($('input[name="username"]').val());
var gid = $.trim($("input[name='gid']").val());
var password = $.trim($("input[name='password']").val());
var status = $('input[name="status"]').prop('checked') ? 0 : 1;
if (username == '') {
return layer.alert('用户名不能为空', {
icon: 2
});
}
if (isNaN(gid)) {
return layer.alert('请选择一个角色', {
icon: 2
});
}
if (password == '') {
return layer.alert('密码不能为空', {
icon: 2
});
}
// 提交数据
$.post('/admin/admin/save', $('form').serialize(), function(res) {
if (res.status != 'undefined' && res.status == '0') {
layer.msg(res.message, {
icon: 1
});
setTimeout(() => {
window.parent.location.reload();
}, 1000);
} else if (res.status != 'undefined') {
layer.alert(res.message, {
icon: 2
});
} else {
layer.alert('保存出错', {
icon: 2
});
}
}, 'json');
}
</script>
</html>
3- 修改管理员视图
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>修改管理员</title>
<link rel="stylesheet" href="/static/plugin/layui/css/layui.css">
<script src="/static/plugin/layui/layui.js"></script>
<style>
body {
padding: 10px;
}
.cms-label {
text-align-last: justify;
}
</style>
</head>
<body>
<form action="" class="layui-form">
@csrf
<input type="hidden" name="id" value="{{$admin['id']}}">
<div class="layui-form-item">
<label for="username" class="layui-form-label cms-label">用户名</label>
<div class="layui-input-inline">
<input type="text" class="layui-input" name="username" id="username" value="{{$admin['username']}}">
</div>
</div>
<div class="layui-form-item">
<label for="gid" class="layui-form-label cms-label">角色</label>
<div class="layui-input-inline">
<select name="gid" id="gid" value="{{$admin['gid']}}">
<option value=""></option>
@foreach($groups as $group)
<option {{$admin['gid'] == $group['gid'] ? 'selected' : ''}} value="{{$group['gid']}}">{{$group['title']}}</option>
@endforeach
</select>
</div>
</div>
<div class="layui-form-item">
<label for="password" class="layui-form-label cms-label">密码</label>
<div class="layui-input-inline">
<input type="password" name="password" id="password" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label for="real_name" class="layui-form-label cms-label">姓名</label>
<div class="layui-input-inline">
<input type="text" name="real_name" id="real_name" class="layui-input" value="{{$admin['real_name']}}">
</div>
</div>
<div class="layui-form-item">
<label for="status" class="layui-form-label cms-label">状态</label>
<div class="layui-input-inline">
<input type="checkbox" name="status" id="status" class="layui-input" title="禁用" {{$admin['status'] ? 'checked' : ''}}>
</div>
</div>
<div class="layui-form-item">
<div class="layui-input-block">
<!-- 设置form中的button是普通button, 给button加type="button" -->
<button type="button" class="layui-btn" onclick="save()">提交</button>
</div>
</div>
</form>
</body>
<script>
layui.use(['layer', 'form'], function() {
layer = layui.layer;
form = layui.form;
$ = layui.jquery;
});
/* 提交修改 */
function save() {
var username = $.trim($('input[name="username"]').val());
var gid = $.trim($("input[name='gid']").val());
var password = $.trim($("input[name='password']").val());
if(username == '') {
return layer.alert('用户名不能为空', {icon: 2});
}
if(isNaN(gid)) {
return layer.alert('请选择一个角色', {icon: 2});
}
// if(password == '') {
// return layer.alert('密码不能为空', {icon: 2});
// }
// 提交数据
$.post('/admin/admin/update', $('form').serialize(), function(res) {
if(res.status != 'undefined' && res.status == '0') {
layer.msg(res.message, {icon: 1});
setTimeout(() => {
window.parent.location.reload();
}, 1000);
} else if(res.status != 'undefined') {
layer.alert(res.message, {icon: 2});
} else {
layer.alert('提交出错', {icon: 2});
}
}, 'json');
}
</script>
</html>
4-管理员控制器
<?php
namespace App\Http\Controllers\admins;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
class Admin extends Controller {
public function index() {
//print_r(DB::table('admin')->lists());die;
$data['admins'] = DB::table('admin')->lists();
// printf('<pre>%s</pre>', print_r($data, true));die;
// 取代关联查询的方法1:遍历表1查询结果集A,通过结果集A中的外键去查询表2对应的记录,返回想要的字段值
foreach($data['admins'] as &$admin) {
$gid = $admin['gid'];
// 在循环中查询数据库,效率不好,
$adminGroup = DB::table('admin_group')->select('title')->where('gid', $gid)->first();
// printf('<pre>%s</pre>', print_r($gName));
$admin['gName'] = $adminGroup->title;
}
// 传给视图渲染
$data['groups'] = $groups;
return view('admins/admin/index', $data);
}
/* 跳转到新增管理员界面 */
public function add() {
$data['groups'] = DB::table('admin_group')->select(['gid', 'title'])->lists();
return view('admins/admin/add', $data);
}
public function save(Request $req) {
$admin['username'] = trim($req->username);
$admin['password'] = trim($req->password);
$admin['gid'] = trim($req->gid);
$admin['real_name'] = trim($req->real_name);
$admin['status'] = $req->status == 'on' ? 1 : 0;
// 判空
if($admin['username'] == '') {
return json_encode(['status' => 1, 'message' => '用户名不能为空']);
}
if($admin['password'] == '') {
return json_encode(['status' => 1, 'message' => '密码不能为空']);
}
// 判断该用户名是否已存在
$admin_user = DB::table('admin')->where('username', $admin['username'])->first();
if($admin_user) {
return json_encode(['status' => 1, 'message' => '该用户名已存在']);
}
// 执行保存
/* php提供的密码加密函数 */
$admin['password'] = password_hash($admin['password'], PASSWORD_DEFAULT);
$admin['add_time'] = time();
// 保存数据
$res = DB::table('admin')->insert($admin);
if($res) {
return json_encode(['status' => 0, 'message' => '用户添加成功']);
}
}
public function delOrResume(Request $req) {
$id = $req->id;
$status = $req->resume == '' ? 1 : $req->resume;
$msg = $status ? '删除' : '恢复';
// 一般删除是执行update用户状态, 不要物理删除
// $res = DB::table('admin')->where('id', $id)->delete();
$res = DB::table('admin')->where('id', $id)->update(['status'=>$status]);
if($res) {
return json_encode(['status' => 0, 'message' => '用户' . $msg . '成功']);
} else {
return json_encode(['status' => 1, 'message' => '用户' . $msg . '失败']);
}
}
public function edit(Request $req) {
$id = $req->id;
// 验证$id是否有效
if(!filter_var($id, FILTER_VALIDATE_INT, ['option' => [`min_range` => 1]])) {
if($req->ajax())
return json_encode(['status' => 1, 'message' => '无效的id值']);
else
return response('无效的id值', 200);
}
// 验证$id是否存在
$admin = DB::table('admin')->where('id', $id)->getFirst();
// printf('<pre>%s</pre>', print_r($admin, true));
if(empty($admin)) {
if($req->ajax())
return json_encode(['status' => 1, 'message' => '该管理员不存在']);
else
return response('该管理员不存在', 200);
}
$data['admin'] = $admin;
$data['groups'] = DB::table('admin_group')->select(['gid', 'title'])->lists();
return view('/admins/admin/edit', $data);
}
public function update(Request $req) {
/* echo '<pre>';
dump(isset($req->password)); */
// 获取所有参数
$admin = $req->all();
// token不是更新字段项
unset($admin['_token']);
// 状态值
$admin['status'] = (isset($admin['status']) && $admin['status']) == 'on' ? 1 : 0;
// 真实姓名
$admin['real_name'] = isset($admin['real_name']) ? $admin['real_name'] : '';
// 如果
if(isset($admin['password'])) {
$admin['password'] = password_hash($admin['password'], PASSWORD_DEFAULT);
} else {
unset($admin['password']);
}
$id = $admin['id'];
unset($admin['id']);
// DB::connection()->enableQueryLog(); // 开启QueryLog
$res = DB::table('admin')->where('id', $id)->update($admin);
// dump(DB::getQueryLog());die;
if($res) {
return $this->returns($req, '修改成功!', 0);
} else {
return $this->returns($req, '修改失败!');
}
}
private function returns($req, $msg, $status = 1) {
if($req->ajax())
return json_encode(['status' => $status, 'message' => $msg]);
else
return response($msg, 200);
}
}
5-权限验证中间件
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
/* 权限验证中间件 */
class RightCheck {
public function handle($request, Closure $next, $guard = null) {
$loginAdmin = Auth::user();
// 当前用户的角色id
$gid = $loginAdmin->gid;
// 获取角色信息
$group = DB::table('admin_group')->where('gid', $gid)->getFirst();
// 角色所拥有的权限(json字符串)
$rights = [];
// 如果角色的权限字段有值
if($group && $group['rights']) {
// 把json字符串转为php数组(入参的第二个参数为true, 则转为数组; 为false, 则返回php对象)
$rights = json_decode($group['rights'], true);
} else {
return json_encode(['status' => 1, 'message' => '你无权执行此操作']);
}
// 获取当前请求的控制器和方法
/* $routeController的值: namespace\Controller@action */
$routeController = $request->route()->action['controller'];
/* 转成数组, 拿到最后一个数组元素值 */
$routeController = explode('\\', $routeController);
/* 弹出最后一个元素 */
$routeController = array_pop($routeController);
/* 把Controller@action拆分成两个值 */
list($controller, $action) = explode('@', $routeController);
// dump($controller);dump($action);die;
/* 查询当前请求的控制器和方法对应的权限id */
$menu = DB::table('admin_menu')->select('mid', 'status')->where('controller', $controller)->where('action', $action)->getFirst();
/* 查不到这个请求权限 */
if(!$menu) {
// return response('该功能不存在', 200);
return $this->noRight($request, '该功能不存在');
}
// dump($menu['mid']); dump($rights);die;
// 没有权限
if(!in_array($menu['mid'], $rights)) {
// 注意,返回必须是底层 response相应, 否则,VerifyCsrfToken.php会报错。
return $this->noRight($request, '权限不足');
}
// 有该权限, 但是该权限被禁用了
if($menu['status'] == 1) {
// 注意,返回必须是底层 response相应, 否则,VerifyCsrfToken.php会报错。
return $this->noRight($request, '该功能已被禁用');
}
$loginAdmin->rights = $rights;
// 把用户信息存到$request对象中
$request->loginInfo = $loginAdmin;
// 放行
return $next($request);
}
/**
* 判断请求是否是ajax请求,若是,则response封装json格式字符串,若不是,则response封装普通文本字符串。
*/
private function noRight($request, $msg) {
// 注意,返回必须是底层 response相应, 否则,VerifyCsrfToken.php会报错。
if($request->ajax()) {
return response(json_encode(['status'=>1, 'message'=>$msg]), 200);
} else {
return response($msg, 200);
}
}
}
7-路由
Route::namespace('admins')->middleware(['auth', 'right.check'])->group(function() {
// 用户管理
/* 因为加了命名空间, 所以Admin控制前相对Controllers的命名空间前缀admins就可以不写了. 对比上面的'admins\Home@index'... */
Route::get('/admin/admin/index', 'Admin@index');
Route::get('/admin/admin/add', 'Admin@add');
Route::post('/admin/admin/save', 'Admin@save');
Route::post('/admin/admin/del_resume', 'Admin@delOrResume');
Route::get('/admin/admin/edit', 'Admin@edit');
Route::post('/admin/admin/update', 'Admin@update');
}
可以使用PHP提供的密码加密函数 password_hash(待加密数据, 加密方式)
给管理员密码加密.
使用namespace()
方法给多条路由设置命名空间, 把需要在各条路由指定的中间件改成在命名空间指定, 简化路由指定中间件的写法.
复习中间件的使用: 1.创建中间件; 2.注册中间件; 3.触发中间件.
实战课用到的知识好多, 干货太多, 作业很难总结完.