生成操作日志
有些项目需要记录用户的操作日志用于安全审计或行为分析等,这个功能在 Leap 中没有内置,但 Leap 提供了一种简单的方式来自行实现。
下面通过示例代码演示如何把操作日志保存到数据库中。
1. 创建实体
创建一个 OperationLog
实体用于保存操作日志:
package hello.models;
import leap.orm.annotation.Column;
import leap.orm.annotation.Id;
import leap.orm.annotation.Table;
import leap.orm.model.Model;
import java.sql.Timestamp;
@Table(name = "op_log", autoCreate = true)
public class OperationLog extends Model {
protected Integer id;
protected String verb;
protected String path;
protected String name;
protected String title;
protected String userId;
protected String loginName;
protected boolean success;
protected Integer status;
protected Timestamp timestamp;
// 省略 getter 和 setter
}
2. 实现拦截器
实现路由拦截器 OperationLogInterceptor
:
package hello.interceptors;
import hello.models.OperationLog;
import leap.core.security.UserPrincipal;
import leap.core.validation.Validation;
import leap.lang.Strings;
import leap.web.action.ActionContext;
import leap.web.action.ActionExecution;
import leap.web.action.ActionInterceptor;
import leap.web.api.meta.model.MApiOperation;
import leap.web.api.meta.model.MApiPath;
import leap.web.route.Route;
import java.sql.Timestamp;
public class OperationLogInterceptor implements ActionInterceptor {
@Override
public void completeExecuteAction(ActionContext context, Validation validation, ActionExecution execution) throws Throwable {
Route route = context.getRoute();
MApiPath path = route.getExtension(MApiPath.class);
if(null != path) {
MApiOperation op = route.getExtension(MApiOperation.class);
UserPrincipal user = context.getRequest().getUser();
if(null == user || user.isAnonymous()) {
//do not save the operation log for anonymous user.
return;
}
if(op.getMethod().isGet()) {
//do not save the operation log for query.
return;
}
OperationLog log = new OperationLog();
log.setVerb(context.getMethod());
log.setPath(context.getPath());
log.setName(op.getName());
log.setTitle(op.getTitle());
log.setSuccess(execution.isSuccess());
log.setUserId(user.getId().toString());
log.setLoginName(user.getLoginName());
log.setTimestamp(new Timestamp(System.currentTimeMillis()));
if(null != execution.getStatus()) {
log.setStatus(execution.getStatus().value());
}
if(!Strings.isEmpty(log.getTitle())) {
log.setTitle(op.getSummary());
}
log.create();
}
}
}
3. 配置拦截器
修改 src/main/resources/conf/beans.xml
:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.leapframework.org/schema/beans">
...
<bean type="leap.web.action.ActionInterceptor" class="hello.interceptors.OperationLogInterceptor"/>
</beans>
4. 验证测试
为了更访问的测试,我们实现两个 API 接口(一个成功,一个失败):
在 src/main/java/hello/controllers/GreetingController.java
中创建两个方法:
public class GreetingController extends ApiController {
...
@POST("/test_log_ok")
public ApiResponse testLogOk() {
return ApiResponse.OK;
}
@POST("/test_log_err")
public ApiResponse testLogErr() {
return ApiResponse.err(HTTP.Status.BAD_REQUEST, "err");
}
}
启动工程(需要接入 debug-auth-server
):
获取访问令牌:
**[terminal]
curl -X POST http://localhost:8088/oauth2/token\?grant_type\=password\&username\=user1\&password\=pass1\&client_id\=client1\&client_secret\=secret1
分别访问刚才创建的两个API操作:
**[terminal]
curl -X POST http://localhost:8080/greeting/test_log_ok\?access_token\=24579fa7-c5ef-411e-9b04-5cd62ee72bd8
curl -X POST http://localhost:8080/greeting/test_log_err\?access_token\=24579fa7-c5ef-411e-9b04-5cd62ee72bd8
打开浏览器访问 http://localhost:8080/operation_log
。
如果看到以下的内容,说明日志保存成功。
[
{
id: 1,
verb: "POST",
path: "/greeting/test_log_ok",
name: "testLogOk",
title: "测试成功操作",
userId: "1d@3KIkWi",
loginName: "user1",
success: true,
status: 200,
timestamp: "2017-06-14T15:40:14.807+0800"
},
{
id: 2,
verb: "POST",
path: "/greeting/test_log_err",
name: "testLogErr",
title: "测试失败操作",
userId: "1d@3KIkWi",
loginName: "user1",
success: false,
status: 400,
timestamp: "2017-06-14T15:40:32.311+0800"
}
]
需要开启 Restd 自动生成
operation_log
的访问接口。