前言

最近在写后端的时候,需要与其他后端服务进行沟通调用,为此就涉及到了信任的问题,对方如何知道我的请求是可信的,总不能随便一个人的请求它就要进行反馈,所以就用到了OAuth2中的Client Credentials 客户端模式来实现认证,通过一个指定的api请求获取到token,然后之后的其它api通信都通过携带这个token来实现认证。

其实就和前端的token校验是模式是一样的,但是唯一不同的就是我怎么去发送一个Client Credentials规范的请求来获取token。

OAuth2 有哪些模式?

为了了解Client Credentials 模式,就不得不了解下OAuth2了。

OAuth2是一个关于授权的开发网络标准,专门用于第三方应用来获取用户数据的,当然也不止是获取用户的数据,比如调用用户的资源也是可行的。

他主要解决的就是可信度的问题,比如最常见的场景就是第三方登录,用户肯定不会把自己的账号密码告诉你,我们通过第三方的接口,获取到了一个code,后端通过这个code来请求第三方的api来获取用户的基本信息,比如常见的谷歌登录、Github登录,国内的QQ啊微信啊,其实都是这方式,但是这仅仅是OAuth2中的authorization code(授权码模式)其中一种模式。

OAuth2包含以下四种:

  • resource owner password credentials 密码模式
  • authorization code 授权码模式
  • implicit 简化模式
  • client credentials 客户端模式

它们解决不同情况下的授权问题。

密码模式

最不推荐的一种模式,他让用户自己将账号密码提交给你,然后你使用账号密码直接登录使用,从而换取到access_token

理论上客户端是不能存储密码的,虽然用户给了你,但是谁知道呢,除非是非常可信的客户端,比如大厂或者自营的。

授权码模式

算是非常正宗的OAuth2授权模式,用户点击登录跳转到第三方登录页,比如谷歌登录页,用户登录完毕后会重定向回来,此时会在链接上携带code参数,客户端获取到code参数再通过后端请求第三方的api,从而获取到授权信息。

常见的就有:

  • 谷歌第三方登录
  • Github第三方登录
  • 微信扫码登录

它们基本上都是保持了这种规范,可能触发的方式之类的会有不同。

简化模式

这种方式不在是携带code返回,而是直接携带access_token返回,这种方式简化了流程,但是也使得access_token暴露了。

客户端模式

客户端模式要求请求时提供客户端ID和客户端的密匙(Secret),这种方式不再像前三种一样,需要用户参与授权流程的操作,用户只需要提供好客户端id和密匙,后端就能直接通过他们来获取到access_token,不用什么链接重定向,用户登录操作之类的。

这种方式非常适合用来消费用户的资源,常见的就是第三方付费的api了,用户去开通了一个服务,再用第三方的客户端来调用这些服务。

后端如何发送一个客户模式请求

了解完OAuth2的模式之后,可以明白使用client credentials客户端模式来发请求是合理的,那我们又该如何发送这个请求呢?

不多说,直接上个xhr的demo代码:

var xhr = new XMLHttpRequest();

// 将id和密匙进行 Base64 编码
var clientCredentials = btoa('客户端id:客户端密匙');

// 打开一个 POST 请求
xhr.open('POST', 'API请求地址', true);

// 设置请求头,包括基本认证和内容类型
xhr.setRequestHeader('Authorization', 'Basic ' + clientCredentials);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');

// 处理响应
xhr.onreadystatechange = function() {
    if (xhr.readyState === 4) { // 请求完成
        if (xhr.status === 200) { // HTTP 200 OK
            var response = JSON.parse(xhr.responseText);
            // 处理成功响应
            console.log(response);
        } else {
            // 处理错误
            console.error('Error: ' + xhr.status);
        }
    }
};

// 发送请求
xhr.send();

具体的要求,比如有的API要求携带query参数,自己api地址那拼接就好了,有的还可能要求一些头信息,这些都可以自己调整。

如果我们使用axios,还可以简化操作:

import axios from "axios";

const instance = axios.create();

instance({
  url: "API地址",
  method: "POST",
  headers: {
    "Content-Type": "application/x-www-form-urlencoded"
  },
  auth: {
      username: "客户端id",
      password: "客户端密码"
  }
}).then(res => {
  console.log(res);
})

如果你有了解过Basic Auth你会发现axios这个不就是Basic Auth请求吗?

其实客户端模式的请求和Basic Auth基本认证差不多的,它们的都是将账号密码或者id密匙加密成base64,然后通过Authorization头信息携带过去,最终返回token。

所以我们完全可以利用axios的auth参数属性来实现我们的客户端请求。

我们可以看一下一个Basic Auth的xhr例子:

var xhr = new XMLHttpRequest();
var userCredentials = btoa('username:password'); // Base64 编码

xhr.open('GET', 'https://example.com/resource', true);
xhr.setRequestHeader('Authorization', 'Basic ' + userCredentials);

xhr.onreadystatechange = function() {
    if (xhr.readyState === 4 && xhr.status === 200) {
        console.log(xhr.responseText);
    } else if (xhr.readyState === 4) {
        console.error('Error: ' + xhr.status);
    }
};

xhr.send();

可以看到基本无差。

和Basic Auth的区别?

那么和Basic Auth有什么区别呢?

其实这要从理念上来区别,虽然他们的动作一致,但是理念不同。

首先用户密码的授权方式非常不安全,第一用户信任度的问题,第二是如果用户修改了密码,你这授权不就获取不到了,其次安全问题,如果存放了密码,万一泄露了损失的可是用户。

如果使用客户端id和密匙,这个东西首先是通过用户登录平台来获取的,不受修改账号密码限制,而且可以设置有效性。

如果泄露了,那么用户只需要登录平台,删除旧的客户端id和密匙,重新生成新的使用即可,相比较Basic Auth的方式,client credentials还是更加安全的。

分类: Nest.js 标签: NesjsOAuth2客户端模式client credentialsBasic Auth

评论

暂无评论数据

暂无评论数据

目录