本节展示如何在前台发起的请求中加入令牌以及接收到响应信息后如何更新令牌。
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) | - |
- 序言
- 第一章:Hello World
- 第一节:Angular准备工作
- 1 Node.js
- 2 npm
- 3 WebStorm
- 第二节:Hello Angular
- 第三节:Spring Boot准备工作
- 1 JDK
- 2 MAVEN
- 3 IDEA
- 第四节:Hello Spring Boot
- 1 Spring Initializr
- 2 Hello Spring Boot!
- 3 maven国内源配置
- 4 package与import
- 第五节:Hello Spring Boot + Angular
- 1 依赖注入【前】
- 2 HttpClient获取数据【前】
- 3 数据绑定【前】
- 4 回调函数【选学】
- 第二章 教师管理
- 第一节 数据库初始化
- 第二节 CRUD之R查数据
- 1 原型初始化【前】
- 2 连接数据库【后】
- 3 使用JDBC读取数据【后】
- 4 前后台对接
- 5 ng-if【前】
- 6 日期管道【前】
- 第三节 CRUD之C增数据
- 1 新建组件并映射路由【前】
- 2 模板驱动表单【前】
- 3 httpClient post请求【前】
- 4 保存数据【后】
- 5 组件间调用【前】
- 第四节 CRUD之U改数据
- 1 路由参数【前】
- 2 请求映射【后】
- 3 前后台对接【前】
- 4 更新数据【前】
- 5 更新某个教师【后】
- 6 路由器链接【前】
- 7 观察者模式【前】
- 第五节 CRUD之D删数据
- 1 绑定到用户输入事件【前】
- 2 删除某个教师【后】
- 第六节 代码重构
- 1 文件夹化【前】
- 2 优化交互体验【前】
- 3 相对与绝对地址【前】
- 第三章 班级管理
- 第一节 JPA初始化数据表
- 第二节 班级列表
- 1 新建模块【前】
- 2 初识单元测试【前】
- 3 初始化原型【前】
- 4 面向对象【前】
- 5 测试HTTP请求【前】
- 6 测试INPUT【前】
- 7 测试BUTTON【前】
- 8 @RequestParam【后】
- 9 Repository【后】
- 10 前后台对接【前】
- 第三节 新增班级
- 1 初始化【前】
- 2 响应式表单【前】
- 3 测试POST请求【前】
- 4 JPA插入数据【后】
- 5 单元测试【后】
- 6 惰性加载【前】
- 7 对接【前】
- 第四节 编辑班级
- 1 FormGroup【前】
- 2 x、[x]、{{x}}与(x)【前】
- 3 模拟路由服务【前】
- 4 测试间谍spy【前】
- 5 使用JPA更新数据【后】
- 6 分层开发【后】
- 7 前后台对接
- 8 深入imports【前】
- 9 深入exports【前】
- 第五节 选择教师组件
- 1 初始化【前】
- 2 动态数据绑定【前】
- 3 初识泛型
- 4 @Output()【前】
- 5 @Input()【前】
- 6 再识单元测试【前】
- 7 其它问题
- 第六节 删除班级
- 1 TDD【前】
- 2 TDD【后】
- 3 前后台对接
- 第四章 学生管理
- 第一节 引入Bootstrap【前】
- 第二节 NAV导航组件【前】
- 1 初始化
- 2 Bootstrap格式化
- 3 RouterLinkActive
- 第三节 footer组件【前】
- 第四节 欢迎界面【前】
- 第五节 新增学生
- 1 初始化【前】
- 2 选择班级组件【前】
- 3 复用选择组件【前】
- 4 完善功能【前】
- 5 MVC【前】
- 6 非NULL校验【后】
- 7 唯一性校验【后】
- 8 @PrePersist【后】
- 9 CM层开发【后】
- 10 集成测试
- 第六节 学生列表
- 1 分页【后】
- 2 HashMap与LinkedHashMap
- 3 初识综合查询【后】
- 4 综合查询进阶【后】
- 5 小试综合查询【后】
- 6 初始化【前】
- 7 M层【前】
- 8 单元测试与分页【前】
- 9 单选与多选【前】
- 10 集成测试
- 第七节 编辑学生
- 1 初始化【前】
- 2 嵌套组件测试【前】
- 3 功能开发【前】
- 4 JsonPath【后】
- 5 spyOn【后】
- 6 集成测试
- 7 @Input 异步传值【前】
- 8 值传递与引入传递
- 9 @PreUpdate【后】
- 10 表单验证【前】
- 第八节 删除学生
- 1 CSS选择器【前】
- 2 confirm【前】
- 3 功能开发与测试【后】
- 4 集成测试
- 5 定制提示框【前】
- 6 引入图标库【前】
- 第九节 集成测试
- 第五章 登录与注销
- 第一节:普通登录
- 1 原型【前】
- 2 功能设计【前】
- 3 功能设计【后】
- 4 应用登录组件【前】
- 5 注销【前】
- 6 保留登录状态【前】
- 第二节:你是谁
- 1 过滤器【后】
- 2 令牌机制【后】
- 3 装饰器模式【后】
- 4 拦截器【前】
- 5 RxJS操作符【前】
- 6 用户登录与注销【后】
- 7 个人中心【前】
- 8 拦截器【后】
- 9 集成测试
- 10 单例模式
- 第六章 课程管理
- 第一节 新增课程
- 1 初始化【前】
- 2 嵌套组件测试【前】
- 3 async管道【前】
- 4 优雅的测试【前】
- 5 功能开发【前】
- 6 实体监听器【后】
- 7 @ManyToMany【后】
- 8 集成测试【前】
- 9 异步验证器【前】
- 10 详解CORS【前】
- 第二节 课程列表
- 第三节 果断
- 1 初始化【前】
- 2 分页组件【前】
- 2 分页组件【前】
- 3 综合查询【前】
- 4 综合查询【后】
- 4 综合查询【后】
- 第节 班级列表
- 第节 教师列表
- 第节 编辑课程
- TODO返回机制【前】
- 4 弹出框组件【前】
- 5 多路由出口【前】
- 第节 删除课程
- 第七章 权限管理
- 第一节 AOP
- 总结
- 开发规范
- 备用