我正在创建一个应用程序,该应用程序使用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}`
}
});
}
}