登录后,我的 api 不会 return 来自我的 FastAPI 端点的 flutter 主页上的有效负载

My api won't return the payload on the flutter homepage from my FastAPI endpoint after signing in

我很抱歉这个菜鸟问题,但是有没有办法让我的端点在登录后使用我的用户名和来自我 flutter 主页 json 文件的其他数据 return 文本在?登录端点有效,我可以从日志中看到我得到 INFO: 127.0.0.1:52744 - "POST /auth/login HTTP/1.1" 200 OK 登录按钮,但是当 flutter 页面更改为主页时得到 INFO:127.0.0.1:52746 - "GET /auth/me HTTP/1.1" 401 Unauthorized。我尝试包括 flutter_secure_stroage 来存储登录时获得的 jwt 令牌,但仍然出现错误。这个错误来自我的端点吗?还是来自我的前端 Flutter?谢谢。

这是我的登录端点:

@router.post("/login", response_model=Token)
def login_for_access_token(
    form_data: OAuth2PasswordRequestForm = Depends(), db: Session = Depends(get_db)
):
    vendor = authenticate_vendor(form_data.username, form_data.password, db)
    if not vendor:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Incorrect username or password",
        )
    accesss_token_expires = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
    accesss_token = create_access_token(
        data={"sub": vendor.email}, expires_delta=accesss_token_expires
    )
    return {"access_token": accesss_token, "token_type": "bearer"}


oauth_scheme = OAuth2PasswordBearer(tokenUrl="/auth/login")


def get_current_user_from_token(
    token: str = Depends(oauth_scheme), db: Session = Depends(get_db)
):
    credentials_exception = HTTPException(
        status_code=status.HTTP_401_UNAUTHORIZED,
        detail="Could not validate credentials",
    )
    try:
        payload = jwt.decode(
            token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM]
        )
        username: str = payload.get("sub")
        print("username/email extracted is", username)
        if username is None:
            raise credentials_exception
    except JWTError:
        raise credentials_exception
    vendor = get_vendor(username=username, db=db)
    if vendor is None:
        raise credentials_exception
    return vendor

这里是 auth/me 端点:


@router.get("/me")
def get_current_user_from_token(
    token: str = Depends(oauth_scheme), db: Session = Depends(get_db)
):
    credentials_exception = HTTPException(
        status_code=status.HTTP_401_UNAUTHORIZED,
        detail="Could not validate credentials",
    )
    try:
        payload = jwt.decode(
            token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM]
        )
        username: str = payload.get("sub")
        print("username/email extracted is", username)
        if username is None:
            raise credentials_exception
    except JWTError:
        raise credentials_exception
    vendor = get_vendor(username=username, db=db)
    if vendor is None:
        raise credentials_exception
    return vendor

现在我的前端 flutter,

这是我的 Main.dart on flutter:

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  Future<String> get jwtOrEmpty async {
    var jwt = await storage.read(key: "jwt");
    if (jwt == null) return "";
    return jwt;
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Authentication Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: FutureBuilder(
          future: jwtOrEmpty,
          builder: (context, snapshot) {
            if (!snapshot.hasData) return CircularProgressIndicator();
            if (snapshot.data != "") {
              var str = snapshot.data;
              var jwt = str.toString().split(".");

              if (jwt.length != 3) {
                return LoginPage();
              } else {
                var payload = json.decode(
                    ascii.decode(base64.decode(base64.normalize(jwt[1]))));
                if (DateTime.fromMillisecondsSinceEpoch(payload["exp"] * 1000)
                    .isAfter(DateTime.now())) {
                  return HomePage(str.toString(), payload);
                } else {
                  return LoginPage();
                }
              }
            } else {
              return LoginPage();
            }
          }),
    );
  }
}

这是主页:

class HomePage extends StatelessWidget {
  const HomePage(this.jwt, this.payload, {Key? key}) : super(key: key);

  factory HomePage.fromBase64(String jwt) => HomePage(
      jwt,
      json.decode(
          ascii.decode(base64.decode(base64.normalize(jwt.split(".")[1])))));

  final String jwt;
  final Map<String, dynamic> payload;

  @override
  Widget build(BuildContext context) => Scaffold(
        appBar: AppBar(title: const Text("Secret Data Screen")),
        body: Center(
          child: FutureBuilder(
              future: http.read(Uri.parse('$SERVER_IP/auth/me'),
                  headers: {"Authorization": jwt}),
              builder: (context, snapshot) => snapshot.hasData
                  ? Column(
                      children: <Widget>[
                        Text("${payload['username']}, here's the data:"),
                        Text(snapshot.data.toString(),
                            style: Theme.of(context).textTheme.displayLarge)
                      ],
                    )
                  : snapshot.hasError
                      ? const Text("error occurred")
                      : const CircularProgressIndicator()),
        ),
      );
}

这是login.dart:

class LoginPage extends StatelessWidget {
  final TextEditingController _usernameController = TextEditingController();
  final TextEditingController _passwordController = TextEditingController();

  LoginPage({Key? key}) : super(key: key);

  void displayDialog(context, title, text) => showDialog(
        context: context,
        builder: (context) =>
            AlertDialog(title: Text(title), content: Text(text)),
      );

  Future<String?> attemptLogIn(String username, String password) async {
    var res = await http.post(Uri.parse("$SERVER_IP/auth/login"),
        body: {"username": username, "password": password});
    if (res.statusCode == 200) return res.body;
    return null;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: const Text("Log In"),
        ),
        body: Padding(
          padding: const EdgeInsets.all(8.0),
          child: Column(
            children: <Widget>[
              TextField(
                controller: _usernameController,
                decoration: const InputDecoration(labelText: 'Username'),
              ),
              TextField(
                controller: _passwordController,
                obscureText: true,
                decoration: const InputDecoration(labelText: 'Password'),
              ),
              TextButton(
                  onPressed: () async {
                    var username = _usernameController.text;
                    var password = _passwordController.text;
                    var jwt = await attemptLogIn(username, password);
                    if (jwt != null) {
                      storage.write(key: "jwt", value: jwt);
                      Navigator.push(
                          context,
                          MaterialPageRoute(
                              builder: (context) => HomePage.fromBase64(jwt)));
                    } else {
                      displayDialog(context, "An Error Occurred",
                          "No account was found matching that username and password");
                    }
                  },
                  child: const Text("Log In")),
            ],
          ),
        ));
  }
}

/login returns {"access_token": accesss_token, "token_type": "bearer"} 字符串,保存,而FutureBuilder for jwtOrEmpty 需要令牌不完整的响应。

另外 /me 需要承载类型,意思是这样的 {"Authorization": "Bearer $jwt"}