一、Magic Api介绍
magic-api是一款方便易用的接口开发工具,可以帮助开发人员快速构建RESTful API接口。
二、结合项目(非纯magic-api项目)实现接口鉴权
1、magic-api的SpringBoot项目配置,放到application.yml中
yaml
# 完整配置示例
magic-api:
web: /magic/web # UI请求的界面以及UI服务地址
resource: #配置存储方式
type: database # 配置存储在数据库中
tableName: onl_magic_api # 数据库中的表名
prefix: /magic-api # key前缀
readonly: false # 是否是只读模式
prefix: /magic # 接口前缀,可以不配置
auto-import-module: db # 自动导入的模块
auto-import-package: java.lang.*,java.util.* #自动导包
allow-override: false #禁止覆盖应用接口
sql-column-case: camel #启用驼峰命名转换
editor-config: classpath:./editor-config.js #编辑器配置
support-cross-domain: true # 跨域支持,默认开启
show-sql: true #配置打印SQL
compile-cache-size: 500 #配置编译缓存容量
persistence-response-body: true #是否持久化保存ResponseBody
date-pattern: # 配置请求参数支持的日期格式
- yyyy-MM-dd
- yyyy-MM-dd HH:mm:ss
- yyyyMMddHHmmss
- yyyyMMdd
response: |- #配置JSON格式,格式为magic-script中的表达式
{
code: code,
message: message,
records: data,
timestamp,
}
response-code:
success: 200 #执行成功的code值
invalid: 0 #参数验证未通过的code值
exception: -1 #执行出现异常的code值
banner: true # 打印banner
thread-pool-executor-size: 8 # async语句的线程池大小
throw-exception: false #执行出错时是否抛出异常
backup: #备份相关配置
enable: false #是否启用
crud: # CRUD相关配置
logic-delete-column: del_flag #逻辑删除列
logic-delete-value: 1 #逻辑删除值
cache: # 缓存相关配置
enable: true # 启用缓存
page:
size: size # 页大小的参数名称
page: page # 页码的参数名称
default-page: 1 # 未传页码时的默认首页
default-size: 10 # 未传页大小时的默认页大小
debug:
timeout: 60 # 断点超时时间,默认60s
2、magic-api的编辑器的配置magic-editor-config.js,放到SpringBoot项目启动类下的resource目录或者可根据1中的editor-config自行配置。请在下面的配置中根据自己的实际项目配置:
完整配置示例:
javascript
var MAGIC_EDITOR_CONFIG = {
title: 'test',
theme: 'default',
defaultExpand: true,
checkUpdate: true,
jdbcDrivers: ['driver1', 'driver2'],
datasourceTypes: ['type1', 'type2'],
options: [['key1', '描述', 'defaultValue1'], ['key2', '描述', 'defaultValue2']],
blockClose: false, // 是否阻止离开页面
autoSave: true,
decorationTimeout: 10000,
logMaxRows: Infinity,
editorFontFamily: 'JetBrainsMono, Consolas, "Courier New",monospace, 微软雅黑', //'YaHei Consolas Hybrid',
editorFontSize: 14,
fontLigatures: true,
header: {
skin: true,
document: true,
repo: true,
qqGroup: true
},
getMagicTokenValue: function () {
# 我的项目是配置在iframe中,链接后加入的自己项目的token,可以直接从链接中取自己项目的token
let _href = window.location.href;
return _href.split("token=")[1];
},
request: {
beforeSend: function (config) {
# 我的项目是配置在iframe中,链接后加入的自己项目的token,可以直接从链接中取自己项目的token
let _href = window.location.href;
config.headers.token = _href.split("token=")[1]
return config;
},
onError: function (err) {
console.log('请求出错');
return Promise.reject(err)
}
},
response: {
onSuccess: function (resp) {
console.log('请求成功', resp);
return resp;
},
onError: function (err) {
console.log('请求出错');
return Promise.reject(err)
},
},
themes: {
editor: {
base: 'vs-dark',
rules: [
{foreground: 'A9B7C6'},
{token: 'keywords', foreground: 'CC7832', fontStyle: 'bold'},
{token: 'keyword', foreground: 'CC7832', fontStyle: 'bold'},
{token: 'number', foreground: '6897BB'},
{token: 'string', foreground: '6A8759', fontStyle: 'bold'},
{token: 'string.sql', foreground: '6A8759'},
{token: 'tag.sql', foreground: 'E8BF6A'},
{token: 'attribute.name.sql', foreground: 'BABABA'},
{token: 'attribute.value.sql', foreground: '6A8759'},
{token: 'predefined.sql', foreground: 'A9B7C6', fontStyle: 'italic'},
{token: 'predefined.magicscript', foreground: 'A9B7C6', fontStyle: 'italic'},
{token: 'key', foreground: '9876AA'},
{token: 'string.key.json', foreground: '9876AA'},
{token: 'string.value.json', foreground: '6A8759'},
{token: 'keyword.json', foreground: '6897BB'},
{token: 'operator.sql', foreground: 'CC7832', fontStyle: 'bold'},
{token: 'string.invalid', foreground: '008000', background: 'FFCCCC'},
{token: 'string.escape.invalid', foreground: '008000', background: 'FFCCCC'},
{token: 'string.escape', foreground: '000080', fontStyle: 'bold'},
{token: 'comment', foreground: '808080', fontStyle: 'italic'},
{token: 'comment.doc', foreground: '629755', fontStyle: 'italic'},
{token: 'comment.todo', foreground: 'A8C023', fontStyle: 'italic'},
{token: 'string.escape', foreground: 'CC7832'}
],
colors: {
'editor.background': '#2B2B2B',
'editorLineNumber.foreground': '#999999', //行号的颜色
'editorGutter.background': '#313335', //行号背景色
'editor.lineHighlightBackground': '#323232', //光标所在行的颜色
'dropdown.background': '#3C3F41', //右键菜单
'dropdown.foreground': '#BBBBBB', //右键菜单文字颜色
'list.activeSelectionBackground': '#4B6EAF', //右键菜单悬浮背景色
'list.activeSelectionForeground': '#FFFFFF', //右键菜单悬浮文字颜色
'editorSuggestWidget.selectedBackground': '#113A5C' //代码提示选中行的背景色
}
},
styles: {
'main-background-color': '#3C3F41', // 主要背景色
'main-border-color': '#323232', // 主要边框色
'main-color': '#bbb', // 主要文字颜色
'main-selected-background-color': '#323232', // 主要选中背景色
'main-hover-background-color': '#353739', // 主要悬浮背景色
'main-hover-icon-background-color': '#4C5052', // 主要悬浮图标背景色
'main-selected-color': '#fff', // 主要选中文字颜色
'main-icon-color': '#AFB1B3', // 主要图标颜色
'header-title-color': '#bbb', // 顶部名字颜色
'header-version-color': '#999', // 顶部版本号颜色
'header-default-color': '#AFB1B3', // 顶部其它文字颜色
'empty-background-color': '#282828', // 中间空的背景颜色
'empty-key-color': '#489DF6', // 中间空的快捷键文字颜色
'empty-color': '#A0A0A0', // 中间空的文字颜色
'button-hover-background-color': '#365880', // 按钮悬浮背景颜色
'button-hover-border-color': '#43688C', // 按钮悬浮边框颜色
'button-background-color': '#4C5052', // 按钮背景颜色
'button-border-color': '#5E6060', // 按钮边框颜色
'button-disabled-color': '#5a5a5a', // 按钮禁用时的颜色
'navbar-body-background-color': '#3C3F41', // 导航条内容背景颜色
'navbar-body-border-color': '#555555', //导航条内边框颜色
'resource-label-color': '#bbb', // 资源树形菜单label颜色
'resource-span-color': '#787878', // 资源树形菜单span颜色
'tree-hover-background-color': '#0d293e', // 树形菜单悬浮背景色
'tree-icon-color': '#aeb9c0', // 树形菜单图标颜色
'table-border-color': '#646464', // 表格边框颜色
'input-border-color': '#646464', // input边框颜色
'input-foucs-color': '#3D6185', // input focus边框颜色
'input-background-color': '#45494A', // input背景颜色
'select-background-color': '#3C3F41', // select背景颜色
'select-hover-background-color': '#3C3F41', // select悬浮背景色
'select-option-background-color': '#3C3F41', // select选项背景色
'select-option-hover-background-color': '#4B6EAF', // select选项悬浮背景色
'select-option-border-color': '#808080', // select选项边框色
// 数据类型颜色
'data-type-default-color': '#a9b7c6',
'data-type-string-color': '#6a8759',
'data-type-integer-color': '#6897bb',
'data-type-byte-color': '#6897bb',
'data-type-long-color': '#6897bb',
'data-type-float-color': '#6897bb',
'data-type-double-color': '#6897bb',
'data-type-short-color': '#6897bb',
'data-type-number-color': '#6897bb',
'data-type-boolean-color': '#cc7832',
'data-type-class-color': '#9876aa',
'data-type-key-color': '#FF8E8E',
'run-log-background-color': '#2b2b2b', // 运行日志背景颜色
// 日志级别颜色
'log-level-info': '#ABC023',
'log-level-error': '#CC666E',
'log-level-debug': '#299999',
'log-level-warn': 'unset',
'log-level-trace': '#5394EC',
'log-color-cyan': '#009191',
'log-color-link': '#287BDE',
'todo-color': '#A8C023',
'debug-line-background-color': '#2D6099', // 调试时,断点行背景颜色
'breakpoints-background-color': '#C75450', // 断点圆圈背景颜色
'breakpoint-line-background-color': '#3a2323', // 断点所在行的背景颜色
'select-inputable-background-color': '#45494a', // select输入框背景颜色
'select-inputable-border': 'transparent',
'tab-selected-background-color': '#4E5254', // tab 选中时的背景颜色
'message-em-color': '#68dd9a', // 消息 em 颜色
'checkbox-background-color': '#43494A',
'checkbox-border-color': '#6B6B6B',
'checkbox-text-color': '#bbb',
'checkbox-selected-background-color': '#43494A',
'checkbox-selected-border-color': '#6B6B6B',
'toolbox-list-label-color': '#bbb',
'toolbox-list-span-color': '#787878',
'toolbox-border-color': '#323232',
'toolbox-list-hover-background': '#0D293E',
'toolbox-border-right-color': '#555555',
'footer-border-color': '#323232',
'tab-bar-border-color': '#323232',
'dialog-border-color': '#282828',
'dialog-shadow-color': '#151515',
'table-col-border-color': '#333638',
'table-row-border-color': '#333638',
'table-hover-background': '#4B6EAF',
'debug-line-background': '#2D6099',
'breakpoints-background': '#C75450',
'breakpoint-line-background': '#3a2323',
'table-even-background': '#414547',
'button-disabled-background': '#5A5A5A',
'toolbox-list-header-icon-color': '#AFB1B3',
'log-error-color': '#CC666E',
'text-string-color': '#6A8759',
'text-number-color': '#6897BB',
'text-boolean-color': '#CC7832',
'text-property-color': '#9876aa',
'text-key-color': '#9876aa',
'suggest-hover-background': '#113A5C',
'suggest-hover-color': '#fff',
'statusbar-em-color': '#68dd9a',
}
}
}
3、magic-api的UI界面操作权限,重头戏来了,大家都很期待,废话不多说,直接上代码:
typescript
@Component
@Order(10)
@Slf4j
public class MagicApiConfig implements AuthorizationInterceptor {
private CommonAPI commonAPI;
@Autowired
public void setCommonAPI(CommonAPI commonAPI) {
this.commonAPI = commonAPI;
}
/**
* 配置是否需要登录
*/
@Override
public boolean requireLogin() {
return false;
}
/**
* 是否拥有页面按钮的权限
*/
@Override
public boolean allowVisit(MagicUser magicUser, MagicHttpServletRequest request, Authorization authorization) {
String token = request.getHeader("Magic-Token");
if (!StringUtils.hasText(token)) {
return false;
}
String username = JwtUtil.getUsername(token);
if (!StringUtils.hasText(token)) {
return false;
}
LoginUser user = commonAPI.getUserByName(username);
return !Objects.isNull(user);
// Authorization.SAVE 保存
// Authorization.DELETE 删除
// Authorization.VIEW 查询
// Authorization.LOCK 锁定
// Authorization.UNLOCK 解锁
// Authorization.DOWNLOAD 导出
// Authorization.UPLOAD 上传
// Authorization.PUSH 推送
}
/**
* 是否拥有对该接口的增删改权限
* 此方法可以不重写,则走默认的 boolean allowVisit(MagicUser magicUser, MagicHttpServletRequest request, Authorization authorization) 方法
*/
@Override
public boolean allowVisit(MagicUser magicUser, MagicHttpServletRequest request, Authorization authorization, MagicEntity entity) {
String token = request.getHeader("Magic-Token");
if (!StringUtils.hasText(token)) {
return false;
}
String username = JwtUtil.getUsername(token);
if (!StringUtils.hasText(token)) {
return false;
}
LoginUser user = commonAPI.getUserByName(username);
return !Objects.isNull(user);
// Authorization.SAVE 保存
// Authorization.DELETE 删除
// Authorization.VIEW 查询
// Authorization.LOCK 锁定
// Authorization.UNLOCK 解锁
// 自行写逻辑判断是否拥有如果有,则返回true,反之为false
}
/**
* 是否拥有对该分组的增删改权限
* 此方法可以不重写,则走默认的 boolean allowVisit(MagicUser magicUser, MagicHttpServletRequest request, Authorization authorization) 方法
*/
@Override
public boolean allowVisit(MagicUser magicUser, MagicHttpServletRequest request, Authorization authorization, Group group) {
String token = request.getHeader("Magic-Token");
if (!StringUtils.hasText(token)) {
return false;
}
String username = JwtUtil.getUsername(token);
if (!StringUtils.hasText(token)) {
return false;
}
LoginUser user = commonAPI.getUserByName(username);
return !Objects.isNull(user);
// Authorization.SAVE 保存
// Authorization.DELETE 删除
// Authorization.VIEW 查询
// 自行写逻辑判断是否拥有如果有,则返回true,反之为false
}
}
4、magic-api接口访问权限,在UI界面操作添加的接口根据自己的项目需要登录用户才有访问权限,未登录则无权访问,要怎么实现呢?别急也别慌,听我慢慢说。
看这里,文档中已经写的很明白了,就是配置拦截器呗。咦,拦截器嗳,这个太简单了,我会我会,举起手来。 话不多说,老样子,直接上代码,清晰明了,大家一看就明白。
java
@Component
@Slf4j
public class MagicApiInterceptor implements RequestInterceptor {
private CommonAPI commonAPI;
@Autowired
public void setCommonAPI(CommonAPI commonAPI) {
this.commonAPI = commonAPI;
}
/**
* 接口请求之前
*
* @param info 接口信息
* @param context 脚本变量信息
*/
@Override
public Object preHandle(ApiInfo info, MagicScriptContext context, MagicHttpServletRequest request, MagicHttpServletResponse response) throws Exception {
String token = request.getHeader("Magic-Token");
if (!StringUtils.hasText(token)) {
return Result.error("暂无权限");
}
String username = JwtUtil.getUsername(token);
if (!StringUtils.hasText(token)) {
return Result.error("暂无权限");
}
LoginUser user = commonAPI.getUserByName(username);
if (Objects.isNull(user)) {
return Result.error("暂无权限");
}
return null;
}
/**
* 接口执行之后
*
* @param info 接口信息
* @param context 变量信息
* @param value 即将要返回到页面的值
*/
@Override
public Object postHandle(ApiInfo info, MagicScriptContext context, Object value, MagicHttpServletRequest request, MagicHttpServletResponse response) throws Exception {
String token = request.getHeader("Magic-Token");
if (!StringUtils.hasText(token)) {
return Result.error("暂无权限");
}
String username = JwtUtil.getUsername(token);
if (!StringUtils.hasText(token)) {
return Result.error("暂无权限");
}
LoginUser user = commonAPI.getUserByName(username);
if (Objects.isNull(user)) {
return Result.error("暂无权限");
}
return null;
}
}
就此,magic-api的接口权限已经跟自己的项目结合了。是不是超级easy!
对了,最后还是要插个嘴(嘴伸过来[坏笑]),上述代码中的commonApi是我自己项目的接口,大佬们自己根据项目更改哈。不要问为啥报错,问我就要插嘴。
三、magic-api存入数据库需要的SQL语句,对应配置中对应的表名
sql
DROP TABLE IF EXISTS `onl_magic_api`;
CREATE TABLE `onl_magic_api` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '自增主键',
`file_path` varchar(255) NULL DEFAULT NULL COMMENT '连接',
`file_content` text NULL COMMENT '内容',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 COMMENT = 'magic-api主表' ROW_FORMAT = Dynamic;