Angular 7请求OAuth Token报401 Unauthorized问题排查与解决
我之前在Angular 7项目里请求OAuth Token时也碰到过一模一样的401问题——Postman测试完全正常,但自己写的代码就是返回Unauthorized,折腾了好一会儿才找到问题根源。
问题重现
先看看我最开始写的代码:
signin() { let signinData = this.signinForm.value; this.encoded = btoa("my-trusted-client:secret"); let header = new Headers(); header.append('Authorization', 'Basic ' + this.encoded); header.append('Content-type', 'application/x-www-form-urlencoded; charset=utf-8'); this.http.post(this.url + '/oauth/token?grant_type=password&username=' + signinData.username + '&password=' + signinData.password, { headers: header }) .subscribe(data => { console.log(data); }) }
返回的错误信息是这样的:
{ "error": "Unauthorized", "message": "Unauthorized", "path": "/barometre/oauth/token", "status": 401, "timestamp": "2019-04-19T12:35:47.699+0000" }
问题根源
问题出在请求参数的传递方式上:虽然我设置了Content-type为application/x-www-form-urlencoded,但实际是把参数直接拼在了URL里,而且Post请求的body部分是空的(第二个参数错误地传了headers配置,不是请求体)。OAuth的Token接口通常要求参数通过表单形式放在请求体中,而不是URL查询参数,这就导致服务端无法正确解析参数,直接返回了401。
修复方案
我们需要把grant_type、username、password这些参数通过表单形式放在请求体里传递,具体步骤如下:
- 新增一个序列化函数,把普通对象转换成
URLSearchParams格式:
serialize(obj: any): URLSearchParams { let params: URLSearchParams = new URLSearchParams(); for (var key in obj) { if (obj.hasOwnProperty(key)) { var element = obj[key]; params.set(key, element); } } return params; };
- 重构
signin方法,正确传递请求体参数:
signin() { let signinData = this.signinForm.value; this.encoded = btoa("my-trusted-client:secret"); let header = new Headers(); header.append('Authorization', 'Basic ' + this.encoded); header.append('Content-type', 'application/x-www-form-urlencoded; charset=utf-8'); // 构造请求参数对象 let data = { grant_type: 'password', username: signinData.username, password: signinData.password }; let params: URLSearchParams = this.serialize(data); // 正确传递请求体参数 this.http.post(this.url + '/oauth/token', params, { headers: header }) .subscribe(data => { console.log(data); }) }
为什么这样能解决问题?
现在参数是通过URLSearchParams序列化后放在Post请求的body里,完全符合application/x-www-form-urlencoded的格式要求,服务端能正确解析到grant_type、用户名和密码。而Postman之所以能成功,就是因为它默认会把表单参数正确放在请求体中,和我们修改后的请求格式完全一致。
内容的提问来源于stack exchange,提问作者Ragnar Lodbrok
相关产品推荐
相关产品推荐

