zoukankan      html  css  js  c++  java
  • flask登录的大型应用初探

    0.所谓“大型应用”是指falsk官网(https://dormousehole.readthedocs.io/en/latest/patterns/packages.html) 大型应用 篇的说法,并不是我要构建一个基于 登录的大型工程项目。

    1.前面几次,从前端到后端再到数据库都做了简单尝试,目前已经能做到前后端互联互通了。一个上道儿的程序员应该有一定程度的代码洁癖和归纳癖。本次就尝试依照flask提供的推荐方法,归拢一下往日的后端代码。

    2.下面几篇文章必须仔细阅读。归拢方法都源于此:
      2.1 应用上下文:https://dormousehole.readthedocs.io/en/latest/appcontext.html
      2.2 大型应用:https://dormousehole.readthedocs.io/en/latest/patterns/packages.html
      2.3 使用 Setuptools 部署: https://dormousehole.readthedocs.io/en/latest/patterns/distribute.html 
      2.4 官方示例代码:https://github.com/pallets/flask/tree/1.1.1/examples/tutorial

    3. 核心代码没有做过调整。仅仅是创建了setup.py文件 然后将原有代码移入hellologin目录下,改造后的目录结构如下:

    4.下面把所有代码重新粘贴出来:

    from setuptools import setup
    
    setup(
        name='hellologin',
        version='1.0',
        long_description=__doc__,
        packages=['hellologin'],
        include_package_data=True,
        zip_safe=False,
        install_requires=['Flask']
    )
    setup.py
    import os
    from flask import Flask, jsonify
    from flask_cors import CORS
    
    def create_app(test_config=None):
        app = Flask(__name__)
        app.secret_key =b'x15fx07xd3xd9xbf*x82xd1xe6xb4xf2x95xddx8fx12'
        #命令行中运行后拷贝出随机值  python -c "import os; print(os.urandom(16))"
    
        CORS(app,supports_credentials=True)
    
        @app.route('/hello')
        def helloworld():
            returnData = {'code': 0, 'msg': 'success', 'data': 'hello world' }
            return jsonify(returnData),200
    
        app.add_url_rule("/", endpoint="helloworld")    
        
    
        with app.app_context():
            from hellologin import login
            app.register_blueprint(login.login_page)
    
        return app
    __init__.py
    from sqlalchemy import create_engine
    from sqlalchemy.orm import scoped_session, sessionmaker
    from sqlalchemy.ext.declarative import declarative_base
    
    engine = create_engine("mysql+mysqlconnector://root:123@localhost:3306/hello_login", encoding="utf-8",echo=True)
    db_session = scoped_session(sessionmaker(autocommit=False,autoflush=False,bind=engine))
    
    Base = declarative_base()
    Base.query = db_session.query_property()
    
    def init_db():
        # 在这里导入定义模型所需要的所有模块,这样它们就会正确的注册在
        # 元数据上。否则你就必须在调用 init_db() 之前导入它们。
        import models
        Base.metadata.create_all(bind=engine)
    database.py
    import jwt
    from jwt import PyJWTError
    from datetime import datetime,timedelta
    
    SECRECT_KEY = b'x92R!x8exc6x9cxb3x89#xa6x0cxcbxf6xcbxd7xbc'
    
    
    def genToken(exp,data):
      payload = {
        'exp': exp,
        'data': data 
        }
      token = jwt.encode(payload,key= SECRECT_KEY,algorithm= 'HS256')
      return bytes.decode(token)
    
    def verfiyToken(tokenStr):
      try:
        tokenBytes =  tokenStr.encode('utf-8')
        payload = jwt.decode(tokenBytes,key= SECRECT_KEY,algorithm= 'HS256')
        return payload
      except PyJWTError as e:
        print("jwt验证失败: %s" % e)
        return None
    jwt_token.py
    import time
    import json
    from flask import Blueprint,request,current_app,jsonify
    from flask_login import LoginManager,login_user,logout_user,login_required,current_user
    from hellologin.user import UserLogin
    
    login_page = Blueprint('login_page',__name__)
    
    login_manager = LoginManager()
    login_manager.login_view = None
    login_manager.init_app(current_app)
    
    
    
    @login_manager.request_loader
    def load_user_from_request(request):
      token = request.headers.get('Authorization')
      if token == None:
        return None
    
      payload = UserLogin.verfiyUserToken(token)
      if payload != None:
        alternativeID = payload['data']['alternativeID']
        sessionID = payload['data']['sessionID']
        user = UserLogin.queryUser(alternativeID=alternativeID,sessionID=sessionID)
      else:
        user = None
      return user
    
    @login_page.route('/hello2')
    def hello():
        returnData = {'code': 0, 'msg': 'success', 'data': 'hello login' }
        return jsonify(returnData),200
    
    @login_page.route('/first')
    @login_required
    def firstPage():
      returnData = {'code': 0, 'msg': 'success', 'data': {'token':current_user.token,'tips':'First Blood(来自' + current_user.userName +')'}}
      return returnData,200
    
    @login_page.route('/login', methods=['POST'])
    def login():
      if request.method == 'POST':
        username = request.form['username']
        password = request.form['password']
        user = UserLogin.queryUser(userName = username)
        if (user != None) and (user.verifyPassword(password)) :
          login_user(user)
          returnData = {'code': 0, 'msg': 'success', 'data': {'token':user.token}}
          return json.dumps(returnData),200
        else :
          returnData = {'code': 1, 'msg': 'failed', 'data': {'tips':'username or password is not correct'} }
          return json.dumps(returnData),200  
    
    @login_page.route('/logout') 
    @login_required
    def logout():
      userName = current_user.userName
      # alternativeID = current_user.alternativeID
      sessionID = current_user.sessionID
      UserLogin.dropSessionID(sessionID)
      logout_user()
      returnData = {'code': 0, 'msg': 'success', 'data': {'tips':'Bye ' + userName} }
      return json.dumps(returnData),200  
    
    
    @login_page.route('/changepws') 
    @login_required
    def changePws():
      user = UserLogin.queryUser(userID = current_user.id)  
      user.changePws()
      returnData = {'code': 0, 'msg': 'success', 'data': {'tips':'password was changed'} }
      return json.dumps(returnData),200  
    login.py
    from sqlalchemy import ForeignKey,Column, Integer, String, Date,BigInteger,DateTime
    from sqlalchemy.orm import relationship,backref
    from werkzeug.security import check_password_hash,generate_password_hash
    from hellologin.database import Base
    import uuid
    
    
    class Operator(Base):
        __tablename__ = 'operators'
        id = Column(Integer, primary_key=True)
        username = Column(String(50), unique=True,nullable=False)
        password = Column(String(200), unique=False,nullable=False)
        alternativeID = Column(String(200), unique=True,nullable=False)
    
        def __init__(self, id=None,username=None, password=None):
            self.id = id
            self.username = username
            self.password = generate_password_hash(username + password) #加盐加密函数,通过随机产生不同salt(盐粒)混入原文,使每次产生的密文不一样。
            self.alternativeID = str(uuid.uuid1())
    
        def __repr__(self):
            return '<operator %r>' % (self.username)
    
        def verifyPassword(self,password=None):
            if password is None:
                return False
            return check_password_hash(self.password,self.username + password)
    
    
    class OperatorSession(Base):
        __tablename__ = 'oper_sessions'
        id = Column(Integer, primary_key=True)
        sessionID = Column(String(200), nullable=False)
        exp_utc = Column(DateTime, nullable=False)
        operator_id = Column(Integer, ForeignKey('operators.id'),nullable=False)
        operator = relationship(Operator,backref=backref('operators',
                             uselist=True,
                             cascade='delete,all'))
    
    # class OperatorSession(Base):
    #     __tablename__ = 'oper_sessions'
    #     # __table_args__ = {'prefixes': ['TEMPORARY']}
    #     id = Column(Integer, primary_key=True)
    #     sessionID = Column(String(200), nullable=False)
    #     exp_utc = Column(DateTime, nullable=False)
    #     operator_id = Column(Integer, ForeignKey('operators.id'),nullable=False)
    
    #     def __repr__(self):
    #         return 'seesionID: %r' % self.seesionID
    models.py
    import uuid
    from flask_login import UserMixin
    from werkzeug.security import check_password_hash,generate_password_hash
    from datetime import datetime,timedelta,time
    from hellologin.jwt_token import genToken,verfiyToken
    from hellologin.database import init_db,db_session
    from hellologin.models import Operator,OperatorSession
    
    class UserLogin(UserMixin):#之所以命名为UserLogin,只是为了区分数据库的User表。此类仅仅是为了登陆管理而存在
      def __init__(self,operater,sessionID=None):
        self.id = operater.id
        self.userName = operater.username
        self.alternativeID = operater.alternativeID
        self.oper = operater
        self.sessionID = None
        self.token = None
        exp = datetime.utcnow() + timedelta(seconds=60)
        self.genSessionID(exp,sessionID)
        self.genToken(exp)
    
      def get_id(self):
        return self.id
    
      def get(user_id):
        if not user_id:
          return None
        user = Operator.query.filter_by(id= userID).first()
        return UserLogin(user)
    
      def verifyPassword(self,password=None):
        return self.oper.verifyPassword(password)
    
      def genSessionID(self,exp,sessionID=None):
        if sessionID == None:
          self.sessionID = str(uuid.uuid4())
          os = OperatorSession(sessionID= self.sessionID,exp_utc= exp,operator= self.oper)
          db_session.add(os)
          # self.oper.sessions.append(OperatorSession(sessionID= self.sessionID,exp_utc= exp))
          db_session.commit()
        else:            
            self.sessionID = sessionID
    
      def genToken(self,exp):
        token = genToken(exp,{'alternativeID':self.alternativeID,'sessionID':self.sessionID})
        self.token = token
        return token
    
      @staticmethod
      def queryUser(**kwargs):
        if 'userName' in kwargs:
          username = kwargs['userName']
          user = Operator.query.filter_by(username = username).first()
          return UserLogin(user)
        elif ('alternativeID' in kwargs) and ('sessionID' in kwargs):
          alternativeID = kwargs['alternativeID']
          sessionID = kwargs['sessionID']
          user = db_session.query(Operator).filter_by(alternativeID=alternativeID).join(OperatorSession).filter_by(sessionID=sessionID).first()
          if user:
            return UserLogin(user,sessionID)
          else:
            return None
    
      @staticmethod
      def verfiyUserToken(token):
        payload = verfiyToken(token)
        if not payload :
          removeSessions = db_session.query(OperatorSession).filter(OperatorSession.exp_utc < datetime.utcnow()).delete(synchronize_session=False)
          db_session.commit()
    
        return payload
    
      @staticmethod
      def dropSessionID(sessionID):
        removeSession = db_session.query(OperatorSession).filter_by(sessionID=sessionID).first()
        db_session.delete(removeSession)
        db_session.commit()
    user.py

    5.运行以上代码,当然依然需要在venv中(之所以改成flask run 运行,是由于此文章:https://dormousehole.readthedocs.io/en/latest/server.html  此文说明了两者差别,不过目前本人还没体会到优势。)
      5.1 venvscriptsactivate
      5.2 $env:FLASK_APP="hellologin"
      5.3 flask run

    6.其实已经能顺利和前端代码关联。而且前端不需要任何调整。

    7.接下来,需要认真体会模块后的好处。到目前为止,我没有感受到任何优势。还需要再理解几遍官方文档。

  • 相关阅读:
    dig理解dns主备
    Bind的DNS解析设置forward
    DNS服务器的配置与应用: BIND9 的安装与配置
    注意自己的dns设置
    /etc/named/named.conf.options中的Options参数
    安装Bind过程中提示丢失MSVCR110.dll的解决办法
    MS14-025引起的问题
    MS14-025引起的问题
    MS14-082引起的问题
    WSUS更新服务器
  • 原文地址:https://www.cnblogs.com/yaoshi641/p/13448007.html
Copyright © 2011-2022 走看看