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)和密钥对这两部分进行加密,生成签名。
签名生成过程:
- 将 Header 和 Payload 分别进行 Base64 编码。
- 将 Base64 编码的 Header 和 Payload 用点(
.
)连接起来:<Base64-encoded Header>.<Base64-encoded Payload>
。- 使用指定的签名算法和密钥对上一步生成的字符串进行加密,得到签名。
示例:假设使用 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彩色喷墨打印机,故障时开机灯全闪烁,一般来说这种故障问题都不太大,要么就是打印机内部卡纸了,要么就是传感器故障,一般情况下卡纸的问题比较多… …; 但是遇到一用户又菜又爱玩,…...
在 Mac ARM 架构的 macOS 系统上启用 F1 键作为 Snipaste 的截屏快捷键
在 Mac ARM 架构的 macOS 系统上启用 F1 键作为 Snipaste 的截屏快捷键,主要涉及到两个方面:确保 F1 键作为标准功能键工作 和 在 Snipaste 中设置 F1 为快捷键。 因为 Mac 默认情况下,F1-F12 键通常用作控制屏幕亮度、音量等系统功能的快捷键…...
基于AT89C51单片机的教室智能照明控制系统
点击链接获取Keil源码与Project Backups仿真图: https://download.csdn.net/download/qq_64505944/90419908?spm1001.2014.3001.5501 C16 部分参考设计如下: 摘 要 本项目的智能教室灯光控制系统通过合理的软硬件设计,有效地提升了教室…...
JavaSE学习笔记25-反射(reflection)
反射 在Java中,反射(Reflection) 是一种强大的机制,允许程序在运行时检查和操作类、方法、字段等信息。通过反射,可以动态地创建对象、调用方法、访问字段,甚至修改私有成员。反射的核心类是 java.lang.re…...
ctf网络安全题库 ctf网络安全大赛答案
此题解仅为部分题解,包括: 【RE】:①Reverse_Checkin ②SimplePE ③EzGame 【Web】①f12 ②ezrunner 【Crypto】①MD5 ②password ③看我回旋踢 ④摩丝 【Misc】①爆爆爆爆 ②凯撒大帝的三个秘密 ③你才是职业选手 一、 Re ① Reverse Chec…...
旋转位置编码(ROPE)详解:从Transformer到现代前沿
旋转位置编码(ROPE)详解:从Transformer到现代前沿 标签:NLP, Transformer, 位置编码, ROPE, 深度学习, 机器学习 摘要:本文详细介绍了旋转位置编码(ROPE)在Transformer模型中的应用࿰…...
ROS2机器人开发--服务通信与参数通信
服务通信与参数通信 在 ROS 2 中,服务(Services)通信和参数(Parameters)通信是两种重要的通信机制。服务是基于请求和响应的双向通信机制。参数用于管理节点的设置,并且参数通信是基于服务通信实现的。 1 …...
安全运维,等保测试常见解决问题。
1. 未配置口令复杂度策略。 # 配置密码安全策略 # vi /etc/pam.d/system-auth # local_users_only 只允许本机用户。 # retry 3 最多重复尝试3次。 # minlen12 最小长度为12个字符。 # dcredit-1 至少需要1个数字字符。 # ucredit-1 至少需要1个大…...
【数据标准】数据标准化是数据治理的基础
导读:数据标准化是数据治理的基石,它通过统一数据格式、编码、命名与语义等,全方位提升数据质量,确保准确性、完整性与一致性,从源头上杜绝错误与冲突。这不仅打破部门及系统间的数据壁垒,极大促进数据共享…...
Java 18~20 新特性
文章目录 一、Java 18 新特性1.1、UTF-8 作为默认字符集(JEP 400)1.2、简易 Web 服务器(JEP 408)1.3、代码片段标签 snippet(JEP 413)1.4、使用方法句柄重新实现反射核心(JEP 416)1.…...
程序员学商务英语之At the Hotel
Dialogue-3 Room service-Cleaning the Room客房服务-打扫房间 A: Who will do the dishes after dinner tonight? 今晚饭后谁来洗碗? B: It’s your turn. 轮到你了。 Go do the room right now. clean the room去打扫房间。Doing the laundry is the last thi…...
探秘路由表:网络世界的导航地图
一、引言 在当今数字化时代,网络已经成为我们生活中不可或缺的一部分。无论是浏览网页、观看视频,还是进行在线办公、游戏娱乐,我们都在与网络进行着频繁的交互。而在这背后,网络中的数据传输就如同现实生活中的快递配送…...
UniApp SelectorQuery 讲解
一、SelectorQuery简介 在UniApp中,SelectorQuery是一个非常强大的工具,它允许开发者查询节点信息。通过这个API,我们可以获取到页面元素的尺寸、位置、滚动条位置等信息。这在处理动态布局、动画效果或是用户交互时尤为重要。 二、基本使用…...
三数之和:经典问题的多种优化策略
三数之和:经典问题的多种优化策略 大家好,我是Echo_Wish。今天我们来聊一个经典的算法问题——三数之和(3Sum)。它是许多面试和算法竞赛中常见的问题之一,也常常考察我们对算法优化的理解和技巧。我们不仅要解决问题&…...
Flash-00
下载网站:Flash CC 2015中文版下载-Adobe Flash CC 2015(动画制作软件)免费下载-当快软件园 1-什么是Flash? Flash是一款多媒体设计软件,二维交互式动画设计工具,常用于矢量动画制作 2-Flash的应用领域? 动画制作&a…...
FTP 实验(ENSP模拟器实现)
FTP 概述 FTP(File Transfer Protocol,文件传输协议)是一种用于在网络上进行文件传输的标准协议。它允许用户在两台计算机之间上传和下载文件。 1、FTP采用客户端-服务器模型,客户端通过FTP客户端软件,连接到FTP服务…...
DeepSeek在初创企业、教育和数字营销领域应用思考
如今,像 DeepSeek 这样的人工智能工具正在改变企业的运营方式,优化流程并显著提高生产力。通过重复任务的自动化、大量数据的分析以及内容创建效率的提高,组织正在寻找新的竞争和卓越方式。本文介绍了 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基础配置 官方文档:HAProxy version 2.2.22 - Configuration Manual HAProxy 的配置文件haproxy.cfg由两大部分组成,分别是global和proxies部分。 global:全局配置段 进程及安全配置相关的参数性能调整相关参数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/,登录个人账号,右上角加号…...
独立开发者之PLG 和 SLG 是什么
什么是 PLG 和 SLG PLG(产品驱动增长)是一种策略,通过产品本身吸引用户并推动客户获取、保留和扩展。例如,提供免费试用或免费模式,让用户直接体验产品价值,如 Slack 和 Dropbox 那样。SLG(销售…...
Python 基本语法的详细解释
目录 (1)注释 (2)缩进 (3)变量和数据类型 变量定义 数据类型 (4)输入和输出 输出:print() 函数 输入:input() 函数 (1)注释 注…...
腾讯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芯片为主控的开发板,支持IEEE802.11 b/g/n Wi-Fi,以及BT/BLE4.2协议蓝牙。芯片内置高性能32位处理器,主频高达240MHz。内置2MB Flash以及288KB RAM。硬件采用DIP封装,PCB板载天线,…...
BGP分解实验·19——BGP选路原则之起源
当用不同的方式为BGP注入路由时,起源代码将标识路由的来源。 (在BGP表中,Network为“i”,重分布是“?”) 实验拓扑如下: R2上将来自IGP的路由10.3.3.3/32用network指令注入BGP;在R4上将来自I…...
使用ESP-IDF来驱动INMP441全向麦克风
之前的文章我们讲过了I2S。 I2S是什么通信协议?它如何传输音频数据?它和I2C是什么关系?_i2c接口和i2s-CSDN博客文章浏览阅读836次,点赞12次,收藏14次。这个可以参考ADC来理解,我们的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
一、物体贴地 物体->变换->对齐物体 ->对齐弹窗(对齐模式:反方,相对于:场景原点,对齐:z)。 之后可以设置原点->原点--3d游标 二、面上有阴影 在编辑模式下操作过后,物体面有阴影。 数据-&g…...
关于在mac中配置Java系统环境变量
引言 在 macOS 上开发 Java 或 Flutter 应用时,正确配置环境变量是至关重要的。环境变量不仅能让系统找到开发工具的位置,还能简化命令行操作。本文将手把手教你从零开始安装 Java SDK,并详细配置环境变量,涵盖常见问题解决和优化…...
透彻理解:方差、协方差、相关系数、协方差矩阵及其应用
最近看了几篇跨领域特征对齐方面的经典文献,学者们搞了很多花样,如有的提出一阶统计特征对齐,有的提出二阶统计特征对齐,有的学者提出高阶统计特征对齐。 通俗而言,就是在统计特征层面对跨域特征进行对齐,…...
【SPIE出版,见刊快速,EI检索稳定,浙江水利水电学院主办】2025年物理学与量子计算国际学术会议(ICPQC 2025)
2025年物理学与量子计算国际学术会议(ICPQC 2025)将于2025年4月18-20日在中国杭州举行。本次会议旨在汇聚全球的研究人员、学者和业界专家,共同探讨物理学与量子计算领域的最新进展与前沿挑战。随着量子技术的快速发展,其在信息处…...
jmeter后端监视器的妙用和实现方法
JMeter 的后端监视器(Backend Listener)是一个强大的工具,可用于收集、存储和分析测试过程中的性能指标。它允许将测试数据发送到外部系统(如 InfluxDB、Graphite 等),并借助这些系统的可视化工具ÿ…...
Docker 的安全配置与优化(二)
Docker 安全优化策略 (一)多阶段构建优化镜像大小 多阶段构建是 Docker 17.05 版本引入的强大功能,它允许在一个 Dockerfile 中定义多个构建阶段,每个阶段都可以使用不同的基础镜像和依赖项,最终只将必要的文件和依赖…...
宝塔扩容——阿里云如何操作
一、创建快照 磁盘快照,将数据备份,防止丢失。 1.登录“阿里云”账号 2.点击“控制台”——“云服务器 ECS” 3.点击“基本信息”下,右下角“系统盘” 4.点击“创建快照” 二、磁盘扩容 1.点击“云盘扩容” 2. 选择自己要扩容的大小 …...
vscode settings(一):全局| 用户设置常用的设置项
参考资料 Visual Studio Code权威指南 by 韩骏 一. 全局设置与用户设置 1.1 Vscode支持两种不同范围的设置 用户设置(User Settings):这是一个全局范围的设置,会应用到所有的Visual Studio Code实例中。工作区设置(Workspace Settings):设…...
DeepSeek R1本地+私有云版医疗AI部署开发成功案例技术剖析
1. 引言 1.1 研究背景与意义 随着科技的飞速发展,人工智能(AI)在医疗领域的应用正逐渐成为推动医疗行业变革的重要力量。近年来,医疗 AI 取得了显著的进展,从疾病诊断、药物研发到医疗管理等各个环节,AI 技术都展现出了巨大的潜力。它能够处理和分析海量的医疗数据,为…...
CMOS图像传感器——偏振光图像传感器技术
最近,在索尼官网看到了其提到的偏光图像传感器技术Polarsens,感到好奇,查阅了相关资料,总结如下。。。。 一、偏振光 偏振实际上是光的一种基本属性,表述光电场振动的方向。振动方向和光波前进的方向构成的平面叫振动面。大多数的光源,比如太阳,发出非偏振光,这里我们…...
ROS2 中 TF 变换发布与订阅:实现 base_link 和 test_link 实时可视化显示
视频讲解 ROS2 中 TF 变换发布与订阅:实现 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 的宏变量是一个强大的工具,能够显著提高 SQL 模型的动态化能力和可维护性。通过合理使用宏变量,可以实现动态时间范围、多环境配置、参数化查询等功能,从而简化数据模型的开发和维护流程。随着数据团队的规模扩大和业务复杂度的增加&…...
逻辑函数的神经网络实现
1.单层感知器实现基本逻辑函数 先给大家抛出一道例题 (一)种类 a.OR函数 目标:当至少一个输入为1时,输出1;否则输出0。 权重设置: 输入权重:所有 wi1(i1,2,...,m)。…...
链表-基础训练(二)链表 day14
两两交换链表中的节点 题目示意: 给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。 你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。 原先我的思路是图像上的思路,但是我感觉还是很复杂…...
使用大语言模型对接OA系统,实现会议室预定功能
随着人工智能技术的不断进步,越来越多的企业开始借助 AI 助手来提高工作效率,尤其是在日常事务的自动化处理中。比如,在许多公司里,会议室的预定是一个常见且频繁的需求,通常需要员工手动检查空闲时间并做出选择。而通…...
hugging face---transformers包
一、前言 不同于计算机视觉的百花齐放,不同网络适用不同情况,NLP则由Transformer一统天下。transformer是2017年提出的一种基于自注意力机制的神经网络架构,transformers库是hugging face社区创造的一个py库,通过该库可以实现统一…...
1. 自定义组件基础
相关资源: 📎day10 图片素材.zip 1. 自定义组件基础 概念:在ArkUI中由框架直接提供的称为系统组件 -> Column,Button等,由开发者定义的称为自定义组件 作用:自定义组件可以对 UI和业务逻辑进行封装&…...
连接Sql Server时报错无法通过使用安全套接字层加密与 SQL Server 建立安全连接
文章目录 一. 前言二. 解决方案 方案1方案2 三. 总结 一. 前言 在《数据库原理》这门课的实验上,需要使用SQL Server,然后使用jdbc连接sql server突然报错为: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 语言科研绘图 --- 散点图-汇总
在发表科研论文的过程中,科研绘图是必不可少的,一张好看的图形会是文章很大的加分项。 为了便于使用,本系列文章介绍的所有绘图都已收录到了 sciRplot 项目中,获取方式: R 语言科研绘图模板 --- sciRplothttps://mp.…...