带有Angular的Express Auth中间件

时间:2019-01-17 07:02:01

标签: angular express

我正在创建一个应用程序,该应用程序使用Http拦截器在Angular中进行身份验证,并在ExpressJS中进行身份验证中间件。当我通过应用导航到某个路由时,拦截器和auth中间件将起作用,但是当我直接通过URL对其进行访问时,拦截器和auth中间件将起作用。当我直接点击路线时,我从服务器收到“无效授权”响应。

我希望将身份验证中间件应用于多个路由,因此我在要验证的路由之前编写了它。

router.use(express.static(path.join(__dirname, distDir)));

router.use('/auth', require('./auth/authRoutes'));

router.use([auth.checkTokens, auth.verifyTokens.verifyUserToken]);

router.use('/users', require('./user/userRoutes'));

router.use('/courses', require('./course/courseRoutes'));

router.get('/*', (req, res, next) => {
    res.sendFile(path.resolve(__dirname, distDir + '/index.html'));
})

我的检查令牌方法查找在标头中传递的令牌:

exports.checkTokens = (req, res, next) => {
    const userToken = req.headers['authorization'];
    if (userToken) {
        req.access_token = userToken;
        next();
    } else {
        return res.status(400).send({ auth: false, error: 'Invalid grant' });    
    }
}

令牌存储在本地存储中,然后在从前端发送请求之前将其附加到Http拦截器中。

这是正确的实现吗?我想使用本地存储来轻松测试拦截器的功能。将令牌存储在cookie中是否可以解决我的特定问题?

-更新-

我要导航的路线是“ api /课程”。当我单击通过应用程序的链接时,我可以使身份验证有效,但是当我直接在URL地址栏中键入地址时,则无法进行身份验证。我希望能够对用户进行身份验证,即使他们直接在地址栏中键入URL。

这是我的课程组成部分:

@Component({
  selector: 'app-courses',
  templateUrl: './courses.component.html',
  styleUrls: ['./courses.component.less']
})
export class CoursesComponent extends HttpService implements OnInit {
  courses: Course[];

  constructor(private http: HttpClient,
    httpErrorHandler: HttpErrorHandler) {
      super(http, httpErrorHandler, 'JWTService')
    this.courses = [];
  }

  ngOnInit() {
    this.getCourses().subscribe((data: Course[]) => {
      this.courses = data;
    });
  }

  private getCourses(): Observable<Course[]> {
    return super.get<Course[]>('courses', this.courses, 'get courses')
  }
}

我在路由中设置了一个身份验证保护:

export const HomeRoutes: Routes = [
    {
        path: '', 
        component: HomeComponent,
        canActivate: [AuthGuard]
    },
    {
        path: 'user-settings', 
        component: UserSettingsComponent,
        canActivate: [AuthGuard]
    },
    {
        path: 'courses',
        component: CoursesComponent,
        canActivate: [AuthGuard]
    },
    {
        path: 'course-form',
        component: CourseFormComponent,
        canActivate: [AuthGuard]
    }
  ];

这是我的身份验证管理员:

@Injectable({
  providedIn: 'root'
})
export class AuthGuard implements CanActivate {
  constructor(private router: Router) { }

  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
    if (localStorage.getItem('access_token')) {
      // logged in so return true
      return true;
    }

    // not logged in so redirect to login page with the return url
    this.router.navigate(['/auth/login'], { queryParams: {} });
    // this.router.navigate(['/auth'], { queryParams: { returnUrl: state.url } });
    return false;
  }
}

这是我在Angular中的拦截器类:

@Injectable()
export class TokenInterceptor implements HttpInterceptor {
  private isRefreshingToken = false;
  private accessToken = localStorage.getItem('access_token')
  tokenSubject: BehaviorSubject<string> = new BehaviorSubject<string>(null);
  oldToken = localStorage.getItem('access_token');

  constructor(public authService: AuthService, private http: HttpClient) {}

  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    request = this.addToken(request, this.accessToken);
    return next.handle(request).pipe(
      catchError(error => {
        console.log(error)
        if (error instanceof HttpErrorResponse) {
          switch((<HttpErrorResponse>error).status) {
            case 400:
            // console.log('handling 400 error')
              return this.handle400Error(error);
            case 401: 
            // console.log('handling 401 error')
              this.authService.refresh().subscribe((data: { access_token: string, refresh_token: string}) => {
                localStorage.setItem('access_token', data.access_token)
                localStorage.setItem('refresh_token', data.refresh_token)
              })
              return this.handle401Error(request, next);
          }
        } else {
          return throwError(error);
        }
      })
    );
  }

  private handle400Error(error: HttpErrorResponse): Observable<HttpEvent<any>> {
    if (error && error.status === 400 && error.error && error.error.error === 'invalid_grant') {
      // If we get a 400 and the error message is 'invalid_grant', the token is no longer valid so logout.
      return this.logoutUser();
    }
    return throwError(error);
  }

  private logoutUser() {
    this.authService.logout()
    return throwError('Error. Logged out user.')
  }

  private handle401Error(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    if (!this.isRefreshingToken) {
      this.isRefreshingToken = true;

      // Reset here so that the following requests wait until the token
      // comes back from the refreshToken call.
      this.tokenSubject.next(null);

      return this.authService.refreshToken().pipe(
        switchMap((newToken: string) => {
          newToken = localStorage.getItem('access_token');
          if (newToken) {
            this.tokenSubject.next(newToken);
            if (this.oldToken === newToken) {
              return this.logoutUser();
            } else {
              return next.handle(this.addToken(req, newToken));
            }
          }

          // If we don't get a new token, we are in trouble so logout.
          return this.logoutUser();
        }),
        catchError(error => {
          // If there is an exception calling 'refreshToken', bad news so logout.
          return this.logoutUser();
        }),
        finalize(() => {
          this.isRefreshingToken = false;
        })
      );
    } else {
      return this.tokenSubject.pipe(
        filter(token => token != null),
        take(1),
        switchMap(token => {
          return next.handle(this.addToken(req, token));
        })
      )
    }
  }

  private addToken(request: HttpRequest<any>, access_token: string): HttpRequest<any> {
    return request.clone({
      setHeaders: {
        Authorization: `Bearer: ${access_token}`
      }
    });
  }
}

0 个答案:

没有答案