带着问题看源码2-NodeRed的用户认证机制是怎样的

博客迁移

不恰饭的小站

文章目录

  • 博客迁移
    • 1. 几种常用的认证机制
      • 1.1. HTTP Basic Auth Basic Auth是开放平台的两种认证方式,简单点说明就是每次请求API时都提供用户的username和password。
      • 1.2. OAuth OAuth是一个关于授权(authorization)的开放网络标准,在全世界得到广泛应用,目前的版本是2.0版。 OAuth在"客户端"与"服务提供商"之间,设置了一个授权层(authorization layer)。"客户端"不能直接登录"服务提供商",只能登录授权层,以此将用户与客户端区分开来。"客户端"登录授权层所用的token(token),与用户的密码不同。用户可以在登录的时候,指定授权层token的权限范围和有效期。 "客户端"登录授权层以后,"服务提供商"根据token的权限范围和有效期,向"客户端"开放用户储存的资料。 客户端必须得到用户的授权(authorization grant),才能获得token(access token)。 主要流程如下: ![主要流程](https://img-blog.csdnimg.cn/img_convert/efaed7414879d1b810e5e6da5cc66039.png))
    • 2. NodeRed中的认证机制使用 NodeRed使用 OAuth 2.0 实现认证
      • 2.1. 基于用户名/密码凭据的身份验证 要在编辑器和管理API上启用用户身份验证,在settings.js文件中取消adminAuth属性的注释: ```adminAuth: {
      • 2.2. 针对任何OAuth/OpenID提供者(如Twitter或GitHub)进行身份验证 要使用外部身份验证源,Node-RED可以使用Passport提供的各种策略。 Node-RED认证模块可以用于Twitter和GitHub。他们总结了一些具体的策略细节,使其更易于使用。但它们也可以用作其他类似策略的身份验证模板。 下面的示例演示如何配置针对Twitter的身份验证,而不使用我们提供的auth模块。

1. 几种常用的认证机制

1.1. HTTP Basic Auth Basic Auth是开放平台的两种认证方式,简单点说明就是每次请求API时都提供用户的username和password。

  • 优点:
    • 使用非常简单,
    • 开发和调试工作简单,
    • 没有复杂的页面跳转逻辑和交互过程;
    • 更利于发起方控制;
  • 缺点:
    • 安全性低,每次都需要传递用户名和密码,用户名和密码很大程度上存在被监听盗取的可能;一次密码盗用,会导致所有使用此密码的全部应用处于风险之中
    • 同时应用本地还需要保存用户名和密码,在应用本身的安全性来说,也存在很大问题;
    • 开放平台服务商出于自身安全性的考虑(第三方可以得到该服务商用户的账号密码,对于服务商来说是一种安全隐患),未来也会限制此认证方式(Twitter就计划在6月份停止Basic Auth的支持)
    • 用户如果更改了用户名和密码,还需要重新进行密码校验的过程。

1.2. OAuth OAuth是一个关于授权(authorization)的开放网络标准,在全世界得到广泛应用,目前的版本是2.0版。 OAuth在"客户端"与"服务提供商"之间,设置了一个授权层(authorization layer)。“客户端"不能直接登录"服务提供商”,只能登录授权层,以此将用户与客户端区分开来。"客户端"登录授权层所用的token(token),与用户的密码不同。用户可以在登录的时候,指定授权层token的权限范围和有效期。 "客户端"登录授权层以后,"服务提供商"根据token的权限范围和有效期,向"客户端"开放用户储存的资料。 客户端必须得到用户的授权(authorization grant),才能获得token(access token)。 主要流程如下: 带着问题看源码2-NodeRed的用户认证机制是怎样的_第1张图片)

2. NodeRed中的认证机制使用 NodeRed使用 OAuth 2.0 实现认证

2.1. 基于用户名/密码凭据的身份验证 要在编辑器和管理API上启用用户身份验证,在settings.js文件中取消adminAuth属性的注释: ```adminAuth: {

type: "credentials",
users: [
    {
        username: "admin",
        password: "$2a$08$zZWtXTja0fB1pzD4sHCMyOCMYz2Z6dNbM6tl8sJogENOMcxWV9DN.",
        permissions: "*"
    },
    {
        username: "george",
        password: "$2b$08$wuAqPiKJlVN27eF5qJp.RuQYuy6ZYONW7a/UWYxDTtwKFCdB8F19y",
        permissions: "read"
    }
] } ```users属性是一个user对象数组。这允许您定义多个用户,每个用户可以拥有不同的权限。 上面的示例配置定义了两个用户。一个叫admin的人可以在编辑器内做任何事情,并且有一个密码为password的密码。另一个叫george,他被赋予只读访问权限。 请注意,密码是使用bcrypt算法生成的。

2.2. 针对任何OAuth/OpenID提供者(如Twitter或GitHub)进行身份验证 要使用外部身份验证源,Node-RED可以使用Passport提供的各种策略。 Node-RED认证模块可以用于Twitter和GitHub。他们总结了一些具体的策略细节,使其更易于使用。但它们也可以用作其他类似策略的身份验证模板。 下面的示例演示如何配置针对Twitter的身份验证,而不使用我们提供的auth模块。

    type:"strategy",
    strategy: {
        name: "twitter",
        label: 'Sign in with Twitter',
        icon:"fa-twitter",
        strategy: require("passport-twitter").Strategy,
        options: {
            consumerKey: TWITTER_APP_CONSUMER_KEY,
            consumerSecret: TWITTER_APP_CONSUMER_SECRET,
            callbackURL: "http://example.com/auth/strategy/callback",
            verify: function(token, tokenSecret, profile, done) {
                done(null, profile);
            }
        },
    },
    users: [
       { username: "knolleary",permissions: ["*"]}
    ] }; ```
### 2.3. 自定义用户身份验证 除了将用户硬编码到设置文件中,还可以插入自定义代码对用户进行身份验证。这使得与现有的身份验证方案集成成为可能。

1. 将以下内容保存到一个名为/user-authentication.js的文件中 ```module.exports = {    type: "credentials",    users: function(username) {
       return new Promise(function(resolve) {
           // Do whatever work is needed to check username is a valid
           // user.
           if (valid) {
               // Resolve with the user object. It must contain
               // properties 'username' and 'permissions'
               var user = { username: "admin", permissions: "*" };
               resolve(user);
           } else {
               // Resolve with null to indicate this user does not exist
               resolve(null);
           }
       });    },    authenticate: function(username,password) {
       return new Promise(function(resolve) {
           // Do whatever work is needed to validate the username/password
           // combination.
           if (valid) {
               // Resolve with the user object. Equivalent to having
               // called users(username);
               var user = { username: "admin", permissions: "*" };
               resolve(user);
           } else {
               // Resolve with null to indicate the username/password pair
               // were not valid.
               resolve(null);
           }
       });    },    default: function() {
       return new Promise(function(resolve) {
           // Resolve with the user object for the default user.
           // If no default user exists, resolve with null.
           resolve({anonymous: true, permissions:"read"});
       });    } } ```

2. 在settings.js中设置adminAuth属性来加载这个模块: ```adminAuth: require("./user-authentication") ```
### 2.4. 自定义的身份验证token 在某些情况下,您可能需要使用自己的身份验证token,而不使用Node-RED生成的身份验证token。例如:
* 您希望使用基于OAuth的用户身份验证,但是您还需要自动访问管理API,它不能执行OAuth要求的交互式身份验证步骤
* 您希望将Node-RED集成到现有系统中,其中用户已经登录,并且不希望他们在访问编辑器时再次登录

adminAuth可以包括一个token函数。如果对管理api的请求不包含Node-RED识别为自己的身份验证token,则将调用此函数。将请求中提供的token传递给它,它应该返回一个与经过身份验证的用户进行解析的承诺,如果token无效,则返回null。

```adminAuth: {
    ...
    tokens: function(token) {
        return new Promise(function(resolve, reject) {
            // Do whatever work is needed to check token is valid
            if (valid) {
                // Resolve with the user object. It must contain
                // properties 'username' and 'permissions'
                var user = { username: 'admin', permissions: '*' };
                resolve(user);
            } else {
                // Resolve with null as this user does not exist
                resolve(null);
            }
        });
    },
    ... } ```默认情况下,它将使用授权http头,并期待一个Bearer类型token——只将token的值传递给函数。如果它不是Bearer类型token,则授权头的完整值将传递给函数,其中包含类型和值。 要使用不同的HTTP头,可以使用tokenHeader设置来确定使用哪个头: ```adminAuth: {
    ...
    tokens: function(token) {
        ...
    },
    tokenHeader: "x-my-custom-token" } ```
### 2.5. 使用自定义token访问编辑器 要在不提示登录的情况下使用自定义Token访问编辑器,请向URL添加?access_token=< access_token
>。编辑器将在本地存储该Token,并将其用于所有未来的请求。

## 3. 功能实现的参与者
### 3.1. oauth2orize(主要功能及在本模块中的意义) OAuth2orize 是 NodeJS 的授权服务器工具包。它提供了一套中间件, 这些中间件与 passport 身份验证策略和特定于应用程序的路由处理程序相结合, 可用于组装实现 OAuth 2.0 协议的服务器。

主要方法:
* oauth2orize.createServer 提供授权服务器
* server.exchange 授权码交换访问Token。用户名及密码验证通过后,第三方(浏览器)可以使用授权码换到访问Token,通过Token就可以访问服务器资源。

### 3.2. passport passport.js是Nodejs中的一个做登录验证的中间件,极其灵活和模块化,并且可与Express、Sails等Web框架无缝集成。passport模块本身不能做认证,所有的认证方法都以策略模式封装为插件,需要某种认证时将其添加到package.json即可。

主要方法:
* passport.use 提供策略及配置
* passport.authenticate 对请求进行身份验证并指定认证策略
* passport.initialize 在基于Connect或express的应用程序中,需要Passport. initialize()中间件来初始化Passport。
* passport.session 如果您的应用程序使用持久登录会话,还必须使用passport.session()中间件。 请注意,启用会话支持是完全可选的,尽管大多数应用程序都推荐启用会话支持。如果启用,请确保在passport.session()之前使用session(),以确保登录会话以正确的顺序恢复。

### 3.3. express-session session 的运作通过一个 session_id 来进行。session_id 通常是存放在客户端的 cookie 中,比如在 express 中,默认是 connect.sid 这个字段,当请求到来时,服务端检查 cookie 中保存的 session_id 并通过这个 session_id 与服务器端的 session data 关联起来,进行数据的保存和修改。 express 中操作 session 要用到 express-session (https://github.com/expressjs/session ) 这个模块,主要的方法就是session(options),其中 options 中包含可选参数,主要有:
* name: 设置 cookie 中,保存 session 的字段名称,默认为 connect.sid 。
* store: session 的存储方式,默认存放在内存中,也可以使用 redis,mongodb 等。express 生态中都有相应模块的支持。
* secret: 通过设置的 secret 字符串,来计算 hash 值并放在 cookie 中,使产生的 signedCookie 防篡改。
* cookie: 设置存放 session id 的 cookie 的相关选项,默认为
    * (default: { path: '/', httpOnly: true, secure: false, maxAge: null })
* genid: 产生一个新的 session_id 时,所使用的函数, 默认使用 uid2 这个 npm 包。
* rolling: 每个请求都重新设置一个 cookie,默认为 false。
* resave: 即使 session 没有被修改,也保存 session 值,默认为 true。

### 3.4. passport-http-bearer Passport的HTTP Bearer认证策略。

这个模块允许您在Node.js应用程序中使用Bearer Token(由RFC 6750指定)验证HTTP请求。Bearer Token通常使用保护API端点,并且通常使用OAuth 2.0发布。

通过插入Passport,BearerToken支持可以轻松地集成到支持连接式中间件的任何应用程序或框架中,包括Express。
### passport-oauth2-client-password 策略 OAuth 2.0 Passport客户端密码认证策略。

这个模块允许您验证请求体中包含客户端凭据的请求,正如OAuth
2.0规范所定义的那样。这些凭据通常用于保护Token端点,并用作HTTP基本身份验证的替代方案。

## 4. 功能分阶段描述 auth:packages/node_modules/@node-red/editor-api/lib/auth/index.js auth_users:packages/node_modules/@node-red/editor-api/lib/auth/users.js auth_tokens:packages/node_modules/@node-red/editor-api/lib/auth/tokens.js auth_strategies:packages/node_modules/@node-red/editor-api/lib/auth/strategies.js storage:packages/node_modules/@node-red/runtime/lib/storage/index.js ```mermaid  %% 配置 %%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#FFFFFF'}}}%% sequenceDiagram autonumber

%%实现 title: NodeRed中的用户认证机制(settings=credentials)

participant 其他 auth -->> auth: 配置passport认证策略 auth -->> auth: 创建 oauth2 认证服务器 auth -->> auth: 配置 oauth2 授权码交换访问Token处理 其他 -->> auth: 启动初始化流程init auth -->> auth_users: 启动初始化流程init,初始化 Users 模块 auth -->> auth_tokens: 启动初始化流程init,初始化 Tokens 模块 其他 -->> auth: 将 /auth/login 路径路由到 login 、 errorHandler 方法  其他 -->> auth: 将 /auth/token 路径路由到 ensureClientSecret 、 authenticateClient 、 getToken、 errorHandler 方法 其他
-->> auth: 将 /auth/revoke 路径路由到  needsPermission 、revoke、 errorHandler 方法 auth -->> auth: ...

其他 -->> auth: 用户登陆操作,调用login方法,根据不同的settings配置,返回显示界面 auth -->> auth: ...

其他 -->> auth: 用户输入用户名称和密码,进行认证 auth -->> auth_users: 验证客户端ID(node-red-editor)是否有效,auth_users 使用 oauth2-client-password 策略 auth -->> auth: ...

auth -->> auth_strategies: 根据已配置的 oauth2 授权码交换访问Token方法,获取Token auth_strategies -->> auth_users : 用户认证 authenticate(判断用户是否为合法用户,验证用户名和密码) auth_users -->> auth_strategies: 返回用户信息信息 auth_strategies -->> auth_strategies: 判断用户权限, permissions.hasPermission auth_strategies -->> auth_strategies: 创建Token auth_strategies -->> auth_tokens : 创建会话信息,设置会话结束时间,做Token超时处理 auth_tokens -->> storage: 保存会话信息到文件(/home/freeman/.node-red/.sessions.json) auth -->> auth: ...

其他 -->> auth: 用户登陆后所有操作,调用 bearerStrategy 方法,判断请求所携带的Token是否有效,若无效则返回错误信息,禁止操作 auth -->> auth: ...

其他 -->> auth: 用户登出操作,调用 revoke 方法 auth -->> auth: ... ```
## 5. 功能使用场景 存在于使用的各阶段(初始化、运行、结束),是开启用户验证后必需的功能。

## 6. 实现方式特点
1. 使用策略模式对算法封装
2. 模块化使用第三方库,完成用户认证
3. 可扩展实现定制的用户认证

你可能感兴趣的:(NodeRed,nodered)