tornado websocket implements QR code scanning

Websocket realizes scanning QR code login

First of all, let’s briefly talk about the steps of QR code login:
1. The web page requests a QR code from the server, and the server generates a QR code and provides it to the web page; this QR code is actually an address
2. The client passes Scan the QR code on the web page, jump to the address pointed to by the QR code, and then notify the server of the authentication result through the authentication mechanism.
3. The client informs the web page to take corresponding actions according to different authentication results

After we have a simple concept of steps, we can clearly see that there are two main problems that need to be solved in the whole process:

1. If dealing with the user's authorization mechanism, ensure the security of the user's authority

2. How to make the web page respond in a timely manner according to the user's choice on the client

For the first question, on the one hand, when we generate the QR code, we will generate a unique session_id flag at the same time, and then add it to the url of the QR code. When the client scans the QR code, it jumps to When specifying the address, we can ensure that the QR code is generated by ourselves according to the session_id; on the other hand, when the client jumps over, it will bring the user's id and the user's token at the same time, which is guaranteed by the verification of the id and token. The validity of the user can also be related to the binding of the corresponding session_id and user id to inform the web page which user scanned the QR code.

In addition, let's talk a little more, the user id and user token brought up when the client scans, in actual use, we obtain it through the authentication logic of OAuth2.0 when the user logs in, so we can directly pass the token Verify security.

Then the second question, how to notify the web page to make changes in a timely manner. There are two solutions at the beginning: one is that the web page uses a polling mechanism, and asks the server after a short period of time whether a user has scanned the QR code and the scanning result; the other is that the web page is in When the QR code is requested, a websocket request is initiated, so that the web page and the server can always be connected. After the client scans the QR code, the server can notify the web page in time to achieve real-time.

The advantages and disadvantages of the two are also relatively obvious. In the actual development process, we use the websocket method. There is actually another problem here. After the user scans the QR code on the client and authorizes it, what if the notification is sent to the corresponding web page. When the web page requests the QR code, we will register the current request with the session_id as the key. When the client scans or authorizes, the session_id will be brought up, and the corresponding session_id can be obtained according to the session_id. Request, and then send back the operation result to the web page.

Tornado websocket implements QR code scanning and login

Web page request QR code

@router.Route('qrcode', name='ConnectQRcode')
class ConnectQRcode(tornado.websocket.WebSocketHandler):
    @tornado.gen.coroutine
    def open(self):
         #生成唯一的id,并带在url中
        req_id = id(self)
        url = "https://xxxxxxx?uid=" + uid + "&req_id=" + str(req_id)
        # 根据URL生成对应的二维码
        q = qrcode.main.QRCode()
        q.add_data(url)
        q.make()
        m = q.make_image()
        png_name = "static/" + uid + ".png"
        m.save(png_name)
        # 注册回调函数
        self.application.cart.register(req_id, self.callback)
        # 返回二维码
        self.write_message("https://xxxx/" + png_name)


    def on_close(self):
        # 关闭的时候删除掉注册的回调函数
        self.application.cart.unregister(id(self), self.callback)
        logging.info("remove register")
        logging.info('WebSocket closed')

    def callback(self, event, extra_info):
        # 回调函数具体的操作
        self.write_message(xxxx)

The client scans the QR code

@router.Route('/qrscan', name='qrscan')
class ConnectQRscan(tornado.web.RequestHandler):
    @tornado.gen.coroutine
    def get(self):
        try:
            # 得到本次请求的req_id, 然后根据req_id调用对应的回调函数
            req_id = self.get_argument('req_id', strip=False)
            self.application.cart.notify(req_id, "scan", {'req_id': req_id})
            ret = {}
        except Exception as e:
            logging.error("qrscan error: %s" % e)
            ret = {'errorno': -1, 'errormsg': "Error", 'data': None}
        self.write(ret)

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325722279&siteId=291194637