From b4921f54570e110741860f2298a533a528fa4b3c Mon Sep 17 00:00:00 2001 From: RainSun Date: Tue, 21 Apr 2020 11:09:13 +0800 Subject: [PATCH] finish v1 --- Dockerfile | 6 ++ ReadMe.md | 53 ++++++++++++ go.sh | 3 + gunicorn.conf.py | 8 ++ lib/__init__.py | 0 lib/allFunctions.py | 202 ++++++++++++++++++++++++++++++++++++++++++++ lib/db.py | 9 ++ lib/utils.py | 82 ++++++++++++++++++ phonebook.py | 46 ++++++++++ requirements.txt | 18 ++++ 10 files changed, 427 insertions(+) create mode 100644 Dockerfile create mode 100644 ReadMe.md create mode 100644 go.sh create mode 100644 gunicorn.conf.py create mode 100644 lib/__init__.py create mode 100644 lib/allFunctions.py create mode 100644 lib/db.py create mode 100644 lib/utils.py create mode 100644 phonebook.py create mode 100644 requirements.txt diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..f723a76 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,6 @@ +FROM python:3.7-alpine + +COPY requirements.txt /app/requirements.txt +WORKDIR /app +RUN pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple +COPY . /app \ No newline at end of file diff --git a/ReadMe.md b/ReadMe.md new file mode 100644 index 0000000..f81e89e --- /dev/null +++ b/ReadMe.md @@ -0,0 +1,53 @@ +# db电话簿 +## 服务器配置 +```js +// 安装venv +python3 -m venv venv +// 启动venv +. venv/bin/activate +// 升级pip +pip install --upgrade pip +// 根据依赖文件安装环境 +pip install -r requirements.txt +// 设置全局变量 +export FLASK_APP=phonebook.py +export FLASK_ENV=development +flask run --host=0.0.0.0 -p 80 +// 启动永久服务 +gunicorn phonebook:app -c gunicorn.conf.py +// 查看已启动服务 +pstree -ap|grep gunicorn +// 关闭某服务 +kill (pid) +//关闭venv +deactivate +// 创建当前环境的依赖文件 +pip freeze > requirements.txt +``` + +## 错误码 +/login +450:没用手机号登录过教务 +422:账号或者密码错误 +400:数据不合法 +510:数据库查询失败 +511:教务挂了 +512:未注册 +/sign +400:数据不合法 +510:数据库插入失败 +511:已注册过,无需再次注册 +512:数据库查询失败 +/del +400:数据不合法 +510:数据库删除失败 +/update +400:数据不合法 +510:数据库覆写失败 +/get +400:数据不合法 +510:数据库查询失败 +511:用户认证失败 +512:数据库查询失败 + +docker run -it --name beta -v /root/data/phonebook:/app -p 127.0.0.1:6000:80 --network=lacus lacus/flask_env /bin/sh \ No newline at end of file diff --git a/go.sh b/go.sh new file mode 100644 index 0000000..5d16001 --- /dev/null +++ b/go.sh @@ -0,0 +1,3 @@ +#!/bin/sh +echo start +gunicorn phonebook:app -c gunicorn.conf.py \ No newline at end of file diff --git a/gunicorn.conf.py b/gunicorn.conf.py new file mode 100644 index 0000000..80f8002 --- /dev/null +++ b/gunicorn.conf.py @@ -0,0 +1,8 @@ +# 并行工作线程数 +workers = 4 +bind = '0.0.0.0:80' +daemon = True +timeout = 120 +accesslog = './logs/acess.log' +errorlog = './logs/error.log' +autostart = True \ No newline at end of file diff --git a/lib/__init__.py b/lib/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/lib/allFunctions.py b/lib/allFunctions.py new file mode 100644 index 0000000..cde5e07 --- /dev/null +++ b/lib/allFunctions.py @@ -0,0 +1,202 @@ +from utils import randomId, md5Check, connection, signCode +from db import col +import json + +# 前端传来的参数应为 cid, pwd, sign + + +def manageLogin(request): + try: + data = json.loads(request.form['data']) + if not data.__contains__('cid'): + raise Exception + if not data.__contains__('pwd'): + raise Exception + if not data.__contains__('sign'): + raise Exception + if md5Check(data): + # 数据校验通过 + try: + # 查询用户信息 + user_info = col().find_one({'cid': data['cid']}, {"_id": 0}) + if user_info: + # 获取到用户正常返回 + if user_info['pwd'] == signCode(data['pwd']): + return {'user_info': user_info}, 200 + else: + return '账号或密码错误', 422 + else: + # 数据库没有用户信息,查询一下教务 + return connection(data['cid'], data['pwd']) + except Exception as e: + print(e) + return '数据库查询失败', 510 + else: + return '数据不合法', 400 + except Exception as e: + print(e) + return '数据不合法', 400 + +# 前端传来的参数应为 cid, pwd, sign, sex, tel, department, position, adds, qq, wx + + +def manageSign(request): + try: + data = json.loads(request.form['data']) + if not data.__contains__('cid'): + raise Exception + if not data.__contains__('pwd'): + raise Exception + if not data.__contains__('sign'): + raise Exception + if not data.__contains__('sex'): + raise Exception + if not data.__contains__('tel'): + raise Exception + if not data.__contains__('department'): + raise Exception + if not data.__contains__('position'): + raise Exception + if not data.__contains__('adds'): + raise Exception + if not data.__contains__('qq'): + raise Exception + if not data.__contains__('wx'): + raise Exception + if md5Check(data): + # 数据校验通过 + try: + user_info = col().find_one({'cid': data['cid']}, {"_id": 0}) + if user_info: + return '已注册过,无需再次注册', 511 + else: + try: + data.pop('sign') + data['pwd'] = signCode(data['pwd']) + data['openid'] = randomId() + data_cache = data.copy() + # 插入用户信息 + col().insert_one(data) + return {'user_info': data_cache}, 200 + except Exception as e: + print(e) + return '数据库插入失败', 510 + except Exception as e: + print(e) + return '数据库查询失败', 512 + else: + return '数据不合法', 400 + except Exception as e: + print(e) + return '数据不合法', 400 + +# 前端传来的参数应为 openid sign + + +def manageDel(request): + try: + data = json.loads(request.form['data']) + if not data.__contains__('openid'): + raise Exception + if not data.__contains__('sign'): + raise Exception + if md5Check(data): + # 数据校验通过 + try: + col().remove({'openid': data['openid']}) + return 'OK', 200 + except Exception as e: + print(e) + return '数据库删除失败', 510 + else: + return '数据不合法', 400 + except Exception as e: + print(e) + return '数据不合法', 400 + +# 前端传来的参数应为 cid, pwd, sign, sex, tel, department, position, adds, qq, wx, openid, id, name + + +def manageUpdate(request): + try: + data = json.loads(request.form['data']) + if not data.__contains__('cid'): + raise Exception + if not data.__contains__('name'): + raise Exception + if not data.__contains__('id'): + raise Exception + if not data.__contains__('pwd'): + raise Exception + if not data.__contains__('sign'): + raise Exception + if not data.__contains__('sex'): + raise Exception + if not data.__contains__('tel'): + raise Exception + if not data.__contains__('department'): + raise Exception + if not data.__contains__('position'): + raise Exception + if not data.__contains__('adds'): + raise Exception + if not data.__contains__('qq'): + raise Exception + if not data.__contains__('wx'): + raise Exception + if not data.__contains__('openid'): + raise Exception + if md5Check(data): + # 数据校验通过 + try: + data.pop('sign') + data['pwd'] = signCode(data['pwd']) + # 更新用户信息 + col().update({'openid': data['openid']}, data) + return 'OK', 200 + except Exception as e: + print(e) + return '数据库覆写失败', 510 + else: + return '数据不合法', 400 + except Exception as e: + print(e) + return '数据不合法', 400 + +# 前端传来的参数应为 openid, sign + + +def manageGet(request): + try: + data = json.loads(request.form['data']) + if not data.__contains__('openid'): + raise Exception + if not data.__contains__('sign'): + raise Exception + if md5Check(data): + # 数据校验通过 + try: + # 查询用户信息 + user_info = col().find_one( + {'openid': data['openid']}, {"_id": 0}) + if user_info: + user_list = [] + try: + for i in col().find({}, {"sex": 1, "tel": 1, "department": 1, "position": 1, "adds": 1, "qq": 1, "wx": 1, "id": 1, "name": 1}): + if i.get('_id'): + i.pop('_id') + user_list.append(i) + return {'user_list': user_list}, 200 + except Exception as e: + print(e) + return ('数据库查询失败', 512) + else: + return '用户认证失败', 511 + except Exception as e: + print(e) + return '数据库查询失败', 510 + else: + return '数据不合法', 400 + except Exception as e: + print(e) + return '数据不合法', 400 diff --git a/lib/db.py b/lib/db.py new file mode 100644 index 0000000..daac1a0 --- /dev/null +++ b/lib/db.py @@ -0,0 +1,9 @@ +from pymongo import MongoClient +from bson import ObjectId, json_util + +# 获取数据集 +def col(): + # 链接数据库 + conn = MongoClient('mongodb://pb:wO0pM5rD9eP3@mongo:27017/pb') + return conn.pb.user + diff --git a/lib/utils.py b/lib/utils.py new file mode 100644 index 0000000..260a980 --- /dev/null +++ b/lib/utils.py @@ -0,0 +1,82 @@ +import json +import requests +from urllib.parse import quote +import base64 +from bs4 import BeautifulSoup +import random +from hashlib import md5 +import datetime + +# 链接教务获取信息 +def connection(username,password): + try: + s = requests.Session() + # 获取统一身份系统的网页 + r = s.get(url='http://mysso-cust-edu-cn-s.webvpn.cust.edu.cn:8118/cas/login?service=https%3A%2F%2Fwebvpn.cust.edu.cn%2Fauth%2Fcas_validate%3Fentry_id%3D1') + # soup = BeautifulSoup(r.text,'lxml') + soup = BeautifulSoup(r.text,'html.parser') + execution=soup.find_all(name='input')[6]['value'] + formdata={ + 'username':username, + 'password':password, + 'execution':execution, + '_eventId':'submit', + 'geolocation':'' + } + r = s.post(url='http://mysso-cust-edu-cn-s.webvpn.cust.edu.cn:8118/cas/login?service=https%3A%2F%2Fwebvpn.cust.edu.cn%2Fauth%2Fcas_validate%3Fentry_id%3D1',data=formdata) + soup=BeautifulSoup(r.text,'html.parser') + flag = soup.find(name='title') + if(flag.text=="手机号设置"): + return ('没用手机号登陆过教务', 450) + r = s.get(url='http://portal-cust-edu-cn-s.webvpn.cust.edu.cn:8118/custp/index') + soup=BeautifulSoup(r.text,'html.parser') + try: + ip = soup.findAll(name='a')[7]['href'][7:].split("-") + except: + return ('账号或密码错误', 422) + r = s.get(url='http://mysso-cust-edu-cn-s.webvpn.cust.edu.cn:8118/cas/login?service=http://'+ip[0]+'.'+ip[1]+'.'+ip[2]+'.'+ip[3]+':8080/welcome',allow_redirects=False) + ticket = r.headers['Location'][68:] + asp_net_sessionid_param = {'Ticket':ticket,'Url':'http://'+ip[0]+'.'+ip[1]+'.'+ip[2]+'.'+ip[3]+':8080/welcome'} + asp_net_sessionid_param = base64.b64encode(quote(json.dumps(asp_net_sessionid_param)).encode('utf-8')).decode('utf-8') + asp_net_sessionid_param = {'param':asp_net_sessionid_param} + headers = {'Content-Type': 'application/json'} + r = s.post(url='http://'+ip[0]+'-'+ip[1]+'-'+ip[2]+'-'+ip[3]+'-8080-p.webvpn.cust.edu.cn:8118/api/LoginApi/LGSSOLocalLogin?sf_request_type=ajax',data=json.dumps(asp_net_sessionid_param),headers=headers) + data = json.loads(r.content.decode('utf-8')) + student_name = data['data']['StudentDto']['XM'] + student_id = data['data']['StudentDto']['XH'] + return ({'name': student_name, 'id': student_id}, 512) + except Exception as e: + print(e) + return ('教务挂了', 511) + +# 给str签名,用于加密密码 +def signCode(code): + d = str(code) + d = d.replace(' ', '') + md = md5() + md.update(d.encode('utf-8')) + r = md.hexdigest().upper() + return r + +# 生成唯一不重名字符串 +def randomId(): + nowTime = datetime.datetime.now().strftime("%Y%m%d%H%M%S") # 生成当前时间 + randomNum = random.randint(0, 100) # 生成的随机整数n,其中0<=n<=100 + if randomNum <= 10: + randomNum = str(0) + str(randomNum) + uniqueNum = str(nowTime) + str(randomNum) + return uniqueNum + +# MD5 校验 +def md5Check(data): + d = data.copy() + try: + d.pop('sign') + except KeyError: + pass + d = str(d) + d = d.replace(' ', '') + md = md5() + md.update(d.encode('utf-8')) + r = md.hexdigest().upper() + return r == data['sign'] \ No newline at end of file diff --git a/phonebook.py b/phonebook.py new file mode 100644 index 0000000..814126b --- /dev/null +++ b/phonebook.py @@ -0,0 +1,46 @@ +import sys +sys.path.append('./lib') +from flask import Flask, request, redirect +from flask_cors import CORS +from allFunctions import manageLogin, manageSign, manageUpdate, manageDel, manageGet + +app = Flask(__name__) +CORS(app, resources=r'/*') + +# 测试 +@app.route('/v1/') +def base(): + return 'Hello! Glad to serve you.' + +# 登录 +@app.route('/v1/login', methods=['POST']) +def login(): + return manageLogin(request) + +# 注册 +@app.route('/v1/sign', methods=['POST']) +def sign(): + return manageSign(request) + +# 修改 +@app.route('/v1/update', methods=['POST']) +def update(): + return manageUpdate(request) + +# 获取 +@app.route('/v1/get', methods=['POST']) +def get(): + return manageGet(request) + +# 删除 +@app.route('/v1/del', methods=['POST']) +def dele(): + return manageDel(request) + +# 访问拦截器转发到根路由 +@app.errorhandler(404) +def miss(e): + return redirect('/v1/') + +if __name__ == '__main__': + app.run(host="0.0.0.0", debug=True, port="80") diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..7248c1a --- /dev/null +++ b/requirements.txt @@ -0,0 +1,18 @@ +beautifulsoup4==4.8.2 +bs4==0.0.1 +certifi==2019.11.28 +chardet==3.0.4 +Click==7.0 +Flask==1.1.1 +Flask-Cors==3.0.8 +gunicorn==20.0.4 +idna==2.8 +itsdangerous==1.1.0 +Jinja2==2.10.3 +MarkupSafe==1.1.1 +pymongo==3.10.1 +requests==2.22.0 +six==1.14.0 +soupsieve==1.9.5 +urllib3==1.25.7 +Werkzeug==0.16.0