# @elysiumjs/themis 中文文档

规范地址：https://npmcn.com/package/%40elysiumjs/themis/

版本：0\.4\.4
npm 元数据更新时间：2026-11-13T03:09:30.899Z
中文译文更新时间：2026-11-13T19:01:59.224Z
软件包许可：Apache-2\.0

本文由上游 README 翻译，可能存在偏差，请结合原文核对 API、命令与配置。版权与许可归原作者所有。

原始 npm 页面：https://www.npmjs.com/package/%40elysiumjs/themis
原文对照：https://npmcn.com/package/%40elysiumjs/themis/?lang=original
项目主页：https://github.com/na2axl/elysiumjs/tree/main/@elysiumjs/themis#readme
源码仓库：https://github.com/na2axl/elysiumjs

弃用说明：has moved into @elysiumjs/olympus: import from @elysiumjs/olympus/themis; passwords, JWT and signed URLs from @elysiumjs/olympus/harpocrates\.

## 关键信息（来自 npm 元数据）

- 包体积（解压后）：109 kB，23 个文件
- 模块格式：ES Module
- TypeScript 类型：包内自带
- 运行依赖：3 个
- peer 依赖：@elysiumjs/core ^0\.24\.0
- 首次发布：2026-11-06T23:52:10.750Z
- 最新版本发布：2026-11-11T18:58:06.458Z
- 近 30 天下载量：3135（2026-10-14 至 2026-11-12，npm 官方下载统计）
- 已发布版本数：17（近 12 个月 17 个）

## 中文简介

Elysium.js框架的认证:密码,令牌,会话,守卫,CSRF,TOTP,OAuth和密码.

## 安装

```sh
npm install @elysiumjs/themis
```

也可以使用：`pnpm add @elysiumjs/themis`、`yarn add @elysiumjs/themis`、`bun add @elysiumjs/themis`

## README

# @elysiumjs/themis (英语)

认证 [极乐世界](https://npmcn.com/package/%40elysiumjs/core) 应用:密码,指使,会话,卫士,CSRF,TOTP,OAuth/OpenID Connection和密码. 它运行于邦,节点.js和Cloudflare工人.

## 安装

```bash
bun add @elysiumjs/themis
# Sessions in Redis or Workers KV
bun add @elysiumjs/themis-redis   # or @elysiumjs/themis-workers
```

## 设置

```ts
import { Application, Middleware } from '@elysiumjs/core';
import { Redis } from '@elysiumjs/redis';
import {
    apiKeyGuard,
    CsrfMiddleware,
    jwtGuard,
    sessionGuard,
    SessionMiddleware,
    ThemisModule
} from '@elysiumjs/themis';
import { RedisSessionStore } from '@elysiumjs/themis-redis';

@Application.register({
    modules: [
        ThemisModule.with({
            secret: env.APP_SECRET,
            session: {
                store: new RedisSessionStore({ connection: () => Redis.connection() }),
                ttl: 60 * 60 * 24 * 7
            },
            guards: {
                session: sessionGuard({ user: (id) => users.find(id) }),
                jwt: jwtGuard({ secret: env.JWT_SECRET, user: (claims) => users.find(claims.sub) }),
                apiKey: apiKeyGuard({ find: (hash) => keys.ownerOf(hash) })
            }
        }),
        AppModule
    ]
})
@Middleware.register(SessionMiddleware, CsrfMiddleware)
class App extends Application {}
```

`secret` 签名会话 cookie, 加密 cookie 会话并绑定 CSRF 令牌 。 在环境上保密。 32 字节以下的秘密在启动时被拒绝(`WeakSecretError`),更短的JWT和已签名的URL秘密也一样.

## 认证

```ts
import type { AuthUser } from '@elysiumjs/themis';

import { Http } from '@elysiumjs/core';
import { Auth } from '@elysiumjs/themis';

declare module '@elysiumjs/themis' {
    interface AuthUser {
        id: string;
    }
}

@Http.controller('/me')
@Auth()
class MeController {
    @Http.get()
    public me(@Auth.user() user: AuthUser) {
        return { id: user.id };
    }

    @Http.get('/tokens')
    @Auth({ guards: ['jwt'] })
    public tokens() {
        /* ... */
    }
}
```

-   `@Auth()` 按顺序尝试配置的守护者 `@Auth({ guards })` 只有这些名字 守护是执行 `Guard`,或者一个警卫班,这个班次通过服务集装箱建造一次,以便其建造者得到注射服务。 登记 `AuthMiddleware`这样 `@Middleware.register(AuthMiddleware.guards(['jwt']))` 与 `@Auth({ guards: ['jwt'] })`。 。 。 .
-   一个要求没有警卫接受一个401问题的答案. `@Auth({ optional: true })` 让它通过,并 `Auth.check()` 告诉用户是否被签名 .
-   用户可通过 `@Auth.user()` 在一个处理器参数上, `Auth.currentUser()` 财务报告和审定财务报表 `ctx.currentUser`这样cerberus就可以读它与 `getSubject: (ctx) => ctx.currentUser`。通过增加 `AuthUser` 接口。 后面,我们走 `@Auth({ optional: true })`,键入参数 `AuthUser | undefined`。 。 。 .
-   `Auth.login(id)` 在会话上签名一个用户(并重新生成) `Auth.logout()` 把他们签出来.

## 会议

```ts
import { Session } from '@elysiumjs/themis';

const session = Session.current();
session.set('cart', items);
session.flash('notice', 'Saved');
session.regenerate();
```

-   这些商店是 `MemorySessionStore`, (中文(简体) ), `RedisSessionStore` 联 合 国 [`@elysiumjs/themis-redis`](https://npmcn.com/package/%40elysiumjs/themis-redis) 财务报告和审定财务报表 `KvSessionStore` 联 合 国 [`@elysiumjs/themis-workers`](https://npmcn.com/package/%40elysiumjs/themis-workers) (a) 工人KV装订)。 没有商店,会话就活在过程的记忆中,他们就会在外部测试中警告.
-   `store: 'cookie'` 将整个会话保存在 AES- GCM 加密饼干中 。 它必须保持在4000个字符以下(`SessionTooLargeError`),而所复制的饼干在过期前仍然有效,所以在商店中保留可撤销的会话.
-   会话饼干已经签了, `HttpOnly`, (中文(简体) ), `SameSite=Lax` 财务报告和审定财务报表 `Secure` 默认.
-   处理器运行后会话被保存,当请求以客户端出错(4xx)而结束时也会被保存,例如签名后出现抛出的问题.

## 中国证监会

`CsrfMiddleware` 以会话中保存的同步令牌保护 cookie 会话 。 不安全的方法需要 `x-csrf-token` 页眉或 `_csrf` 要匹配的字段 `Csrf.token()`或者他们回答一个403个问题.

## 密码

```ts
import { Password } from '@elysiumjs/themis';

const hash = await Password.hash(input);
if ((await Password.verify(input, hash)) && Password.needsRehash(hash)) {
    await users.updateHash(id, await Password.hash(input));
}
```

散热器为rg2id(`BunPasswordHasher`) Bun和PBKDF2-SHA256号机上安装的`Pbkdf2PasswordHasher`在其他地方,有60万次重复(在Cloudflare Workers上为100,000次,在此处为PBKDF2上限). `ScryptPasswordHasher`从 `@elysiumjs/themis/scrypt`, 用途 `node:crypto`选择一个 `Password.use(hasher)`Hashes描述它们的格式,所以 `verify` 读取所有三个.

## 键

```ts
import { ApiKeys, Jwt, SignedUrl } from '@elysiumjs/themis';

const token = await Jwt.sign({ role: 'admin' }, { secret, subject: user.id, expiresIn: 3600 });
const claims = await Jwt.verify(token, { secret });

const { key, hash } = await ApiKeys.generate({ prefix: 'sk' }); // store the hash, show the key once
await ApiKeys.verify(key, hash);

const link = await SignedUrl.sign('https://app.example.com/download/1', { secret, expiresIn: 600 });
await SignedUrl.verify(link, { secret });
```

`Jwt` 带有密钥(HS256)或不对称密钥及其算法的符号。 无效或过期的符是401个问题,无效或过期的链接是403个问题.

`Jwt.verify` 未过期即拒签; 通过 `requiredClaims` (a) 更改必须携带的凭证(`[]` 接受一个没有 `exp`) (中文(简体) ). `currentDate` 对照另一时间检查过期日期 `issuedAt` 从另一个时间开始.

### 使用时读取的秘密

每一个秘密 `ThemisModule` 秘密,JWT秘密和已签名的 URL 秘密)可以是函数,同步或async,每次需要该秘密时都会被调用. 从一个与请求相联的商店读取,比如Cloudflare Workers上绑有的秘密,一个旋转的秘密会被取出而不重启. 每个读数都检查32字节的最小值.

```ts
ThemisModule.with({ secret: () => secrets.get('app-secret') });
```

在旋转时,用当前键,然后用上一个键进行校验:

```ts
const claims = await Jwt.verify(token, {
    keys: [
        { secret: () => secrets.get('jwt-current') },
        { secret: () => secrets.get('jwt-previous') }
    ]
});
```

密钥按顺序进行,直到有人验证了签名;过期的令牌或错误的主张立即被拒绝.

## 二要素代码

```ts
import { BackupCodes, Totp } from '@elysiumjs/themis';

const secret = Totp.generateSecret();
const qr = Totp.uri({ secret, issuer: 'Acme', account: user.email });
const counter = await Totp.verify(code, secret, { window: 1 }); // null when wrong

const { codes, hashes } = await BackupCodes.generate(10);
const { valid, remaining } = await BackupCodes.consume(typed, hashes);
```

存储柜台 `Totp.verify` 返回并拒绝一个计数器没有更大的代码,这样一个代码就一次起作用.

普通的散列车让任何有表格测试副本的人猜测下线. 传递一个用 HMAC 来散开代码的密钥, 以及使用这些密钥 :

```ts
const { codes, hashes } = await BackupCodes.generate(8, {
    secret: () => secrets.get('backup-codes')
});
const { valid, used } = await BackupCodes.consume(typed, hashes, {
    secret: () => secrets.get('backup-codes')
});
```

`used` 是匹配的散列代码,以标记所使用的代码.

## OAuth 和 OpenID 连接

```ts
import { OAuthClient } from '@elysiumjs/themis';

const google = new OAuthClient({
    issuer: 'https://accounts.google.com',
    clientId: env.GOOGLE_CLIENT_ID,
    clientSecret: env.GOOGLE_CLIENT_SECRET,
    redirectUri: 'https://api.example.com/auth/google/callback'
});

// GET /auth/google
const url = await google.authorize();

// GET /auth/google/callback
const { tokens, claims, userInfo } = await google.callback(request.url);
```

客户端使用PKCE的授权代码流. 状态,验证器和OpenID Connectnce保存在会话中. 一个没有发现的提供者需要 `authorizationServer` 和它的终点。 错误状态或交换失败的回调回答400个问题. 它的细节名称是 OAuth 错误代码, 提供者回答; 其它的失败都会被记录下来, 用固定的细节回答 .

## 密码

```ts
import { Passkeys } from '@elysiumjs/themis';

const passkeys = new Passkeys({
    rpName: 'Acme',
    rpID: 'example.com',
    origin: 'https://example.com',
    repository: new PasskeyTable()
});

const options = await passkeys.registrationOptions({ id: user.id, name: user.email });
const record = await passkeys.verifyRegistration(response);

const challenge = await passkeys.authenticationOptions();
const used = await passkeys.verifyAuthentication(assertion); // used.userId signs the user in
```

本届会议继续面临挑战。 应用程序通过执行存储证书 `PasskeyRepository`。无法验证400个问题的答案. `updateCounter` 获取上一个计数器: 答案 `false` 当存储的不再是它, 而输掉比赛的签入被拒绝.

一个在两个调用之间保持自身状态的应用程序,例如多步流,或者一个没有会话饼干的客户端,设置 `challenges: 'caller'` 并传递挑战回:

```ts
const passkeys = new Passkeys({ ...options, challenges: 'caller' });

const request = await passkeys.authenticationOptions({ id: user.id });
// keep request.challenge with the flow, then:
const used = await passkeys.verifyAuthentication(assertion, { challenge, userId: user.id });
```

`residentKey: 'required'` 使每一个新的密码都可以发现, 对于一个没有用户名的登录 `timeout` (以毫秒计)是客户端需要回答多久. 一个用户处理用户名而不是密码的断言被拒绝. 与一个 `userId`,另一个用户的密码像一个未知的一样被拒绝。 登记需要: `userId` 这是为.

## 常见问题

### @elysiumjs/themis 是什么？

Elysium\.js框架的认证:密码,令牌,会话,守卫,CSRF,TOTP,OAuth和密码\.注意：该包已被作者标记为弃用——has moved into @elysiumjs/olympus: import from @elysiumjs/olympus/themis; passwords, JWT and signed URLs from @elysiumjs/olympus/harpocrates\.

### 如何安装 @elysiumjs/themis？

在项目目录运行 npm install @elysiumjs/themis，或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖：@elysiumjs/core（^0\.24\.0）。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖，Yarn 需要手动安装。

### @elysiumjs/themis 支持 TypeScript 吗？

支持。包内自带 TypeScript 类型声明（package\.json 声明了 types 入口），安装后即可获得类型提示，无需再安装 @types 包。

### @elysiumjs/themis 支持 ESM 还是 CommonJS？

以 ES Module 形式发布，推荐使用 import 引入。在 CommonJS 项目中，Node\.js 20\.19\+ / 22\.12\+ 可以直接 require\(\) 不含顶层 await 的 ES Module，更早的版本需要改用动态 import\(\)。

### @elysiumjs/themis 可以商用吗？

@elysiumjs/themis 使用 Apache-2\.0 许可。宽松许可：允许商业使用、修改和再分发，并附带明确的专利授权；分发时需保留许可与 NOTICE 文件，修改过的文件要注明改动。以上为概括，不构成法律意见，请以许可原文为准。

### @elysiumjs/themis 的下载量有多少？

根据 npm 官方下载统计，@elysiumjs/themis 在 2026/10/14 – 2026/11/12 这 30 天内被下载 3,135 次，日均约 105 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入，适合横向比较热度，不能直接等同于使用人数。

### @elysiumjs/themis 还在维护吗？

最新版本 0\.4\.4 发布于 2026/11/12（2 天前）；至今共发布 17 个版本，其中过去 12 个月发布了 17 个；首次发布于 2026/11/07。作者已将其标记为弃用，新项目建议寻找替代方案。

