4、添加注解
下面我们写完了注解,接下来我们肯定要使用这个注解,但是在什么地方引用呢,当然在我们程序的入库,那就是Controller层
,这个是前端操作第一个进来的入口。例如:我们在分类的Controller
中添加分类的方法上添加一个注解。
@OperationLogSys(desc = "添加分类", operationType = OperationType.INSERT)
完整代码:
/** * 添加分类 * @return */ @ApiOperation(value = "添加分类") @PostMapping("/create") @OperationLogSys(desc = "添加分类", operationType = OperationType.INSERT) public JsonResult<Object> categoryCreate(@RequestBody @Valid Category category) { int isStatus = categoryService.saveCategory(category); if (isStatus == 0) { return JsonResult.error("添加分类失败"); } return JsonResult.success(); }
我们先测试一下这个注解是否有用,操作数据是否保存到了数据中。
打开postman,因为我们上一篇加了登录验证,所以我们在操作其他接口时,要先登录,重启项目之后也要重新登录。
我们先进行登录:登录成功之后,然后我们打开添加分类的接口,新添加一个分类。
看着添加成功了,我们去看下数据库,操作日志有没有数据。看到有数据,也是我们添加的分类,就说明我们添加成功了。好啦,我只添加一个操作日志,大家把所有的增加、删除、修改都进行添加一下,查询数据比较多可加可不加。
接下来我们写一下操作日志,操作日志就比较简单啦,不用写注解啦,直接添加即可。
四、登录日志
接下来我们写一下登录日志,我们只需要再登录的接口的地方写一个插入登录操作的日志即可,由于我们设计的登录日志和操作日志是两张表,所以我们还得写一套登录日志的CRDU操作,废话不多说,开整。
下面我直接写了,大家应该可以看懂。
1、建表语句
DROP TABLE IF EXISTS `person_login_log`; CREATE TABLE `person_login_log` ( `id` BIGINT(20) NOT NULL PRIMARY KEY AUTO_INCREMENT COMMENT '登录访问id', `login_name` VARCHAR(50) NULL DEFAULT '' COMMENT '登录账号', `ip_address` VARCHAR(128) NULL DEFAULT '' COMMENT '登录IP地址', `login_location` VARCHAR(255) NULL DEFAULT '' COMMENT '登录地点', `browser_type` VARCHAR(50) NULL DEFAULT '' COMMENT '浏览器类型', `os` VARCHAR(50) NULL DEFAULT '' COMMENT '操作系统', `login_status` TINYINT NULL DEFAULT 0 COMMENT '登录状态,默认0, 0-成功, 1-失败', `create_time` DATETIME NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间' ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin ROW_FORMAT = Dynamic COMMENT '登录日志表';
2、实体类
新建一个LoginOperationLog.java
类
package com.blog.personalblog.entity; import lombok.Data; import java.time.LocalDateTime; /** * @author: SuperMan * @create: 2022-04-05 **/ @Data public class LoginOperationLog { /** * 主键id */ private Integer id; /** * 登录账号 */ private String loginName; /** * 登录IP地址 */ private String ipAddress; /** * 登录地点 */ private String loginLocation; /** * 浏览器类型 */ private String browserType; /** * 操作系统 */ private String os; /** * 登录状态,默认0, 0-成功, 1-失败 */ private Integer loginStatus; /** * 创建时间 */ private LocalDateTime createTime; }
3、业务类
(1)新建一个LoginOperationLogService.java
业务接口类
package com.blog.personalblog.service; import com.blog.personalblog.config.page.PageRequest; import com.blog.personalblog.entity.LoginOperationLog; import java.util.List; /** * @author: SuperMan * @create: 2022-04-05 **/ public interface LoginOperationLogService { /** * 添加登录日志 * * @param loginOperationLog * @return */ void saveOperationLog(LoginOperationLog loginOperationLog); /** * 登录日志列表(分页) * * @param pageRequest * @return */ List<LoginOperationLog> getLoginOperationLogPage(PageRequest pageRequest); }
(2)新建一个LoginOperationLogServiceImpl.java
业务实现类
package com.blog.personalblog.service.Impl; import com.blog.personalblog.config.page.PageRequest; import com.blog.personalblog.entity.LoginOperationLog; import com.blog.personalblog.mapper.LoginOperationLogMapper; import com.blog.personalblog.service.LoginOperationLogService; import com.github.pagehelper.PageHelper; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.util.List; /** * * @author: SuperMan * @create: 2022-04-05 **/ @Service public class LoginOperationLogServiceImpl implements LoginOperationLogService { @Resource private LoginOperationLogMapper loginOperationLogMapper; @Override public void saveOperationLog(LoginOperationLog loginOperationLog) { loginOperationLogMapper.createLoginOperationLog(loginOperationLog); } @Override public List<LoginOperationLog> getLoginOperationLogPage(PageRequest pageRequest) { int pageNum = pageRequest.getPageNum(); int pageSize = pageRequest.getPageSize(); PageHelper.startPage(pageNum,pageSize); List<LoginOperationLog> loginOperationLogList = loginOperationLogMapper.getLoginOperationLogPage(); return loginOperationLogList; } }
4、数据接口Mapper
新建一个LoginOperationLogMapper.java
接口
package com.blog.personalblog.mapper; import com.blog.personalblog.entity.LoginOperationLog; import org.springframework.stereotype.Repository; import java.util.List; /** * * @author: SuperMan * @create: 2022-04-06 **/ @Repository public interface LoginOperationLogMapper { /** * 创建登录日志 * @param loginOperationLog * @return */ int createLoginOperationLog(LoginOperationLog loginOperationLog); /** * 分类列表(分页) * @return */ List<LoginOperationLog> getLoginOperationLogPage(); }
5、xml文件
新建一个登录日志的LoginOperationLogMapper.xml
文件,用来写sql语句。
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.blog.personalblog.mapper.LoginOperationLogMapper"> <resultMap id="BaseResultMap" type="com.blog.personalblog.entity.LoginOperationLog"> <result column="id" jdbcType="INTEGER" property="id"/> <result column="login_name" jdbcType="VARCHAR" property="loginName"/> <result column="ip_address" jdbcType="VARCHAR" property="ipAddress"/> <result column="login_location" jdbcType="VARCHAR" property="loginLocation"/> <result column="browser_type" jdbcType="VARCHAR" property="browserType"/> <result column="os" jdbcType="VARCHAR" property="os"/> <result column="login_status" jdbcType="INTEGER" property="loginStatus"/> <result column="create_time" jdbcType="TIMESTAMP" property="createTime"/> </resultMap> <insert id="createLoginOperationLog" parameterType="com.blog.personalblog.entity.LoginOperationLog" useGeneratedKeys="true" keyProperty="id"> INSERT INTO person_login_log (login_name, ip_address, login_location, browser_type, os, login_status) VALUES(#{loginName}, #{ipAddress}, #{loginLocation}, #{browserType}, #{os}, #{loginStatus}) </insert> <select id="getLoginOperationLogPage" resultMap="BaseResultMap"> select * from person_login_log </select> </mapper>
6、接口层
新建一个日志的接口,我看了一下上边的操作日志我也没写接口层,所以我们将操作日志和登录日志写在一个Controller层中了。
先建一个OperationLogController.java
package com.blog.personalblog.controller; import com.blog.personalblog.config.page.PageRequest; import com.blog.personalblog.config.page.PageResult; import com.blog.personalblog.entity.LoginOperationLog; import com.blog.personalblog.entity.OperationLog; import com.blog.personalblog.service.LoginOperationLogService; import com.blog.personalblog.service.OperationLogService; import com.blog.personalblog.util.JsonResult; import com.blog.personalblog.util.PageUtil; import com.github.pagehelper.PageInfo; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; import javax.validation.Valid; import java.util.List; /** * * @author: SuperMan * @create: 2022-04-06 **/ @Api(tags = "操作日志") @RestController @RequestMapping("/log") public class OperationLogController { @Resource private LoginOperationLogService loginOperationLogService; @Resource private OperationLogService operationLogService; /** * 操作日志列表 * @param pageRequest * @return */ @ApiOperation(value = "操作日志列表") @PostMapping("list") public JsonResult<Object> OperationLogListPage(@RequestBody @Valid PageRequest pageRequest) { List<OperationLog> operationLogPage = operationLogService.getOperationLogPage(pageRequest); PageInfo pageInfo = new PageInfo(operationLogPage); PageResult pageResult = PageUtil.getPageResult(pageRequest, pageInfo); return JsonResult.success(pageResult); } /** * 登录日志列表 * @param pageRequest * @return */ @ApiOperation(value = "登录日志列表") @PostMapping("list") public JsonResult<Object> LoginOperationLogListPage(@RequestBody @Valid PageRequest pageRequest) { List<LoginOperationLog> loginOperationLogPage = loginOperationLogService.getLoginOperationLogPage(pageRequest); PageInfo pageInfo = new PageInfo(loginOperationLogPage); PageResult pageResult = PageUtil.getPageResult(pageRequest, pageInfo); return JsonResult.success(pageResult); } }
好啦,准备工作基本上完成了,接下来我们再登录的时候插入我们的登录日志即可。
7、登录日志编写
打开我们的UserController.java类,然后找到我们的登录方法,我们再UserController中新添加一个方法,用来获取登录信息并且将登录的信息插入到登录日志表中。我们设计的表里有要获取浏览器类型和操作系统,所以我们使用了user-agent-utils 是一个用来解析 User-Agent 字符串的 Java 类库。 其能够识别的内容包括: 超过150种不同的浏览器; 7种不同的浏览器类型; 超过60种不同的操作系统; 6种不同的设备类型; 9种不同的渲染引擎; 9种不同的Web应用,如HttpClient、Bot。
1、添加maven
<dependency> <groupId>eu.bitwalker</groupId> <artifactId>UserAgentUtils</artifactId> <version>1.21</version> </dependency>
2、客户端工具类
package com.blog.personalblog.util; import cn.hutool.core.convert.Convert; import org.springframework.web.context.request.RequestAttributes; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; /** * 客户端工具类 * */ public class ServletUtils { /** * 获取String参数 */ public static String getParameter(String name) { return getRequest().getParameter(name); } /** * 获取String参数 */ public static String getParameter(String name, String defaultValue) { return Convert.toStr(getRequest().getParameter(name), defaultValue); } /** * 获取Integer参数 */ public static Integer getParameterToInt(String name) { return Convert.toInt(getRequest().getParameter(name)); } /** * 获取Integer参数 */ public static Integer getParameterToInt(String name, Integer defaultValue) { return Convert.toInt(getRequest().getParameter(name), defaultValue); } /** * 获取request */ public static HttpServletRequest getRequest() { return getRequestAttributes().getRequest(); } /** * 获取response */ public static HttpServletResponse getResponse() { return getRequestAttributes().getResponse(); } /** * 获取session */ public static HttpSession getSession() { return getRequest().getSession(); } public static ServletRequestAttributes getRequestAttributes() { RequestAttributes attributes = RequestContextHolder.getRequestAttributes(); return (ServletRequestAttributes) attributes; } }
下面进行解析agent字符串
UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent"));
其他的也没有什么,下面就是我们添加登录日志的方法
/** * 获取登录日志 */ public void getLoginInfoLog(LoginModel loginModel, Integer status) { RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); HttpServletRequest request = (HttpServletRequest) Objects.requireNonNull(requestAttributes).resolveReference(RequestAttributes.REFERENCE_REQUEST); //解析agent字符串 UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent")); //登录账号 LoginOperationLog loginOperationLog = new LoginOperationLog(); loginOperationLog.setLoginName(loginModel.getUsername()); //登录IP地址 String ipAddr = IpUtil.getIpAddr(request); loginOperationLog.setIpAddress(ipAddr); //登录地点 String ipInfo = IpUtil.getIpInfo(ipAddr); loginOperationLog.setLoginLocation(ipInfo); //浏览器类型 String browser = userAgent.getBrowser().getName(); loginOperationLog.setBrowserType(browser); //操作系统 String os = userAgent.getOperatingSystem().getName(); loginOperationLog.setOs(os); //登录状态 loginOperationLog.setLoginStatus(status); loginOperationLogService.saveOperationLog(loginOperationLog); }
然后我们在登录的方法中添加这个方法,一共传了两个参数,其中status是登录的状态,成功是0,失败是1。
/** * 登录 * @param loginModel * @return */ @ApiOperation(value = "登录") @PostMapping("/login") @OperationLogSys(desc = "登录", operationType = OperationType.LOGIN) public JsonResult<Object> login(@RequestBody LoginModel loginModel){ logger.info("{} 在请求登录! ", loginModel.getUsername()); Subject subject = SecurityUtils.getSubject(); UsernamePasswordToken token = new UsernamePasswordToken(loginModel.getUsername(), loginModel.getPassword(), false); try { subject.login(token); Map<String, Object> ret = new HashedMap(); ret.put("token", subject.getSession().getId()); logger.info("{} login success", loginModel.getUsername()); getLoginInfoLog(loginModel, 0); return JsonResult.success(ret); } catch (IncorrectCredentialsException e) { logger.info("login fail {}", e.getMessage()); return JsonResult.error(ErrorCode.NOT_LOGIN); } catch (LockedAccountException e) { logger.info("login fail {}", e.getMessage()); return JsonResult.error(ErrorCode.ERROR_CODE); } catch (AuthenticationException e) { logger.info("login fail {}", e.getMessage()); return JsonResult.error(ErrorCode.USER_NOT_EXIST); } catch (Exception e) { e.printStackTrace(); getLoginInfoLog(loginModel, 1); logger.info("login fail {}", e.getMessage()); return JsonResult.error(ErrorCode.ERROR_CODE); } }
下面是UserController
的完整代码:(有些地方改了一点,大家可以看一下)
package com.blog.personalblog.controller; import cn.hutool.core.util.StrUtil; import com.blog.personalblog.annotation.OperationLogSys; import com.blog.personalblog.annotation.OperationType; import com.blog.personalblog.entity.ErrorCode; import com.blog.personalblog.entity.LoginModel; import com.blog.personalblog.entity.LoginOperationLog; import com.blog.personalblog.service.LoginOperationLogService; import com.blog.personalblog.util.IpUtil; import com.blog.personalblog.util.JsonResult; import com.blog.personalblog.entity.User; import com.blog.personalblog.service.UserService; import com.blog.personalblog.util.MD5Util; import com.blog.personalblog.util.PhoneUtil; import com.blog.personalblog.util.ServletUtils; import eu.bitwalker.useragentutils.UserAgent; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.apache.commons.collections.map.HashedMap; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.IncorrectCredentialsException; import org.apache.shiro.authc.LockedAccountException; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.subject.Subject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.*; import org.springframework.web.context.request.RequestAttributes; import org.springframework.web.context.request.RequestContextHolder; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import javax.validation.Valid; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; /** * @author: SuperMan * 欢迎关注我的公众号:码上言 * @create: 2021-11-03 */ @Api(tags = "用户管理") @RestController @RequestMapping("/user") public class UserController { private Logger logger = LoggerFactory.getLogger(this.getClass()); @Resource private UserService userService; @Resource private LoginOperationLogService loginOperationLogService; /** * 用户列表 * @return */ @ApiOperation(value = "用户列表") @PostMapping("/list") public JsonResult<Object> list() { List<User> userList = userService.findAll(); return JsonResult.success(userList); } /** * 添加用户 * @return */ @ApiOperation(value = "添加用户") @PostMapping("/create") @OperationLogSys(desc = "添加用户", operationType = OperationType.INSERT) public JsonResult<Object> userCreate(@RequestBody @Valid User user) { if (StrUtil.isEmpty(user.getPassWord())) { return JsonResult.error("密码为空,请填写密码!"); } //密码加密存储 user.setPassWord(MD5Util.MD5(user.getPassWord())); //判断手机号,这里用hutool工具类也可以 if (!PhoneUtil.checkMobile(user.getPhone())) { return JsonResult.error("手机号码格式错误!"); } userService.createUser(user); return JsonResult.success(); } /** * * 修改用户 * @return */ @ApiOperation(value = "修改用户") @PostMapping("/update") @OperationLogSys(desc = "修改用户", operationType = OperationType.UPDATE) public JsonResult<Object> userUpdate(@RequestBody @Valid User user) { if (StrUtil.isEmpty(user.getPassWord())) { return JsonResult.error("密码为空,请填写密码!"); } //密码加密存储 user.setPassWord(MD5Util.MD5(user.getPassWord())); //判断手机号,这里用hutool工具类也可以 if (!PhoneUtil.checkMobile(user.getPhone())) { return JsonResult.error("手机号码格式错误!"); } userService.updateUser(user); return JsonResult.success(); } /** * 删除 * @return */ @ApiOperation(value = "删除用户") @PostMapping("/delete/{id}") @OperationLogSys(desc = "删除用户", operationType = OperationType.DELETE) public JsonResult<Object> userDelete(@PathVariable(value = "id") int id) { userService.deleteUser(id); return JsonResult.success(); } /** * 登录 * @param loginModel * @return */ @ApiOperation(value = "登录") @PostMapping("/login") @OperationLogSys(desc = "登录", operationType = OperationType.LOGIN) public JsonResult<Object> login(@RequestBody LoginModel loginModel){ logger.info("{} 在请求登录! ", loginModel.getUsername()); Subject subject = SecurityUtils.getSubject(); UsernamePasswordToken token = new UsernamePasswordToken(loginModel.getUsername(), loginModel.getPassword(), false); try { subject.login(token); Map<String, Object> ret = new HashedMap(); ret.put("token", subject.getSession().getId()); logger.info("{} login success", loginModel.getUsername()); getLoginInfoLog(loginModel, 0); return JsonResult.success(ret); } catch (IncorrectCredentialsException e) { logger.info("login fail {}", e.getMessage()); return JsonResult.error(ErrorCode.NOT_LOGIN); } catch (LockedAccountException e) { logger.info("login fail {}", e.getMessage()); return JsonResult.error(ErrorCode.ERROR_CODE); } catch (AuthenticationException e) { logger.info("login fail {}", e.getMessage()); return JsonResult.error(ErrorCode.USER_NOT_EXIST); } catch (Exception e) { e.printStackTrace(); getLoginInfoLog(loginModel, 1); logger.info("login fail {}", e.getMessage()); return JsonResult.error(ErrorCode.ERROR_CODE); } } /** * 登录info信息 * @return */ @GetMapping("/info") public JsonResult<Object> info(){ Map<String, Object> ret = new HashMap<>(3); ret.put("roles", "[admin]"); ret.put("name", "admin"); ret.put("avatar","https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif"); return JsonResult.success(ret); } /** * 获取登录日志 */ public void getLoginInfoLog(LoginModel loginModel, Integer status) { RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); HttpServletRequest request = (HttpServletRequest) Objects.requireNonNull(requestAttributes).resolveReference(RequestAttributes.REFERENCE_REQUEST); //解析agent字符串 UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent")); //登录账号 LoginOperationLog loginOperationLog = new LoginOperationLog(); loginOperationLog.setLoginName(loginModel.getUsername()); //登录IP地址 String ipAddr = IpUtil.getIpAddr(request); loginOperationLog.setIpAddress(ipAddr); //登录地点 String ipInfo = IpUtil.getIpInfo(ipAddr); loginOperationLog.setLoginLocation(ipInfo); //浏览器类型 String browser = userAgent.getBrowser().getName(); loginOperationLog.setBrowserType(browser); //操作系统 String os = userAgent.getOperatingSystem().getName(); loginOperationLog.setOs(os); //登录状态 loginOperationLog.setLoginStatus(status); loginOperationLogService.saveOperationLog(loginOperationLog); } }
8、测试
我们在postman中模拟登录,然后我们在sql中查看有没有登录日志,这里要说一下,如果你是用的地址为localhost
或者127.0.0.1
进行请求,我们在获取ip归属地的时候会获取不到,我们可以使用电脑的ip就可以看到。好啦,到这里我们的操作日志就完成了,到此我们所有的后端功能都开发完了,下面我们就写前端代码,后端提供api即可,我们的项目开发2/3了,即将完成了,大家努力啊。
看到现在的阅读量,着实有点惨不忍睹,希望大家多多点赞、收藏、分享啊,大家帮我宣传一下,感谢感谢!