本节展示如何在前台发起的请求中加入令牌以及接收到响应信息后如何更新令牌。 angular提供的**拦截器**机制与spring的过滤器一样可以提供在header中传递令牌的功能。以前台获取所有的教师为例,时序图如下: ![](https://img.kancloud.cn/83/4a/834a6de5e2a0f32bb6f604d417e63865_552x507.png) # 初始化拦截器 于src/app/core中使用`ng g class authTokenInterceptor`命令创建一个拦截器。 ``` panjiedeMac-Pro:core panjie$ ng g class authTokenInterceptor CREATE src/app/core/auth-token-interceptor.spec.ts (212 bytes) CREATE src/app/core/auth-token-interceptor.ts (38 bytes) ``` 初始化代码如下: src/app/core/auth-token-interceptor.ts ```javascript import {Injectable} from '@angular/core'; import {HttpEvent, HttpHandler, HttpInterceptor, HttpRequest} from '@angular/common/http'; import {Observable} from 'rxjs'; @Injectable() export class AuthTokenInterceptor implements HttpInterceptor ➋{ intercept(req: HttpRequest<any> ➌, next: HttpHandler ➍): Observable<HttpEvent<any>> ➎{ console.log('拦截到请求信息。请求地址:' + req.url + '; 请求方法:' + req.method); return next.handle(req); ➏ } } ``` * ➊ 固有写法(声明该类可被注入) * ➋ 继承HttpInterceptor(可以规避一些语法方面的错误) * ➌ 上一个处理者传递过来的请求信息 * ➍ 下一个处理者(可能也是一个拦截器,也可能直接就转发给后台了) * ➎ 返回值,此返回值将传递给上一个处理者 * ➏ 将请求信息传递给下一个处理者,被将下一个处理者返回的信息返回给上一个处理者 在当面项中可以理解为:上一个处理者指的教师列表组件对应的C层;下一个处理者指的是后台。 # 提供拦截器 有了拦截器后,使用以下代码为项目提供此拦截器,以使拦截器在项目中生效: src/app/app.module.ts ```javascript providers: [ {provide: HTTP_INTERCEPTORS ➊, useClass: AuthTokenInterceptor ➋, multi: true ➌} ], ``` * 当依赖需要➊时,将实例化的➋注入。 * ➌ 此依赖支持多个(可以为项目配置多个拦截器,发起请求时数据依次通过配置的多个拦截器) # 测试 使用`ng serve`启动前台并来到教师管理`http://localhost:4200/teacher`,查看控制台信息如下: ![](https://img.kancloud.cn/45/19/45197af34910e59aed93fb709b96274a_492x75.png) 如此便可以在前台发起请求时进一步定制header中的auth-token的值了。 # 加入header HttpRequest中提供了直接修改header的方法,利用此方法可以方便的向请求中添加header信息: src/app/core/auth-token-interceptor.ts ```javascript console.log('拦截到请求信息。请求地址:' + req.url + '; 请求方法:' + req.method); req.headers.set('auth-token', '123456'); return next.handle(req); ``` ## 测试 再次刷新`http://localhost:4200/teacher`后查看控制台中的网络选项卡,找到后台的请求记录查看header情况: ![](https://img.kancloud.cn/f0/c5/f0c5de1ec2463b8d10deaf6deb0dc847_966x386.png) 却没有找到对应的key为auth-token的记录。这是由于angular拦截器中的request与spring过滤器中的request一样,都不希望request中的数据被用户修改。所以即使是使用了`req.headers.set`方法设置了header中auth-token的值,也不会起作用。 ## req.clone spring中解决此问题的时候使用的装饰器,很大部分原因是由于spring已经提供了好用的装饰器HttpServletRequestWrapper。而angular为了解决此问题提供了一个clone方法。使用方法如下: ``` console.log('拦截到请求信息。请求地址:' + req.url + '; 请求方法:' + req.method); const reqClone = req.clone({ ➊ headers: req.headers.set('auth-token', '123456') }); return next.handle(reqClone); } ``` * ➊ 调用clone()方法由复制一份与原请求相同的请求信息 * ➋ 在复制的过程中,使用附加了auth-token的header信息覆盖原信息 再次测试: ![](https://img.kancloud.cn/93/d1/93d103ee115b8471ce6b22f049cc2bee_433x201.png) 此外,angular认为在拦截器中对header进行设置是一项较常规的操作,所以还提供了更简单的`setHeaders`方法: ```javascript intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { const reqClone = req.clone({ setHeaders: {'auth-token': '123456'} }); return next.handle(reqClone); } ``` # 集成测试 最后,启动后台以进行集成测试以验证前后台的配合情况。 测试前先清除session缓存以免发生CORS错误。 ![](https://img.kancloud.cn/50/9d/509db51e1ed2fea9531cab814e49737f_1194x406.png) ## 测试一:无效token 传入无效token 123456 ![](https://img.kancloud.cn/78/e3/78e3279369f042e316d4e992faa33b71_460x341.gif) 分发新token并返回。 ## 测试二:不传token ```javascript intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { const reqClone = req.clone({ setHeaders: {'auth-token': '123456'} ✘ }); return next.handle(reqClone); } ``` ![](https://img.kancloud.cn/fb/21/fb2113ec42db272fe21855457128f366_460x341.gif) 未传入token,则分发新token ## 测试三:传入有效token ```javascript intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { const reqClone = req.clone({ setHeaders: {'auth-token': 'af1c0c77-67d0-4ec2-8321-2f88e32f76af★'} }); return next.handle(reqClone); } ``` * ★ 本地测试时,请修改为测试一或测试二中返回的auth-token值 ![](https://img.kancloud.cn/0b/53/0b53f15ff8206cbd7d86d18f38db58cf_460x341.gif) 传入有效token时,在向应信息中又获取到了此有效token # 参考文档 | 名称 | 链接 | 预计学习时长(分) | | --- | --- | --- | | 源码地址 | [https://github.com/mengyunzhi/spring-boot-and-angular-guild/releases/tag/step5.2.0](https://github.com/mengyunzhi/spring-boot-and-angular-guild/releases/tag/step5.2.0) | - |