npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@fefeding/base-server

v0.0.46

Published

企业级基础服务组件 - 基于 MidwayJS 的 SSO 单点登录系统后端,提供用户认证、会话管理、应用管理、微信生态集成等核心能力

Readme

@fefeding/base-server

企业级基础服务组件 — MidwayJS 实现

简介

@fefeding/base-server 是基于 MidwayJS 框架的企业级基础服务,提供完整的用户认证、会话管理、应用管理、微信生态集成等核心能力。作为 SSO 单点登录系统的后端 API 服务,处理所有认证逻辑和数据库操作。

双重部署模式:

  • 作为独立微服务部署,提供 HTTP API 接口
  • 作为 MidwayJS 组件@fefeding/base-server)集成到其他项目

系统架构

┌───────────────────────────────────────────────────────────────┐
│                     SSO Server (Web 层)                        │
│   登录页面渲染 / 会话接口代理 / 管理端代理                       │
└───────────────────────────┬───────────────────────────────────┘
                            │ HTTP API (API_KEY 鉴权)
┌───────────────────────────┼───────────────────────────────────┐
│                 Base Server (本项目)                            │
│                                                               │
│  ┌─────────────────────────────────────────────────────────┐  │
│  │                    Controller 层                         │  │
│  │  ┌──────────────┐ ┌──────────────┐ ┌──────────────┐    │  │
│  │  │  Session     │ │  Account     │ │  App          │    │  │
│  │  │  会话管理     │ │  账户管理     │ │  应用管理      │    │  │
│  │  └──────┬───────┘ └──────┬───────┘ └──────┬───────┘    │  │
│  │  ┌──────────────┐ ┌──────────────┐                      │  │
│  │  │  Wx          │ │  Address     │                      │  │
│  │  │  微信生态     │ │  IP地址       │                      │  │
│  │  └──────┬───────┘ └──────┬───────┘                      │  │
│  └─────────┼────────────────┼────────────────┼────────────┘  │
│            │                │                │                │
│  ┌─────────┼────────────────┼────────────────┼────────────┐  │
│  │         │          Service 层                │           │  │
│  │  SessionService   AccountService    AppService           │  │
│  │  UserService      AuthMapService    WxService            │  │
│  │  WkService        VerificationCodeService                │  │
│  │  AccessTokenService  WxMessageService                    │  │
│  │  BaiduOcrService   PermissionService                     │  │
│  └──────────────────────────┬──────────────────────────────┘  │
│                             │                                 │
│  ┌──────────────────────────┼──────────────────────────────┐  │
│  │                    Model 层 (TypeORM)                     │  │
│  │  Account | User | Session | App | AuthMap                │  │
│  │  VerificationCode | AppAccessToken | WxMessage           │  │
│  └──────────────────────────┬──────────────────────────────┘  │
│                             │                                 │
│  ┌──────────────────────────┴──────────────────────────────┐  │
│  │              MySQL (db_account)                           │  │
│  └──────────────────────────────────────────────────────────┘  │
└───────────────────────────────────────────────────────────────┘

核心功能

多方式登录认证

| 登录方式 | 接口 | 说明 | |---------|------|------| | 账号密码 | POST /api/session/loginByAccount | 传统账号密码认证 | | 微信小程序 | POST /api/session/loginByWx | appType=WxMiniApp,通过 code 换取 openid | | 微信公众号 | POST /api/session/loginByWx | appType=WxGzh,OAuth2 授权码模式 | | 企业微信 Web | POST /api/session/loginByWx | appType=WkWeb,企业微信 OAuth2 | | 企业微信小程序 | POST /api/session/loginByWx | appType=WkMiniApp | | 公众号扫码 | POST /api/session/createWxLoginQrcode | 生成带参数二维码,扫码事件触发登录 | | 验证码登录 | POST /api/session/loginByVerifyCode | 通过验证码(如公众号扫码验证码)登录 | | 授权码登录 | POST /api/session/loginByAuthCode | SSO 单点登录核心,用临时授权码换 Session |

会话管理

  • Session 创建与生命周期管理(自动续期)
  • Session 缓存(内存缓存 + 数据库持久化)
  • 登出与状态管理(ACTIVE / OFFLINE / INACTIVATED)
  • 临时授权码(auth_code)机制,5 分钟有效,一次性使用

账户与用户管理

  • 账户注册、查询、修改、删除
  • 密码修改与验证
  • 用户信息管理(头像、昵称等)
  • 微信 openid / unionid 自动关联

应用管理

  • 多应用配置(支持微信小程序、公众号、企业微信等类型)
  • App AccessToken 自动管理与刷新
  • 应用密钥安全管理

微信生态集成

  • 微信公众号:消息接收、用户信息获取、菜单管理、素材管理、草稿与发布
  • 微信小程序:code2Session、手机号获取
  • 企业微信:OAuth2 登录、用户身份获取

安全防护

  • API Token 验证(@decorators.checkApiToken
  • IP 白名单控制(@decorators.checkIP
  • 登录态校验(@decorators.checkLogin
  • 腾讯云短信验证码

OAuth2 + OIDC 标准支持(v0.0.42 新增)

  • 标准端点:Token、UserInfo、JWKS、Discovery、Revocation
  • 客户端管理:注册、查询、密钥重新生成
  • 安全增强:PKCE(防授权码拦截)、nonce(防重放攻击)
  • Token 类型:access_token、id_token、refresh_token
  • 签名算法:RS256(RSA 密钥对)
  • 自动发现:OIDC Discovery 文档
  • 标准库支持:兼容主流 OAuth2 客户端库

项目结构

base-server/
├── src/
│   ├── config/                       # 配置文件
│   │   ├── config.default.ts         # 默认配置
│   │   ├── config.local.ts           # 本地开发配置
│   │   └── devops.config.ts          # 部署配置
│   ├── controller/                   # 控制器
│   │   ├── session.controller.ts     # 会话管理(登录/登出/授权码)
│   │   ├── account.controller.ts     # 账户管理(CRUD/密码修改)
│   │   ├── app.controller.ts         # 应用管理(CRUD/AccessToken)
│   │   ├── oauth.controller.ts       # OAuth2/OIDC 标准端点
│   │   ├── wx.controller.ts          # 微信生态(消息/素材/菜单/手机号)
│   │   └── address.controller.ts     # IP 地址查询
│   ├── service/                      # 服务层
│   │   ├── session.service.ts        # 会话核心服务
│   │   ├── account.service.ts        # 账户服务
│   │   ├── user.service.ts           # 用户服务
│   │   ├── app.service.ts            # 应用服务
│   │   ├── authMap.service.ts        # 授权码映射服务(支持OAuth2)
│   │   ├── oauthClient.service.ts    # OAuth2 客户端管理服务
│   │   ├── jwt.service.ts            # JWT 签名与验证服务(RSA)
│   │   ├── accessToken.service.ts    # App AccessToken 服务
│   │   ├── verificationCode.service.ts # 验证码服务
│   │   ├── wxMessage.service.ts      # 微信消息服务
│   │   ├── platformSdk/              # 第三方平台 SDK 封装
│   │   │   ├── wx.service.ts         # 微信 API
│   │   │   ├── wk.service.ts         # 企业微信 API
│   │   │   └── baiduOcr.service.ts   # 百度 OCR 服务
│   │   └── permission/               # 权限服务
│   │       └── psermission.service.ts
│   ├── model/                        # TypeORM 实体模型
│   │   ├── account.entity.ts         # 账户表
│   │   ├── user.entity.ts            # 用户表
│   │   ├── session.entity.ts         # 会话表
│   │   ├── app.entity.ts             # 应用表
│   │   ├── authMap.entity.ts         # 授权码映射表(支持OAuth2)
│   │   ├── oauthClient.entity.ts     # OAuth2 客户端表
│   │   ├── appAccessToken.entity.ts  # 应用 AccessToken 表
│   │   ├── verificationCode.entity.ts # 验证码表
│   │   └── wxMessage.entity.ts       # 微信消息记录表
│   ├── dto/                          # 数据传输对象
│   │   ├── session.dto.ts            # 会话相关DTO
│   │   ├── oauth.dto.ts              # OAuth2相关DTO
│   │   └── ...
│   ├── middleware/                    # 中间件
│   ├── filter/                       # 过滤器
│   ├── configuration.ts              # MidwayJS 配置入口
│   └── index.ts                      # 导出(组件模式入口)
├── data/
│   ├── db_account.sql                # 数据库初始化脚本
│   └── t_oauth_client.sql            # OAuth2客户端表初始化脚本
├── bootstrap.js                      # 生产启动入口
├── server.js                         # TARS 配置加载入口
└── Dockerfile                        # Docker 构建文件

快速开始

作为独立服务部署

1. 安装依赖

npm install

2. 环境配置

复制 .tpl.env.env,修改以下关键配置:

# 服务配置
PORT=8089                            # 服务端口
PREFIX=base-server                   # 路由前缀

# API 鉴权
API_KEY="2024@fefeding#"             # API 鉴权密钥(调用方需携带)
APP_ID=2                             # 自身应用ID

# SSO 地址(用于生成回调链接)
SSO_URL=https://base.ycnull.com/sso

# Session 配置
SESSION_TIMEOUT=86400000             # Session 超时时间(毫秒)

# OAuth2 + OIDC 配置
OAUTH_ISSUER=sso-server              # OAuth2 Issuer 标识
OAUTH_TOKEN_VALIDITY=3600           # access_token 有效期(秒)
OAUTH_REFRESH_VALIDITY=2592000      # refresh_token 有效期(秒)
JWT_PRIVATE_KEY=                     # RSA 私钥(PEM格式,推荐环境变量)
JWT_PUBLIC_KEY=                      # RSA 公钥(PEM格式,推荐环境变量)
# 或使用文件路径
JWT_PRIVATE_KEY_PATH=/path/to/private.pem
JWT_PUBLIC_KEY_PATH=/path/to/public.pem

# 数据库配置
DB_HOST=127.0.0.1
DB_PORT=3306
DB_USERNAME=root
DB_PASSWORD=123456

# IP 白名单
IP_WHITE_LIST=58.251.36.54

# 腾讯云配置
TENCENT_CREDENTIAL_SECRETID=
TENCENT_CREDENTIAL_SECRETKEY=

# 腾讯云 COS 配置
COS_DEFAULT_BUCKET=base-1331355487
COS_DEFAULT_REGION=ap-guangzhou

3. 数据库初始化

# 创建数据库
mysql -u root -p -e "CREATE DATABASE IF NOT EXISTS db_account;"

# 导入基础表结构
mysql -u root -p db_account < data/db_account.sql

# 导入 OAuth2 客户端表(v0.0.42 新增)
mysql -u root -p db_account < data/t_oauth_client.sql

4. 启动服务

# 开发模式
npm run dev

# 生产模式
npm start

作为组件集成使用

1. 安装依赖

npm install @fefeding/base-server

2. 配置导入

src/configuration.ts 中配置导入:

import { Configuration, App } from '@midwayjs/core';
import * as busboy from '@midwayjs/busboy';
import * as codeDye from '@midwayjs/code-dye';
import * as orm from '@midwayjs/typeorm';
import { join } from 'path';

import * as baseMidwayjs from '@cicctencent/midwayjs-base';
import * as baseServerMidwayjs from '@fefeding/base-server';

@Configuration({
    imports: [
        {
            component: codeDye,
            enabledEnvironment: ['local'],
        },
        busboy,
        orm,
        baseMidwayjs,
        baseServerMidwayjs,
    ],
    importConfigs: [join(__dirname, './config')],
})
export class ContainerLifeCycle {
    @App()
    app: baseMidwayjs.koa.Application;

    async onReady() {
        // 添加自定义过滤器
    }

    async onServerReady() {
        if (process.env.NODE_ENV !== 'production') {
            console.log(
                `服务已启动: http://${process.env.IP || '127.0.0.1'}:${process.env.PORT || 7001}`
            );
        }
    }
}

3. 使用服务

组件模式下,所有 Service 和 Model 均可注入使用:

import { Inject, Controller, All, Provide, Body } from '@midwayjs/core';
import { BaseController, decorators } from '@cicctencent/midwayjs-base';
import { AccountService, UserService, SessionService } from '@fefeding/base-server';

@Provide()
@Controller('/api/account')
export class AccountController extends BaseController {
    @Inject()
    userService: UserService;

    @Inject()
    accountService: AccountService;

    @All('/myInfo')
    @decorators.checkLogin(true)
    async myInfo() {
        const userId = this.ctx.currentSession.userId;
        const userInfo = await this.userService.getUser(userId);
        return userInfo;
    }
}

组件模式导出列表:

| 类别 | 导出项 | |------|--------| | Model | AccountModel, UserModel, SessionModel, AppModel, AuthMapModel, OAuthClientModel, AppAccessTokenModel, VerificationCodeModel, WxMessageModel | | Service | AccountService, UserService, SessionService, AppService, AuthMapService, OAuthClientService, JwtService, AccessTokenService, VerificationCodeService, WxMessageService, WxService, WkService, BaiduOcrService |

API 接口文档

会话管理 (/api/session)

| 路径 | 方法 | 鉴权 | 说明 | |------|------|------|------| | /loginByAccount | POST | API_KEY + IP | 账号密码登录 | | /loginByWx | POST | - | 微信/企业微信登录 | | /loginByAuthCode | POST | API_KEY | 授权码换取 Session | | /loginByVerifyCode | POST | API_KEY + IP | 验证码登录 | | /getLoginSession | POST | API_KEY | 获取 Session 信息 | | /createAuthCode | POST | API_KEY + IP | 生成临时授权码 | | /createWxLoginQrcode | POST | API_KEY | 生成公众号登录二维码 | | /logout | POST | API_KEY | 登出 |

账户管理 (/api/account)

| 路径 | 方法 | 鉴权 | 说明 | |------|------|------|------| | /search | POST | API_KEY + IP + Login | 搜索账户 | | /queryUser | POST | API_KEY + Login | 查询用户 | | /createAccount | POST | API_KEY + Login | 创建账户 | | /getUser | POST | API_KEY + Login | 获取用户信息 | | /getByLoginId | POST | API_KEY + Login | 通过 loginId 获取账户 | | /updatePassword | POST | API_KEY + Login | 修改密码 | | /updateUser | POST | API_KEY + Login | 更新用户信息 | | /remove | POST | API_KEY + Login | 删除账户 |

应用管理 (/api/app)

| 路径 | 方法 | 鉴权 | 说明 | |------|------|------|------| | /search | POST | API_KEY + IP + Login | 搜索应用 | | /getApp | POST/GET | API_KEY + IP | 获取应用信息 | | /create | POST | API_KEY + IP | 创建应用 | | /update | POST | API_KEY + IP | 修改应用 | | /remove | POST | API_KEY + IP + Login | 删除应用 | | /getAppAccessToken | POST/GET | API_KEY + IP | 获取应用 AccessToken | | /invalidateAppAccessToken | POST | API_KEY + IP | 失效应用 AccessToken | | /invalidateToken | POST | API_KEY + IP | 失效指定 Token |

微信接口 (/api/wx)

| 路径 | 方法 | 鉴权 | 说明 | |------|------|------|------| | /getUserPhoneNumber | POST | API_KEY | 获取微信用户手机号 | | /addDraft | POST | API_KEY | 创建公众号草稿 | | /publishArticle | POST | API_KEY | 发布文章 | | /uploadimg | POST | API_KEY | 上传图文消息图片 | | /addMaterial | POST | API_KEY | 上传永久素材 | | /clreaQuota | POST | API_KEY | 清除调用次数 | | /createMenus | POST | API_KEY | 创建菜单 | | /msg | POST | API_KEY | 微信消息回调处理 |

地址接口 (/api/address)

| 路径 | 方法 | 鉴权 | 说明 | |------|------|------|------| | /getIPAddress | ALL | 无 | 获取客户端 IP 地址 |

OAuth2 标准端点 (/oauth) - v0.0.42 新增

| 路径 | 方法 | 鉴权 | 说明 | |------|------|------|------| | /.well-known/openid-configuration | GET | 无 | OIDC Discovery 文档 | | /jwks | GET | 无 | JWKS 公钥发布 | | /token | POST | client_secret | Token 端点(授权码换Token/刷新Token) | | /userinfo | GET/POST | Bearer Token | OIDC UserInfo 端点 | | /revoke | POST | client_secret | Token 撤销端点(RFC 7009) | | /authorize | POST | API_KEY + IP | 创建OAuth2授权码(SSO调用) | | /client/validate | POST | API_KEY + IP | 验证OAuth2客户端(SSO调用) | | /client/register | POST | API_KEY + Login | 注册OAuth2客户端 | | /client/search | POST | API_KEY + Login | 查询OAuth2客户端列表 | | /client/regenerateSecret | POST | API_KEY + Login | 重新生成client_secret |

SSO 单点登录协议

Base Server 是 SSO 单点登录的核心,提供两种接入方式:

方式一:传统 SSO 协议(内部系统)

适用于内部系统快速接入,使用自定义授权码机制。

1. 用户访问应用 → 应用重定向到 SSO 登录页
2. SSO 调用 /api/session/loginByWx 或 /api/session/loginByAccount 完成登录
3. SSO 调用 /api/session/createAuthCode 生成临时授权码
4. SSO 将 auth_code 回调给应用
5. 应用调用 /api/session/loginByAuthCode 用授权码换取完整 Session
6. 应用调用 /api/session/getLoginSession 验证 token 有效性

特点:

  • 简单直接,适合内部系统
  • 使用 Session 机制,服务端维护登录态
  • 授权码有效期:5 分钟,一次性使用

方式二:OAuth2 + OIDC 标准协议(推荐)

符合 OAuth2 和 OpenID Connect 国际标准,适合第三方系统接入和标准化对接。

核心端点

| 端点 | 路径 | 说明 | |------|------|------| | OIDC Discovery | GET /.well-known/openid-configuration | 自动发现配置 | | JWKS | GET /oauth/jwks | 公钥发布(验证 ID Token 签名) | | Token | POST /oauth/token | 授权码换 Token / 刷新 Token | | UserInfo | GET/POST /oauth/userinfo | 获取用户信息(Bearer Token) | | Revoke | POST /oauth/revoke | 撤销 Token | | Authorize | POST /oauth/authorize | 创建授权码(SSO 调用) | | Client Validate | POST /oauth/client/validate | 验证客户端(SSO 调用) | | Client Register | POST /oauth/client/register | 注册客户端(管理端) | | Client Search | POST /oauth/client/search | 查询客户端(管理端) |

标准流程

┌─────────┐                                      ┌──────────┐                  ┌─────────────┐
│  用户    │                                      │   应用    │                  │ Base Server │
└────┬────┘                                      └─────┬────┘                  └──────┬──────┘
     │                                                 │                              │
     │  1. 访问应用                                    │                              │
     │────────────────────────────────────────────────>│                              │
     │                                                 │                              │
     │  2. 重定向到 SSO 登录页                          │                              │
     │<────────────────────────────────────────────────│                              │
     │  (携带 client_id, redirect_uri, scope, state)   │                              │
     │                                                 │                              │
     │  3. 用户在 SSO 完成登录                          │                              │
     │─────────────────────────────────────────────────────────────────────────────>│
     │                                                 │                              │
     │  4. SSO 调用 /oauth/authorize 创建授权码          │                              │
     │<─────────────────────────────────────────────────────────────────────────────│
     │                                                 │                              │
     │  5. 回调应用(携带 code, state)                  │                              │
     │────────────────────────────────────────────────>│                              │
     │                                                 │                              │
     │                                                 │  6. 用 code 换 Token          │
     │                                                 │─────────────────────────────>│
     │                                                 │  POST /oauth/token           │
     │                                                 │  (client_id, client_secret,  │
     │                                                 │   code, redirect_uri)        │
     │                                                 │                              │
     │                                                 │  7. 返回 Token               │
     │                                                 │<─────────────────────────────│
     │                                                 │  (access_token, id_token,    │
     │                                                 │   refresh_token)             │
     │                                                 │                              │
     │  8. 登录成功                                     │                              │
     │<────────────────────────────────────────────────│                              │
     │                                                 │                              │
     │                                                 │  9. 获取用户信息(可选)       │
     │                                                 │─────────────────────────────>│
     │                                                 │  GET /oauth/userinfo         │
     │                                                 │  Authorization: Bearer xxx   │
     │                                                 │                              │
     │                                                 │  10. 返回用户信息             │
     │                                                 │<─────────────────────────────│

接入步骤

1. 注册 OAuth2 客户端

POST /oauth/client/register
Content-Type: application/json
x-api-key: YOUR_API_KEY

{
  "name": "我的应用",
  "redirect_uris": ["https://myapp.com/callback"],
  "scopes": ["openid", "profile", "email"],
  "grant_types": ["authorization_code", "refresh_token"],
  "token_validity": 3600,
  "refresh_validity": 2592000
}

# 响应(client_secret 仅此一次返回,请妥善保管)
{
  "clientId": "cli_xxxxxxxx",
  "clientSecret": "xxxxxxxxxx",
  "name": "我的应用",
  "redirectUris": ["https://myapp.com/callback"],
  "scopes": ["openid", "profile", "email"],
  "tokenValidity": 3600,
  "refreshValidity": 2592000
}

2. 用户登录并获取授权码

用户在 SSO 登录页完成认证后,SSO 服务调用:

POST /oauth/authorize
Content-Type: application/json
x-api-key: YOUR_API_KEY

{
  "sessionId": "用户登录后的session_id",
  "clientId": "cli_xxxxxxxx",
  "redirectUri": "https://myapp.com/callback",
  "scope": "openid profile",
  "codeChallenge": "PKCE挑战码(可选)",
  "codeChallengeMethod": "S256",
  "nonce": "随机字符串(防重放)"
}

# 响应
{
  "ret": 0,
  "data": "授权码字符串"
}

3. 用授权码换取 Token

POST /oauth/token
Content-Type: application/json

{
  "grant_type": "authorization_code",
  "code": "授权码",
  "client_id": "cli_xxxxxxxx",
  "client_secret": "xxxxxxxxxx",
  "redirect_uri": "https://myapp.com/callback",
  "code_verifier": "PKCE验证码(如使用了PKCE)"
}

# 响应
{
  "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
  "id_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
  "scope": "openid profile"
}

4. 获取用户信息

GET /oauth/userinfo
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...

# 响应
{
  "sub": "12345",
  "name": "张三",
  "email": "[email protected]",
  "picture": "https://avatar.example.com/12345.jpg",
  "nickname": "小张",
  "gender": 1
}

5. 刷新 Token

POST /oauth/token
Content-Type: application/json

{
  "grant_type": "refresh_token",
  "refresh_token": "refresh_token字符串",
  "client_id": "cli_xxxxxxxx",
  "client_secret": "xxxxxxxxxx"
}

# 响应
{
  "access_token": "新的access_token",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": "openid profile"
}

安全特性

PKCE(Proof Key for Code Exchange)

防止授权码被拦截窃取,适用于移动应用和 SPA:

// 1. 生成 code_verifier(43-128位随机字符串)
const codeVerifier = generateRandomString(64);

// 2. 生成 code_challenge(S256方法)
const codeChallenge = base64url(sha256(codeVerifier));

// 3. 授权请求时携带 code_challenge
// 4. Token 请求时携带 code_verifier

ID Token(OIDC 标准)

包含用户身份声明的 JWT Token:

{
  "iss": "sso-server",
  "sub": "12345",
  "aud": "cli_xxxxxxxx",
  "exp": 1234567890,
  "iat": 1234560000,
  "nonce": "随机字符串",
  "name": "张三",
  "email": "[email protected]",
  "picture": "https://avatar.example.com/12345.jpg"
}

验证方式:

  1. /.well-known/openid-configuration 获取 jwks_uri
  2. /oauth/jwks 获取公钥
  3. 使用公钥验证 ID Token 签名

Token 有效期

| Token 类型 | 默认有效期 | 说明 | |-----------|-----------|------| | access_token | 3600秒(1小时) | 访问资源凭证 | | id_token | 3600秒(1小时) | 身份凭证 | | refresh_token | 2592000秒(30天) | 刷新凭证 | | 授权码 | 300秒(5分钟) | 一次性使用 |

OIDC Discovery

客户端可通过自动发现端点获取配置:

GET /.well-known/openid-configuration

# 响应
{
  "issuer": "sso-server",
  "authorization_endpoint": "https://base.ycnull.com/oauth/authorize",
  "token_endpoint": "https://base.ycnull.com/oauth/token",
  "userinfo_endpoint": "https://base.ycnull.com/oauth/userinfo",
  "jwks_uri": "https://base.ycnull.com/oauth/jwks",
  "revocation_endpoint": "https://base.ycnull.com/oauth/revoke",
  "response_types_supported": ["code"],
  "grant_types_supported": ["authorization_code", "refresh_token"],
  "subject_types_supported": ["public"],
  "id_token_signing_alg_values_supported": ["RS256"],
  "scopes_supported": ["openid", "profile", "email"],
  "token_endpoint_auth_methods_supported": ["client_secret_post", "client_secret_basic", "none"],
  "claims_supported": ["sub", "name", "email", "picture", "nonce"],
  "code_challenge_methods_supported": ["S256", "plain"]
}

客户端库推荐

  • Node.js: openid-client
  • Java: spring-security-oauth2
  • Python: authlib
  • Go: golang.org/x/oauth2
  • PHP: league/oauth2-client

两种方式对比

| 特性 | 传统 SSO 协议 | OAuth2 + OIDC | |------|-------------|---------------| | 适用场景 | 内部系统快速接入 | 第三方系统、标准化对接 | | 标准化 | 自定义协议 | 国际标准(RFC 6749、OIDC) | | 客户端管理 | 无需注册 | 需注册 client_id/client_secret | | Token 类型 | Session Token | access_token + id_token + refresh_token | | Token 格式 | UUID | JWT(RS256签名) | | 安全增强 | 基础 | PKCE、nonce、签名验证 | | 用户信息 | Session 中包含 | 通过 UserInfo 端点获取 | | 客户端库 | 需自行实现 | 标准库支持 | | 跨域支持 | 较弱 | 强(CORS友好) | | 离线访问 | 不支持 | 支持(refresh_token) |

数据库

数据库表

| 表名 | 说明 | |------|------| | t_account | 账户表(loginId, account, password, openId, unionId, appId) | | t_user | 用户表(name, avatar, alias, gender, status) | | t_session | 会话表(id, loginId, userId, appId, status, clientIP, ext) | | t_app | 应用表(name, type, appId, secret, appKey) | | t_auth_map | 授权码映射表(code, token, clientId, redirectUri, scope, 过期时间) | | t_oauth_client | OAuth2 客户端表(clientId, clientSecret, name, redirectUris, scopes, grantTypes) | | t_app_access_token | 应用 AccessToken 表(accessToken, 过期时间) | | t_verification_code | 验证码表(code, type, status, loginId) | | t_wx_message | 微信消息记录表 |

应用类型 (EAppType)

| 值 | 类型 | 说明 | |----|------|------| | 1 | WxGzh | 微信公众号 | | 2 | WxMiniApp | 微信小程序 | | 3 | Own | 自有应用 | | 5 | WkWeb | 企业微信 Web 应用 | | 6 | WkMiniApp | 企业微信小程序 |

配置说明

核心配置项

// config/config.default.ts
{
  koa: {
    port: 8089,                    // 服务端口
    globalPrefix: '/base-server',  // 全局路由前缀
  },
  typeorm: {                       // MySQL 数据库配置
    dataSource: {
      default: { type: 'mysql', ... }
    }
  },
  session: {
    serviceKey: 'session:service', // Session 服务标识
    timeout: 86400000,             // Session 超时时间(毫秒)
  },
  authCodeOption: {
    timeout: 5 * 60 * 1000,       // 授权码有效期 5 分钟
  },
  oauth: {                         // OAuth2 + OIDC 配置
    issuer: 'sso-server',          // Issuer 标识
    tokenValidity: 3600,           // access_token 有效期(秒)
    refreshValidity: 2592000,      // refresh_token 有效期(秒)
    jwtPrivateKeyPath: '/path/to/private.pem',  // RSA 私钥路径
    jwtPublicKeyPath: '/path/to/public.pem',    // RSA 公钥路径
  },
  verificationCode: {
    timeout: 5 * 60 * 1000,       // 验证码有效期
    phoneLimit: 3,                 // 每号码 5 分钟内最多发送次数
  },
  tencentCloudSMSConfig: { ... },  // 腾讯云短信配置
  tencentCosOption: { ... },       // 腾讯云 COS 配置
}

RSA 密钥配置(OAuth2 JWT 签名)

生产环境必须配置 RSA 密钥对,用于 JWT Token 签名和验证。

生成密钥对:

# 生成私钥(2048位)
openssl genrsa -out private.pem 2048

# 从私钥提取公钥
openssl rsa -in private.pem -pubout -out public.pem

配置方式(三选一):

# 方式1:环境变量(推荐,PEM格式字符串)
export JWT_PRIVATE_KEY="$(cat private.pem)"
export JWT_PUBLIC_KEY="$(cat public.pem)"

# 方式2:环境变量(文件路径)
export JWT_PRIVATE_KEY_PATH=/path/to/private.pem
export JWT_PUBLIC_KEY_PATH=/path/to/public.pem

# 方式3:配置文件
// config/config.default.ts
{
  oauth: {
    jwtPrivateKeyPath: '/path/to/private.pem',
    jwtPublicKeyPath: '/path/to/public.pem',
  }
}

注意事项:

  • 私钥必须妥善保管,不可泄露
  • 开发环境会自动生成临时密钥对(重启后失效)
  • 生产环境必须配置固定密钥对

部署说明

Docker 部署

docker build . -t base-server
docker run -p 8089:8089 base-server

PM2 部署

npm run deploy    # 安装依赖 + 构建 + 清理开发依赖
npm start         # pm2 start bootstrap.js --name base-server
npm run restart   # pm2 restart base-server
npm stop          # pm2 stop base-server

NPM 发布

npm run build     # 构建
npm publish       # 发布到 npm(public access)

开发指南

npm run dev       # 本地开发
npm test          # 运行测试
npm run cov       # 测试覆盖率
npm run lint      # 代码检查
npm run lint:fix  # 自动修复

版本信息

当前版本: 0.0.42

常见问题

1. Session 过期时间如何配置?

在配置文件中设置 session.timeout,单位为毫秒:

// config/config.default.ts
{
  session: {
    timeout: 86400000,  // 24小时
  }
}

2. 如何调试微信登录?

  1. 本地开发时,可以使用 微信开发者工具 进行调试
  2. 公众号登录需要配置回调域名白名单
  3. 企业微信登录需要在企业微信后台配置可信域名

3. API_KEY 验证失败怎么办?

确保请求头中包含正确的 x-api-key

curl -H "x-api-key: 2024@fefeding#" http://localhost:8089/base-server/api/session/getLoginSession

4. 如何查看 Session 缓存状态?

Session 使用 MidwayJS Cache Manager 管理,默认使用内存缓存。可通过以下方式查看:

// 在服务中注入缓存实例
@InjectClient(CachingFactory, 'session')
sessionCache: MidwayCache;

// 获取缓存中的 Session
const session = await this.sessionCache.get(sessionId);

5. 数据库连接失败如何排查?

  1. 检查数据库配置是否正确(host、port、username、password)
  2. 确认数据库已创建:CREATE DATABASE db_account;
  3. 检查数据库用户是否有足够权限
  4. 查看日志中的具体错误信息

6. 如何自定义 Session 状态?

Session 状态定义在 @fefeding/common 包中:

enum EStatus {
  ACTIVED = 1,      // 活跃
  OFFLINE = 2,      // 离线
  INACTIVATED = 3,  // 未激活
}

7. 授权码登录流程是什么?

SSO 单点登录核心流程:

用户 → 应用A → 重定向到 SSO → 登录成功 → 生成 auth_code → 回调应用A → 用 auth_code 换取 Session

授权码特性:

  • 有效期:5 分钟
  • 一次性使用:使用后立即失效
  • 安全性:auth_code 不包含用户信息,必须通过 API 换取 Session

8. 如何使用 OAuth2 标准协议接入?

推荐使用 OAuth2 + OIDC 标准协议接入,步骤如下:

  1. 注册客户端:调用 /oauth/client/register 获取 client_id 和 client_secret
  2. 配置回调地址:在注册时指定 redirect_uris(必须精确匹配)
  3. 用户登录:引导用户到 SSO 登录页,携带 client_id、redirect_uri、scope 等参数
  4. 获取授权码:用户登录成功后,SSO 会回调应用并携带授权码
  5. 换取 Token:调用 /oauth/token 用授权码换取 access_token 和 id_token
  6. 获取用户信息:调用 /oauth/userinfo 获取用户详细信息

详细流程请参考 OAuth2 + OIDC 标准协议 章节。

9. 如何验证 ID Token 的签名?

ID Token 使用 RS256 算法签名,验证步骤:

// 1. 获取公钥
const jwksResponse = await fetch('https://base.ycnull.com/oauth/jwks');
const jwks = await jwksResponse.json();

// 2. 使用 JWT 库验证
const jwt = require('jsonwebtoken');
const decoded = jwt.verify(idToken, jwks.keys[0], {
  algorithms: ['RS256'],
  issuer: 'sso-server',
  audience: 'your_client_id'
});

// 3. 验证 nonce(防重放攻击)
if (decoded.nonce !== storedNonce) {
  throw new Error('Nonce mismatch');
}

10. client_secret 泄露了怎么办?

立即调用 /oauth/client/regenerateSecret 重新生成:

POST /oauth/client/regenerateSecret
Content-Type: application/json
x-api-key: YOUR_API_KEY

{
  "clientId": "cli_xxxxxxxx"
}

# 响应(新的 client_secret)
{
  "clientId": "cli_xxxxxxxx",
  "clientSecret": "新的密钥"
}

注意:重新生成后,旧的 client_secret 立即失效。

11. 如何实现 PKCE 安全增强?

PKCE 适用于移动应用和 SPA,防止授权码被拦截:

// 1. 生成 code_verifier(43-128位随机字符串)
const crypto = require('crypto');
const codeVerifier = crypto.randomBytes(32).toString('base64url');

// 2. 生成 code_challenge(S256方法)
const codeChallenge = crypto
  .createHash('sha256')
  .update(codeVerifier)
  .digest('base64url');

// 3. 授权请求时携带 code_challenge
// SSO 调用 /oauth/authorize 时传递 codeChallenge 和 codeChallengeMethod: 'S256'

// 4. Token 请求时携带 code_verifier
// 应用调用 /oauth/token 时传递 code_verifier

12. Token 过期后如何刷新?

使用 refresh_token 刷新 access_token:

POST /oauth/token
Content-Type: application/json

{
  "grant_type": "refresh_token",
  "refresh_token": "your_refresh_token",
  "client_id": "cli_xxxxxxxx",
  "client_secret": "xxxxxxxxxx"
}

# 响应(新的 access_token)
{
  "access_token": "新的access_token",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": "openid profile"
}

注意:刷新后不会返回新的 refresh_token,原有的 refresh_token 可继续使用。

更新日志

v0.0.42 (当前版本)

  • 重大更新:新增 OAuth2 + OIDC 标准端点支持
  • 新增 OAuthController:提供标准 OAuth2/OIDC 端点(Token、UserInfo、JWKS、Discovery)
  • 新增 OAuthClientService:OAuth2 客户端注册与管理
  • 新增 JwtService:JWT 签名与验证服务(RSA 密钥对)
  • 新增 t_oauth_client 数据表:存储 OAuth2 客户端信息
  • 支持 PKCE(Proof Key for Code Exchange)安全增强
  • 支持 ID Token(OIDC 标准,包含用户身份声明)
  • 支持 Refresh Token(长期离线访问)
  • 支持 OIDC Discovery(自动发现配置)
  • 授权码映射表扩展:支持 client_id、redirect_uri、scope、PKCE、nonce
  • 完善文档:新增 OAuth2 + OIDC 接入指南

v0.0.39

  • 优化文档结构和注释
  • 完善 API 接口说明

v0.0.38

  • 新增企业微信小程序登录支持
  • 优化 Session 缓存机制

v0.0.37

  • 新增公众号扫码登录功能
  • 新增验证码登录接口

v0.0.36

  • 新增 IP 白名单控制
  • 优化错误处理机制

v0.0.35

  • 新增 SSO 单点登录核心功能
  • 支持多种登录方式(微信/账号密码/授权码)

贡献指南

开发环境搭建

  1. Fork 本仓库
  2. 克隆到本地:git clone https://github.com/your-username/base-server.git
  3. 安装依赖:npm install
  4. 启动开发服务:npm run dev

代码规范

  • 使用 TypeScript 编写代码
  • 遵循 ESLint 规则:npm run lint
  • 提交前自动检查:npm run lint:fix

提交规范

提交信息格式:

<type>(<scope>): <subject>

<body>

<footer>

类型(type):

  • feat: 新功能
  • fix: 修复 Bug
  • docs: 文档更新
  • style: 代码格式调整
  • refactor: 重构
  • test: 测试相关
  • chore: 构建/工具相关

示例:

feat(session): 新增公众号扫码登录功能

- 新增 createWxLoginQrcode 接口
- 新增 loginByVerifyCode 接口
- 支持验证码状态管理

Closes #123

Pull Request 流程

  1. 创建特性分支:git checkout -b feature/your-feature
  2. 提交更改:git commit -m 'feat: your feature'
  3. 推送分支:git push origin feature/your-feature
  4. 创建 Pull Request

许可证

MIT License