Flask-Login не устанавливает куки для браузера с угловым? - PullRequest
0 голосов
/ 28 сентября 2018

Я создаю полноценный веб-проект с использованием Angular 6 и Python Flask, в основном с использованием расширения Flask-Security.

В настоящее время я пытаюсь реализовать систему входа пользователей с использованием login_user() * 1004.* (Метод Flask-Login) .В основном, login_user() работает, но я не вижу сессионные куки в моем браузере.

Как сказано в документации, каждое изменение / новый экземпляр session объекта устанавливает / изменяет куки соответственно, поэтому login_user()создает новый экземпляр session.

Я запускаю и тестирую проект на 'http://127.0.0.1:4200' (угловой порт по умолчанию) и Flask, используя' http://127.0.0.1:5000/'.

В крайнем случае я попытался создать приложение Flask без какого-либо реального интерфейса, запустив и протестировав его с 'http://127.0.0.1:5000/',, и это сработало.Мне удалось увидеть файлы cookie, которые login_user() должны были установить с самого начала.

В основном мой вопрос, почему он не работает с Angular?код внешнего интерфейса:

export class Login {
    constructor(private userSerivce : UserService, private router : Router) {}

    outputMessage : string = null;

    loginOnSubmit(form : FormGroup): void {
        let formDict = {
            "email" : form.controls["email"].value,
            "password" : form.controls["password"].value
        }
        this.userSerivce.loginOnSubmit(formDict).subscribe({
            next : value => {
                //whatever, still didn't get here
            },
            error : response => {this.outputMessage = response.error}
        })
    }

функция входа в бэкэнд:

@user_app.route('/signin', methods=['POST'])
def signIn():
    session.permanent = True
    status_code = 200
    output_string = None
    form = json.loads(request.data.decode('utf-8'))
    user = User.query.filter_by(email=form['email']).first()
    if user is not None:
        if utils.verify_password(form['password'],user.password) and user.is_authenticated:
            user.active = True
            db.session.commit()
            if login_user(user, True, datetime.timedelta(days=24), False, True):
                i=1 #debugging purposes only
        else:
            status_code = 400
            output_string = "error"
    else:
        status_code = 400
        output_string = "error"

    return jsonify(1), status_code

Модели в точности соответствуют документации, я даже использовал один и тот же код в своем пустом приложении Flask (те же классы и база данныхи, как я уже сказал, это сработало).

1 Ответ

0 голосов
/ 28 сентября 2018

Вы не можете установить файлы cookie браузера, используя сеанс сервера.Вы должны будете отправить куки в ответе.Если вы хотите установить куки в ответе, вы можете сделать что-то вроде этого:

from flask import make_response # and your other stuff
# ... other imports ...
def login():
# ... some authentication code here to get your access_token (like a jwt)...
    resp = make_response(redirect('http://localhost:4200')) # your angular app
    resp.set_cookie('token', access_token) # set the cookie on the response header for the browser to extract
    return resp # return the response with the new cookie attached

Поскольку ваше клиентское приложение не находится в том же домене, что и серверное приложение, установка сеанса не будетпомочь вам так, как вы хотите для аутентификации.Лучший способ сделать то, что вы хотите, - это передавать JWT туда и обратно между клиентом и сервером.

Одна вещь, которую вы можете попытаться сделать (если вы хотите установить какую-либо аутентификацию на внешнем интерфейсе), будетчтобы аутентифицировать вашего пользователя, верните JWT обратно в Angular.Затем вы можете установить HTTP-заголовок, чтобы каждый раз приходить на сервер.Бэкэнд будет анализировать запрос и извлекать JWT из заголовка.Затем вы будете использовать этот заголовок для аутентификации запроса пользователя к вашему бэкэнду, расшифровывая JWT, когда он входит. Существует много литературы по этому вопросу.Я добавлю несколько хороших уроков в конце этого поста.

Вы можете использовать (на английском языке) HttpInterceptor.Примерно так:

import { Injectable } from "@angular/core";
import { HttpInterceptor, HttpHandler, HttpEvent } from "@angular/common/http";
import { AuthService } from "../auth/auth.service";
import { HttpRequest } from '@angular/common/http';
import { Observable } from "rxjs";

@Injectable()
export class TokenInterceptor implements HttpInterceptor {
  constructor(public auth: AuthService) { }

  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {

    if (this.auth.isLoggedIn()) {
      request = request.clone({
        setHeaders: {
          Authorization: `Bearer ${this.auth.getToken()}`
        }
      });
    }

    return next.handle(request);
  }
}

У вас может быть служба аутентификации, например:

import { Injectable } from '@angular/core';
import { HttpClient, HttpParams, HttpHeaders } from '@angular/common/http';
import { CookieService } from 'ngx-cookie-service';
import { map } from 'rxjs/operators';
import { environment } from '../../environments/environment';

@Injectable({
  providedIn: 'root'
})
export class AuthService {

  redirectUrl: string;
  // cookie service from ngx-cookie-service
  constructor(private http: HttpClient, private cookieService: CookieService) { }

  checkToken() {
    return this.cookieService.check('token');
  }

  getToken() {
    return this.cookieService.get('token');
  }

  loginWithUsernameAndPassword(userName: string, password: string) {
    return this.http.post<any>(`${environment.API_URL}/auth/login`,
        new HttpParams({fromObject: {userName, password}}),
        {
          headers: new HttpHeaders().set('Content-Type', 'application/x-www-form-urlencoded')
        }
      ).pipe(map(user => {
        if (user && user.token) {
          this.cookieService.set('token', user.token);
        }
        return user;
      }));
  }

  logout() {
    this.cookieService.delete('token');
  }

  isLoggedIn() {
    return this.cookieService.check('token');
  }

  registerWithUsernameAndPassword(userName, password, email) {
    return this.http.post<any>(`${environment.API_URL}/auth/create`,
      new HttpParams({fromObject: {userName, password, email}}),
      {
        headers: new HttpHeaders().set('Content-Type', 'application/x-www-form-urlencoded')
      }
    )
      .pipe(map(user => {
        console.log(user);
        return user;
      }));
  }

}

В вашем AppModule вы можете указать провайдера с именем HTTP_INTERCEPTORS и использовать HttpInterceptor вы создали - в моем случае я бы назвал это TokenInterceptor:

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppComponent } from './app.component';
import { AppRoutingModule } from './app-routing/app-routing.module';

import { SharedModule } from './shared/shared.module';
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
import { CookieService } from 'ngx-cookie-service';
import { AuthService } from './auth/auth.service';
import { TokenInterceptor } from './interceptors/token.interceptor';

@NgModule({
  imports: [
    BrowserModule,
    AppRoutingModule,
    SharedModule,
    HttpClientModule
  ],
    declarations: [
    AppComponent,
  ],
  exports: [],
  providers: [
    AuthService,
    CookieService,
    {
      provide: HTTP_INTERCEPTORS,
      useClass: TokenInterceptor,
      multi: true
    }
  ],
  bootstrap: [AppComponent]
})
export class AppModule {
}

Хорошая ссылка для перехватчика: https://angular.io/api/common/http/HttpInterceptor и: https://medium.com/@ryanchenkie_40935/angular-authentication-using-the-http-client-and-http-interceptors-2f9d1540eb8

Каноническим источником во Flask был бы Мигель Гринберг, который написал несколько учебных пособий по аутентификации JWT - https://blog.miguelgrinberg.com/post/json-web-tokens-with-public-key-signatures

вот еще один учебник для JWT во Flask: https://realpython.com/token-based-authentication-with-flask/

...