使用 Python 全栈实现桌面图形程序的用户认证

2021-06-21 19:58:50 浏览数 (1)

使用 Python 编写桌面图形界面程序之后,我们一般是直接使用 Pyinstaller 之类的工具打包成二进制文件,然后提供下载供用户使用。

这样做很方便,用户直接下载打开就可以使用了。但是同时也带来了一个风险,也就是软件传播的风险(如果程序涉及到一定的权限私密性的话)。

如何避免这种情况呢,一般是在服务器新起一个服务器认证后端,为程序添加一个用户认证的过程,如果用户认证不通过,则禁止登录,这也是很多 IM 产品的逻辑。

我们通过一个 Django 后端服务 和 PyQt5 来简单实现一下。

桌面客户端的实现

首先,创建一个桌面主窗口和一个登录窗口:

代码语言:javascript复制
class LoginWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super(LoginWindow, self).__init__()
        self.setWindowTitle("登录 - 公众号:州的先生")
        self.setFixedWidth(500)
        self.main_widget = QtWidgets.QWidget()
        self.main_layout = QtWidgets.QFormLayout()  # 表单布局层
        self.main_widget.setLayout(self.main_layout)
        self.username_input = QtWidgets.QLineEdit()  # 用户名输入框
        self.pwd_input = QtWidgets.QLineEdit()  # 密码输入框
        self.pwd_input.setEchoMode(QtWidgets.QLineEdit.Password)  # 密码输入框设置文本显示*号
        self.login_btn = QtWidgets.QPushButton("登录")  # 登录按钮
        self.login_btn.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
        self.main_layout.addRow(self.tr("&用户名:"), self.username_input)
        self.main_layout.addRow(self.tr("&密码:"), self.pwd_input)
        self.main_layout.addRow(self.login_btn)
        self.setCentralWidget(self.main_widget)

运行它,我们可以得到一个简单的登录窗口,如下图所示:

下面,我们再创建一个主窗口,当登录成功之后,程序自动切换到这个主窗口上:

代码语言:javascript复制
class MainWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.setWindowTitle("主窗口 - 公众号:州的先生")
        self.setFixedSize(500,300)

接着在 LoginWindow 类里面新增一个名为 login 的方法,作为「登录」按钮点击的处理槽函数:

代码语言:javascript复制
    # 登陆验证
    def login(self):
        username = self.username_input.text()
        pwd = self.pwd_input.text()
        if username == '' or pwd == '':
            error_box = QtWidgets.QMessageBox()
            error_box.setWindowTitle("出错!")
            error_box.setIcon(QtWidgets.QMessageBox.Warning)
            error_box.setText("请输入用户名或密码")
            error_box.exec_()
        else:
            self.main_window = MainWindow()
            self.main_window.show()
            self.close()

在这里,我们只是对用户名和密码进行了简单的非空验证,如果存在空输入,则弹出错误提示款;如果都有输入,则切换到主窗口。

我们再将「登录」按钮的点击信号绑定到这个方法上:

代码语言:javascript复制
self.login_btn.clicked.connect(self.login)  # 绑定登录按钮点击信号

现在运行,可以看到实际的效果:

这样,我们在桌面客户端程序上的功能已经完成了。

后端认证系统的实现

下面,我们实现一个后端用户系统,用来验证桌面客户端程序输入而来的用户名密码是否正确。

新建一个Django项目

因为 Django 自带了一个强大的用户认证系统,所以我们直接使用它来作为我们桌面客户端程序的后端认证系统。

创建一个 Django 项目和 APP 应用:

生成和执行数据库迁移:

创建一个超级用户,用来管理后台:

启动开发服务器,可以发现系统已经运行正常了,我们进入到 Django 自带的强大后台管理界面:

我们在用户里面可以看到之前创建的超级用户:

创建一个用户登录的视图函数

有了后端的用户认证系统,我们继续在 Django 项目里面创建一个视图函数,用于接收客户端程序传输过来的用户名密码并进行验证。

打开 /qt_login_backend/app_auth/views.py 文件,在里面输入如下代码:

代码语言:javascript复制
@csrf_exempt
def auth(request):
    if request.method == 'POST':
        username = request.POST.get('username','')
        password = request.POST.get('password','')
        if username != '' and password != '':
            user = authenticate(username=username, password=password)
            if user is not None:
                if user.is_active:
                    login(request, user)
                    return JsonResponse({'status':True})
                else:
                    return JsonResponse({'status':False,'data':'用户被禁用'})
            else:
                errormsg = '用户名或密码错误!'
                return JsonResponse({'status':False,'data':errormsg})
        else:
            errormsg = '用户名或密码未输入!'
            return JsonResponse({'status':False,'data':errormsg})
    else:
        return JsonResponse({'status':False,'data':'方法不允许'})

然后在 /qt_login_backend/qt_login_backend/urls.py 文件中添加路由映射:

代码语言:javascript复制
from app_auth import views
urlpatterns = [
    path('admin/', admin.site.urls),
    path('auth/',views.auth),
]

桌面程序代码添加登录请求

完成了后端认证系统的构建之后,我们在图形界面程序代码里面对登录的槽函数进行一下修改,使用户输入的用户名和免能够发送到后端认证系统上进行认证,代码如下:

代码语言:javascript复制
    # 登陆验证
    def login(self):
        username = self.username_input.text()
        pwd = self.pwd_input.text()
        if username == '' or pwd == '':
            error_box = QtWidgets.QMessageBox()
            error_box.setWindowTitle("出错!")
            error_box.setIcon(QtWidgets.QMessageBox.Warning)
            error_box.setText("请输入用户名或密码")
            error_box.exec_()
        else:
            auth_url = 'http://127.0.0.1:8000/auth/'
            r = requests.post(auth_url,
                              data={'username': username, 'password': pwd},
                              timeout=10
                              )
            if r.status_code == 200:
                resp_data = r.json()
                if resp_data['status']:
                    self.main_window = MainWindow()
                    self.main_window.show()
                    self.close()
                else:
                    error_box = QtWidgets.QMessageBox()
                    error_box.setWindowTitle("出错!")
                    error_box.setIcon(QtWidgets.QMessageBox.Warning)
                    error_box.setText(resp_data['data'])
                    error_box.exec_()
            else:
                error_box = QtWidgets.QMessageBox()
                error_box.setWindowTitle("出错!")
                error_box.setIcon(QtWidgets.QMessageBox.Warning)
                error_box.setText('登录出错')
                error_box.exec_()

这样,我们就完成了改造。下面来测试一下:

后续

除了验证用户,也有一部分的桌面程序是需要绑定机器进行限制的,如何实现这种限制功能呢?

0 人点赞