【问题标题】:How can I guard routes in Angular?如何在 Angular 中保护路线?
【发布时间】:2022-02-05 15:23:14
【问题描述】:

目前,登录后我可以将 JWT 获取到前端。我的应用目前有一个日志页面作为登录页面,一旦用户登录,路由就会检查身份验证以重定向到受保护的主路径。

我的第一个直觉是从后端(Django)发送一个布尔值并用它来创建一个守卫。但我一直认为这似乎是在前端处理此问题的更好做法。

我所做的是创建一个 auth.service.ts 和一个 auth.guard.ts。在服务中,我尝试从浏览器中检索令牌,然后验证它没有过期。然后我在警卫上调用该方法并返回一个布尔值。问题是每次我在本地存储中查找令牌时,我都会返回 null。

有没有更好的方法来实现这一目标?

auth.guard.ts

import { Injectable } from '@angular/core';
import {
  ActivatedRouteSnapshot,
  CanActivate,
  Router,
  RouterStateSnapshot,
  UrlTree,
} from '@angular/router';
import { Observable, of } from 'rxjs';
import { catchError, map } from 'rxjs/operators';
import { AuthService } from './auth.service';

@Injectable({
  providedIn: 'root',
})
export class AuthGuard implements CanActivate {
  constructor(private authService: AuthService, private router: Router) {}
  canActivate(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
  ):
    | Observable<boolean | UrlTree>
    | Promise<boolean | UrlTree>
    | boolean
    | UrlTree {
    console.log(this.authService.isAuthenticated());
    if(!this.authService.isAuthenticated()){
      this.router.navigate(['/login']);
      return false;
    }
    return true;
  }
}

auth.service.ts

import { Injectable } from '@angular/core';
import { JwtHelperService } from '@auth0/angular-jwt';

@Injectable({
  providedIn: 'root'
})
export class AuthService {
  public jwtHelper: JwtHelperService = new JwtHelperService();
  constructor() { }

  isAuthenticated(){
    const jwt = localStorage.getItem('token');
    return !this.jwtHelper.isTokenExpired(jwt!);
  }
}

app-routing.module.ts

...
import { AuthGuard } from './user/services/auth.guard'

const routes: Routes = [
  {
    path: '',
    component: LandingComponent,
    children: [
      { path: '', component: HomeComponent, canActivate: [AuthGuard],},
      { path: 'home', component: HomeComponent, canActivate: [AuthGuard],},
      {
        path: 'cohort-charts',
        component: CohortChartsComponent,
        children: [
          { path: 'selection', component: CohortSelectionComponent },
          { path: 'edit', component: CohortEditComponent },
          { path: '', redirectTo: 'selection', pathMatch: 'full' },
        ],
      },
    ],
  },
  {
    path: 'login',
    component: LoginComponent,
  },
  { path: '**', redirectTo: '' },
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule],
})
export class AppRoutingModule {}

【问题讨论】:

    标签: django angular authentication routes angular-router-guards


    【解决方案1】:

    我猜每次你在本地存储中查找令牌时,你都会返回 null,因为你没有保存令牌,或者如果你这样做了,你试图将令牌存储为对象,而不是 序列化(作为字符串,对其进行字符串化),因此它不会存储,或者当您获取它时,您不会粘贴它

    无论如何,我想管理整个 jwt/身份验证部分的最佳做法是使用拦截器

    Interceptor 是一种服务,它拦截您所有的 http 调用,您可以设置它自动执行某些操作(例如,magaging jwt)。

    有关如何添加和更新标头以及如何使用拦截器拦截请求和响应的更多信息:

    https://angular.io/guide/http#adding-and-updating-headers

    https://angular.io/guide/http#intercepting-requests-and-responses

    我让你一瞥你是如何做到的/你需要什么:

    1. 提供 Angular 拦截器
    app.module.ts:
     providers: [
        {
          provide: HTTP_INTERCEPTORS,
          useClass: HttpJwtAuthInterceptor,
          multi: true,
        },
        { provide: BASE_PATH, useValue: environment.apiUrl },
      ],
    
    1. 首先,在您的 auth.service.ts 中,存储/获取令牌的 2 种方法
    // STORE the token in localstore:
    setToken(token:string){
    
       // First, serialize it (but just if token is not string type).
       const tokenString:string = JSON.stringify( token );
     
       localStorage.setItem('token', tokenString);
    }
     
    // READ the token from localstorage and Deserialize
    getToken(): string | null{
     
       let token = localStorage.getItem( 'token' );
     
       if( token !=null){
    
           // You just need to parse if you serialized it inside setToken() method
           token = JSON.parse(carItemsString);
      }
     
      return token;
     
    }
    
    1. 然后,在您的拦截器中:
    import { Injectable } from '@angular/core';
    import {
      HttpRequest,
      HttpHandler,
      HttpEvent,
      HttpInterceptor,
    } from '@angular/common/http';
    
    import { AuthService } from '../_services/auth.service';
    
    @Injectable()
    export class AuthInterceptor implements HttpInterceptor {
    
      constructor(private authService: AuthService) {}
    
      intercept(
        request: HttpRequest<any>,
        next: HttpHandler
      ): Observable<HttpEvent<any>> {
      
      const url="\yourAPI\endpoint";
      
        //  Get your token
        cont myToken = this.authService.getToken();
         
        // Add authorization header with token if available   
     
        if (myToken) {
        
           request = request.clone({
              setHeaders: {
                Authorization: `Bearer ${myToken}`,
                'Content-Type': 'application/json',
              },
              url,
            });
            
        } 
        
        …
    return next.handle(request);
    
        }
    

    【讨论】:

    • 在字符串化之后我仍然得到空值。同样在 app 模块中,提供了 useValue,我们在其中提供了什么?
    • 在我创建拦截器之前,一切正常且清晰。我应该用什么替换“ ${currentUser.user.api_token}”?根据拦截器是否有令牌返回真或假不是更容易吗?感谢您的帮助@Juan Vicente Berzosa Tejero
    • 抱歉,只是“Bearer ${myToken}”,我刚刚编辑过。无论如何,解决您的“问题的要点是每次我在本地存储中查找令牌时,我都会返回 null”,这是第 2 步)(如何存储/获取值在 LocalStorage 中,将对象序列化为字符串,因为 LocalStorage 只能管理字符串)。拦截器只是一个“好习惯”(但您可以通过另一种方式做到这一点,例如,在您的调用中“手动”插入标头到后面)。
    • 我接受了答案,因为它帮助我找到了本地存储中的内容。我仍然不确定创建警卫和拦截器有什么区别。设置授权标头如何帮助我保护某些路由?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-03
    • 1970-01-01
    • 1970-01-01
    • 2017-02-11
    相关资源
    最近更新 更多