- 系统登录接口
- 访问业务接口
- 系统退出接口
- 会话失效,刷新 Token
- 快速单点登录接口
- OAuth2 第三方登录
- 无条件登录接口
- 其它常用接口
- 接口发现
- 开发一个API接口
- Swagger 新增模块
- 安全参数配置
本文介绍如何使用 JeeSite 作为服务端 API,提供手机端、前后分离应用、第三方服务接口调用。如实现接口登录、登出、权限认证、基础数据、业务办理等。 如何自己来开发API接口提供服务。
系统登录接口
JeeSite 的系统默认登录,设置了 Base64 或 DES 加密,如果不想加密,可将 secretKey 设置为空即可,或更改密钥,配置如下application.yml
(v4.0.x:jeesite.yml
):
shiro:
# 登录提交信息加密(如果不需要加密,设置为空即可)
loginSubmit:
# 加密用户名、密码、验证码,后再提交(key设置为3个,用逗号分隔)加密方式:DES(4.1.9及之前版本默认设置)
# v4.2.0+ 开始支持 Base64 加密方式,方便移动端及第三方系统处理认证,可直接设置 Key 为 Base64(4.2.0+默认设置)
#secretKey: thinkgem,jeesite,com
secretKey: Base64
#secretKey: ~
# 如果是JS请求可能会有跨域访问问题,可将如下参数设置为,允许的域名,全部域名设置*号,否则设置为空
accessControlAllowOrigin: '*'
如果开启了加密,你就需要先将DES加密工具引入:
JS:<script src="${ctxStatic}/common/des.js?${_version}"></script>
Java: com.jeesite.common.codec.DesUtils
引入完成之后就可以通过如下方法进行调用加密了:
JS:
<script>
var secretKey = '${@Global.getConfig("shiro.loginSubmit.secretKey")}';
var username = DesUtils.encode('system', secretKey);
var password = DesUtils.encode('admin', secretKey);
console.log('&username=' + username + '&password=' + password);
</script>
Java:
String secretKey = Global.getConfig("shiro.loginSubmit.secretKey");
String username = DesUtils.encode("system", secretKey);
String password = DesUtils.encode("admin", secretKey);
System.out.println("&username=" + username + "&password=" + password);
以上两种语言,输出结果相同如下:
&username=F3EDC7D2C193E0B8DCF554C726719ED2&password=235880C505ACCDA5C581A4F4CDB81DA0
下面我们就可以拿着这个用户名密码进行测试登录了。
通过 Ajax 或 HttpClient 进行 POST 或 GET 请求如下地址,即可进行登录验证:
http://127.0.0.1:8980/js/a/login?__login=true&__ajax=json&username=F3EDC7D2C193E0B8DCF554C726719ED2&password=235880C505ACCDA5C581A4F4CDB81DA0&validCode=&__sid=
你也可以添加登录附加参数如下:
1、可以指定登录设备类型(在线用户列表区分、登录验证码按设备区分,可根据设备指定session超时时间,默认PC):
param_deviceType=mobileApp
2、可以指定登录的系统(区分不同的菜单,默认default)
param_sysCode=mobileApp
3、可以指定登录页面和主框架页的视图(默认:employee)
param_userType=member
若登录信息不正确,则返回如下失败JSON数据:
{
"username": "F3EDC7D2C193E110B8DCF554C726719ED2",
"rememberMe": false,
"rememberUserCode": false,
"shiroLoginFailure": "org.apache.shiro.authc.UnknownAccountException",
"message": "账号或密码错误, 请重试.",
"isValidCodeLogin": false,
"result": "false",
"sessionid":"2a6669501bf24afebcf4ff63eb048a56"
}
访问业务接口
如果失败,第二次登录或访问业务接口,你需要附加一个 __sid
参数,或将 __sid
放到 Header 里面,用来指明是同一个会话,如:
http://127.0.0.1:8980/js/a/login?__login=true&__ajax=json&username=F3EDC7D2C193E0B8DCF554C726719ED2&password=235880C505ACCDA5C581A4F4CDB81DA0&__sid=2a6669501bf24afebcf4ff63eb048a56
注意:若参数配置的密码失败次数超过了预警值,则返回的结果信息中的 isValidCodeLogin
会变为 true
,这时你需要调用 http://127.0.0.1:8980/js/validCode?__sid=2a6669501bf24afebcf4ff63eb048a56
地址来获取验证码图片,并在登录参数里加 validCode
表示用户输入的验证码。另外请注意,移动端一般调用是无cookie的,你需要在请求参数中包含 __sid
参数与获取验证码的 __sid
一致,否则获取到的验证码值将无法与你登录请求会话匹配。
若登录信息正确,则返回如下登录成功JSON数据:
{
"user": {
"id": "system",
"status": "0",
"remarks": "",
"userCode": "system",
"loginCode": "system",
"userName": "超级管理员",
"userType": "none",
"mgrType": "0",
"lastLoginIp": "127.0.0.1",
"lastLoginDate": "2018-03-14 22:34:44",
"userWeight": 0,
"oldLastLoginIp": "127.0.0.1",
"corpName_": "JeeSite",
"corpCode_": "0",
"oldLoginDate": "2018-03-14 22:34:44",
"avatarUrl": "/ctxPath/static/images/user1.jpg"
},
"result": "true",
"message": "登录成功!",
"sessionid": "5fe9c7c45ded4425b03eff8f78179637"
}
系统退出接口
通过 Ajax 或 HttpClient 进行 POST 或 GET 请求如下地址,即可进行退出账号:
http://127.0.0.1:8980/js/a/logout?__ajax=json&__sid=5fe9c7c45ded4425b03eff8f78179637
注意:无 cookie 环境下,必须要指定要退出的 sessionid
返回JSON数据:
{"result":"true","message":"退出成功!"}
会话失效,刷新 Token
token 就是 __sid,当会话失效后,系统会返回 "result":"login"
则代表 __sid
已失效,需要重新登录。
你需要写一个通用的 post 或 get 方法,逻辑如下:
- 正常调用业务地址,若返回
"result":"login"
则代表__sid
会话已失效,需要重新登录,否则正常处理; - 若会话已失效,则重新调用
login
登录方法重新获取新的_sid
; - 根据新的
__sid
再调用业务地址,返回正确数据。
伪代码如下:
var __sid = ''; // 当前 sid
function post(url, data, callback){
$.post('http://host/'+url, {__sid: __sid}, function(data){
// 如果 sid 失效,则刷新 sid 并重试
if (Object.prototype.toString.call(res) === '[object Object]'
&& data.result == 'login'){
refreshSid(function(data){
if (data.result == 'true'){
$.post('http://host/'+url, {__sid: __sid}, function(data){
callback(data);
});
}else{
// 身份认证失败
callback(data);
}
});
}else{
callback(data);
}
});
}
function refreshSid(callback){
// 注意:此处手机登录后可以生成一个当前手机设备的 uuid,将此 uuid 存储到,用户表的 mobileImei 字段
// 下面请求的这个 /mobile/login 地址为开发者自己实现,通过存储的 uuid 无条件登录系统。
// uuid 的时效性可以自己控制,解绑设备,清理该 uuid 即可。
$.post('http://host/a/mobile/login', {uuid: '', __sid: __sid}, function(data){
if (data.result == 'true'){
__sid = data.sessionid;
}
callback(data);
});
}
快速单点登录接口
详见:简单登录接口
OAuth2 第三方登录
详见:oauth2-第三方登录
无条件登录接口
详见:无条件登录接口
其它常用接口
在登录成功的信息里,也有个 sessionid 属性,该属性值将作为你以后访问系统的凭证,相当于token令牌,举例如下:
1、获取用户权限信息:
http://127.0.0.1:8980/js/a/authInfo?__sid=5fe9c7c45ded4425b03eff8f78179637
2、获取用户菜单信息:
http://127.0.0.1:8980/js/a/menuTree?__sid=5fe9c7c45ded4425b03eff8f78179637
3、重新获取登录信息:
http://127.0.0.1:8980/js/a/index?__ajax=json&__sid=5fe9c7c45ded4425b03eff8f78179637
4、获取当前用户信息:
http://127.0.0.1:8980/js/a/sys/user/info?__ajax=json&__sid=5fe9c7c45ded4425b03eff8f78179637
5、获取数据字典列表:
http://127.0.0.1:8980/js/a/sys/dictData/treeData?dictType=sys_status&__sid=5fe9c7c45ded4425b03eff8f781...
接口发现
教给大家一个接口发现的方法。
v4.2.0+ 版本,链接增加 __ajax=json 参数,或增加 __ajax=xml 参数,或增加 __ajax 的 Header 请求头。
举例:用户列表的访问地址是 /a/sys/empUser/list,如果直接访问,则返回页面的视图界面,如果加 __ajax=json 参数,则返回视图所需要的json 数据,如:/a/sys/empUser/list?__ajax=json,这样返回的数据,就可以在你的前端分离应用中使用了。
v4.1.9 及之前版本,链接地址增加 .json 或 .xml 后缀。如:/a/sys/empUser/list.json,即可返回视图数据。
在 v4.2.0 版本中默认关闭了 .json、.xml 后缀匹配视图,您可以通过以下参数设置为 true 启用:
# Web 相关
web:
# MVC 视图相关
view:
# 使用 .json、.xml 后缀匹配视图(不推荐使用,推荐使用 favorParameter) v4.2.0
favorPathExtension: false
# 使用 __ajax=json、__ajax=xml 参数名匹配视图 v4.2.0
favorParameter: true
所有列表加载的数据均使用 listData 为后缀获取数据,如用户列表的数据地址为 /a/sys/empUser/listData,则直接返回表格所需的 JSON 数据。
listData 只是一个命名规则,如果你发现了不遵循规范的地址,怎么办?你可以通过Chrome浏览器的开发者界面(F12),打开 Network,Filter 中选择 XHR,好了,准备就绪,这是你点击列表里的查询按钮,即可监控到访问的数据的地址是什么
开发一个API接口
在你的 Controller 映射方法上增加 @ResponseBody 即可返回 JSON 数据,而不返回视图
或者替换 @Controller 为 @RestController,则应用所有映射方法均返回JSON数据。
如果你想两用,就如 接口发现 章节所述,增加 .json 后缀即可。
另外,对于移动端或高并发的应用对于流量是非常珍贵的,通用方法可能会返回一些很多无用的数据,这时,你可以使用 @JsonView 注解进行配置,返回需要的数据即可。
Swagger 新增模块
@Configuration
@ConditionalOnProperty(name="web.swagger.enabled", havingValue="true", matchIfMissing=false)
public class CustomApiConfig {
@Bean
@ConditionalOnProperty(name="web.swagger.custom.enabled", havingValue="true", matchIfMissing=true)
public Docket customApi() {
String moduleCode = "custom";
String moduleName = "自定义模块";
String basePackage = "com.jeesite.modules.custom.web";
return SwaggerConfig.docket(moduleCode, moduleName, basePackage)
.select()
.apis(
// Predicates.and(Predicates.and(
// RequestHandlerSelectors.withClassAnnotation(Api.class),
// RequestHandlerSelectors.withMethodAnnotation(ResponseBody.class)),
// RequestHandlerSelectors.basePackage(basePackage))
RequestHandlerSelectors.basePackage(basePackage)
).build();
}
}
安全参数配置
一些安全配置,解决:请求方法限定、一个账号多地登录、是否允许刷新主页、是否允许嵌入iframe、 是否允许跨域及跨域访问的域名设置、跨域访问允许的方法和标头、允许来源地址等。
# Shiro 相关配置
shiro:
# 允许的请求方法设定,解决安全审计问题
allowRequestMethods: GET,POST,OPTIONS,PUT,DELETE
# 是否允许账号多地登录,如果设置为false,同一个设备类型的其它地点登录的相同账号被踢下线
isAllowMultiAddrLogin: true
# 是否允许多账号多设备登录,如果设置为false,其它地点登录的相同账号全部登录设备将被踢下线
isAllowMultiDeviceLogin: true
# 是否允许刷新主框架页,如果设置为false,刷新主页将导致重新登录。如安全性比较高的,如银行个人首页不允许刷新。
isAllowRefreshIndex: true
# 是否允许嵌入到外部网站iframe中(true:不限制,false:不允许)
isAllowExternalSiteIframe: true
# 是否允许跨域访问 CORS,如果允许,设置允许的域名,全部域名设置*号,如果不允许,此设置应该为空
#accessControlAllowOrigin: http://demo.jeesite.com
accessControlAllowOrigin: "*"
# 允许跨域访问时 CORS,可以使用的方法和标头
accessControlAllowMethods: GET, POST, OPTIONS
accessControlAllowHeaders: Origin, No-Cache, X-Requested-With, If-Modified-Since, Pragma, Last-Modified, Cache-Control, Expires, Content-Type, X-E4M-With
# 是否允许接收跨域的Cookie凭证数据 CORS
accessControlAllowCredentials: true
# 允许的网站来源地址,不设置为全部地址(避免一些跨站点请求伪造 CSRF、防盗链)
# allowReferers: http://127.0.0.1,http://localhost
allowReferers: ~
# 是否在登录后生成新的Session(默认false)
isGenerateNewSessionAfterLogin: false