当前位置: 首页 > news >正文

Node.js 登录鉴权

目录

Session

express-session 配置

express-session 函数

ts 要配置声明文件 express-session.d.ts

express-session 使用

express-session 带角色

Token

什么是 JWT

token

jsonwebtoken 使用

jsonwebtoken 带角色


Session

express 使用 express-session 管理会话,session 是服务端存储,不会显式的返回一个 token,与客户端通过 cookie 来保持会话的标识状态。

安装

npm install express express-session
npm install --save-dev typescript @types/express @types/express-session

express-session 配置

secret

  • 用于加密 session ID 的密钥。这个密钥确保 session ID 的安全性,如果没有这个密钥,攻击者可以篡改会话 ID。你应该使用一个随机的、复杂的字符串作为 secret,以保证会话安全。不要公开你的密钥,最好将其存储在环境变量中。
  • 示例:secret: 'xxxxxxxx'

name

  • 设置会话 cookie 的名称。默认是 connect.sid,你可以修改为更符合你需求的名称。如果你有多个会话,或需要更好的安全性,可以修改 cookie 的名称来避免碰撞。
  • 示例:name: 'connect.sid'

store

  • 用于存储会话数据的存储引擎。默认情况下,会话数据保存在内存中,但对于生产环境,建议使用外部存储解决方案,如 Redis 或 MongoDB。你可以使用 session-store 兼容的第三方存储引擎。
  • 示例:store: new RedisStore({ host: 'localhost', port: 6379 })

cookie

  • 配置会话 cookie 的属性。它定义了会话 cookie 如何在客户端存储和传输。
    • path: 会话 cookie 的作用路径,默认为 '/',即适用于整个网站。
    • httpOnly: 如果为 true,客户端 JavaScript 无法访问该 cookie,增加安全性,防止 XSS 攻击。默认是 true。
    • secure: 如果为 true,cookie 只能通过 HTTPS 连接发送,适用于生产环境。默认为 false。
    • maxAge: 会话 cookie 的最大生存时间,单位为毫秒。超过该时间,cookie 会过期,默认是会话结束时过期(浏览器关闭)。
    • sameSite: 控制 cookie 是否在跨站请求时发送。strict 只会在同源请求中发送,lax 允许在某些跨站请求中发送cookie,none 允许所有请求发送,通常与 secure 一起使用。
  • 示例:cookie: {
                   httpOnly: true,
                   secure: false,  // 仅在生产环境下设置为 true,要求 HTTPS
                   maxAge: 24 * 60 * 60 * 1000,  // 1 天
                   sameSite: 'lax'
                }

resave

  • 是否在每次请求后重新保存会话,即使会话没有发生变化。如果为 true,会话每次请求都会被重新保存。对于大多数应用,推荐将其设置为 false,以提高性能。
  • 示例:resave: false

saveUninitialized

  • 是否保存未初始化的会话。当用户请求时,即使没有对会话进行任何修改,仍会保存一个空的会话。默认是 true,可以设置为 false 来避免存储不必要的空会话。建议设置 false
  • saveUninitialized: false

unset

  • 设置删除会话时的行为。
    • destroy: 会话被销毁时删除 cookie。
    • keep: 会话被销毁时不删除 cookie,即使会话被销毁,客户端仍然携带 session ID。
  • 示例:unset: 'destroy'

rolling

  • 每次请求时重新设置会话 cookie 的过期时间。如果为 true,则每次请求都会更新会话的 maxAge。如果为 false,则会话的过期时间从创建时开始计算。
  • 示例:rolling: true

genid

  • 自定义生成会话 ID 的函数。默认情况下,express-session 使用随机生成的 UUID 来作为会话 ID。如果你需要自定义会话 ID 的生成方式,可以提供该函数。
  • 示例:genid: (req) => {
                   return uuidv4();  // 使用自定义的生成逻辑
               }

secure

  • 如果为 true,则仅通过 HTTPS 请求才会发送 cookie。通常在生产环境下使用,以确保会话 cookie 的安全性。
  • 示例:secure: true,  // 生产环境下启用,确保 HTTPS 连接

示例:

app.use(session({secret: 'your_secret_key',  // 加密会话 IDresave: false,              // 不要在每次请求后重新保存会话saveUninitialized: false,   // 不保存未初始化的会话cookie: {httpOnly: true,           // 防止 JavaScript 访问secure: false,            // 开发时使用 HTTP,不启用 HTTPSmaxAge: 24 * 60 * 60 * 1000,  // 1 天有效sameSite: 'lax'           // 跨站请求时携带 cookie}
}));

express-session 函数

设置会话数据

用于登录时

// 设置会话数据
app.post('/login', (req, res) => {req.session.user = { id: 123, username: 'john' };res.send('Logged in');
});

获取会话数据

// 获取会话中的数据
app.get('/', (req, res) => {if (req.session.user) {res.send(`Hello ${req.session.user.username}`);}
});

销毁会话

调用 req.session.destroy() 来销毁当前的会话数据,这通常用于用户登出时

// 销毁会话,登出
app.post('/logout', (req, res) => {req.session.destroy((err) => {if (err) {return res.status(500).send('登出时出错');}res.send('登出');});
});

清除会话数据

如果只想删除会话中的某个字段,而不是销毁整个会话,可以通过 req.session 删除特定字段

// 清除特定会话数据
app.post('/', (req, res) => {delete req.session.user;  // 删除 user 数据res.send('User data cleared');
});

获取会话 ID

express-session 会话数据是通过会话 ID 来标识的,你可以通过 req.sessionID 获取当前请求的会话 ID

// 获取会话 ID
app.get('/', (req, res) => {res.send(`你的会话ID是 ${req.sessionID}`);
});

手动设置会话 ID

你可以通过 req.sessionID 手动设置会话 ID。不过通常会话 ID 是由 express-session 自动管理的,除非你有特殊需求

// 设置会话 ID(不常见)
app.post('/', (req, res) => {req.sessionID = 'customSessionId';res.send('Session ID set');
});

修改会话 Cookie

例如,修改 cookie 的过期时间

// 设置会话 cookie 的最大过期时间
app.post('/', (req, res) => {req.session.cookie.maxAge = 1000 * 60 * 60;  // 设置 1 小时res.send('Cookie expiration time set');
});

重新加载会话

当会话存储的内容有变化时,你可以使用 reload 来重新加载当前会话。例如用户数据被修改时

app.get('/', (req, res) => {req.session.reload((err) => {if (err) {return res.status(500).send('Failed to reload session');}res.send('Session reloaded');});
});

手动保存会话数据

如果 resave: true 修改会话数据后会自动保存,否则需要手动保存。

app.get('/', (req, res) => {req.session.user = { id: 456, username: 'alice' };req.session.save((err) => {if (err) {return res.status(500).send('Failed to save session');}res.send('Session saved');});
});

更新会话的过期时间

app.get('/', (req, res) => {req.session.touch();  // 延长会话过期时间,每次延迟时间为 + maxAgeres.send('Session touched');
});

ts 要配置声明文件 express-session.d.ts

// src/types/express-session.d.ts
import { SessionData } from 'express-session';declare module 'express-session' {interface SessionData { user?: { id?: number, username?: string, role?: any } } // 可以根据需要修改 user 的类型}

express-session 使用

import express, { Request, Response } from 'express';
import session from 'express-session';const app = express();
const port = 3000;app.use(express.json());// 会话配置
app.use(session({secret: '123456',   // 会话id密钥resave: false,      // 是否每次都重新保存会话saveUninitialized: false,   // 否保存未初始化的会话cookie: { maxAge: 1000 * 60 * 30 }  // 会话 cookie 过期时间
}));// 用户信息,一般在数据库中存储
const users = [{ id: 1, username: 'admin', password: '123456' },{ id: 2, username: 'user', password: '123456' }
];// 登录
app.get('/login', (req: Request, res: Response) => {const { username, password } = req.query;// 验证用户账号密码,真实情况,从数据库获取const user = users.find(u => u.username === username && u.password === password);if (user) {req.session.user = { username: user.username };res.send({ 'msg': '登录成功' });} else {res.status(401).json({ 'msg': '无效的用户名或密码' });}
});// 登录后用户可以访问的接口
app.get('/profile', (req: Request, res: Response) => {if (req.session.user) {res.send(`Hello ${req.session.user.username}, welcome to your profile!`);} else {res.status(401).send('You must be logged in to access this page');}
});// 注销登出
app.get('/logout', (req: Request, res: Response) => {req.session.destroy((err) => {if (err) {return res.status(500).send('Failed to destroy session');}res.send('Logout successful');});
});app.listen(port, () => {console.log(`Server running at http://localhost:${port}`);
});

express-session 带角色

import express, { Request, Response, NextFunction } from 'express';
import session from 'express-session';const app = express();
const port = 3000;app.use(express.json());// 会话配置
app.use(session({secret: '123456',   // 会话id密钥resave: false,      // 是否每次都重新保存会话saveUninitialized: false,   // 否保存未初始化的会话cookie: { maxAge: 1000 * 60 * 30 }  // 会话 cookie 过期时间
}));// 登录接口
app.post('/login', (req: Request, res: Response) => {const { username, password } = req.body;// 假设通过某种方式验证用户名和密码,真实情况,直接查询用户信息时,带上用户的角色集合if (username === 'admin' && password === '123456') {// 登录成功,设置会话并分配角色req.session.user = { username, role: 'admin' };  // 设置角色为 'admin'res.send('登录成功');} else if (username === 'user' && password === '123456') {req.session.user = { username, role: 'user' };  // 设置角色为 'user'res.send('登录成功');} else {res.status(401).send('无效的用户或密码');}
});// 角色验证中间件
const requireRole = (role: string) => {return (req: Request, res: Response, next: NextFunction) => {if (req.session.user?.role === role) {  // 会话中的角色 和 传入的角色 是否匹配next(); // 角色匹配,继续请求} else {res.status(403).send('Forbidden: 无权限访问');}};
};// 受保护路由:只有管理员可以访问
app.get('/admin', requireRole('admin'), (req: Request, res: Response) => {res.send('Welcome to the admin dashboard');
});// 受保护路由:只有普通用户可以访问
app.get('/user', requireRole('user'), (req: Request, res: Response) => {res.send('Welcome to the user dashboard');
});// 注销接口:销毁会话
app.post('/logout', (req: Request, res: Response) => {req.session.destroy((err) => {if (err) {return res.status(500).send('Failed to destroy session');}res.send('Logout successful');});
});app.listen(port, () => {console.log(`Server running at http://localhost:${port}`);
});

Token

express 使用 jsonwebtoken 管理 Token,jsonwebtoken 是一个用于在 Node.js 中签发和验证 JSON Web Tokens (JWT) 的库(用于生成和验证JWT)。它常用于身份验证和授权,确保 API 请求的安全性。

什么是 JWT

先介绍一下 jwt

JWT(JSON Web Token)是一个开放标准(RFC 7519),它定义了一种简洁、自包含的方式,用于在应用程序之间传递信息,通常用于身份验证和授权。

JWT 主要有 三部分 组成:

  • Header(头部)
  • Payload(载荷)
  • Signature(签名)

一. Header(头部)

JWT 的头部通常包含两部分信息:

  • typ(类型):JWT 类型,通常为 JWT。
  • alg(算法):用于签名 JWT 的算法。常见的算法包括:
    • HMAC SHA256 (HS256)
    • RSA (RS256)
    • ECDSA (ES256)
    • None(不使用签名算法,适用于不需要签名的情况,但很少使用)

示例:

{"alg": "HS256","typ": "JWT"
}

二. Payload(载荷)

JWT 的载荷部分包含了要传递的声明(Claims),这些声明可以是关于实体(通常是用户)的信息,以及一些附加的元数据。声明是一个 JSON 对象。以下几种类型的声明

1) 注册声明(Registered Claims)

这些是 JWT 中预定义的声明,用于提供关于 JWT 的一些标准化信息。常见的注册声明包括:

  • iss(Issuer):签发者,通常是令牌的生成方。
  • sub(Subject):主题,通常是 JWT 的主体(例如用户 ID)。
  • aud(Audience):接收者,指定令牌的目标用户或受众。
  • exp(Expiration Time):过期时间,指定 JWT 的有效期,JWT 到期后不能再使用。
  • nbf(Not Before):生效时间,指定 JWT 从何时起生效。
  • iat(Issued At):签发时间,表示 JWT 被创建的时间。
  • jti(JWT ID):JWT 的唯一标识符,常用于防止重放攻击。
2) 公共声明(Public Claims)

公共声明是用户自定义的声明,用于承载应用特定的信息(如用户id、用户权限、角色等)。

3) 私有声明(Private Claims)

私有声明是为了应用双方定义的声明,通常是为了服务之间的交互或存储应用特定的数据。这些声明没有预定义的名称,只要双方约定好了即可,也可以添加userId 或 role 等信息。

示例:

{"sub": "1234567890",      // 用户 ID"name": "John Doe",       // 用户名"role": "admin",          // 用户角色"permissions": ["read:messages","write:messages"],"iat": 1516239022,        // 签发时间"exp": 1616239022,        // 过期时间"nbf": 1516240000         // 生效时间
}

三. Signature(签名)

JWT 的签名部分是为了确保消息在传输过程中不被篡改。它是由以下部分组成的:

  • 头部(Header)
  • 载荷(Payload)

然后,用特定的签名算法(如 HMAC SHA256、RSA)和密钥对这两部分进行加密,生成签名。

签名生成过程:

  1. HeaderPayload 分别进行 Base64 编码。
  2. 将 Base64 编码的 Header 和 Payload 用点(.)连接起来:<Base64-encoded Header>.<Base64-encoded Payload>
  3. 使用指定的签名算法和密钥对上一步生成的字符串进行加密,得到签名。

示例:假设使用 HMAC SHA256 算法,密钥为 secret,我们将得到以下签名:

HMACSHA256(base64UrlEncode(header) + "." +base64UrlEncode(payload),secret)

一个完整的 JWT 通常由三部分组成,并且它们通过点(.)连接。即:Header.Payload.Signature

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwicm9sZSI6ImFkbWluIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

JWT的用途

1. 身份验证(Authentication)

  • 在用户登录时,服务器会生成一个 JWT,包含用户的信息(如 ID 和角色等),并返回给客户端。
  • 客户端将 JWT 存储在本地(例如在浏览器的 LocalStorage 中或 HTTP cookie 中),每次发送请求时将 JWT 附加到 HTTP 头部(通常是 Authorization: Bearer <token>)。
  • 服务器在接收到请求时,通过解码 JWT 验证用户的身份,若有效,则允许访问相应的资源。

2. 信息传递(Information Exchange)

JWT 可以用来在不同系统或服务之间安全地传递信息。由于签名部分能够验证数据的完整性和身份,JWT 是一个理想的选择。


token

回到 Node.js 的 token

安装 jsonwebtoken

npm install express jsonwebtoken
npm install --save-dev typescript @types/express @types/jsonwebtoken

jsonwebtoken 函数

生成 JWT

jwt.sign(payload, secretOrPrivateKey, [options, callback])

参数:

  • payload:要包含在 token 中的有效载荷,通常是用户的身份信息等。
  • secretOrPrivateKey:用于签署 token 的密钥。
  • [options]:可选的配置对象,用于设置 token 的过期时间、受众、发行者等。如下:
    • expiresIn:JWT 过期时间。可以是一个数字(秒数),或是一个字符串(如 1h,30m,2d 等)。
    • notBefore:JWT 生效的时间。可以是一个数字(秒数),或是一个字符串(如 10s,1h)。
    • audience:JWT 的受众,指定该 JWT 面向的用户(例如,一个特定的服务)。
    • issuer:JWT 的签发者,用于标识生成 JWT 的主体。
    • jwtid:JWT 的唯一标识符。
    • subject:JWT 的主题(通常是用户 ID)。
    • algorithm:签名算法,默认使用 HS256。可以是其他算法,如 RS256、ES256 等。
    • keyid:密钥 ID,通常用于非对称加密。
    • header:自定义 JWT 头部信息,可以指定 typ、alg 等头部参数。
  • [callback]:可选的回调函数,如果传入,则该函数将在生成 token 后执行。

示例:

import jwt, { SignOptions } from 'jsonwebtoken';// 用户信息(载荷部分)
const payload = {sub: '123456',              // 用户 IDname: 'Tom',                // 用户名role: 'admin',               // 用户角色permissions: ['read', 'write'] // 用户权限
};// 签名密钥(使用对称加密算法)
const secret = 'your-secret-key';// JWT 选项
const options: SignOptions = {expiresIn: '1h',             // 令牌有效期 1 小时notBefore: '0s',              // 立即生效audience: 'your-audience',    // 受众issuer: 'Tom',                  // 签发者jwtid: 'unique-id-12345',     // JWT 唯一标识符algorithm: 'HS256'           // 使用 HMAC SHA256 算法
};// 生成 JWT
const token = jwt.sign(payload, secret, options);
console.log('JWT:', token);

验证 JWT

验证 JWT 的有效性,确保其未被篡改且未过期。如果验证成功,返回解码后的有效载荷。

jwt.verify(token, secretOrPublicKey, [options, callback])

参数:

  • token:需要验证的 JWT。
  • secretOrPublicKey:用于验证签名的密钥(对于 HMAC 使用密钥,对于 RSA 或 ECDSA 使用公钥)。
  • [options]:可选的配置对象,用于设置验证的参数,如 audience、issuer 等。如下:
    • algorithms: 用于指定可接受的算法列表,防止被使用不安全的算法进行签名验证。
    • issuer: 用于指定验证令牌时必须符合的签发者。
    • subject: 用于指定验证令牌时必须符合的主题。
    • audience: 用于指定验证令牌时必须符合的受众。
    • ignoreExpiration: 如果设置为 true,JWT 的过期时间不会被验证(默认为 false)。
    • clockTolerance: 容忍时间差,用于 exp(过期时间)字段。
    • maxAge: 设置令牌的最大有效期(例如 '1h' 表示 1 小时)。
    • jwtid: 用于指定验证令牌时必须符合的 JWT 唯一标识符。
  • [callback]:可选的回调函数,如果传入,则该函数将在验证后执行。

示例:        // 验证成功,返回 JWT 内容

import jwt from 'jsonwebtoken';// 要验证的 JWT
const token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTYiLCJuYW1lIjoiVG9tIiwicm9sZSI6ImFkbWluIiwicGVybWlzc2lvbnMiOlsicmVhZCIsIndyaXRlIl0sImlhdCI6MTc0MDMxMjY3NywibmJmIjoxNzQwMzEyNjc3LCJleHAiOjE3NDAzMTI2NzgsImF1ZCI6InlvdXItYXVkaWVuY2UiLCJpc3MiOiJUb20iLCJqdGkiOiJ1bmlxdWUtaWQtMTIzNDUifQ.WW6gGA6WcIId96BogCTFEXH9O-A0HN-rVddazqqdrb8";// 签名密钥(使用对称加密算法)
const secret = 'your-secret-key';const options = {algorithms: ['HS256' as jwt.Algorithm],         // 令牌的算法必须是 HS256issuer: 'Tom',                 // 令牌的签发者必须是 'Tom'subject: '123456',            // 令牌的主题必须是 '123456'audience: 'your-audience',     // 令牌的受众必须是 'your-audience'ignoreExpiration: false,       // 不忽略过期时间,必须验证clockTolerance: 30,            // 容忍 30 秒的时间差(比如在时钟差异较大时)maxAge: '1h',                  // 设置令牌最大有效期为 1 小时jwtid: 'unique-id-12345',      // JWT 唯一标识符必须为 'unique-id-12345'
};try {// 验证 JWT,并检查过期时间等const decoded = jwt.verify(token, secret, options);console.log('Decoded JWT:', decoded);
} catch (err) {console.error('无效的 token:', err);
}

解码 JWT

解码 JWT,而不进行验证。此函数仅用于从 token 中提取载荷,而不检查其签名是否有效。适用于读取 token 内容,但不需要验证其合法性的场景。

jwt.decode(token, [options])

参数:

  • token:需要解码的 JWT。
  • [options]:可选的配置对象,设置为 complete: true 时会返回完整的 token 包括头部和载荷部分。
    • json (默认值:true): 如果为 true,返回解码后的 JSON 对象。如果为 false,返回解码后的原始字符串。
    • complete (默认值:false): 如果为 true,则返回一个完整的 JWT 对象,包含头部(header)、载荷(payload)和签名(signature)。如果为 false,只返回解码后的载荷部分。

示例:

import jwt from 'jsonwebtoken';// 要解码的 JWT
const token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTYiLCJuYW1lIjoiVG9tIiwicm9sZSI6ImFkbWluIiwicGVybWlzc2lvbnMiOlsicmVhZCIsIndyaXRlIl0sImlhdCI6MTc0MDMxMjY3NywibmJmIjoxNzQwMzEyNjc3LCJleHAiOjE3NDAzMTI2NzgsImF1ZCI6InlvdXItYXVkaWVuY2UiLCJpc3MiOiJUb20iLCJqdGkiOiJ1bmlxdWUtaWQtMTIzNDUifQ.WW6gGA6WcIId96BogCTFEXH9O-A0HN-rVddazqqdrb8";// 解码 JWT,返回完整的 JWT 对象(包含 header、payload 和 signature)
const decodedComplete = jwt.decode(token, { complete: true });
console.log('Decoded JWT (complete):', decodedComplete);

jsonwebtoken 使用

import express from 'express';
import jwt, { JwtPayload } from 'jsonwebtoken';const app = express();
const port = 3000;
const secret = 'your-secret-key';app.use(express.json());// 模拟登录接口
app.post('/login', (req, res) => {const { username, password } = req.body;// 假设用户名密码验证通过const payload = { username };// 生成 jwt const token = jwt.sign(payload, secret, { expiresIn: '1s' });// 返回 jwtres.json({ token });
});// token 验证通过才能访问的接口
app.get('/profile', (req, res) => {// 获取请求头 Authorization: Bearer <token>const token = req.headers.authorization?.split(' ')[1];if (!token) {res.status(401).send('Token is required');}try {// 验证 tokenconst decoded = jwt.verify(token as string, secret) as JwtPayload;// 把 token 中存的用户信息返回res.send(`Hello ${decoded.username}`);} catch (err) {res.status(401).send('Invalid or expired token');}
});app.listen(port, () => {console.log(`Server running at http://localhost:${port}`);
});

jsonwebtoken 带角色

import express, { Request, Response, NextFunction } from 'express';
import jwt, { JwtPayload } from 'jsonwebtoken';const app = express();
const port = 3000;const secret = 'your-secret-key'; // 用于签署 JWT 的密钥app.use(express.json());// 模拟登录接口
app.post('/login', (req: Request, res: Response) => {const { username, password } = req.body;// 假设用户名密码验证通过const payload = {username,role: 'user', // 角色};// 生成 JWT,设置有效期为 1 小时const token = jwt.sign(payload, secret, { expiresIn: '1h' });// 返回生成的 JWTres.json({ token });
});// 角色验证中间件
const requireRole = (role: string) => {return (req: Request, res: Response, next: NextFunction): void => {  // 确保返回类型是 voidconst token = req.headers.authorization?.split(' ')[1]; // 提取 Bearer tokenif (!token) {res.status(401).send('Token is required');}try {// 验证并解码 JWT,获取载荷中的角色信息const decoded = jwt.verify(token as string, secret) as JwtPayload;// 检查角色是否匹配if (decoded.role !== role) {res.status(403).send('You do not have the necessary permissions');}// 如果角色验证通过,继续执行下一个中间件或路由处理函数next();} catch (err) {res.status(401).send('Invalid or expired token');}};
};// 受保护路由:只有 admin 可以访问
app.get('/profile', requireRole('admin'), (req: Request, res: Response) => {// 如果角色验证通过,执行该路由处理函数res.send('Welcome to your profile');
});// 启动服务器
app.listen(port, () => {console.log(`Server running on http://localhost:${port}`);
});

相关文章:

Node.js 登录鉴权

目录 Session express-session 配置 express-session 函数 ts 要配置声明文件 express-session.d.ts express-session 使用 express-session 带角色 Token 什么是 JWT token jsonwebtoken 使用 jsonwebtoken 带角色 Session express 使用 express-session 管理会话&…...

EPSON L3118彩色喷墨打印机灯全闪故障维修一例

一台EPSON L3118彩色喷墨打印机&#xff0c;故障时开机灯全闪烁&#xff0c;一般来说这种故障问题都不太大&#xff0c;要么就是打印机内部卡纸了&#xff0c;要么就是传感器故障&#xff0c;一般情况下卡纸的问题比较多… …&#xff1b; 但是遇到一用户又菜又爱玩&#xff0c…...

在 Mac ARM 架构的 macOS 系统上启用 F1 键作为 Snipaste 的截屏快捷键

在 Mac ARM 架构的 macOS 系统上启用 F1 键作为 Snipaste 的截屏快捷键&#xff0c;主要涉及到两个方面&#xff1a;确保 F1 键作为标准功能键工作 和 在 Snipaste 中设置 F1 为快捷键。 因为 Mac 默认情况下&#xff0c;F1-F12 键通常用作控制屏幕亮度、音量等系统功能的快捷键…...

基于AT89C51单片机的教室智能照明控制系统

点击链接获取Keil源码与Project Backups仿真图&#xff1a; https://download.csdn.net/download/qq_64505944/90419908?spm1001.2014.3001.5501 C16 部分参考设计如下&#xff1a; 摘 要 本项目的智能教室灯光控制系统通过合理的软硬件设计&#xff0c;有效地提升了教室…...

JavaSE学习笔记25-反射(reflection)

反射 在Java中&#xff0c;反射&#xff08;Reflection&#xff09; 是一种强大的机制&#xff0c;允许程序在运行时检查和操作类、方法、字段等信息。通过反射&#xff0c;可以动态地创建对象、调用方法、访问字段&#xff0c;甚至修改私有成员。反射的核心类是 java.lang.re…...

ctf网络安全题库 ctf网络安全大赛答案

此题解仅为部分题解&#xff0c;包括&#xff1a; 【RE】&#xff1a;①Reverse_Checkin ②SimplePE ③EzGame 【Web】①f12 ②ezrunner 【Crypto】①MD5 ②password ③看我回旋踢 ④摩丝 【Misc】①爆爆爆爆 ②凯撒大帝的三个秘密 ③你才是职业选手 一、 Re ① Reverse Chec…...

旋转位置编码(ROPE)详解:从Transformer到现代前沿

旋转位置编码&#xff08;ROPE&#xff09;详解&#xff1a;从Transformer到现代前沿 标签&#xff1a;NLP, Transformer, 位置编码, ROPE, 深度学习, 机器学习 摘要&#xff1a;本文详细介绍了旋转位置编码&#xff08;ROPE&#xff09;在Transformer模型中的应用&#xff0…...

ROS2机器人开发--服务通信与参数通信

服务通信与参数通信 在 ROS 2 中&#xff0c;服务&#xff08;Services&#xff09;通信和参数&#xff08;Parameters&#xff09;通信是两种重要的通信机制。服务是基于请求和响应的双向通信机制。参数用于管理节点的设置&#xff0c;并且参数通信是基于服务通信实现的。 1 …...

安全运维,等保测试常见解决问题。

1. 未配置口令复杂度策略。 # 配置密码安全策略 # vi /etc/pam.d/system-auth # local_users_only 只允许本机用户。 # retry 3 最多重复尝试3次。 # minlen12 最小长度为12个字符。 # dcredit-1 至少需要1个数字字符。 # ucredit-1 至少需要1个大…...

【数据标准】数据标准化是数据治理的基础

导读&#xff1a;数据标准化是数据治理的基石&#xff0c;它通过统一数据格式、编码、命名与语义等&#xff0c;全方位提升数据质量&#xff0c;确保准确性、完整性与一致性&#xff0c;从源头上杜绝错误与冲突。这不仅打破部门及系统间的数据壁垒&#xff0c;极大促进数据共享…...

Java 18~20 新特性

文章目录 一、Java 18 新特性1.1、UTF-8 作为默认字符集&#xff08;JEP 400&#xff09;1.2、简易 Web 服务器&#xff08;JEP 408&#xff09;1.3、代码片段标签 snippet&#xff08;JEP 413&#xff09;1.4、使用方法句柄重新实现反射核心&#xff08;JEP 416&#xff09;1.…...

程序员学商务英语之At the Hotel

Dialogue-3 Room service-Cleaning the Room客房服务-打扫房间 A: Who will do the dishes after dinner tonight? 今晚饭后谁来洗碗&#xff1f; B: It’s your turn. 轮到你了。 Go do the room right now. clean the room去打扫房间。Doing the laundry is the last thi…...

探秘路由表:网络世界的导航地图

一、引言 在当今数字化时代&#xff0c;网络已经成为我们生活中不可或缺的一部分。无论是浏览网页、观看视频&#xff0c;还是进行在线办公、游戏娱乐&#xff0c;我们都在与网络进行着频繁的交互。而在这背后&#xff0c;网络中的数据传输就如同现实生活中的快递配送&#xf…...

UniApp SelectorQuery 讲解

一、SelectorQuery简介 在UniApp中&#xff0c;SelectorQuery是一个非常强大的工具&#xff0c;它允许开发者查询节点信息。通过这个API&#xff0c;我们可以获取到页面元素的尺寸、位置、滚动条位置等信息。这在处理动态布局、动画效果或是用户交互时尤为重要。 二、基本使用…...

三数之和:经典问题的多种优化策略

三数之和&#xff1a;经典问题的多种优化策略 大家好&#xff0c;我是Echo_Wish。今天我们来聊一个经典的算法问题——三数之和&#xff08;3Sum&#xff09;。它是许多面试和算法竞赛中常见的问题之一&#xff0c;也常常考察我们对算法优化的理解和技巧。我们不仅要解决问题&…...

Flash-00

下载网站&#xff1a;Flash CC 2015中文版下载-Adobe Flash CC 2015(动画制作软件)免费下载-当快软件园 1-什么是Flash&#xff1f; Flash是一款多媒体设计软件&#xff0c;二维交互式动画设计工具&#xff0c;常用于矢量动画制作 2-Flash的应用领域&#xff1f; 动画制作&a…...

FTP 实验(ENSP模拟器实现)

FTP 概述 FTP&#xff08;File Transfer Protocol&#xff0c;文件传输协议&#xff09;是一种用于在网络上进行文件传输的标准协议。它允许用户在两台计算机之间上传和下载文件。 1、FTP采用客户端-服务器模型&#xff0c;客户端通过FTP客户端软件&#xff0c;连接到FTP服务…...

DeepSeek在初创企业、教育和数字营销领域应用思考

如今&#xff0c;像 DeepSeek 这样的人工智能工具正在改变企业的运营方式&#xff0c;优化流程并显著提高生产力。通过重复任务的自动化、大量数据的分析以及内容创建效率的提高&#xff0c;组织正在寻找新的竞争和卓越方式。本文介绍了 DeepSeek 如何用于提高三个关键领域的生…...

ubuntu新系统使用指南

1. 更新源 2. 配置rime 输入法 sudo apt install ibus-rimeibus-setup #打开配置界面添加雾凇拼音 cd ~/Documents/Tool/input_source/plumgit clone --depth 1 https://github.com/rime/plum plum #没有梯子就劝退cd plum/bash rime-install iDvel/rime-ice:others/recipe…...

HaProxy配置详解

一、haproxy基础配置 官方文档&#xff1a;HAProxy version 2.2.22 - Configuration Manual HAProxy 的配置文件haproxy.cfg由两大部分组成&#xff0c;分别是global和proxies部分。 global&#xff1a;全局配置段 进程及安全配置相关的参数性能调整相关参数Debug参数 pro…...

PCL 基于FPFH特征的SAC-IA算法

文章目录 一、简介二、PCL中的相关类型二、实现代码三、实现效果参考资料一、简介 该算法的大致过程如下所示: 通过这种随机采样并使用FPFH描述子进行匹配的方式,可以快速找到一个较好的转换矩阵,从而实现两个物体的初始配准。 二、PCL中的相关类型 类型为:pcl::SampleCons…...

Git操作整体流程

文章目录 1.Git创建个人仓库2、Git全局配置3、Git本地管理4. Git本地管理常用命令汇总5、使用Git命令将项目提交到远程码云管理6.使用IDEA进行管理7、Idea里面的终端8、关于提交总结 1.Git创建个人仓库 打开https://gitee.com/&#xff0c;登录个人账号&#xff0c;右上角加号…...

独立开发者之PLG 和 SLG 是什么

什么是 PLG 和 SLG PLG&#xff08;产品驱动增长&#xff09;是一种策略&#xff0c;通过产品本身吸引用户并推动客户获取、保留和扩展。例如&#xff0c;提供免费试用或免费模式&#xff0c;让用户直接体验产品价值&#xff0c;如 Slack 和 Dropbox 那样。SLG&#xff08;销售…...

Python 基本语法的详细解释

目录 &#xff08;1&#xff09;注释 &#xff08;2&#xff09;缩进 &#xff08;3&#xff09;变量和数据类型 变量定义 数据类型 &#xff08;4&#xff09;输入和输出 输出&#xff1a;print() 函数 输入&#xff1a;input() 函数 &#xff08;1&#xff09;注释 注…...

腾讯SQL面试题变体实现:最长连续天数与允许1天中断的进阶解法

腾讯SQL面试题变体实现:最长连续天数与允许1天中断的进阶解法 作者:某七年数据开发工程师 | 2025年02月23日 关键词:滑动窗口、容错机制、连续区间优化 一、变体题型需求分析 在原题如何找出连续5天涨幅超过5%的股票基础上,需实现两个扩展场景: 最长连续天数:输出每只股…...

KubeKey一键安装部署k8s集群和KubeSphere详细教程

目录 一、KubeKey简介 二、k8s集群KubeSphere安装 集群规划 硬件要求 Kubernetes支持版本 操作系统要求 SSH免密登录 配置集群时钟 所有节点安装依赖 安装docker DNS要求 存储要求 下载 KubeKey 验证KubeKey 配置集群文件 安装集群 验证命令 登录页面 一、Ku…...

w803|联盛德|WM IoT SDK2.X测试|pinout|(2):w803开发板简介

概述 W803-Pico是一款基于联盛德W803芯片为主控的开发板&#xff0c;支持IEEE802.11 b/g/n Wi-Fi&#xff0c;以及BT/BLE4.2协议蓝牙。芯片内置高性能32位处理器&#xff0c;主频高达240MHz。内置2MB Flash以及288KB RAM。硬件采用DIP封装&#xff0c;PCB板载天线&#xff0c;…...

BGP分解实验·19——BGP选路原则之起源

当用不同的方式为BGP注入路由时&#xff0c;起源代码将标识路由的来源。 &#xff08;在BGP表中&#xff0c;Network为“i”&#xff0c;重分布是“&#xff1f;”&#xff09; 实验拓扑如下&#xff1a; R2上将来自IGP的路由10.3.3.3/32用network指令注入BGP;在R4上将来自I…...

使用ESP-IDF来驱动INMP441全向麦克风

之前的文章我们讲过了I2S。 I2S是什么通信协议&#xff1f;它如何传输音频数据&#xff1f;它和I2C是什么关系&#xff1f;_i2c接口和i2s-CSDN博客文章浏览阅读836次&#xff0c;点赞12次&#xff0c;收藏14次。这个可以参考ADC来理解&#xff0c;我们的ADC也是有左对齐和右对…...

C/C++跳动的爱心

系列文章 序号直达链接1C/C李峋同款跳动的爱心2C/C跳动的爱心3C/C经典爱心4C/C满屏飘字5C/C大雪纷飞6C/C炫酷烟花7C/C黑客帝国同款字母雨8C/C樱花树9C/C奥特曼10C/C精美圣诞树11C/C俄罗斯方块小游戏12C/C贪吃蛇小游戏13C/C孤单又灿烂的神14C/C闪烁的爱心15C/C哆啦A梦16C/C简单…...

blender笔记2

一、物体贴地 物体->变换->对齐物体 ->对齐弹窗(对齐模式&#xff1a;反方&#xff0c;相对于&#xff1a;场景原点&#xff0c;对齐&#xff1a;z)。 之后可以设置原点->原点--3d游标 二、面上有阴影 在编辑模式下操作过后&#xff0c;物体面有阴影。 数据-&g…...

关于在mac中配置Java系统环境变量

引言 在 macOS 上开发 Java 或 Flutter 应用时&#xff0c;正确配置环境变量是至关重要的。环境变量不仅能让系统找到开发工具的位置&#xff0c;还能简化命令行操作。本文将手把手教你从零开始安装 Java SDK&#xff0c;并详细配置环境变量&#xff0c;涵盖常见问题解决和优化…...

透彻理解:方差、协方差、相关系数、协方差矩阵及其应用

最近看了几篇跨领域特征对齐方面的经典文献&#xff0c;学者们搞了很多花样&#xff0c;如有的提出一阶统计特征对齐&#xff0c;有的提出二阶统计特征对齐&#xff0c;有的学者提出高阶统计特征对齐。 通俗而言&#xff0c;就是在统计特征层面对跨域特征进行对齐&#xff0c;…...

【SPIE出版,见刊快速,EI检索稳定,浙江水利水电学院主办】2025年物理学与量子计算国际学术会议(ICPQC 2025)

2025年物理学与量子计算国际学术会议&#xff08;ICPQC 2025&#xff09;将于2025年4月18-20日在中国杭州举行。本次会议旨在汇聚全球的研究人员、学者和业界专家&#xff0c;共同探讨物理学与量子计算领域的最新进展与前沿挑战。随着量子技术的快速发展&#xff0c;其在信息处…...

jmeter后端监视器的妙用和实现方法

JMeter 的后端监视器&#xff08;Backend Listener&#xff09;是一个强大的工具&#xff0c;可用于收集、存储和分析测试过程中的性能指标。它允许将测试数据发送到外部系统&#xff08;如 InfluxDB、Graphite 等&#xff09;&#xff0c;并借助这些系统的可视化工具&#xff…...

Docker 的安全配置与优化(二)

Docker 安全优化策略 &#xff08;一&#xff09;多阶段构建优化镜像大小 多阶段构建是 Docker 17.05 版本引入的强大功能&#xff0c;它允许在一个 Dockerfile 中定义多个构建阶段&#xff0c;每个阶段都可以使用不同的基础镜像和依赖项&#xff0c;最终只将必要的文件和依赖…...

宝塔扩容——阿里云如何操作

一、创建快照 磁盘快照&#xff0c;将数据备份&#xff0c;防止丢失。 1.登录“阿里云”账号 2.点击“控制台”——“云服务器 ECS” 3.点击“基本信息”下&#xff0c;右下角“系统盘” 4.点击“创建快照” 二、磁盘扩容 1.点击“云盘扩容” 2. 选择自己要扩容的大小 …...

vscode settings(一):全局| 用户设置常用的设置项

参考资料 Visual Studio Code权威指南 by 韩骏 一. 全局设置与用户设置 1.1 Vscode支持两种不同范围的设置 用户设置(User Settings)&#xff1a;这是一个全局范围的设置&#xff0c;会应用到所有的Visual Studio Code实例中。工作区设置(Workspace Settings)&#xff1a;设…...

DeepSeek R1本地+私有云版医疗AI部署开发成功案例技术剖析

1. 引言 1.1 研究背景与意义 随着科技的飞速发展,人工智能(AI)在医疗领域的应用正逐渐成为推动医疗行业变革的重要力量。近年来,医疗 AI 取得了显著的进展,从疾病诊断、药物研发到医疗管理等各个环节,AI 技术都展现出了巨大的潜力。它能够处理和分析海量的医疗数据,为…...

CMOS图像传感器——偏振光图像传感器技术

最近,在索尼官网看到了其提到的偏光图像传感器技术Polarsens,感到好奇,查阅了相关资料,总结如下。。。。 一、偏振光 偏振实际上是光的一种基本属性,表述光电场振动的方向。振动方向和光波前进的方向构成的平面叫振动面。大多数的光源,比如太阳,发出非偏振光,这里我们…...

ROS2 中 TF 变换发布与订阅:实现 base_link 和 test_link 实时可视化显示

视频讲解 ROS2 中 TF 变换发布与订阅&#xff1a;实现 base 安装环境依赖 sudo apt-get install ros-humble-tf2-ros ros-humble-tf2-geometry-msgs ros-humble-tf-transformations 创建一个包名为tf_test_pkg的包 ros2 pkg create --build-type ament_python tf_test_pkg -…...

SQLMesh 系列教程9- 宏变量及内置宏变量

SQLMesh 的宏变量是一个强大的工具&#xff0c;能够显著提高 SQL 模型的动态化能力和可维护性。通过合理使用宏变量&#xff0c;可以实现动态时间范围、多环境配置、参数化查询等功能&#xff0c;从而简化数据模型的开发和维护流程。随着数据团队的规模扩大和业务复杂度的增加&…...

逻辑函数的神经网络实现

1.单层感知器实现基本逻辑函数 先给大家抛出一道例题 &#xff08;一&#xff09;种类 a.OR函数 目标&#xff1a;当至少一个输入为1时&#xff0c;输出1&#xff1b;否则输出0。 权重设置&#xff1a; 输入权重&#xff1a;所有 wi1&#xff08;i1,2,...,m&#xff09;。…...

链表-基础训练(二)链表 day14

两两交换链表中的节点 题目示意&#xff1a; 给定一个链表&#xff0c;两两交换其中相邻的节点&#xff0c;并返回交换后的链表。 你不能只是单纯的改变节点内部的值&#xff0c;而是需要实际的进行节点交换。 原先我的思路是图像上的思路&#xff0c;但是我感觉还是很复杂…...

使用大语言模型对接OA系统,实现会议室预定功能

随着人工智能技术的不断进步&#xff0c;越来越多的企业开始借助 AI 助手来提高工作效率&#xff0c;尤其是在日常事务的自动化处理中。比如&#xff0c;在许多公司里&#xff0c;会议室的预定是一个常见且频繁的需求&#xff0c;通常需要员工手动检查空闲时间并做出选择。而通…...

hugging face---transformers包

一、前言 不同于计算机视觉的百花齐放&#xff0c;不同网络适用不同情况&#xff0c;NLP则由Transformer一统天下。transformer是2017年提出的一种基于自注意力机制的神经网络架构&#xff0c;transformers库是hugging face社区创造的一个py库&#xff0c;通过该库可以实现统一…...

1. 自定义组件基础

相关资源&#xff1a; &#x1f4ce;day10 图片素材.zip 1. 自定义组件基础 概念&#xff1a;在ArkUI中由框架直接提供的称为系统组件 -> Column&#xff0c;Button等&#xff0c;由开发者定义的称为自定义组件 作用&#xff1a;自定义组件可以对 UI和业务逻辑进行封装&…...

连接Sql Server时报错无法通过使用安全套接字层加密与 SQL Server 建立安全连接

文章目录 一. 前言二. 解决方案 方案1方案2 三. 总结 一. 前言 在《数据库原理》这门课的实验上&#xff0c;需要使用SQL Server&#xff0c;然后使用jdbc连接sql server突然报错为&#xff1a;SQLServerException: “Encrypt”属性设置为“true”且 “trustServerCertific…...

python使用httpx_sse调用sse流式接口对响应格式为application/json的错误信息的处理

目录 问题描述方案 问题描述 调用sse流式接口使用httpx_sse的方式 import httpxfrom httpx_sse import connect_sse# 省略无关代码try:with httpx.Client() as client:with connect_sse(client, "GET", url, paramsparam) as event_source:clear_textbox(response_t…...

R 语言科研绘图 --- 散点图-汇总

在发表科研论文的过程中&#xff0c;科研绘图是必不可少的&#xff0c;一张好看的图形会是文章很大的加分项。 为了便于使用&#xff0c;本系列文章介绍的所有绘图都已收录到了 sciRplot 项目中&#xff0c;获取方式&#xff1a; R 语言科研绘图模板 --- sciRplothttps://mp.…...