Express Auth Middleware с Angular - PullRequest
       1

Express Auth Middleware с Angular

0 голосов
/ 17 января 2019

Я создаю приложение, которое использует перехватчик Http для аутентификации в Angular и промежуточное программное обеспечение аутентификации в ExpressJS. Промежуточное программное обеспечение перехватчика и аутентификации работает, когда я перехожу к маршруту через приложение, но не когда я нажимаю на него напрямую через URL. Когда я попадаю на маршрут напрямую, я получаю ответ «недопустимое разрешение» от сервера.

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

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}`
      }
    });
  }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...