【问题标题】:How to make correct Angular Guards with checking auth如何通过检查身份验证来制作正确的 Angular Guard
【发布时间】:2022-10-24 14:32:02
【问题描述】:

我正在尝试使用警卫,但我无法让他们工作。

我想要CheckAuthGuard,它将检查localStorage中的令牌; AuthGuard,它将检查用户是否已登录,RolesAuthGuard 将检查用户是否具有所需的角色。

我之前问过问题:“如何检查 localStorage 中的用户令牌”。我试图在onInit 中创建检查功能,但一个人告诉我,我需要使用早先加载的保护onInit。(这需要直接进入管理页面,而不是从主页)

起初一切都解决了,但后来我破坏了代码。我希望你能告诉我如何正确地做到这一点。

这是我使用的代码

auth.guard.ts

import {ActivatedRouteSnapshot, CanActivate, CanActivateChild, Router, RouterStateSnapshot} from "@angular/router";
import {Observable, of} from "rxjs";
import {Injectable} from "@angular/core";
import {AuthService} from "../servicies/auth.service";
import {ToastService} from "../servicies/toast.service";

@Injectable({
  providedIn:'root'
})
export class CheckAuthGuard implements CanActivate, CanActivateChild {

  constructor(private auth:AuthService, private router:Router) {
  }

  canActivate(route:ActivatedRouteSnapshot, state:RouterStateSnapshot):Observable<boolean>{
    if (this.auth.isAuth){
      return of(true)
    } else {
      if (localStorage.getItem('token')) {
        this.auth.checkAuth().subscribe(
          (data) => {
            console.log(data)
            console.log(this.auth.isAuth)
            console.log(`Check auth ${this.auth.isAuth}`)
          }
        )
        return of(true)
      }
      else{
        return of(true)
      }
    }
  }

  canActivateChild(route:ActivatedRouteSnapshot, state:RouterStateSnapshot):Observable<boolean>{
    return this.canActivate(route, state)
  }
}

@Injectable({
  providedIn:'root'
})
export class AuthGuard implements CanActivate, CanActivateChild {

  constructor(private auth:AuthService, private router:Router) {
  }

  canActivate(route:ActivatedRouteSnapshot, state:RouterStateSnapshot):Observable<boolean>{
    if (this.auth.isLoggedIn()){
      return of(true)
    } else {
      this.router.navigate(['/login'])
      return of(false)
    }
  }

  canActivateChild(route:ActivatedRouteSnapshot, state:RouterStateSnapshot):Observable<boolean>{
    return this.canActivate(route, state)
  }
}

@Injectable({
  providedIn:"root"
})
export class AuthRolesGuard implements CanActivate, CanActivateChild {
  constructor(private auth:AuthService, private router:Router, private toast: ToastService) {
  }

  canActivate(route:ActivatedRouteSnapshot, state:RouterStateSnapshot):Observable<boolean>{
    if (this.auth.isAuth){
      let userRoles = route.data['roles'] || [] as number[]
      if (this.auth.user.roles.find( role=> userRoles.includes(role))){
        return of(true)
      } else {
        this.router.navigate(['/'])
        this.toast.show({textOrTpl:'You dont have access to this!!', options:{classname:'bg-danger text-light alert', delay:10000}})
        return of(false)
      }
    } else {
      this.router.navigate(['/login'])
      return of(false)
    }
  }

  canActivateChild(route:ActivatedRouteSnapshot, state:RouterStateSnapshot):Observable<boolean>{
    return this.canActivate(route, state)
  }
}

app-routing.module.ts

import {NgModule} from "@angular/core";
import {RouterModule, Routes} from "@angular/router";
import {AuthLayoutComponent} from "./shared/layouts/auth-layout/auth-layout.component";
import {SiteLayoutComponent} from "./shared/layouts/site-layout/site-layout.component";
import {LoginPageComponent} from "./login-page/login-page.component";
import {RegistrationPageComponent} from "./registration-page/registration-page.component";
import {ProfilePageComponent} from "./profile-page/profile-page.component";
import {AuthGuard, AuthRolesGuard, CheckAuthGuard} from "./shared/classes/auth.guard";
import {MainPageComponent} from "./main-page/main-page.component";

const routes: Routes = [
  {
    path:'', component: AuthLayoutComponent, canActivateChild:[CheckAuthGuard], children: [
      {path:'', component: MainPageComponent},
      {path:'login', component:LoginPageComponent},
      {path:'registration', component:RegistrationPageComponent},
      {path: 'profile', component: ProfilePageComponent, canActivate: [AuthGuard]}
    ]
  },
  {
    path:'admin', component: SiteLayoutComponent,canActivate:[AuthGuard],data:{roles:[10]}, children: [

    ]
  }
]

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

}

auth.service.ts

import {Injectable} from "@angular/core";
import {HttpClient} from "@angular/common/http";
import {Observable} from "rxjs";
import {IUser, UserDto} from "../interfaces";
import {tap} from 'rxjs/operators'

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

  constructor(private http:HttpClient) {
  }

  user = {} as IUser
  isAuth:boolean = false
  isLoading:boolean = false
  register(email:string, password:string, username:string): Observable<UserDto>{
    return this.http.post<UserDto>('/api/user/registration', {email, password, username})
      .pipe(
        tap(
          ({accessToken, user}) => {
            localStorage.setItem('token', accessToken)
            this.setAuth(true)
            this.setUser(user)
          }
        )
      )
  }

  login(email:string, password:string): Observable<UserDto>{
    return this.http.post<UserDto>('/api/user/login', {email, password})
      .pipe(
        tap(
          ({accessToken, user}) => {
            localStorage.setItem('token', accessToken)
            this.setAuth(true)
            this.setUser(user)
          }
        )
      )
  }

  isLoggedIn(){
    return this.isAuth
  }

  setAuth(bool:boolean){
    this.isAuth = bool
  }

  setUser(user:IUser){
    this.user = user
  }

  setLoading(bool:boolean){
    this.isLoading = bool
  }

  logout(): Observable<void> {
    console.log('logout')
    this.setAuth(false)
    this.setUser({} as IUser)
    localStorage.removeItem('token')
    return this.http.post<void>('/api/user/logout', {})
  }

  checkAuth():Observable<UserDto>{
     return this.http.get<UserDto>(`/api/user/refresh`, {withCredentials:true, observe:'body', responseType:'json'})
        .pipe(
          tap(
            (data) => {
              localStorage.setItem('token', data.accessToken)
              this.setAuth(true)
              this.setUser(data.user)
            },
            (error) => {
              console.log(error)
            }
          )
        )
  }

}

【问题讨论】:

  • 你究竟是如何“破解”代码的?它的表现如何,您期望它的表现如何?
  • @AllanJuan 嗨,我想直接访问 /admin 并查看管理页面(我的帐户具有 id 10 的角色),但现在如果我直接访问 /admin 或从主页访问我会看到登录页面,这意味着我没有记录身份证。我在 localStorage 中有我的令牌,所以应该可以。例如,如果我想访问/profile 页面,我也会看到登录页面,但我有令牌。如果我从主页转到个人资料页面,我可以看到它。
  • 例如,在我的app-routing.module.ts 中,在个人资料页面中,AuthGuard 在 CheckAuthGuard 之前工作,我不知道如何解决这个问题。
  • 我认为问题出在 CheckAuthGuard 中,它发出请求,然后 AuthGuard 开始工作,无需等待 CheckAuthGurd

标签: angular typescript angular-router-guards


【解决方案1】:

实际上,您的 canActivate 守卫在当前实施方式上并没有真正意义。

我建议你实现如下功能:

canActivate(route:ActivatedRouteSnapshot, state:RouterStateSnapshot):Observable<boolean>{
    if (this.auth.isAuth){
      return of(true)
    }
    if (!localStorage.getItem('token')) {
      return of(false)
    }
    return this.auth.checkAuth().pipe(
      map(() => true), 
      catchError(() => of(false))
    )
  }
}

笔记:

  • 我调整了结构以降低复杂性。如果您从 if 子句返回,则不需要 else 语句
  • 您可以简单地将来自checkAuth 调用的结果映射到所需的输出,方法是将结果通过管道传输到true 或false。
  • 在我看来,如果在localStorage 中没有找到令牌,那么你的守卫返回true 没有多大意义。我假设您想在这种情况下返回 false。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-10-15
    • 2015-12-24
    • 1970-01-01
    • 2015-05-03
    • 2015-06-25
    • 2017-07-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多