# 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) | |