feat: jwt service

This commit is contained in:
chenos 2022-04-09 15:30:43 +08:00
parent e8a7e321f0
commit c6f1454ae8
5 changed files with 51 additions and 31 deletions

View File

@ -45,18 +45,43 @@ const plugins = [
'@nocobase/plugin-ui-routes-storage', '@nocobase/plugin-ui-routes-storage',
'@nocobase/plugin-file-manager', '@nocobase/plugin-file-manager',
'@nocobase/plugin-system-settings', '@nocobase/plugin-system-settings',
'@nocobase/plugin-users', [
'@nocobase/plugin-users',
{
jwt: {
secret: process.env.JWT_SECRET || '09f26e402586e2faa8da4c98a35f1b20d6b033c60',
},
installing: {
adminNickname: 'Super Admin',
adminEmail: 'admin@nocobase.com',
adminPassword: 'admin123',
},
},
],
'@nocobase/plugin-acl', '@nocobase/plugin-acl',
'@nocobase/plugin-china-region', '@nocobase/plugin-china-region',
'@nocobase/plugin-workflow', '@nocobase/plugin-workflow',
[
'@nocobase/plugin-client',
{
dist: resolve(__dirname, '../../app/dist'),
},
],
]; ];
for (const plugin of plugins) { for (const plugin of plugins) {
api.plugin(require(plugin).default); if (Array.isArray(plugin)) {
api.plugin(require(plugin.shift() as string).default, plugin.shift());
} else {
api.plugin(require(plugin).default);
}
} }
api.plugin(require('@nocobase/plugin-client').default, { api.acl.use(async (ctx, next) => {
dist: resolve(__dirname, '../../app/dist'), ctx.permission = {
skip: true,
};
await next();
}); });
if (process.argv.length < 3) { if (process.argv.length < 3) {

View File

@ -1,4 +1,4 @@
import * as jwt from 'jsonwebtoken'; import jwt from 'jsonwebtoken';
export interface JwtOptions { export interface JwtOptions {
secret: string; secret: string;

View File

@ -1,14 +1,9 @@
import { Context, Next } from '@nocobase/actions'; import { Context, Next } from '@nocobase/actions';
import UsersPlugin from '../server'; import UsersPlugin from '../server';
// TODO(feature): 表名应在 options 中配置 export function parseToken(options?: { plugin: UsersPlugin }) {
// 中间件默认只解决解析 token 和附加对应 user 的工作,不解决是否提前报 401 退出。
// 因为是否提供匿名访问资源是应用决定的,不是使用插件就一定不能匿名访问。
export function parseToken(options?: any) {
return async function parseToken(ctx: Context, next: Next) { return async function parseToken(ctx: Context, next: Next) {
const user = await findUserByToken(ctx); const user = await findUserByToken(ctx, options.plugin);
if (user) { if (user) {
ctx.state.currentUser = user; ctx.state.currentUser = user;
setCurrentRole(ctx, user); setCurrentRole(ctx, user);
@ -33,24 +28,21 @@ function setCurrentRole(ctx, user) {
} }
} }
async function findUserByToken(ctx: Context) { async function findUserByToken(ctx: Context, plugin: UsersPlugin) {
const token = getTokenFromCtx(ctx); const token = ctx.getBearerToken();
if (!token) { if (!token) {
return null; return null;
} }
try {
const { userId } = await plugin.jwtService.decode(token);
const pluginUser = ctx.app.getPlugin('@nocobase/plugin-users'); return await ctx.db.getRepository('users').findOne({
filter: {
const { userId } = await pluginUser.jwtService.decode(token); id: userId,
},
return await ctx.db.getRepository('users').findOne({ appends: ['roles'],
filter: { });
id: userId, } catch (error) {
}, console.warn(error);
appends: ['roles'], }
});
}
function getTokenFromCtx(ctx: Context) {
return ctx.get('Authorization').replace(/^Bearer\s+/gi, '');
} }

View File

@ -2,8 +2,8 @@ import { Collection } from '@nocobase/database';
import { Plugin } from '@nocobase/server'; import { Plugin } from '@nocobase/server';
import { resolve } from 'path'; import { resolve } from 'path';
import * as actions from './actions/users'; import * as actions from './actions/users';
import * as middlewares from './middlewares';
import { JwtOptions, JwtService } from './jwt-service'; import { JwtOptions, JwtService } from './jwt-service';
import * as middlewares from './middlewares';
export interface UserPluginConfig { export interface UserPluginConfig {
jwt: JwtOptions; jwt: JwtOptions;
@ -76,7 +76,7 @@ export default class UsersPlugin extends Plugin<UserPluginConfig> {
this.app.resourcer.registerActionHandler(`users:${key}`, action); this.app.resourcer.registerActionHandler(`users:${key}`, action);
} }
this.app.resourcer.use(middlewares.parseToken()); this.app.resourcer.use(middlewares.parseToken({ plugin: this }));
const publicActions = ['check', 'signin', 'signup', 'lostpassword', 'resetpassword', 'getUserByResetToken']; const publicActions = ['check', 'signin', 'signup', 'lostpassword', 'resetpassword', 'getUserByResetToken'];
const loggedInActions = ['signout', 'updateProfile', 'changePassword', 'setDefaultRole']; const loggedInActions = ['signout', 'updateProfile', 'changePassword', 'setDefaultRole'];
@ -100,7 +100,7 @@ export default class UsersPlugin extends Plugin<UserPluginConfig> {
adminNickname = 'Super Admin', adminNickname = 'Super Admin',
adminEmail = 'admin@nocobase.com', adminEmail = 'admin@nocobase.com',
adminPassword = 'admin123', adminPassword = 'admin123',
} = this.options.installing; } = this.options.installing || {};
return { return {
adminNickname, adminNickname,

View File

@ -110,6 +110,9 @@ export function registerMiddlewares(app: Application, options: ApplicationOption
); );
app.use<DefaultState, DefaultContext>(async (ctx, next) => { app.use<DefaultState, DefaultContext>(async (ctx, next) => {
ctx.getBearerToken = () => {
return ctx.get('Authorization').replace(/^Bearer\s+/gi, '');
};
ctx.db = app.db; ctx.db = app.db;
ctx.resourcer = app.resourcer; ctx.resourcer = app.resourcer;
const i18n = app.i18n.cloneInstance({ initImmediate: false }); const i18n = app.i18n.cloneInstance({ initImmediate: false });