обо мне
Небольшой программист в мире программирования, в настоящее время работает руководителем команды в предпринимательской команде.Стек технологий включает Android, Python, Java и Go, который также является основным стеком технологий нашей команды.
Гитхаб:github.com/hylinux1024
Публичный аккаунт WeChat: гневный код
В этом разделе начинается кодирование реализации проекта. Сначала реализуемвойти Зарегистрироватьсясвязанный с модулемAPI
. В этом проекте мы используемПереднее и заднее разделениережиме, прежде чем реализовать функцию входа и регистрации, предполагая, что наш интерфейс открыт, нам нужно определитьСхема проверки интерфейса.
0x00 Схема проверки интерфейса
Наша цель — взаимодействиеПовторный доступ к перехваченным пакетам невозможен., и кНадежность клиентааутентификация.
-
Атака против повтораДоступные параметры:
timestamp
,nonce
,token
иsign
-
Поддержка доверенных клиентовзапросы могут рассмотреть возможность добавления
appkey
иappsecret
параметр
общедоступный параметр
1,timestamp
отметка времени
Единица измерения — миллисекунды или секунды, в зависимости от сервера;
Временные метки не зависят от часового пояса, поэтому для сравнения можно использовать временные метки клиента и сервера;
Если отметка времени между клиентом и сервером сильно различается, вы можете рассмотреть возможность использования времени сервера для калибровки;
Роль метки времени заключается в том,Гарантировать, что запрос действителен в течение определенного периода времени (например, в течение 60 секунд). Требуется верификация в течение срока действияnonce
параметр
2,nonce
случайный номер
Случайное число, сгенерированное клиентом, клиент должен убедиться, что оно отличается каждый раз, когда запрашивает интерфейс.
nonce
рольгарантироватьtimestamp
Является ли запрос в течение срока действия законным.
После того, как сервер получит этот параметр, он сохранит его в коллекции. Сервер обнаружит этоnonce
Появился ли он в этом наборе, если есть, значит, запрос неправомерный.
каждыйnonce
Срок годности устанавливается сtimestamp
Связанный параметр, например, его можно установить на 60 секунд.
3.token
состояние входа
Интерфейс, требующий входа в систему, требуетtoken
параметр.
генерируется серверомtoken
Действует в течение срока действия, еслиtoken
Если срок его действия истекает, клиенту необходимо снова войти в систему.token
Правила генерации могут использовать случайные числа
token = md5(1024位的随机数)
4.sign
Параметры подписи или проверки
msg = 除了timestamp、nonce、token、sign参数之外的其它排序后的参数列表和值列表 = sort(参数1=值1&参数2=值2&参数3=值3...)
sign = md5(msg+token+timestamp+nonce+salt)
salt = 客户端与服务端约定字符串
5.appkey
иappsecret
Сервер назначает доверенных клиентовappkey
иappsecret
параметр. Он может быть сгенерирован случайными числами или пользовательскими правилами, чтобы гарантировать, чтоappkey
иappsecret
соответствует.
Клиент должен гарантироватьappsecret
не протекал.
Просто принесите его, когда клиентский интерфейс запроситappkey
параметр.appsecret
затем добавить кsign
Расчет параметров проверки
sign = md5(token+msg+timestamp+nonce+appsecret)
В сочетании с вышеуказанными параметрами запрос интерфейса должен выглядеть так
http://api.example.com/v1/login?phone=13499990000×tamp=1564486841415&nonce=34C2AF&sign=e10adc3949ba59abbe56e057f20f883e&appkey=A23CE80D
После того, как серверная программа получит запроспроцесс проверкидолжно быть так
- пройти через
appkey
Запрошеноappsecret
, если не найдено, вернуть сообщение об ошибке, иначе продолжить; - пройти через
timestamp
экзаменnonce
Является ли это повторным запросом в течение допустимого времени, если он повторяется несколько раз, вернуть сообщение об ошибке, в противном случае продолжить; - Создается по параметрам запроса
msg
и рассчитатьsign
, сравним этот параметр с параметрами, полученными в запросе, и после успешной проверки запустим нашу бизнес-логику.
Таким образом, у нас есть простая и практичная схема проверки интерфейса.Конечно, могут быть и другие хорошие идеи.Добро пожаловать, чтобы оставить сообщение для обсуждения и совместного изучения.
0x01 show me the code
Теперь начните реализовывать функцию входа и регистрации, я думаю, что этот модуль пройдет, и другие модули будут следовать той же схеме.
Первый взгляд на модуль
├── api
│ ├── __init__.py
│ └── auth.py
├── app.py
├── config.ini
├── datingtoday.sql
├── models.py
├── requirements.txt
├── test
└── venv
добавил одинapi
сопутствующие пакеты.
есть еще одинconfig.ini
, в основном используется для настройки базы данных и другой информации, иmodels.py
В файлах определяются классы сущностей.
api/__init__.py
from flask import jsonify
def make_response_ok(data=None):
resp = {'code': 0, 'msg': 'success'}
if data:
resp['data'] = data
return jsonify(resp)
def make_response_error(code, msg):
resp = {'code': code, 'msg': msg}
return jsonify(resp)
def validsign(func):
"""
验证签名
:param func:
:return:
"""
def decorator():
params = request.form
appkey = params.get('appkey')
sign = params.get('sign')
csign = signature(params)
if not appkey:
return make_response_error(300, 'appkey is none.')
if csign != sign:
return make_response_error(500, 'signature is error.')
return func()
return decorator
существует__init__.py
Первый определяет два инкапсулированных унифицированныхjson
В основном используется метод структуры данных.flask
серединаjsonify
функция, которая преобразует объект вjson
.
Ранее мы говорили о логике проверки интерфейса, функция проверки параметров в этой части на самом деле универсальна, поэтому эта логика также инкапсулирована вvalidsign
метод.
Да, это определение декоратора. Мы надеемся использовать декоратор в методе, к которому обращается интерфейс, чтобы мы могли выполнять общую проверку интерфейса.
auth.py
Основное внимание в этом разделе уделяется реализации интерфейса регистрации входа и текстовых сообщений, поэтому создайтеauth.py
файл для написания интерфейса, связанного с авторизованным входом в систему, что поможет нам организовать код.
Мы знаем, что определение пути доступа для реализации интерфейса напрямую соответствует методу, который следует использовать.@route
этот декоратор. Здесь мы определяем наш интерфейс в новом файле, нам нужно использоватьBlueprint
A blueprint is an object that allows defining application functions without requiring an application object ahead of time. It uses the same decorators as Flask, but defers the need for an application by recording them for later registration.
Грубо говоря, его действие связано с@route
почти.
Так как мы реализуем логин и регистрацию как интерфейс, то есть пользователь авторизуется через смс, а бэкэнд будет определять, является ли пользователь новым пользователем, и если это новый пользователь, то он автоматически зарегистрируется.
0x02 СМС-интерфейс
Сначала определите путь доступа интерфейса как
{host:port}/api/auth/sendsms
请求方法:POST
参数:phone
请求成功
{
"code": 0,
"data": {
"code": "97532",
"phone": "18922986865"
},
"msg": "success"
}
В соответствии с определением интерфейса мы будемauth.py
определитьBlueprint
Объекты используются для отображения наших путей и методов доступа.
bp = Blueprint("auth", __name__, url_prefix='/api/auth')
Здесь будет использована реализация интерфейса SMSredis
, сохраните запрошенный SMS-код подтверждения вredis
, и установите время истечения срока действия. Затем, когда вы войдете в систему, подтвердите снова.
@bp.route("/sendsms", methods=['POST'], endpoint="sendsms")
@validsign
def send_sms():
phone = request.form.get('phone')
m = re.match(pattern_phone, phone)
if not m:
return make_response_error(300, 'phone number format error.')
# 这里需要修改为对接短信服务
code = '97532'
key = f'{phone}-{code}'
r.set(key, code, 60)
return make_response_ok({'phone': phone, 'code': code})
Обратите внимание здесьendpoint="sendsms"
требуется, потому что@validsign
Модифицируем наш метод, каждый метод использует общую проверку, имя метода станет одинаковым, поэтому если не установитьendpoint
приведет кurl
Ошибка сопоставления.
0x03 Интерфейс регистрации входа
Сначала определите путь доступа интерфейса как
{host:port}/api/auth/login
请求方法:POST
参数:phone
参数:code
请求成功
{
"code": 0,
"data": {
"expire_time": "2019-08-10 07:34:20",
"token": "5bea89727e7553284f162d35c9926414",
"user_id": 100784
},
"msg": "success"
}
При выполнении интерфейса входа он сначала проверитredis
в коде подтверждения, а затем проверьте форму авторизацииuser_auth
Посмотрите, является ли это новым пользователем, и, наконец, верните информацию об авторизации входа пользователя.
@bp.route("/login", methods=['POST'], endpoint='login')
@validsign
def login():
phone = request.form.get('phone')
code = request.form.get('code')
key = f'{phone}-{code}'
sms_code = r.get(key)
if sms_code:
sms_code = sms_code.decode()
if code != sms_code:
return make_response_error(503, 'sms code error')
auth_info = UserAuth.query.filter_by(open_id=phone).first()
if not auth_info:
auth_info = register_by_phone(phone)
else:
auth_info = login_by_phone(auth_info)
data = {'token': auth_info.token,
'expired_time': auth_info.expired_time.strftime("%Y-%m-%d %H:%M:%S"),
'user_id': auth_info.user_basic.id}
r.set(f'auth_info_{auth_info.user_id}', str(data))
return make_response_ok(data)
В общем, логика относительно ясна. Наконец, давайте посмотримapp.py
from flask import Flask
from api import auth, config
from models import db
app = Flask(__name__)
# 将blueprint注册到app中
app.register_blueprint(auth.bp)
# 配置app的config,将数据库信息配置好
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config["SQLALCHEMY_DATABASE_URI"] = config['DATABASE']['uri']
# 最好生成一个secret_key
app.secret_key = '8c2c0b555e6e6cb01a5fd36dd981bcee'
db.init_app(app)
@app.route('/')
def hello_world():
return 'Hello World!'
if __name__ == '__main__':
app.run()
конфигурационный файлconfig.ini
# 配置数据库链接
[DATABASE]
uri = mysql+pymysql://user:password@127.0.0.1:3306/datingtoday
# 配置appkey和secret
[APP]
appkey = 432ABZ
appsecret = 1AE32B09224
Модульный тест 0x04
Модульное тестирование необходимо, поскольку интерфейс должен динамически вычислять контрольную сумму. Здесь я использую самый простой способ, напрямую используюunittest
модуль.
Например, чтобы протестировать бизнес-интерфейс для отправки текстовых сообщений, сначала сгенерируйте случайное число.nonce
, а затем вычислить контрольную суммуsign
параметры, последний вызовflask
серединаpost
Метод имитирует запрос интерфейса.
def test_sendsms(self):
import math
nonce = math.floor(random.uniform(100000, 1000000))
params = {'phone': '18922986865', 'appkey': '432ABZ', 'timestamp': datetime.now().timestamp(),
'nonce': nonce}
sign = signature(params)
params['sign'] = sign
respdata = self.app.post("/api/auth/sendsms", data=params)
resp = respdata.json
self.assertEqual(resp['code'], 0, respdata.data)
Если запрос выполнен успешно, тест считается пройденным. Конечно, логика здесь относительно проста, и я надеюсь, что вы сможете оставить сообщение для обсуждения.
0x05 Адрес проекта
Адрес источника:
GitHub.com/ и Linux1024…
Официальный адрес фляги:
palletsprojects.com/p/flask/
Обратите внимание, что в этой статье будет использоватьсяmysql
иredis
базу данных, вам нужно установить ее самостоятельно.