diff --git a/app/admin/common.php b/app/admin/common.php index f726abd..6336e8b 100644 --- a/app/admin/common.php +++ b/app/admin/common.php @@ -1,4 +1,10 @@ = 1024 && $i < 5; $i++) $size /= 1024; + for ($i = 0; $size >= 1024 && $i < 5; $i++) { + $size /= 1024; + } + return round($size, 2) . $delimiter . $units[$i]; } @@ -59,20 +68,20 @@ function list_to_tree($list, $pk = 'id', $pid = 'pid', $child = 'list', $root = return $tree; } -function create_tree_list($pid, $arr, $group, &$tree = []){ - foreach ( $arr as $key => $vo ){ - if($key == 0){ +function create_tree_list($pid, $arr, $group, &$tree = []) +{ + foreach ($arr as $key => $vo) { + if ($key == 0) { $vo['spread'] = true; } if (!empty($group) and in_array($vo['id'], $group)) { $vo['checked'] = true; - } - else{ + } else { $vo['checked'] = false; } - if( $vo['pid'] == $pid ){ - $child= create_tree_list( $vo['id'] ,$arr,$group); - if( $child ){ + if ($vo['pid'] == $pid) { + $child = create_tree_list($vo['id'], $arr, $group); + if ($child) { $vo['children'] = $child; } $tree[] = $vo; @@ -139,7 +148,6 @@ function get_admin_group_info($id) return $group; } - //菜单父子关系排序,用于后台菜单 function get_admin_menus() { @@ -174,13 +182,6 @@ function get_keywords() return $keywords; } -//读取网址分类列表 -function get_websit_cate() -{ - $cate = \think\facade\Db::name('WebsiteCate')->where(['status' => 1])->order('create_time asc')->select(); - return $cate; -} - //读取文章分类列表 function get_article_cate() { @@ -188,7 +189,6 @@ function get_article_cate() return $cate; } - /** * 管理员操作日志 * @param string $type 操作类型 login add edit view delete @@ -219,7 +219,7 @@ function add_log($type, $param_id = '', $param = []) break; case 'check': $title = '审核'; - break; + break; default: $title = '未知'; break; @@ -239,7 +239,7 @@ function add_log($type, $param_id = '', $param = []) $data['module'] = \think\facade\App::initialize()->http->getName(); $data['controller'] = strtolower(app('request')->controller()); $data['function'] = app('request')->action(); - $parameter = $data['module'] . '/' .$data['controller'] . '/' . $data['function']; + $parameter = $data['module'] . '/' . $data['controller'] . '/' . $data['function']; $data['rule_menu'] = $parameter; $data['title'] = \think\facade\Db::name('AdminRule')->where(array('src' => $parameter))->value('title') ?? $title; $content = $login_admin['nickname'] . '在' . date('Y-m-d H:i:s') . '执行了' . $data['title'] . '操作'; diff --git a/app/admin/view/conf/token.html b/app/admin/view/conf/token.html new file mode 100644 index 0000000..6240916 --- /dev/null +++ b/app/admin/view/conf/token.html @@ -0,0 +1,202 @@ +{extend name="common/base"/} + +{block name="body"} +
+

Token配置

+ + + + + + + + + + + + + +
Token签发组织 + + + Token签发作者 + + +
Token Secrect + + Token过期时间 + + +
+
+ Api测试注册 + Api测试登录 + Token测试 +
+
+ 测试结果: +
+
+
+ + + +
+
+{/block} + + + +{block name="script"} + +{include file="common/layui" base='base' extend="[]" use="['form']" callback="init" /} +{/block} + \ No newline at end of file diff --git a/app/api/BaseController.php b/app/api/BaseController.php new file mode 100644 index 0000000..778e491 --- /dev/null +++ b/app/api/BaseController.php @@ -0,0 +1,124 @@ +app = $app; + $this->request = $this->app->request; + + // 控制器初始化 + $this->initialize(); + } + + // 初始化 + protected function initialize() + { + //每页显示数据量 + $this->pageSize = Request::param('page_size', \think\facade\Config::get('app.page_size')); + } + + /** + * Api处理成功结果返回方法 + * @param $message + * @param null $redirect + * @param null $extra + * @return mixed + * @throws ReturnException + */ + protected function apiSuccess($msg = 'success',$data=[]) + { + return $this->apiReturn($data, 0, $msg); + } + + /** + * Api处理结果失败返回方法 + * @param $error_code + * @param $message + * @param null $redirect + * @param null $extra + * @return mixed + * @throws ReturnException + */ + protected function apiError($msg = 'fail',$data=[], $code = 1) + { + return $this->apiReturn($data, $code, $msg); + } + + /** + * 返回封装后的API数据到客户端 + * @param mixed $data 要返回的数据 + * @param integer $code 返回的code + * @param mixed $msg 提示信息 + * @param string $type 返回数据格式 + * @param array $header 发送的Header信息 + * @return Response + */ + protected function apiReturn($data, int $code = 0, $msg = '', string $type = '', array $header = []): Response + { + $result = [ + 'code' => $code, + 'msg' => $msg, + 'time' => time(), + 'data' => $data, + ]; + + $type = $type ?: 'json'; + $response = Response::create($result, $type)->header($header); + + throw new HttpResponseException($response); + } + +} diff --git a/app/api/common.php b/app/api/common.php new file mode 100644 index 0000000..2c26559 --- /dev/null +++ b/app/api/common.php @@ -0,0 +1,7 @@ +order('create_time asc')->select()->toArray(); + return $cate; +} \ No newline at end of file diff --git a/app/api/controller/Index.php b/app/api/controller/Index.php new file mode 100644 index 0000000..900e744 --- /dev/null +++ b/app/api/controller/Index.php @@ -0,0 +1,145 @@ + ['except' => ['index','login', 'reg']], + ]; + + /** + * @api {post} /index/index API页面 + * @apiDescription 返回首页信息 + */ + public function index() + { + $list = Db::name('Article')->select(); + $seo = get_system_config('web'); + $this->apiSuccess('请求成功',['list' => $list,'seo' => $seo]); + } + + /** + * @api {post} /index/login 会员登录 + * @apiDescription 系统登录接口,返回 token 用于操作需验证身份的接口 + + * @apiParam (请求参数:) {string} username 登录用户名 + * @apiParam (请求参数:) {string} password 登录密码 + + * @apiParam (响应字段:) {string} token Token + + * @apiSuccessExample {json} 成功示例 + * {"code":0,"msg":"登录成功","time":1627374739,"data":{"token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJhcGkuZ291Z3VjbXMuY29tIiwiYXVkIjoiZ291Z3VjbXMiLCJpYXQiOjE2MjczNzQ3MzksImV4cCI6MTYyNzM3ODMzOSwidWlkIjoxfQ.gjYMtCIwKKY7AalFTlwB2ZVWULxiQpsGvrz5I5t2qTs"}} + * @apiErrorExample {json} 失败示例 + * {"code":1,"msg":"帐号或密码错误","time":1627374820,"data":[]} + */ + public function login() + { + $param = get_params(); + if(empty($param['username']) || empty($param['password'])){ + $this->apiError('参数错误'); + } + // 校验用户名密码 + $user = Db::name('User')->where(['username' => $param['username']])->find(); + if (empty($user)) { + $this->apiError('帐号或密码错误'); + } + $param['pwd'] = set_password($param['password'], $user['salt']); + if ($param['pwd'] !== $user['password']) { + $this->apiError('帐号或密码错误'); + } + if ($user['status'] == -1) { + $this->apiError('该用户禁止登录,请于平台联系'); + } + $data = [ + 'last_login_time' => time(), + 'last_login_ip' => request()->ip(), + 'login_num' => $user['login_num'] + 1, + ]; + $res = Db::name('user')->where(['id' => $user['id']])->update($data); + if($res){ + //获取jwt的句柄 + $jwtAuth = JwtAuth::getInstance(); + $token = $jwtAuth->setUid($user['id'])->encode()->getToken(); + $this->apiSuccess('登录成功',['token' => $token]); + } + } + + /** + * @api {post} /index/reg 会员注册 + * @apiDescription 系统注册接口,返回是否成功的提示,需再次登录 + + * @apiParam (请求参数:) {string} username 用户名 + * @apiParam (请求参数:) {string} password 密码 + + * @apiSuccessExample {json} 成功示例 + * {"code":0,"msg":"注册成功","time":1627375117,"data":[]} + * @apiErrorExample {json} 失败示例 + * {"code":1,"msg":"该账户已经存在","time":1627374899,"data":[]} + */ + public function reg() + { + $param = get_params(); + if(empty($param['username']) || empty($param['pwd'])){ + $this->apiError('参数错误'); + } + $user = Db::name('user')->where(['username' => $param['username']])->find(); + if (!empty($user)) { + $this->apiError('该账户已经存在'); + } + $param['salt'] = set_salt(20); + $param['password'] = set_password($param['pwd'], $param['salt']); + $param['register_time'] = time(); + $param['headimgurl'] = '/static/admin/images/icon.png'; + $param['register_ip'] = request()->ip(); + $char = mb_substr($param['username'], 0, 1, 'utf-8'); + $uid = Db::name('User')->strict(false)->field(true)->insertGetId($param); + if($uid){ + $this->apiSuccess('注册成功'); + }else{ + $this->apiError('注册失败'); + } + } + + /** + * @api {post} /index/demo 测试页面 + * @apiDescription 返回文章列表信息 + + * @apiParam (请求参数:) {string} token Token + + * @apiSuccessExample {json} 响应数据样例 + * {"code":1,"msg":"","time":1563517637,"data":{"id":13,"email":"test110@qq.com","password":"e10adc3949ba59abbe56e057f20f883e","sex":1,"last_login_time":1563517503,"last_login_ip":"127.0.0.1","qq":"123455","mobile":"","mobile_validated":0,"email_validated":0,"type_id":1,"status":1,"create_ip":"127.0.0.1","update_time":1563507130,"create_time":1563503991,"type_name":"注册会员"}} + */ + public function demo() + { + $list = Db::name('Article')->select(); + $jwtAuth = JwtAuth::getInstance(); + $uid = $jwtAuth->getUid(); + $userInfo = Db::name('User')->where(['id' => $uid])->find(); + $this->apiSuccess('请求成功',['list' => $list,'user' => $userInfo]); + } + + /** + * 获取用户id + * @return mixed + */ + protected function getUid() + { + $jwtAuth = JwtAuth::getInstance(); + return $jwtAuth->getUid(); + } +} diff --git a/app/api/event.php b/app/api/event.php new file mode 100644 index 0000000..4eff890 --- /dev/null +++ b/app/api/event.php @@ -0,0 +1,5 @@ +result([], 110, 'token格式错误'); + } + $jwtAuth = JwtAuth::getInstance(); + $jwtAuth->setToken($token); + if ($jwtAuth->validate() && $jwtAuth->verify()) { + return $next($request); + } else { + $this->result([], 111, 'token已过期'); + } + } else { + $this->result([], 112, 'token不能为空'); + } + + return $next($request); + } + + /** + * 返回封装后的API数据到客户端 + * @param mixed $data 要返回的数据 + * @param integer $code 返回的code + * @param mixed $msg 提示信息 + * @param string $type 返回数据格式 + * @param array $header 发送的Header信息 + * @return Response + */ + protected function result($data, int $code = 0, $msg = '', string $type = '', array $header = []): Response + { + $result = [ + 'code' => $code, + 'msg' => $msg, + 'time' => time(), + 'data' => $data, + ]; + + $type = $type ?: 'json'; + $response = Response::create($result, $type)->header($header); + + throw new HttpResponseException($response); + } + +} diff --git a/app/api/middleware/Install.php b/app/api/middleware/Install.php new file mode 100644 index 0000000..2f23ddb --- /dev/null +++ b/app/api/middleware/Install.php @@ -0,0 +1,21 @@ +isAjax()?to_assign(0,'请先完成系统安装引导'):redirect((string)url('/install/index')); + } + return $next($request); + } +} diff --git a/app/api/service/JwtAuth.php b/app/api/service/JwtAuth.php new file mode 100644 index 0000000..9302ec1 --- /dev/null +++ b/app/api/service/JwtAuth.php @@ -0,0 +1,143 @@ +expTime = get_system_config('api','exptime'); + // claim iss 签发组织 + $this->iss = get_system_config('api','iss'); + // claim aud签发作者 + $this->aud = get_system_config('api','aud'); + // secrect + $this->secrect = get_system_config('api','secrect'); + } + + // 私有化clone函数 + private function __clone() + { + // TODO: Implement __clone() method. + } + + // 获取token + public function getToken() + { + return (string) $this->token; + } + + // 设置token + public function setToken($token) + { + $this->token = $token; + return $this; + } + + // 设置uid + public function setUid($uid) + { + $this->uid = $uid; + return $this; + } + + // 获取uid + public function getUid() + { + return $this->uid; + } + + // 编码jwt token + public function encode() + { + $time = time(); //签发时间 + $this->token = (new Builder())->setHeader('alg', 'HS256') + ->setIssuer($this->iss) + ->setAudience($this->aud) + ->setIssuedAt($time) + ->setExpiration($time + $this->expTime) + ->set('uid', $this->uid) + ->sign(new Sha256(), $this->secrect) + ->getToken(); + + return $this; + } + + public function decode() + { + if (!$this->decodeToken) { + $this->decodeToken = (new Parser())->parse((string) $this->token); // Parses from a string + $this->uid = $this->decodeToken->getClaim('uid'); + } + return $this->decodeToken; + } + + // validate + public function validate() + { + $data = new ValidationData(); // It will use the current time to validate (iat, nbf and exp) + $data->setIssuer($this->iss); + $data->setAudience($this->aud); + $data->setId($this->uid); + + return $this->decode()->validate($data); + } + + // verify token + public function verify() + { + $signer = new Sha256(); + return $this->decode()->verify($signer, $this->secrect); + } + +} diff --git a/app/common.php b/app/common.php index d432aef..5ff6e6c 100644 --- a/app/common.php +++ b/app/common.php @@ -36,7 +36,7 @@ function get_config($key) } //读取系统配置 -function get_system_config($name,$key) +function get_system_config($name,$key='') { $config=[]; if (get_cache('system_config' . $name)) { @@ -48,9 +48,14 @@ function get_system_config($name,$key) } set_cache('system_config' . $name, $config); } - if($config[$key]){ - return $config[$key]; - } + if($key==''){ + return $config; + } + else{ + if($config[$key]){ + return $config[$key]; + } + } } //系统信息 diff --git a/app/home/common.php b/app/home/common.php index c8f66a9..ed02abf 100644 --- a/app/home/common.php +++ b/app/home/common.php @@ -52,6 +52,9 @@ function add_user_log($type, $param_str = '', $param_id = 0, $param = []) break; case 'delete': $title = '删除'; + break; + case 'down': + $title = '下载'; break; case 'join': $title = '报名'; diff --git a/app/home/controller/Index.php b/app/home/controller/Index.php index fc1e02c..0cb6374 100644 --- a/app/home/controller/Index.php +++ b/app/home/controller/Index.php @@ -17,11 +17,17 @@ class Index extends BaseController public function index() { add_user_log('view', '首页'); - return View(); + $version = get_system_config('other','version'); + $count = \think\facade\Db::name('UserLog')->where(array('type' => 'down'))->count(); + return View('',['version'=>$version,'count'=>$count]); } - public function main() + public function down() { - return View(); + $version = get_system_config('other','version'); + add_user_log('down', $version.'版本代码'); + header("Location: https://www.gougucms.com/storage/gougucms_".$version."_full.zip"); + //确保重定向后,后续代码不会被执行 + exit; } } diff --git a/app/home/view/index/index.html b/app/home/view/index/index.html index 0e3d3cf..6d2355b 100644 --- a/app/home/view/index/index.html +++ b/app/home/view/index/index.html @@ -14,12 +14,12 @@
- 立即下载 + 立即下载
- 当前版本:v1.2021.05.05 + 当前版本:{$version} 更新日志 - 下载量:1021 + 下载量:{$count}
diff --git a/app/install/data/gougucms.sql b/app/install/data/gougucms.sql index 6ccfdef..b3f6fe6 100644 --- a/app/install/data/gougucms.sql +++ b/app/install/data/gougucms.sql @@ -253,9 +253,10 @@ CREATE TABLE `cms_config` ( -- Records of cms_config -- ---------------------------- INSERT INTO `cms_config` VALUES (1, '网站配置', 'web', 'a:13:{s:2:\"id\";s:1:\"1\";s:11:\"admin_title\";s:9:\"勾股cms\";s:5:\"title\";s:9:\"勾股cms\";s:4:\"logo\";s:0:\"\";s:4:\"file\";s:0:\"\";s:6:\"domain\";s:24:\"https://www.gougucms.com\";s:3:\"icp\";s:23:\"粤ICP备1xxxxxx11号-1\";s:8:\"keywords\";s:9:\"勾股cms\";s:5:\"beian\";s:29:\"粤公网安备1xxxxxx11号-1\";s:4:\"desc\";s:255:\"勾股CMS是一套基于ThinkPHP6 + Layui + MySql打造的轻量级、高性能快速建站的内容管理系统。后台管理模块,一目了然,操作简单,通用型后台权限管理框架,紧随潮流、极低门槛、开箱即用。 \";s:4:\"code\";s:0:\"\";s:9:\"copyright\";s:32:\"© 2021 gougucms.com MIT license\";s:7:\"version\";s:5:\"1.0.2\";}', 1, 1612514630, 1623721279); -INSERT INTO `eye_config` VALUES (2, '邮箱配置', 'email', 'a:8:{s:2:\"id\";s:1:\"2\";s:4:\"smtp\";s:11:\"smtp.qq.com\";s:9:\"smtp_port\";s:3:\"465\";s:9:\"smtp_user\";s:15:\"gougucms@qq.com\";s:8:\"smtp_pwd\";s:6:\"123456\";s:4:\"from\";s:24:\"勾股CMS系统管理员\";s:5:\"email\";s:18:\"admin@gougucms.com\";s:8:\"template\";s:122:\"

勾股CMS是一套基于ThinkPHP6 + Layui + MySql打造的轻量级、高性能快速建站的内容管理系统。

\";}', 1, 1612521657, 1619088538); -INSERT INTO `cms_config` VALUES (3, '微信配置', 'wechat', 'a:9:{s:2:\"id\";s:1:\"3\";s:5:\"token\";s:8:\"GOUGUCMS\";s:14:\"login_back_url\";s:48:\"http://www.gougucms.com/wechat/index/getChatInfo\";s:5:\"appid\";s:18:\"wxdf96xxxx7cd6f0c5\";s:9:\"appsecret\";s:32:\"1dbf319a4f0dfed7xxxxfd1c7dbba488\";s:5:\"mchid\";s:10:\"151xxxx331\";s:11:\"secrect_key\";s:32:\"beiyuexxxxhunangdmabcxxxxjixxxng\";s:8:\"cert_url\";s:13:\"/extend/cert/\";s:12:\"pay_back_url\";s:42:\"https://www.gougucms.com/wechat/index/notify\";}', 1, 1612522314, 1613789058); -INSERT INTO `cms_config` VALUES (4, '其他配置', 'other', 'a:3:{s:2:\"id\";s:1:\"4\";s:6:\"author\";s:12:\"勾股工作室\";s:7:\"version\";s:5:\"1.0.2\";}', 1, 1613725791, 1613789431); +INSERT INTO `cms_config` VALUES (2, '邮箱配置', 'email', 'a:8:{s:2:\"id\";s:1:\"2\";s:4:\"smtp\";s:11:\"smtp.qq.com\";s:9:\"smtp_port\";s:3:\"465\";s:9:\"smtp_user\";s:15:\"gougucms@qq.com\";s:8:\"smtp_pwd\";s:6:\"123456\";s:4:\"from\";s:24:\"勾股CMS系统管理员\";s:5:\"email\";s:18:\"admin@gougucms.com\";s:8:\"template\";s:122:\"

勾股CMS是一套基于ThinkPHP6 + Layui + MySql打造的轻量级、高性能快速建站的内容管理系统。

\";}', 1, 1612521657, 1619088538); +INSERT INTO `cms_config` VALUES (3, '微信配置', 'wechat', '{s:2:"id";s:1:"3";s:5:"token";s:8:"GOUGUCMS";s:14:"login_back_url";s:49:"https://www.gougucms.com/wechat/index/getChatInfo";s:5:"appid";s:18:"wxdf96xxxx7cd6f0c5";s:9:"appsecret";s:32:"1dbf319a4f0dfed7xxxxfd1c7dbba488";s:5:"mchid";s:10:"151xxxx331";s:11:"secrect_key";s:29:"gougucmsxxxxhumabcxxxxjixxxng";s:8:"cert_url";s:13:"/extend/cert/";s:12:"pay_back_url";s:42:"https://www.gougucms.com/wxappv1/wx/notify";s:9:"xcx_appid";s:18:"wxdf96xxxx9cd6f0c5";s:13:"xcx_appsecret";s:28:"gougucmsxxxxhunangdmabcxxxng";}', 1, 1612522314, 1613789058); +INSERT INTO `cms_config` VALUES (4, 'Api Token配置', 'token', 'a:5:{s:2:\"id\";s:1:\"5\";s:3:\"iss\";s:16:\"www.gougucms.com\";s:3:\"aud\";s:8:\"gougucms\";s:7:\"secrect\";s:8:\"GOUGUCMS\";s:7:\"exptime\";s:4:\"3600\";}', 1, 1627313142, 1627376290); +INSERT INTO `cms_config` VALUES (5, '其他配置', 'other', 'a:3:{s:2:"id";s:1:"4";s:6:"author";s:15:"勾股工作室";s:7:"version";s:13:"v1.2021.07.28";}', 1, 1613725791, 1613789431); -- ---------------------------- @@ -425,9 +426,10 @@ CREATE TABLE `cms_nav_info` ( INSERT INTO `cms_nav_info` VALUES ('1', '0', '1', '首页', '/', '', '0', '1', '1', '0', '0'); INSERT INTO `cms_nav_info` VALUES ('2', '0', '1', '文档', '/', '', '0', '1', '2', '0', '0'); INSERT INTO `cms_nav_info` VALUES ('3', '0', '1', '社区', '/', '', '0', '1', '3', '0', '0'); -INSERT INTO `cms_nav_info` VALUES ('4', '0', '1', '腾讯云优惠', 'https://curl.qcloud.com/PPEgI0oV', '', '1', '1', '4', '0', '0'); -INSERT INTO `cms_nav_info` VALUES ('5', '0', '1', '阿里云特惠', 'https://www.aliyun.com/activity/daily/bestoffer?userCode=dmrcx154', '', '1', '1', '5', '0', '0'); -INSERT INTO `cms_nav_info` VALUES ('6', '0', '1', '后台演示', 'https://www.gougucms.com/admin/index/index.html', '', '1', '1', '6', '0', '0'); +INSERT INTO `cms_nav_info` VALUES ('4', '0', '1', 'API接口', '/api/index', '', '1', '1', '4', '0', '0'); +INSERT INTO `cms_nav_info` VALUES ('5', '0', '1', '腾讯云优惠', 'https://curl.qcloud.com/PPEgI0oV', '', '1', '1', '5', '0', '0'); +INSERT INTO `cms_nav_info` VALUES ('6', '0', '1', '阿里云特惠', 'https://www.aliyun.com/activity/daily/bestoffer?userCode=dmrcx154', '', '1', '1', '6', '0', '0'); +INSERT INTO `cms_nav_info` VALUES ('7', '0', '1', '后台演示', 'https://www.gougucms.com/admin/index/index.html', '', '1', '1', '7', '0', '0'); -- ---------------------------- -- Table structure for `cms_slide` diff --git a/composer.json b/composer.json index 6c799e9..3dace59 100644 --- a/composer.json +++ b/composer.json @@ -27,7 +27,7 @@ "topthink/think-view": "^1.0", "topthink/think-captcha": "^3.0", "phpmailer/phpmailer": "^6.3", - "lcobucci/jwt": "^3.4" + "lcobucci/jwt": "3.3.3" }, "require-dev": { "symfony/var-dumper": "^4.2", diff --git a/config/database.php b/config/database.php index e6a3e71..4ce2b09 100644 --- a/config/database.php +++ b/config/database.php @@ -29,8 +29,8 @@ return [ 'prefix' => 'cms_', // 数据库连接参数 'params' => [], - // 数据库编码默认采用utf8 - 'charset' => 'utf8', + // 数据库编码默认采用utf8mb4 + 'charset' => 'utf8mb4', // 数据库调试模式 'debug' => false, // 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器)