首页 文章

Flask-Login不会将Cookie设置为带有角度的浏览器?

提问于
浏览
3

我正在使用Angular 6和Python Flask构建一个fullstack Web项目,主要使用Flask-Security扩展 .

目前,我尝试使用 login_user() (Flask-Login的方法)实现用户的登录系统 . 基本上, login_user() 有效,但我在浏览器上看不到任何会话cookie .

正如文档所述, session 对象的每个更改/新实例都相应地设置/修改cookie,因此 login_user() 创建 session 的新实例 .

我'm running and testing the project on ' http://127.0.0.1:4200'(Angular默认端口)和Flask,使用'http://127.0.0.1:5000/' .

作为最后的手段,我尝试构建一个没有任何实际前端的Flask应用程序,运行并测试它来自'http://127.0.0.1:5000/ ', and it did work. I' ve设法查看 login_user() 应该从一开始就设置的cookie .

主要是,我的问题,为什么它不适用于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 回答

  • 2

    您无法使用服务器会话设置浏览器cookie . 你必须在回复中发送cookie . 如果要在响应中设置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进入时解密来验证用户对后端的请求 . 有大量关于此的文献 . 我将在本文末尾提供一些很好的教程 .

    你可以使用(在Angular中) 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);
      }
    }
    

    你可以有这样的Auth服务:

    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上的规范来源是Miguel Grinberg,他编写了一些JWT认证教程 - https://blog.miguelgrinberg.com/post/json-web-tokens-with-public-key-signatures

    这里是Flask中JWT的另一个教程:https://realpython.com/token-based-authentication-with-flask/

相关问题