miguelgrinberg / Flask-HTTPAuth

Simple extension that provides Basic, Digest and Token HTTP authentication for Flask routes
MIT License
1.27k stars 228 forks source link

why the Flask view function need authentication even the function did not have @auth.login_required decorators? #136

Closed myahuang closed 2 years ago

myahuang commented 2 years ago

Hi! My app use HTTPBasicAuth modul,the code is showing as following:

@auth.verify_password
def verify_password(username_or_token, password):
    if request.path == "/api/login":
        user = User.query.filter_by(username=username_or_token).first()
        if not user or not user.verify_password(password):
            return False
    else:
        user = User.verify_auth_token(username_or_token)
        if not user:
            return False    
    g.user = user
    return True
@app.route("/user/info")
@auth.login_required
def userinfo():
    return make_response(jsonify(usertiger), 200)
@app.route('/', methods = ['GET'])
def getdata():
    return jsonify(random.randint(0,5))

When the frontend access router '/user/info', the flask server work fine, but why when the frontend access router '/',the flask server response status code 401,this router don't have @auth.login_required decorators

miguelgrinberg commented 2 years ago

Sorry, but I cannot reproduce the problem with my own example code. Please provide the complete application so that I can test it here.

myahuang commented 2 years ago

app/init.py

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_httpauth import HTTPBasicAuth
from flask_cors import CORS

app = Flask(__name__)
CORS(app)
app.config.from_object('config')
db = SQLAlchemy(app)
auth = HTTPBasicAuth()

from . import models, views

app/models.py

from app import db, app
from passlib.apps import custom_app_context
from itsdangerous import TimedJSONWebSignatureSerializer as Serializer, SignatureExpired, BadSignature
class User(db.Model):
    __tablename__ =  'users'
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(32), index=True)
    password = db.Column(db.String(128))

    def hash_password(self, password):
        self.password = custom_app_context.encrypt(password)

    def verify_password(self, password):
        return custom_app_context.verify(password, self.password)

    def generate_auth_token(self, expiration = 600):
        s = Serializer(app.config['SECRET_KEY'], expires_in = expiration)
        return s.dumps({ 'id': self.id })

    @staticmethod
    def verify_auth_token(token):
        s = Serializer(app.config['SECRET_KEY'])
        try:
            data = s.loads(token)
        except SignatureExpired:
            return None # valid token, but expired
        except BadSignature:
            return None # invalid token
        user = User.query.get(data['id'])
        return user

app/views.py

from app import app, db, auth
from flask import jsonify, request, abort, g,make_response
from app.models import *
import random

usertiger={'name':'tiger','role':'admin','avatar':'https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif',
           'introduction':'I am a admin'}

@app.route("/user/info")
@auth.login_required
def userinfo():
    return make_response(jsonify(usertiger), 200)

@app.route('/api/register', methods = ['POST'])
def register():
    username = request.json.get('username')
    password = request.json.get('password')
    if username is None or password is None:
        abort(400) # missing arguments
    if User.query.filter_by(username = username).first() is not None:
        abort(400) # existing user
    user = User(username = username)
    user.hash_password(password)
    db.session.add(user)
    db.session.commit()
    return jsonify({ 'username': user.username })

@auth.verify_password
def verify_password(username_or_token, password):
    print("hello")
    print(request.authorization)
    if request.path == "/api/login":
        #print(username_or_token)
        #print(password)
        user = User.query.filter_by(username=username_or_token).first()
        print(user)
        if not user or not user.verify_password(password):
            print("errro1")
            return False
    else:
        user = User.verify_auth_token(username_or_token)
        if not user:
            print("error2~~",username_or_token)
            return False    
    g.user = user
    print("OK")
    return True

@app.route('/api/login')
@auth.login_required
def get_auth_token():
    token = g.user.generate_auth_token()
    print("hi")
    if isinstance(token, bytes):
        token2=str(token, encoding='utf-8')
    return make_response(jsonify(token2), 200)

@app.route('/', methods = ['GET'])
def getdata():
    print("getting data")
    return jsonify(random.randint(0,5))

config.py

import os
basedir = os.path.abspath(os.path.dirname(__file__))

SQLALCHEMY_DATABASE_URI = "mysql://root:asdfgh@127.0.0.1/rest"
SQLALCHEMY_MIGRATE_REPO = os.path.join(basedir, 'db_repository')
SQLALCHEMY_TRACK_MODIFICATIONS = True
BASEDIR = basedir

CSRF_ENABLED = True
SECRET_KEY = 'jklklsadhfjkhwbii9/sdf\sdf'
miguelgrinberg commented 2 years ago

I don't see the problem with your code. Here is a request to /:

$ curl http://localhost:5000/
3

And in the server log:

getting data
127.0.0.1 - - [26/Sep/2021 19:35:27] "GET / HTTP/1.1" 200 -
myahuang commented 2 years ago

Thanks for your reply! I try many times, and find it may be IDE problems. The problem can be occur when i use IDE teminal to excute python file. But when i use IDE menu 'run' command, the problem will disappear!