Понадобилось сделать подтверждение авторизации через SMS… Подумалось, и придумалось вот что:
Переделывать полностью систему авторизации было влом, да и вообще хотелось бы обойтись «малой кровью», поэтому решено было переделать django’ый декоратор login_required — чтобы не только спрашивал логин и пароль, но и активировал сессии через SMS
Идея будет такая:
- В профиле пользователя сохраняем телефон и флаг использования SMS-авторизации.
- Переписываем декоратор login_required, чтобы после авторизации отправлял на активацию сессии
- Заменяем во всех views from django.contrib.auth.decorators import login_required на наш декоратор
- ???
- PROFIT!
Использован вот этот SMS-шлюз (ибо как-то уже пробовал и работает)
модуль для работы с онным скачать тут
В коде реализована только идея, допиливать можно (и нужно!)
для работы добавить url (/accounts/code) на вьюшку code_prompt
/accounts/code-error — ошибка отправки SMS
добавить шаблон «registration/code.html» — из него методом POST на ту же view отправляется «code» с правильным ответом.
добавить поля в профиль пользователя: phone с телефоном формата 7XXXXXXXXXX и флаг use_sms (boolean)
ну и заменить XXXXXXXXXXXXYYYYYYYYYYYYZZZZZZZZXXXXXXXXXXXXYYYYYYYYYYYYZZZZZZZZ на API_KEY
Что плохо:
- нет задержки перед отправкой SMS — небольшой скриптик опустошит баланс легко и непринужденно (если логин и пароль пользователя известен)
- не учитывается количество отправленных смс
- сохраняется, но не используется время активации сессии
переменные сессии
code — случайный код
code_date — время генерации кода (действителен 300 секунд, при повторном обращении к send_code через 180 и более секунд, после генерации создается новый)
active — дата активации сессии
Но в качестве отправной точки думается хорошо
from django.contrib.auth.decorators import login_required as base_login_required from django.http import HttpResponse, HttpResponseRedirect from django.shortcuts import render_to_response from django.template import RequestContext from django.contrib import auth import datetime import smspilot import random def login_required(function=None,*args,**kwargs): def decorator(function,*args_d,**kwargs_d): def wrapper(request,*args, **kwargs): base = base_login_required(function,*args_d,**kwargs_d) result = base(request,*args,**kwargs) if request.user.is_authenticated(): if request.user.get_profile().use_sms: if not 'active' in request.session: if send_code(request): return HttpResponseRedirect("/accounts/code) else: return HttpResponseRedirect("/accounts/code-error") return result return wrapper if function: return decorator(function,*args,**kwargs) return decorator def generate_code(): random.seed() return str(random.randint(10000,99999)) def send_code(request): user = request.user session = request.session gen_code = True code = "" if 'code' in session and 'code_date' in session: if int((datetime.datetime.now() - session['code_date']).total_seconds())< 180: code = session['code'] gen_code=False if gen_code: session["code"] = generate_code() session["code_date"] = datetime.datetime.now() code = session["code"] send = True if send: sms = smspilot.Sender("XXXXXXXXXXXXYYYYYYYYYYYYZZZZZZZZXXXXXXXXXXXXYYYYYYYYYYYYZZZZZZZZ") sms.addSMS(user.get_profile().phone,u"Код доступа: %s" % code, u'InfoSerivce') result = sms.send() try: if int(result['send'][0]['status']) == 0: return True except: pass return False else: print "Phone: %s, code %s" %(user.get_profile().phone, code) return True def check_code(code,user,session): if 'code' in session and 'code_date' in session: if int((datetime.datetime.now() - session['code_date']).total_seconds())< 300: if code==session["code"]: return True if 'code'in session: del session['code'] if 'code_date' in session: del session['code_date'] return False @base_login_required() def code_prompt(request): if request.POST: if check_code(request.POST['code'], request.user,request.session): request.session['active'] = datetime.datetime.now() return HttpResponseRedirect("/") else: if 'active' in request.session: del request.session['active'] auth.logout(request) return HttpResponseRedirect("/") return render_to_response("registration/code.html", {}, RequestContext(request)) |