博客迁移
不恰饭的小站
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中的认证机制使用 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. 将以下内容保存到一个名为<node-red>/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. 可扩展实现定制的用户认证