As a little programmer in my programming field, I am currently working as team lead in an entrepreneurial team. The technology stack involves Android, Python, Java and Go, which is also the main technology stack of our team. Github: github.com/hylinux1024 wechat official account: angryCode

This section begins the coding implementation of the project. First let’s implement the API of login registration module. In this project, we use the mode of front and back end separation. Before implementing the login and registration function, we assume that our interface is open, so we need to determine the interface verification scheme.

0x00 Interface verification scheme

Our goal is to ensure that the interface cannot be accessed repeatedly by captured packets and to verify the reliability of the client.

  • Defend against replay attacksAvailable parameters aretimestamp,nonce,tokensign
  • Support for trusted clientsRequest can be considered to addappkeyappsecretparameter
Public parameters

1.timestampThe time stamp

The value can be seconds, which is the same as that on the server. Timestamps are time zone independent, so client and server timestamps can be compared. If the timestamp difference between the client and server is large, use the server time to calibrate the timestamp. The purpose of a timestamp is to ensure that the request is valid for a certain amount of time, such as 60 seconds. Validation within the validity period requires the nonce parameter

2,nonceThe random number

A random number generated by the client. The client needs to ensure that it is different each time the interface requests it. The nonce function ensures that the request within the timestamp period is valid. When the server receives this parameter, it stores it in a collection. The server checks to see if the nonce is present in the collection, and if so, the request is invalid. The validity period of each nonce is related to the timestamp parameter, for example, 60 seconds.

3,tokenLogin state

The interface that requires login requires a token parameter. The token generated by the server is valid within the validity period. If the token expires, you need to prompt the client to log in again. The token generation rule can use random numbers

Token = MD5 (1024 bits random number)Copy the code

4,signSignature or verification parameters

MSG = timestamp, nonce, token, sign = sort(parameter 1= value 1& parameter 2= value 2& parameter 3= value 3...) Sign = MD5 (MSG +token+timestamp+nonce+salt) salt = the contract character string between the client and the serverCopy the code

5,appkeyappsecret

The server assigns appKey and AppSecret parameters to trusted clients. It can be generated by random numbers or custom rules. Ensure that appKey and AppSecret correspond. The client must ensure that AppSecret is not leaked. Only appKey parameter is required when the client interface requests it. Appsecret is added to the calculation of the sign verification parameter

sign = md5(token+msg+timestamp+nonce+appsecret)
Copy the code

Combined with the above parameters, an interface request should look like this

http://api.example.com/v1/login?phone=13499990000&timestamp=1564486841415&nonce=34C2AF&sign=e10adc3949ba59abbe56e057f20f 883e&appkey=A23CE80DCopy the code

The validation process should look like this when the server receives the request

  1. throughappkeyQuery toappsecretIf no error message is returned, otherwise continue;
  2. throughtimestampchecknonceWhether the request is repeated within the valid time, if the request is repeated several times, return an error message, otherwise continue;
  3. Constructed by request parametersmsgAnd calculate thesignCompare this parameter with the parameter obtained in the request and verify it successfully before starting our business logic.

So we have a simple and practical interface verification scheme, of course, there may be some other good ideas, welcome to leave a message to discuss learning.

0x01 show me the code

Now start to achieve login registration function, I believe that this module went through, after the other modules are also imitate.

Let’s take a look at modules

├ ─ ─ API │ ├ ─ ─ just set py │ └ ─ ─ auth. Py ├ ─ ─ app. Py ├ ─ ─ config. Ini ├ ─ ─ datingtoday. SQL ├ ─ ─ models. Py ├ ─ ─ requirements. TXT ├ ─ ─test└ ─ ─ venvCopy the code

Added an API-related file package. There is also a config.ini, which is used to configure information such as databases, and the models.py file is where entity classes are defined.

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):
    """Verify signature :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.')
        ifcsign ! = sign:return make_response_error(500, 'signature is error.')
        return func()

    return decorator
Copy the code

In __init__.py we first define two methods that encapsulate uniform JSON data structures, mainly using the Flask jsonify function, which converts an object to JSON.

In the previous section, we talked about the validation logic of the interface. The validation function of this part of the parameter is actually universal, so this logic is encapsulated as validSign method.

Yes, this is the definition of a decorator. We want to use decorators in interface access methods to perform generic interface validation.

auth.py

This section focuses on implementing login registration and SMS interfaces, so creating an auth.py file to write interfaces related to authorized login will help us organize our code. We know that the definition of an access path to implement an interface corresponds directly to a method using the @route decorator. Here we define our interface in a new file, using 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.

In plain English, it does the same thing as @route.

Since we implement login registration as an interface, that is, the user logs in via SMS, the back end will determine whether the user is a new user, if it is a new user, it will automatically register.

0x02 SMS interface

The access path of the interface is defined as

{host:port}/ API /auth/sendsms Request method: POST Parameter: phone Request successful {"code": 0."data": {
        "code": "97532"."phone": "18922986865"
    },
    "msg": "success"
}
Copy the code

Based on the interface definition we will define a Blueprint object in Auth.py that maps our access paths and methods.

bp = Blueprint("auth", __name__, url_prefix='/api/auth')
Copy the code

The realization of the SMS interface will use Redis, save the SMS verification code in Redis, and set the expiration time. And then when you log in, you authenticate.

@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.')
    # Here needs to be modified to connect SMS service
    code = '97532'
    key = f'{phone}-{code}'
    r.set(key, code, 60)
    return make_response_ok({'phone': phone, 'code': code})
Copy the code

Note that the endpoint=”sendsms” is required because @validSign decorates our methods. Each method uses a common validation, and the method name will be the same, so url mapping will fail if you don’t set the endpoint.

0x03 Logging In to register Interface

The access path of the interface is defined as

{host:port}/ API /auth/login Request method: POST Parameter: phone Parameter: code The request succeeds {"code": 0."data": {
        "expire_time": "The 2019-08-10 07:34:20"."token": "5bea89727e7553284f162d35c9926414"."user_id": 100784}."msg": "success"
}
Copy the code

When the login interface is executed, the verification code in Redis is first verified, then the authorization table user_Auth is checked to see if it is a new user, and finally the login authorization information of the user is returned.

@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()
    ifcode ! = 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)
Copy the code

Finally, let’s take a look at app.py

from flask import Flask

from api import auth, config
from models import db

app = Flask(__name__)
# Register Blueprint with the app
app.register_blueprint(auth.bp)
# configure the app config to set the database information
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config["SQLALCHEMY_DATABASE_URI"] = config['DATABASE'] ['uri']
Generate a secret_key
app.secret_key = '8c2c0b555e6e6cb01a5fd36dd981bcee'

db.init_app(app)

@app.route('/')
def hello_world(a):
    return 'Hello World! '


if __name__ == '__main__':
    app.run()
Copy the code

Configuration file config.ini

Configure the database link
[DATABASE]
uri = mysql+pymysql://user:password@127.0. 01.:3306/datingtoday

Configure appKey and secret
[APP]
appkey = 432ABZ
appsecret = 1AE32B09224
Copy the code

0x04 Unit Tests

Unit tests are required because interfaces need to compute checksums dynamically. Here I use the simplest way, using the UnitTest module directly.

For example, to test the service interface of sending SMS messages, a random number nonce is generated first, then the verification code sign parameter is calculated, and finally the POST method in the flask is invoked to simulate the interface request.

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)
Copy the code

If the request succeeds, the test is considered passed. Of course, the logic here is still relatively simple, I hope friends leave a message to discuss.

0x05 Project address

Source code address: github.com/hylinux1024…

Flask official address: palletsprojects.com/p/flask/

Note the use in this articlemysqlandredisDatabase, you need to install.