# x-auth-token
发生401的根本原因在于后台认为当前请求非认证用户(未认证或认证信息失败),若要使后台认为当前请求为认证用户发起的请求,则需要在请求时向后台发送有效的认证信息。
发送认证信息的方式有很多,比如我们前面在登录刚刚学习过的Basic认证,还有我们此节中将介绍的x-auth-token认证。至于应该使用使用样的认证,则需要依照当前后台的支持情况。
## Basic 认证
在学习Basic access authentication认证时,我们说后台提供的认证接口并不拘泥为后台提供的认证地址。这是因为如果后台支持Basic认证,则所有需要认证才能够获取的资源地址,都可以做为认证地址。换一种方式表达就是:每个需要认证才能获取的资源地址,都可以通过传入Basic认证信息的方式来解决认证问题。
获取当前登录用户的资源地址也当然如此。所以,我们可以在请求当前登录信息时,在header中加入用户的认证信息。这样一来,后台通过Basic认证信息便可以验证我们的合法身份,近而不再抛出401异常了。
```typescript
+++ b/first-app/src/app/personal-center/personal-center.component.ts
export class PersonalCenterComponent implements OnInit {
me = {} as Teacher;
constructor(private httpClient: HttpClient) {
}
ngOnInit(): void {
const authString = 'zhangsan:codedemo.club';
const authToken = btoa(authString);
let httpHeaders = new HttpHeaders();
httpHeaders = httpHeaders.append('Authorization', 'Basic ' + authToken);
const url = 'http://angular.api.codedemo.club:81/teacher/me';
this.httpClient.get<Teacher>(url, {headers: httpHeaders})
.subscribe(teacher => {
console.log('请求当前登录用户成功');
this.me = teacher;
},
error => console.log('请求当前登录用户发生错误', error));
}
}
```
> 请在浏览器中打开http://angular.api.codedemo.club/teacher/确认当前系统可用的有效用户名。
![image-20210308141418182](https://img.kancloud.cn/70/5e/705e0eceae69ca839c3bb387b29001d1_2270x690.png)
这种方式虽然可行,但如果你就这么开始使用的话绝对又是在挖坑。很多时候我们加入团队的新成员,都喜欢在违反团队规定的时候问个为什么。问为什么是好的习惯,应该表扬并坚持。但在团队开发中,应该是先遵守团队规范开发,然后再问为什么。而不是违背团队规范的同时还问为什么。
为什么这种方式可用,又不能用呢?其实类似这样的问题,都会有非常有说服力的答案,我们做为新手想不明白为什么是非常正常的事情,这受限你当前接触的领域、所学的知识以及没有过多的接触生产环境多种因素。而如果因为自己想不明白为什么就不按规范开发,就是没有团队精神的表现。
我们的确可以在用户登录时记住用户的用户名和密码,然后用户名密码放到每次请求的header中进行认证。在我们不考虑安全因素的情况下,其在如下场景下则会产生bug。
1. 张三在地点A,登录了系统
2. 张三正常使用系统
3. 管理员在地点B变更了张三用户名或密码
4. 张三使用原用户名密码登录时产生错误
当然了,如下场景也会产生BUG。
1. 张三使用手机APP登录了系统
2. 张三又使用了电脑登录了系统
3. 张三在手机APP上修改了密码
4. 张三在电脑上使用的系统将产生错误
综上所述Basic 认证仅适用于首次登录系统时使用,用户登录成功后则应该使用其它方法来完成用户的认证。
## X-AUTH-TOKEN
在同源情况下浏览器在请求时自动带入了cookie信息,所谓的cookie信息其实就是后台在header中发送给前台的一串字符。后台在首次请求时生成了这串字符,并在响应时加入到响应header中,浏览器接收响应后根据Cookie关键字在响应header中获取到这串字符串,并缓存在了本地。当下次再向该地址发起请求时,浏览器又自动加入了缓存的Cookie信息。
![image-20210308143321976](https://img.kancloud.cn/fa/ac/faac94d9e1c8427c35175bf9f061ccd9_2510x1644.png)
X-auth-token的认证的方式也是这个原理,不同的是header中的cookie会被浏览器在同源请求时自动缓存,自动带入请求header。而X-auth-token则需要我们手动的处理。
我们当前的后台,在请求无论用户认证与否,都可以在响应头中x-auth-token:
![image-20210308154835779](https://img.kancloud.cn/ce/50/ce503208ada3a95f5f363779d6ab6b37_1170x174.png)
用户一旦登录成功,该x-auth-token便会自动在后台完成与登录用户的绑定,我们再次请求时可以将该token以header的形式传入后台。
在当前代码的基础上,我们在控制台的网络中找到这个x-auth-token:
![image-20210308160304037](https://img.kancloud.cn/5e/f0/5ef0978d1714661bbed387a6f2bf7843_2300x804.png)
并把它复制下来,然后将其加入到请求当前登录用户的header中:
**注意**:你在学习时将会得到一组与教程不一样的x-auth-token,你应该使用自己得到的那个,而非照抄教程提供的。
```typescript
+++ b/first-app/src/app/personal-center/personal-center.component.ts
ngOnInit(): void {
let httpHeaders = new HttpHeaders();
httpHeaders = httpHeaders.append('x-auth-token', '02d4bb26-991e-4902-b80f-3ef436909766');
const url = 'http://angular.api.codedemo.club:81/teacher/me';
this.httpClient.get<Teacher>(url, {headers: httpHeaders})
.subscribe(teacher => {
console.log('请求当前登录用户成功');
this.me = teacher;
},
error => console.log('请求当前登录用户发生错误', error));
}
```
![image-20210308160657855](https://img.kancloud.cn/9f/fe/9ffeb37be265602764ad284490e617f5_1382x342.png)
![image-20210308160748603](https://img.kancloud.cn/54/5e/545e75983a105ee2d832af50ba0666bf_806x290.png)
如此以来,我们便可以使用在header中携带X-AUTH-TOKEN的方法模拟了cookie的登录模式。与cookie不同的是,我们需要为每次请求手动的添加这一header信息。
## 获取响应header
在控制台中来获取x-auth-token显然是不切实际的。如果要使用这个x-auth-token来完成用户认证则需要在响应中获取这个header信息。
HttpClient可以很轻构的获取到响应的header信息:
```typescript
+++ b/first-app/src/app/personal-center/personal-center.component.ts
import {HttpClient, HttpHeaders, HttpResponse} from '@angular/common/http';
ngOnInit(): void {
let httpHeaders = new HttpHeaders();
const authString = 'zhangsan:codedemo.club';
const authToken = btoa(authString);
httpHeaders = httpHeaders.append('Authorization', 'Basic ' + authToken);
const url = 'http://angular.api.codedemo.club:81/teacher/me';
this.httpClient.get<Teacher>(url,
{headers: httpHeaders, observe: 'response'}) 👈
.subscribe((teacherResponse: HttpResponse<Teacher>) => { 👈
console.log('请求当前登录用户成功');
this.me = teacherResponse.body as Teacher; 👈
console.log(teacherResponse.headers.get('x-auth-token')); 👈
},
error => console.log('请求当前登录用户发生错误', error));
}
```
在请求选项中,增加`observe`并设置为`response`。此时请求成功接收到响应数据格式为`HttpResponse`,响应的主体位于`HttpResponse.body`中,响应头位于`HttpResponse.headers`中。
![image-20210308163708698](https://img.kancloud.cn/7f/2e/7f2e0c2fc88fb0e9a7a1764680e7bc22_646x128.png)
## 本节作业
是时候考核下自己的所学成果了,请实现以下功能:
1. 用户输入正确的用户名密码后完成登录。
2. 登录完成后,继续访问http://localhost:4200/personal-center时显示当前登录用户信息。
相信自己,根据当前所学,你绝对已经具备了解决上述问题的能力。
| 名称 | 地址 | |
| -------------------------- | ------------------------------------------------------------ | ---- |
| HttpClient获取完成的响应体 | [https://angular.cn/guide/http#reading-the-full-response](https://angular.cn/guide/http#reading-the-full-response) | |
| 本节源码 | [https://github.com/mengyunzhi/angular11-guild/archive/step4.4.zip](https://github.com/mengyunzhi/angular11-guild/archive/step4.4.zip) | |
- 序言
- 第一章 Hello World
- 1.1 环境安装
- 1.2 Hello Angular
- 1.3 Hello World!
- 第二章 教师管理
- 2.1 教师列表
- 2.1.1 初始化原型
- 2.1.2 组件生命周期之初始化
- 2.1.3 ngFor
- 2.1.4 ngIf、ngTemplate
- 2.1.5 引用 Bootstrap
- 2.2 请求后台数据
- 2.2.1 HttpClient
- 2.2.2 请求数据
- 2.2.3 模块与依赖注入
- 2.2.4 异步与回调函数
- 2.2.5 集成测试
- 2.2.6 本章小节
- 2.3 新增教师
- 2.3.1 组件初始化
- 2.3.2 [(ngModel)]
- 2.3.3 对接后台
- 2.3.4 路由
- 2.4 编辑教师
- 2.4.1 组件初始化
- 2.4.2 获取路由参数
- 2.4.3 插值与模板表达式
- 2.4.4 初识泛型
- 2.4.5 更新教师
- 2.4.6 测试中的路由
- 2.5 删除教师
- 2.6 收尾工作
- 2.6.1 RouterLink
- 2.6.2 fontawesome图标库
- 2.6.3 firefox
- 2.7 总结
- 第三章 用户登录
- 3.1 初识单元测试
- 3.2 http概述
- 3.3 Basic access authentication
- 3.4 着陆组件
- 3.5 @Output
- 3.6 TypeScript 类
- 3.7 浏览器缓存
- 3.8 总结
- 第四章 个人中心
- 4.1 原型
- 4.2 管道
- 4.3 对接后台
- 4.4 x-auth-token认证
- 4.5 拦截器
- 4.6 小结
- 第五章 系统菜单
- 5.1 延迟及测试
- 5.2 手动创建组件
- 5.3 隐藏测试信息
- 5.4 规划路由
- 5.5 定义菜单
- 5.6 注销
- 5.7 小结
- 第六章 班级管理
- 6.1 新增班级
- 6.1.1 组件初始化
- 6.1.2 MockApi 新建班级
- 6.1.3 ApiInterceptor
- 6.1.4 数据验证
- 6.1.5 教师选择列表
- 6.1.6 MockApi 教师列表
- 6.1.7 代码重构
- 6.1.8 小结
- 6.2 教师列表组件
- 6.2.1 初始化
- 6.2.2 响应式表单
- 6.2.3 getTestScheduler()
- 6.2.4 应用组件
- 6.2.5 小结
- 6.3 班级列表
- 6.3.1 原型设计
- 6.3.2 初始化分页
- 6.3.3 MockApi
- 6.3.4 静态分页
- 6.3.5 动态分页
- 6.3.6 @Input()
- 6.4 编辑班级
- 6.4.1 测试模块
- 6.4.2 响应式表单验证
- 6.4.3 @Input()
- 6.4.4 FormGroup
- 6.4.5 自定义FormControl
- 6.4.6 代码重构
- 6.4.7 小结
- 6.5 删除班级
- 6.6 集成测试
- 6.6.1 惰性加载
- 6.6.2 API拦截器
- 6.6.3 路由与跳转
- 6.6.4 ngStyle
- 6.7 初识Service
- 6.7.1 catchError
- 6.7.2 单例服务
- 6.7.3 单元测试
- 6.8 小结
- 第七章 学生管理
- 7.1 班级列表组件
- 7.2 新增学生
- 7.2.1 exports
- 7.2.2 自定义验证器
- 7.2.3 异步验证器
- 7.2.4 再识DI
- 7.2.5 属性型指令
- 7.2.6 完成功能
- 7.2.7 小结
- 7.3 单元测试进阶
- 7.4 学生列表
- 7.4.1 JSON对象与对象
- 7.4.2 单元测试
- 7.4.3 分页模块
- 7.4.4 子组件测试
- 7.4.5 重构分页
- 7.5 删除学生
- 7.5.1 第三方dialog
- 7.5.2 批量删除
- 7.5.3 面向对象
- 7.6 集成测试
- 7.7 编辑学生
- 7.7.1 初始化
- 7.7.2 自定义provider
- 7.7.3 更新学生
- 7.7.4 集成测试
- 7.7.5 可订阅的路由参数
- 7.7.6 小结
- 7.8 总结
- 第八章 其它
- 8.1 打包构建
- 8.2 发布部署
- 第九章 总结