diff --git a/sql/mysql.sql b/sql/mysql.sql index ba78cd2a..9a41b82e 100644 --- a/sql/mysql.sql +++ b/sql/mysql.sql @@ -74,7 +74,19 @@ create table device_alarm alarmType varchar(50) ); - +create table log +( + id int auto_increment + primary key, + name varchar(50) not null, + type varchar(50) not null, + uri varchar(200) not null, + address varchar(50) not null, + result varchar(50) not null, + timing bigint not null, + username varchar(50) not null, + createTime varchar(50) not null +); create table device_mobile_position ( diff --git a/src/main/java/com/genersoft/iot/vmp/VManageBootstrap.java b/src/main/java/com/genersoft/iot/vmp/VManageBootstrap.java index 027efafc..56038bd5 100644 --- a/src/main/java/com/genersoft/iot/vmp/VManageBootstrap.java +++ b/src/main/java/com/genersoft/iot/vmp/VManageBootstrap.java @@ -4,6 +4,7 @@ import java.util.logging.LogManager; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.web.servlet.ServletComponentScan; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.scheduling.annotation.EnableScheduling; import springfox.documentation.oas.annotations.EnableOpenApi; @@ -11,6 +12,7 @@ import springfox.documentation.oas.annotations.EnableOpenApi; /** * */ +@ServletComponentScan("com.genersoft.iot.vmp.conf") @SpringBootApplication @EnableScheduling @EnableOpenApi diff --git a/src/main/java/com/genersoft/iot/vmp/common/ApiSaveConstant.java b/src/main/java/com/genersoft/iot/vmp/common/ApiSaveConstant.java new file mode 100644 index 00000000..ec2a7252 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/common/ApiSaveConstant.java @@ -0,0 +1,172 @@ +package com.genersoft.iot.vmp.common; + +public class ApiSaveConstant { + + public static String getVal(String key) { + String[] keyItemArray = key.split("/"); + if (keyItemArray.length <= 1 || !"api".equals(keyItemArray[1])) { + return null; + } + if (keyItemArray.length >= 4) { + switch (keyItemArray[2]) { + case "alarm": + if ("delete".equals(keyItemArray[3])) { + return "删除报警"; + } + break; + case "device": + switch (keyItemArray[3]) { + case "config": + if (keyItemArray.length >= 5 && "basicParam".equals(keyItemArray[4])) { + return "[设备配置] 基本配置设置命令"; + } + break; + case "control": + switch (keyItemArray[4]) { + case "teleboot": + return "[设备控制] 远程启动"; + case "record": + return "[设备控制] 录像控制"; + case "guard": + return "[设备控制] 布防/撤防命令"; + case "reset_alarm": + return "[设备控制] 报警复位"; + case "i_frame": + return "[设备控制] 强制关键帧"; + case "home_position": + return "[设备控制] 看守位控制"; + } + break; + case "query": + if (keyItemArray.length <= 5) return null; + switch (keyItemArray[4]) { + case "devices": + if (keyItemArray.length < 7) return null; + switch (keyItemArray[6]) { + case "sync": + return "[设备查询] 同步设备通道"; + case "delete": + return "[设备查询] 移除设备"; + } + break; + case "channel": + return "[设备查询] 更新通道信息"; + case "transport": + return "[设备查询] 修改数据流传输模式"; + } + break; + } + case "gbStream": + switch (keyItemArray[3]) { + case "del": + return "移除通道与国标的关联"; + case "add": + return "添加通道与国标的关联"; + } + break; + case "media": + break; + case "position": + if ("subscribe".equals(keyItemArray[3])) { + return "订阅位置信息"; + } + break; + case "platform": + switch (keyItemArray[3]) { + case "save": + return "添加上级平台"; + case "delete": + return "移除上级平台"; + case "update_channel_for_gb": + return "向上级平台添加国标通道"; + case "del_channel_for_gb": + return "从上级平台移除国标通道"; + } + break; + case "platform_gb_stream": + break; + case "play": + switch (keyItemArray[3]) { + case "start": + return "开始点播"; + case "stop": + return "停止点播"; + case "convert": + return "转码"; + case "convertStop": + return "结束转码"; + case "broadcast": + return "语音广播"; + } + break; + case "download": + switch (keyItemArray[3]) { + case "start": + return "开始历史媒体下载"; + case "stop": + return "停止历史媒体下载"; + } + break; + case "playback": + switch (keyItemArray[3]) { + case "start": + return "开始视频回放"; + case "stop": + return "停止视频回放"; + } + break; + case "ptz": + switch (keyItemArray[3]) { + case "control": + return "云台控制"; + case "front_end_command": + return "通用前端控制命令"; + } + break; + case "gb_record": + break; + case "onvif": + break; + case "server": + if ("restart".equals(keyItemArray[3])) { + return "重启流媒体服务"; + } + break; + case "proxy": + switch (keyItemArray[3]) { + case "save": + return "保存代理"; + case "del": + return "移除代理"; + case "start": + return "启用代理"; + case "stop": + return "停用代理"; + } + break; + case "push": + switch (keyItemArray[3]) { + case "save_to_gb": + return "将推流添加到国标"; + case "remove_form_gb": + return "将推流移出到国标"; + } + break; + case "user": + switch (keyItemArray[3]) { + case "login": + return "登录"; + case "changePassword": + return "修改密码"; + case "add": + return "添加用户"; + case "delete": + return "删除用户"; + } + break; + } + } + return null; + } +} + diff --git a/src/main/java/com/genersoft/iot/vmp/conf/ApiAccessFilter.java b/src/main/java/com/genersoft/iot/vmp/conf/ApiAccessFilter.java new file mode 100644 index 00000000..9831ddb5 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/conf/ApiAccessFilter.java @@ -0,0 +1,114 @@ +package com.genersoft.iot.vmp.conf; + +import com.genersoft.iot.vmp.common.ApiSaveConstant; +import com.genersoft.iot.vmp.conf.security.SecurityUtils; +import com.genersoft.iot.vmp.service.ILogService; +import com.genersoft.iot.vmp.storager.dao.dto.LogDto; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.web.filter.OncePerRequestFilter; + +import javax.servlet.*; +import javax.servlet.annotation.WebFilter; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.text.SimpleDateFormat; + +@WebFilter(filterName = "ApiAccessFilter", urlPatterns = "/api/*", asyncSupported=true) +public class ApiAccessFilter extends OncePerRequestFilter { + + private final static Logger logger = LoggerFactory.getLogger(ApiAccessFilter.class); + + private final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + + @Autowired + private UserSetup userSetup; + + @Autowired + private ILogService logService; + + + @Override + protected void doFilterInternal(HttpServletRequest servletRequest, HttpServletResponse servletResponse, FilterChain filterChain) throws ServletException, IOException { + String username = null; + if (SecurityUtils.getUserInfo() == null) { + username = servletRequest.getParameter("username"); + }else { + username = SecurityUtils.getUserInfo().getUsername(); + } + long start = System.currentTimeMillis(); // 请求进入时间 + String uriName = ApiSaveConstant.getVal(servletRequest.getRequestURI()); + + filterChain.doFilter(servletRequest, servletResponse); + + if (uriName != null && userSetup.getLogInDatebase()) { + + LogDto logDto = new LogDto(); + logDto.setName(uriName); + logDto.setUsername(username); + logDto.setAddress(servletRequest.getRemoteAddr()); + logDto.setResult(HttpStatus.valueOf(servletResponse.getStatus()).toString()); + logDto.setTiming(System.currentTimeMillis() - start); + logDto.setType(servletRequest.getMethod()); + logDto.setUri(servletRequest.getRequestURI()); + logDto.setCreateTime(format.format(System.currentTimeMillis())); + logService.add(logDto); +// logger.warn("[Api Access] [{}] [{}] [{}] [{}] [{}] {}ms", +// uriName, servletRequest.getMethod(), servletRequest.getRequestURI(), servletRequest.getRemoteAddr(), HttpStatus.valueOf(servletResponse.getStatus()), +// System.currentTimeMillis() - start); + + } + } + + /** + * 获取IP地址 + * + * @param request 请求 + * @return request发起客户端的IP地址 + */ + private String getIP(HttpServletRequest request) { + if (request == null) { + return "0.0.0.0"; + } + + String Xip = request.getHeader("X-Real-IP"); + String XFor = request.getHeader("X-Forwarded-For"); + + String UNKNOWN_IP = "unknown"; + if (StringUtils.isNotEmpty(XFor) && !UNKNOWN_IP.equalsIgnoreCase(XFor)) { + //多次反向代理后会有多个ip值,第一个ip才是真实ip + int index = XFor.indexOf(","); + if (index != -1) { + return XFor.substring(0, index); + } else { + return XFor; + } + } + + XFor = Xip; + if (StringUtils.isNotEmpty(XFor) && !UNKNOWN_IP.equalsIgnoreCase(XFor)) { + return XFor; + } + + if (StringUtils.isBlank(XFor) || UNKNOWN_IP.equalsIgnoreCase(XFor)) { + XFor = request.getHeader("Proxy-Client-IP"); + } + if (StringUtils.isBlank(XFor) || UNKNOWN_IP.equalsIgnoreCase(XFor)) { + XFor = request.getHeader("WL-Proxy-Client-IP"); + } + if (StringUtils.isBlank(XFor) || UNKNOWN_IP.equalsIgnoreCase(XFor)) { + XFor = request.getHeader("HTTP_CLIENT_IP"); + } + if (StringUtils.isBlank(XFor) || UNKNOWN_IP.equalsIgnoreCase(XFor)) { + XFor = request.getHeader("HTTP_X_FORWARDED_FOR"); + } + if (StringUtils.isBlank(XFor) || UNKNOWN_IP.equalsIgnoreCase(XFor)) { + XFor = request.getRemoteAddr(); + } + return XFor; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/conf/UserSetup.java b/src/main/java/com/genersoft/iot/vmp/conf/UserSetup.java index 87ad01a5..8b1b5b0f 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/UserSetup.java +++ b/src/main/java/com/genersoft/iot/vmp/conf/UserSetup.java @@ -25,6 +25,8 @@ public class UserSetup { private Boolean recordPushLive = Boolean.FALSE; + private Boolean logInDatebase = Boolean.TRUE; + private List interfaceAuthenticationExcludes = new ArrayList<>(); public Boolean getSavePositionHistory() { @@ -94,4 +96,12 @@ public class UserSetup { public void setInterfaceAuthenticationExcludes(List interfaceAuthenticationExcludes) { this.interfaceAuthenticationExcludes = interfaceAuthenticationExcludes; } + + public Boolean getLogInDatebase() { + return logInDatebase; + } + + public void setLogInDatebase(Boolean logInDatebase) { + this.logInDatebase = logInDatebase; + } } diff --git a/src/main/java/com/genersoft/iot/vmp/conf/security/SecurityUtils.java b/src/main/java/com/genersoft/iot/vmp/conf/security/SecurityUtils.java index 81b34086..fd29d112 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/security/SecurityUtils.java +++ b/src/main/java/com/genersoft/iot/vmp/conf/security/SecurityUtils.java @@ -48,7 +48,7 @@ public class SecurityUtils { Authentication authentication = getAuthentication(); if(authentication!=null){ Object principal = authentication.getPrincipal(); - if(principal!=null){ + if(principal!=null && !"anonymousUser".equals(principal)){ LoginUser user = (LoginUser) authentication.getPrincipal(); return user; } diff --git a/src/main/java/com/genersoft/iot/vmp/service/ILogService.java b/src/main/java/com/genersoft/iot/vmp/service/ILogService.java new file mode 100644 index 00000000..b7a67ede --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/ILogService.java @@ -0,0 +1,34 @@ +package com.genersoft.iot.vmp.service; + +import com.genersoft.iot.vmp.storager.dao.dto.LogDto; +import com.github.pagehelper.PageInfo; + +/** + * 系统日志 + */ +public interface ILogService { + + /** + * 查询日志 + * @param page 当前页 + * @param count 每页数量 + * @param query 搜索内容 + * @param type 类型 + * @param startTime 开始时间 + * @param endTime 结束时间 + * @return 日志列表 + */ + PageInfo getAll(int page, int count, String query, String type, String startTime, String endTime); + + /** + * 添加日志 + * @param logDto 日志 + */ + void add(LogDto logDto); + + /** + * 清空 + */ + int clear(); + +} diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/LogServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/LogServiceImpl.java new file mode 100644 index 00000000..92fa5960 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/impl/LogServiceImpl.java @@ -0,0 +1,36 @@ +package com.genersoft.iot.vmp.service.impl; + +import com.genersoft.iot.vmp.gb28181.bean.DeviceAlarm; +import com.genersoft.iot.vmp.service.ILogService; +import com.genersoft.iot.vmp.storager.dao.LogMapper; +import com.genersoft.iot.vmp.storager.dao.dto.LogDto; +import com.github.pagehelper.PageHelper; +import com.github.pagehelper.PageInfo; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public class LogServiceImpl implements ILogService { + + @Autowired + private LogMapper logMapper; + + @Override + public PageInfo getAll(int page, int count, String query, String type, String startTime, String endTime) { + PageHelper.startPage(page, count); + List all = logMapper.query(query, type, startTime, endTime); + return new PageInfo<>(all); + } + + @Override + public void add(LogDto logDto) { + logMapper.add(logDto); + } + + @Override + public int clear() { + return logMapper.clear(); + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java index 62340734..d928d873 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java @@ -342,12 +342,14 @@ public class MediaServerServiceImpl implements IMediaServerService, CommandLineR if (redisUtil.zSize(key) == null || redisUtil.zSize(key) == 0) { logger.info("获取负载最低的节点时无在线节点"); + return null; } // 获取分数最低的,及并发最低的 Set objects = redisUtil.ZRange(key, 0, -1); - ArrayList MediaServerObjectS = new ArrayList<>(objects); - String mediaServerId = (String)MediaServerObjectS.get(0); + ArrayList mediaServerObjectS = new ArrayList<>(objects); + + String mediaServerId = (String)mediaServerObjectS.get(0); return getOne(mediaServerId); } diff --git a/src/main/java/com/genersoft/iot/vmp/storager/dao/LogMapper.java b/src/main/java/com/genersoft/iot/vmp/storager/dao/LogMapper.java new file mode 100644 index 00000000..18fa91b6 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/storager/dao/LogMapper.java @@ -0,0 +1,39 @@ +package com.genersoft.iot.vmp.storager.dao; + +import com.genersoft.iot.vmp.gb28181.bean.DeviceAlarm; +import com.genersoft.iot.vmp.storager.dao.dto.LogDto; +import org.apache.ibatis.annotations.Delete; +import org.apache.ibatis.annotations.Insert; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Select; +import org.springframework.stereotype.Repository; + +import java.util.List; + +/** + * 用于存储设服务的日志 + */ +@Mapper +@Repository +public interface LogMapper { + + @Insert("insert into log ( name, type, uri, address, result, timing, username, createTime) " + + "values ('${name}', '${type}', '${uri}', '${address}', '${result}', ${timing}, '${username}', '${createTime}')") + int add(LogDto logDto); + + + @Select(value = {""}) + List query(String query, String type, String startTime, String endTime); + + + @Delete("DELETE FROM log") + int clear(); +} diff --git a/src/main/java/com/genersoft/iot/vmp/storager/dao/dto/LogDto.java b/src/main/java/com/genersoft/iot/vmp/storager/dao/dto/LogDto.java new file mode 100644 index 00000000..cfe29f55 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/storager/dao/dto/LogDto.java @@ -0,0 +1,86 @@ +package com.genersoft.iot.vmp.storager.dao.dto; + +public class LogDto { + + private int id; + private String name; + private String type; + private String uri; + private String address; + private String result; + private long timing; + private String username; + private String createTime; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getUri() { + return uri; + } + + public void setUri(String uri) { + this.uri = uri; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + + public String getResult() { + return result; + } + + public void setResult(String result) { + this.result = result; + } + + public long getTiming() { + return timing; + } + + public void setTiming(long timing) { + this.timing = timing; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getCreateTime() { + return createTime; + } + + public void setCreateTime(String createTime) { + this.createTime = createTime; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/log/LogController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/log/LogController.java new file mode 100644 index 00000000..6dfb5694 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/log/LogController.java @@ -0,0 +1,93 @@ +package com.genersoft.iot.vmp.vmanager.log; + +import com.genersoft.iot.vmp.service.ILogService; +import com.genersoft.iot.vmp.storager.dao.dto.LogDto; +import com.genersoft.iot.vmp.vmanager.bean.WVPResult; +import com.github.pagehelper.PageInfo; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.util.StringUtils; +import org.springframework.web.bind.annotation.*; + +import java.text.ParseException; +import java.text.SimpleDateFormat; + +@Api(tags = "日志管理") +@CrossOrigin +@RestController +@RequestMapping("/api/log") +public class LogController { + + @Autowired + private ILogService logService; + + private SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + + /** + * 分页查询日志 + * + * @param query 查询内容 + * @param page 当前页 + * @param count 每页查询数量 + * @param type 类型 + * @param startTime 开始时间 + * @param endTime 结束时间 + * @return + */ + @ApiOperation("分页查询报警") + @GetMapping("/all") + @ApiImplicitParams({ + @ApiImplicitParam(name="query", value = "查询内容", dataTypeClass = String.class), + @ApiImplicitParam(name="page", value = "当前页", required = true ,dataTypeClass = Integer.class), + @ApiImplicitParam(name="count", value = "每页查询数量", required = true ,dataTypeClass = Integer.class), + @ApiImplicitParam(name="type", value = "类型" ,dataTypeClass = String.class), + @ApiImplicitParam(name="startTime", value = "查询内容" ,dataTypeClass = String.class), + @ApiImplicitParam(name="endTime", value = "查询内容" ,dataTypeClass = String.class), + }) + public ResponseEntity> getAll( + @RequestParam int page, + @RequestParam int count, + @RequestParam(required = false) String query, + @RequestParam(required = false) String type, + @RequestParam(required = false) String startTime, + @RequestParam(required = false) String endTime + ) { + if (StringUtils.isEmpty(query)) query = null; + if (StringUtils.isEmpty(startTime)) startTime = null; + if (StringUtils.isEmpty(endTime)) endTime = null; + + + try { + if (startTime != null) format.parse(startTime); + if (endTime != null) format.parse(endTime); + } catch (ParseException e) { + return new ResponseEntity<>(null, HttpStatus.BAD_REQUEST); + } + + PageInfo allLog = logService.getAll(page, count, query, type, startTime, endTime); + return new ResponseEntity<>(allLog, HttpStatus.OK); + } + + /** + * 清空日志 + * + */ + @ApiOperation("清空日志") + @DeleteMapping("/clear") + @ApiImplicitParams({}) + public ResponseEntity> clear() { + + int count = logService.clear(); + WVPResult wvpResult = new WVPResult(); + wvpResult.setCode(0); + wvpResult.setMsg("success"); + wvpResult.setData(count); + return new ResponseEntity>(wvpResult, HttpStatus.OK); + } + +} diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/user/UserController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/user/UserController.java index de40c2c7..6e4c4169 100644 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/user/UserController.java +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/user/UserController.java @@ -17,9 +17,7 @@ import org.springframework.util.DigestUtils; import org.springframework.web.bind.annotation.*; import javax.security.sasl.AuthenticationException; -import javax.xml.crypto.Data; import java.text.SimpleDateFormat; -import java.util.Date; import java.util.List; @Api(tags = "用户管理") @@ -42,19 +40,25 @@ public class UserController { @ApiImplicitParam(name = "password", required = true, value = "密码(32位md5加密)", dataTypeClass = String.class), }) @GetMapping("/login") - public String login(@RequestParam String username, @RequestParam String password){ - LoginUser user; + public WVPResult login(@RequestParam String username, @RequestParam String password){ + LoginUser user = null; + WVPResult result = new WVPResult<>(); try { user = SecurityUtils.login(username, password, authenticationManager); } catch (AuthenticationException e) { e.printStackTrace(); - return "fail"; + result.setCode(-1); + result.setMsg("fail"); } if (user != null) { - return "success"; + result.setCode(0); + result.setMsg("success"); + result.setData(user); }else { - return "fail"; + result.setCode(-1); + result.setMsg("fail"); } + return result; } @ApiOperation("修改密码") diff --git a/src/main/resources/all-application.yml b/src/main/resources/all-application.yml index fdd81b16..140ef0a4 100644 --- a/src/main/resources/all-application.yml +++ b/src/main/resources/all-application.yml @@ -150,6 +150,8 @@ user-settings: - /api/v1/** # 推流直播是否录制 record-push-live: true + # 是否将日志存储进数据库 + logInDatebase: true # 在线文档: swagger-ui(生产环境建议关闭) swagger-ui: diff --git a/src/main/resources/wvp.sqlite b/src/main/resources/wvp.sqlite index a76d7ee8..908d5853 100644 Binary files a/src/main/resources/wvp.sqlite and b/src/main/resources/wvp.sqlite differ