乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      從頭搭建一個(gè)flask鑒權(quán)系統(tǒng)之登陸

       鷹兔牛熊眼 2019-02-13

       從今天開始,準(zhǔn)備從頭開始搭建一個(gè)基于flask的鑒權(quán)系統(tǒng),一點(diǎn)一滴,積累于生活


      從登陸開始


      01.知識(shí)樹


      本文涉及到如下知識(shí)點(diǎn)

      1. flask-login的簡(jiǎn)單使用

      2. 本地鑒權(quán)實(shí)踐

      3. GitHub鑒權(quán)登陸實(shí)踐,flask-github使用

      4. 可擴(kuò)展的表結(jié)構(gòu)設(shè)計(jì)思路


      02.表結(jié)構(gòu)設(shè)計(jì)


      我們首先設(shè)計(jì)一個(gè)User用戶表,里面的字段可以包括username,password,email等用戶信息,大致如下


      usernamepasswordemail
      user1p1user1@gmail.com
      user2p2user2@gmail.com
      user3p3user3@gmail.com


      因?yàn)槲覀冞€會(huì)涉及到第三方登陸,那么為了后面便于擴(kuò)展,再設(shè)計(jì)一張表,就命名為ThirdAuth,里面可以包括user_id,與user表關(guān)聯(lián),oauth_name,oauth_access_token等字段


      user_idoauth_nameoauth_access_token
      user-id1auth1token1
      user-id2auth2token2
      user-id3auth3token3


      這樣,oauth_name字段可以用來存儲(chǔ)第三方來源,例如github,以此來區(qū)別不同的第三方登陸用戶。

      到此,一個(gè)簡(jiǎn)單的表結(jié)構(gòu)就設(shè)計(jì)好了。


      03.OAuth鑒權(quán)


      簡(jiǎn)單來說,為一個(gè)網(wǎng)站添加第三方登錄指的是提供通過其他第三方平臺(tái)賬號(hào)登入當(dāng)前網(wǎng)站的功能。比如,使用QQ、微信、新浪微博賬號(hào)登錄。對(duì)于某些網(wǎng)站,甚至可以僅提供社交賬號(hào)登錄的選項(xiàng),這樣網(wǎng)站本身就不需要管理用戶賬戶等相關(guān)信息。對(duì)用戶來說,使用第三方登錄可以省去注冊(cè)的步驟,更加方便和快捷。這里,我就是使用GitHub的OAuth認(rèn)證來進(jìn)行鑒權(quán)登陸。

      這里首先需要在自己的GitHub上創(chuàng)建一個(gè)OAuth程序,非常簡(jiǎn)單,訪問這個(gè)地址:https://github.com/settings/applications/new,按照要求填寫即可。



      其中的callback需要填寫一個(gè)回調(diào)函數(shù),具體后面再說。

      創(chuàng)建好這個(gè)OAuth程序后,我們就會(huì)獲得Client ID(客戶端ID)和Client Secret(客戶端密鑰),在后面調(diào)用Github的API時(shí)使用。


      04. 本地鑒權(quán)


      1. 創(chuàng)建表結(jié)構(gòu)

      根據(jù)剛才的表結(jié)構(gòu)設(shè)計(jì),對(duì)于本地鑒權(quán),可以在models.py文件中創(chuàng)建一個(gè)WebUser類,定義對(duì)應(yīng)的數(shù)據(jù)庫(kù)字段。

      對(duì)于password,不建議直接在數(shù)據(jù)庫(kù)中存儲(chǔ)明文,所以這里使用了werkzeug庫(kù)來做hash轉(zhuǎn)換。

      同時(shí)WebUser類還繼承自flask-login的UserMixin類,該類實(shí)現(xiàn)了關(guān)鍵的用于檢測(cè)用戶狀態(tài)的方法:

          is_authenticated,如果用戶已經(jīng)登陸返回True,否則返回False

          is_active,如果用戶允許登陸,返回True,否則返回Flase

          is_anonymous,對(duì)普通用戶必須返回False

          get_id,必須返回用戶的唯一標(biāo)識(shí)

      后面主要使用到了is_authenticated方法。

      而init_user是用來初始化第一個(gè)用戶的,password等幾個(gè)方法分別是用來檢測(cè)密碼是否正確的。

      class WebUser(UserMixin, db.Model):
          __tablename__ = 'webuser'
          id = db.Column(db.Integer, primary_key=True)
          user_id = db.Column(db.String(64), unique=True, index=True)
          email = db.Column(db.String(64), unique=True, index=True)
          username = db.Column(db.String(64), unique=True, index=True)
          password_hash = db.Column(db.String(128))

          @staticmethod
          def init_user():
              users = WebUser.query.filter_by(username='admin').first()
              if users is None:
                  users = WebUser(email='admin@123.com', username='admin', user_id=time.time())
              users.password = '123456'
              db.session.add(users)
              db.session.commit()

          @property
          def password(self):
              raise AttributeError('password is not readable attribute')

          @password.setter
          def password(self, password):
              self.password_hash = generate_password_hash(password)

          def verify_password(self, password):
              return check_password_hash(self.password_hash, password)


      2. 定義登陸表單

      登陸表單比較簡(jiǎn)單,兩個(gè)輸入框,分別為用戶名和密碼,一個(gè)check box,用來選擇是否保持登陸,外加一個(gè)提交按鈕

      class LoginForm(FlaskForm):
          email = StringField('Email', validators=[DataRequired(), Length(164), Email()])
          password = PasswordField('Password', validators=[DataRequired()])
          remember_me = BooleanField('Keep me logged in')
          submit = SubmitField('Log In')


      3. 定義登陸登出函數(shù)

      當(dāng)表單正確提交時(shí),如果用戶名和密碼匹配,則提示登陸成功,并跳轉(zhuǎn)頁面,否則提示登陸失敗。

      因?yàn)槭鞘褂胒lask-login擴(kuò)展,所以登陸直接調(diào)用login_user()即可。

      @auth.route('/login', methods=['GET', 'POST'])
      def login():
          form = LoginForm()
          if form.validate_on_submit():
              user = WebUser.query.filter_by(email=form.email.data).first()
              if user is not None and user.verify_password(form.password.data):
                  login_user(user, form.remember_me.data)
                  return redirect(request.args.get('next'or url_for('main.index'))
              flash('Invalid username or password!')
          return render_template('auth/login.html', form=form)


      對(duì)于登出,同樣簡(jiǎn)單,注意需要用login_required裝飾器保證只有已經(jīng)登陸的用戶才能調(diào)用該函數(shù)。

      @auth.route('/logout')
      @login_required
      def logout():
          flash('You have logged out!')
          return redirect(url_for('main.index'))


      4. web模板

      創(chuàng)建一個(gè)base.html基礎(chǔ)模板(繼承自flask-bootstrap模板),后面其他頁面都繼承自該模板,這樣可以保證所有的頁面風(fēng)格統(tǒng)一,也可以減少代碼量。

      {% extends 'bootstrap/base.html' %} 

      {% block title %}Flasky{% endblock %} 

      {% block navbar %}
      <div class='navbar navbar-inverse' role='navigation'>
          <div class='container'>
              <div class='navbar-header'>
                  <button type='button' class='navbar-toggle'  data-toggle='collapse' data-target='.navbar-collapse'>
                      <span class='sr-only'>Toggle navigation</span>
                      <span class='icon-bar'></span>
                      <span class='icon-bar'></span>
                      <span class='icon-bar'></span>
                  </button>
                  <a class='navbar-brand' href='/'>WebAuth</a>
              </div>
              <div class='navbar-collapse collapse'>
                  <ul class='nav navbar-nav'>
                      <li><a href='/'>Home</a></li>
                  </ul>
                  <ul class='nav navbar-nav navbar-right'>
                      {% if current_user.is_authenticated %}
                      <li><a href='{{ url_for('auth.logout') }}'>Sign Out</a></li>
                      {% else %}
                      <li><a href='{{ url_for('auth.login') }}'>Sign In</a></li>
                      {% endif %}
                  </ul>
              </div>
          </div>
      </div>
      {% endblock %}

      {% block content %}
      <div class='container'>
          {% block page_content %}{% endblock %}
      </div>
      {% endblock %}


      5. 登陸頁面

      登陸頁面繼承自base.html模板,并使用wtf快速渲染表單

      {% extends 'base.html' %}
      {% import  'bootstrap/wtf.html' as wtf %}
      {% block title %}Login{% endblock %}
      {% block page_content %}
      <div class='page-header'>
          <h1>Login</h1>
      </div>

      <div class='col-md-4'>
          {{ wtf.quick_form(form) }}
      </div>
      {% endblock %}


      最后的登陸頁面為



      6. 初始化數(shù)據(jù)庫(kù)

      使用flask-script擴(kuò)展,定義runserver和shell兩個(gè)命令行命令,shell用于數(shù)據(jù)庫(kù)等調(diào)測(cè)操作,runserver用于啟動(dòng)服務(wù)。

      from app import create_app, db
      from flask_script import Manager, Shell, Server
      from app.models import WebUser


      app = create_app('testing')
      manager = Manager(app)


      def make_shell_context():
          return dict(app=app, db=db, WebUser=WebUser)


      manager.add_command('runserver', Server(use_debugger=True, host='0.0.0.0', port='9982'))
      manager.add_command('shell', Shell(make_context=make_shell_context))


      if __name__ == '__main__':
          manager.run(default_command='runserver')


      在命令行輸入python manage.py shell,進(jìn)入調(diào)測(cè)shell,然后輸入db.create_all()和WebUser.init_user(),分別創(chuàng)建表并插入原始用戶。


      7. 登陸測(cè)試

      在輸入框分別鍵入admin@163.com和123456,并點(diǎn)擊登陸,發(fā)現(xiàn)可以正常登陸,效果如下



      其中index頁面代碼為

      {% extends 'base.html' %}
      {% import  'bootstrap/wtf.html' as wtf %}
      {% block title %}Login{% endblock %}
      {% block page_content %}
      <div class='container'>
          {% for message in get_flashed_messages() %}
          <div class='alert alert-warning'>
              <button type='buttonclass='closedata-dismiss='alert'>&times;</button>
              {{ message }}
          </div>
          {% endfor %}
      </div>
      <div class='page-header'>
          <h1>Home</h1>
      </div>
      <div class='col-md-4'>
          這是首頁
      </div>
      <div class='col-md-12'>
      {% if current_user.is_authenticated %}
          {{ current_user.username }}
          {{ name }}
          <div>
          <img style='-webkit-user-select:
       none;' src='{{ avatar }}' />
          </div>
      {% else %}
          Your are not login yet
      {% endif %}
      </div>
      {% endblock %}


      05. GitHub鑒權(quán)


      1. 創(chuàng)建表結(jié)構(gòu)

      類似的,定義需要的字段即可

      class ThirdOAuth(db.Model):
          __tablename__ = 'thirdoauth'
          id = db.Column(db.Integer, primary_key=True)
          user_id = db.Column(db.String(64), unique=True, index=True)
          oauth_name = db.Column(db.String(128))
          oauth_id = db.Column(db.String(128), unique=True, index=True)
          oauth_access_token = db.Column(db.String(128), unique=True, index=True)
          oauth_expires = db.Column(db.String(64), unique=True, index=True)


      2. 發(fā)送授權(quán)請(qǐng)求

      這一步,flask-github已經(jīng)為我們封裝好了,直接調(diào)用即可

      @auth.route('/githublogin', methods=['GET', 'POST'])
      def githublogin():
          return github.authorize(scope='repo')


      這里需要說明,該調(diào)用需要用到我們前面獲得的客戶端ID和密鑰,我這里把相關(guān)信息寫到了一個(gè)配置文件中,并在初始化flask app時(shí)加載

      配置文件

      class Config:
          SECRET_KEY = 'hardtoguess'
          GITHUB_CLIENT_ID = 'cf1AA35ef11d20bcdXXX'
          GITHUB_CLIENT_SECRET = 'ba7c8c8SSe9cd574eb3da1b5e704d11d35aXXXb8'


      初始化app

      def create_app(config_name):
          app = Flask(__name__)
          app.config.from_object(config[config_name])
          config[config_name].init_app(app)
          db.init_app(app)
          cors.init_app(app, supports_credentials=True)
          login_manager.init_app(app)
          bootstrap.init_app(app)
          github.init_app(app)

          from .main import main as main_blueprint
          app.register_blueprint(main_blueprint)
          from .api_1_0 import api_1_0 as api_blueprint
          app.register_blueprint(api_blueprint)
          from .auth import auth as auth_blueprint
          app.register_blueprint(auth_blueprint, url_prefix='/auth')

          return app


      3. 獲取access令牌

      當(dāng)用戶同意授權(quán)或拒絕授權(quán)后,GitHub會(huì)將用戶重定向到我們?cè)O(shè)置的callback URL,我們需要?jiǎng)?chuàng)建一個(gè)視圖函數(shù)來處理回調(diào)請(qǐng)求。如果用戶同意授權(quán),GitHub會(huì)在重定向的請(qǐng)求中加入code參數(shù),一個(gè)臨時(shí)生成的值,用于程序再次發(fā)起請(qǐng)求交換access token。程序這時(shí)需要向請(qǐng)求訪問令牌URL(即https://github.com/login/oauth/access_token)發(fā)起一個(gè)POST請(qǐng)求,附帶客戶端ID、客戶端密鑰、code。請(qǐng)求成功后的的響應(yīng)會(huì)包含訪問令牌(Access Token)。


      很幸運(yùn),上面的一系列工作flask-github會(huì)在背后替我們完成。我們只需要?jiǎng)?chuàng)建一個(gè)視圖函數(shù),定義正確的URL規(guī)則(這里的URL規(guī)則需要和GitHub上填寫的Callback URL匹配),并為其附加一個(gè)github.authorized_handler裝飾器。另外,這個(gè)函數(shù)要接受一個(gè)access_token參數(shù),GitHub-Flask會(huì)在授權(quán)請(qǐng)求結(jié)束后通過這個(gè)參數(shù)傳入訪問令牌。

      同時(shí)判斷,該用戶是否存在于數(shù)據(jù)庫(kù)中,并更新相關(guān)字段。

      @auth.route('/callback/github')
      @github.authorized_handler
      def authorized(access_token):
          if access_token is None:
              flash('Login Failed!')
              return redirect(url_for('main.index'))
          response = github.get('user', access_token=access_token)
          username = response['login']
          u_id = response['id']
          email = response['email']
          avatar = response['avatar_url']
          user = WebUser.query.filter_by(username=username).first()
          if user is None:
              user = WebUser(username=username, user_id=time.time())
              db.session.add(user)
              db.session.commit()
              thirduser = ThirdOAuth(user_id=WebUser.query.filter_by(username=username).first().user_id,
                                     oauth_name='github', oauth_access_token=access_token,
                                     oauth_id=u_id)
              db.session.add(thirduser)
              db.session.commit()
              login_user(user)
              user.email = email
              db.session.add(user)
              db.session.commit()
              session['userid'] = user.user_id
              return render_template('index.html', avatar=avatar)
          else:
              thirduser = ThirdOAuth.query.filter_by(user_id=user.user_id).first()
              thirduser.oauth_access_token = access_token
              db.session.add(thirduser)
              db.session.commit()
              user.email = email
              db.session.add(user)
              db.session.commit()
              login_user(user)
              session['userid'] = user.user_id
              return render_template('index.html', avatar=avatar)


      更多的GitHub開發(fā)文檔資料可以查看:

      https://developer.github.com/apps/building-oauth-apps/understanding-scopes-for-oauth-apps/

      更多flask-github資料可以查看:

      https://github-flask./en/latest/


      4. 更新登陸頁面

      更新登陸頁面,增加一個(gè)以GitHub登陸的按鈕

      <div class='col-md-12'>
          <a class='btn btn-primary' href='{{ url_for('auth.githublogin') }}'>Login with GitHub</a>
      </div>


      現(xiàn)在的登陸頁面為


      更新index路由函數(shù),增加以GitHub登陸時(shí)的頭像

      @main.route('/', methods=['GET', 'POST'])
      def index():
          # print(session)
          if current_user.is_authenticated:
              if 'userid' in session:
                  user = ThirdOAuth.query.filter_by(user_id=session['userid']).first()
                  if user:
                      response = github.get('user', access_token=user.oauth_access_token)
                      avatar = response['avatar_url']
                      username = response['login']
                      return render_template('index.html', username=username, avatar=avatar)
          return render_template('index.html')


      又因?yàn)樵赾allback函數(shù)中增加了session.userid字段,所以在logout時(shí),把該字段手動(dòng)刪除

      @auth.route('/logout')
      @login_required
      def logout():
          logout_user()
          if 'userid' in session:
              session.pop('userid')
          flash('You have logged out!')
          return redirect(url_for('main.index'))


      5. 測(cè)試GitHub登陸

      登陸成功后,如下



      至此,登陸功能完成


      完整代碼:

      https://github.com/zhouwei713/flask-webauth



        本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
        轉(zhuǎn)藏 分享 獻(xiàn)花(0

        0條評(píng)論

        發(fā)表

        請(qǐng)遵守用戶 評(píng)論公約

        類似文章 更多