最近需要记录 用户的操作日志
,功能完成了,现在记录下:
做一些提前准备
POM
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.8.4</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.54</version> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.5</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> </dependency>
|
数据库
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| // 异常操作日志表 DROP TABLE IF EXISTS `exception_log`; CREATE TABLE `exception_log` ( `exc_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键ID', `exc_requ_param` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL COMMENT '请求参数', `exc_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '异常名称', `exc_message` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL COMMENT '异常信息', `oper_user_id` int(11) NULL DEFAULT NULL COMMENT '操作员ID', `oper_user_name` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '操作员姓名', `oper_method` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '请求方法', `base_path` varchar(255) CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT NULL COMMENT '根路径', `oper_url` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '请求URL', `oper_ip` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '请求IP', `oper_ver` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '请求版本号', `oper_create_time` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', PRIMARY KEY (`exc_id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 17 CHARACTER SET = latin1 COLLATE = latin1_swedish_ci COMMENT = '异常日志表' ROW_FORMAT = DYNAMIC;
SET FOREIGN_KEY_CHECKS = 1;
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| // 正常操作日志表 DROP TABLE IF EXISTS `operation_log`; CREATE TABLE `operation_log` ( `oper_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键ID', `oper_modul` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '功能模块', `oper_type` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '操作类型', `oper_desc` varchar(500) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '操作描述', `oper_requ_param` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL COMMENT '请求参数', `oper_resp_param` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL COMMENT '返回参数', `oper_user_id` int(11) NULL DEFAULT NULL COMMENT '操作员ID', `oper_user_name` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '操作员名称', `oper_method` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '操作方法', `base_path` varchar(255) CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT NULL COMMENT '根路径', `oper_url` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '请求Url', `oper_ip` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '请求IP', `oper_ver` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '操作版本号', `oper_create_time` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '操作时间', PRIMARY KEY (`oper_id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 24 CHARACTER SET = latin1 COLLATE = latin1_swedish_ci COMMENT = '操作日志表' ROW_FORMAT = DYNAMIC;
SET FOREIGN_KEY_CHECKS = 1;
|
好,有了基本的环境后,开始写Mapper\Service\Entity层,我这里使用了 mybatis-plus
。
1、Entity
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
| package com.qhzx.td.entity;
import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.extension.activerecord.Model; import com.fasterxml.jackson.annotation.JsonFormat; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.*; import org.springframework.format.annotation.DateTimeFormat;
import java.util.Date;
@Data @EqualsAndHashCode(callSuper = true) @AllArgsConstructor @NoArgsConstructor @Builder @ApiModel(description= "正常操作日志") @TableName("operation_log") public class OperationLogPO extends Model<OperationLogPO> {
@TableId(value = "oper_id", type = IdType.AUTO) private Integer operId;
@ApiModelProperty("功能模块") private String operModul;
@ApiModelProperty("操作类型") private String operType;
@ApiModelProperty("操作描述") private String operDesc;
@ApiModelProperty("请求参数") private String operRequParam;
@ApiModelProperty("返回参数") private String operRespParam;
@ApiModelProperty("操作员ID") private Long operUserId;
@ApiModelProperty("操作员名称") private String operUserName;
@ApiModelProperty("操作方法") private String operMethod;
@ApiModelProperty("根路径") private String basePath;
@ApiModelProperty("请求Url") private String operUrl;
@ApiModelProperty("请求Url") private String operIp;
@ApiModelProperty("操作版本号") private String operVer;
@ApiModelProperty("操作时间") @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8") private Date operCreateTime;
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
| package com.qhzx.td.entity;
import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.extension.activerecord.Model; import com.fasterxml.jackson.annotation.JsonFormat; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.*; import org.springframework.format.annotation.DateTimeFormat;
import java.util.Date;
@Data @EqualsAndHashCode(callSuper = true) @AllArgsConstructor @NoArgsConstructor @Builder @ApiModel(description= "异常操作日志") @TableName("exception_log") public class ExceptionLogPO extends Model<ExceptionLogPO> {
@TableId(value = "exc_id", type = IdType.AUTO) private Integer excId;
@ApiModelProperty("请求参数") private String excRequParam;
@ApiModelProperty("异常名称") private String excName;
@ApiModelProperty("异常信息") private String excMessage;
@ApiModelProperty("操作员ID") private Long operUserId;
@ApiModelProperty("操作员姓名") private String operUserName;
@ApiModelProperty("请求方法") private String operMethod;
@ApiModelProperty("请求URL") private String operUrl;
@ApiModelProperty("请求IP") private String operIp;
@ApiModelProperty("请求版本号") private String operVer;
@ApiModelProperty("创建时间") @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8") private Date operCreateTime;
@ApiModelProperty("根路径") private String basePath;
}
|
2、Mapper
1 2 3 4 5 6 7 8 9 10
| package com.qhzx.td.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.qhzx.td.entity.OperationLogPO;
public interface OperationLogMapper extends BaseMapper<OperationLogPO> { }
|
1 2 3 4 5 6 7 8 9 10
| package com.qhzx.td.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.qhzx.td.entity.ExceptionLogPO;
public interface ExceptionLogMapper extends BaseMapper<ExceptionLogPO> { }
|
3、Service
1 2 3 4 5 6 7 8 9 10
| package com.qhzx.td.services.common;
import com.baomidou.mybatisplus.extension.service.IService; import com.qhzx.td.entity.ExceptionLogPO;
public interface ExceptionLogService extends IService<ExceptionLogPO> { }
|
1 2 3 4 5 6 7 8 9 10
| package com.qhzx.td.services.common;
import com.baomidou.mybatisplus.extension.service.IService; import com.qhzx.td.entity.OperationLogPO;
public interface OperationLogService extends IService<OperationLogPO> { }
|
3.1、Service-impl
1 2 3 4 5 6 7 8 9 10 11
| package com.qhzx.td.services.common.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.qhzx.td.entity.ExceptionLogPO; import com.qhzx.td.mapper.ExceptionLogMapper; import com.qhzx.td.services.common.ExceptionLogService; import org.springframework.stereotype.Service;
@Service public class ExceptionLogServiceImpl extends ServiceImpl<ExceptionLogMapper, ExceptionLogPO> implements ExceptionLogService { }
|
1 2 3 4 5 6 7 8 9 10 11
| package com.qhzx.td.services.common.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.qhzx.td.entity.OperationLogPO; import com.qhzx.td.mapper.OperationLogMapper; import com.qhzx.td.services.common.OperationLogService; import org.springframework.stereotype.Service;
@Service public class OperationLogServiceImpl extends ServiceImpl<OperationLogMapper, OperationLogPO> implements OperationLogService { }
|
4、登录
登录Controller
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| package com.qhzx.td.controller.Login;
import com.qhzx.td.services.system.LoginService; import com.qhzx.td.untils.Result; import com.qhzx.td.vo.LoginBodyVO; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController;
@Api(tags = "登录") @RestController @CrossOrigin @Slf4j @RequiredArgsConstructor(onConstructor = @__(@Autowired)) public class LoginController {
private final LoginService loginService;
@ApiOperation(value = "登录方法") @PostMapping("/login") public Result login(@RequestBody @Validated LoginBodyVO loginBodyVO) { return loginService.login(loginBodyVO.getUsername().trim(), loginBodyVO.getPassword().trim(), loginBodyVO.getCode().trim(), loginBodyVO.getUuid()); } }
|
登录-Service-实现
这里的token令牌是有用的,当然你可以根据自己的业务进行修改,不一定要按照我的方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
| package com.qhzx.td.services.system.impl;
import cn.hutool.core.util.ObjectUtil; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.qhzx.td.constant.Constants; import com.qhzx.td.entity.AdminUserPO; import com.qhzx.td.exception.ComFoundException; import com.qhzx.td.exception.user.CaptchaException; import com.qhzx.td.exception.user.CaptchaExpireException; import com.qhzx.td.framework.security.authc.AuthcService; import com.qhzx.td.mapper.SysUserMapper; import com.qhzx.td.services.system.LoginService; import com.qhzx.td.template.RedisRepository; import com.qhzx.td.untils.IdUtils; import com.qhzx.td.untils.Result; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.logging.log4j.util.Strings; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.ValueOperations; import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;
@Slf4j @Service @RequiredArgsConstructor(onConstructor = @__(@Autowired)) public class LoginServiceImpl extends ServiceImpl<SysUserMapper, AdminUserPO> implements LoginService {
private final RedisRepository redisCache; private final SysUserMapper loginMapper; private final AuthcService authcService; private final RedisTemplate<String, Object> template;
@Override public Result login(String username, String password, String code, String uuid) { String verifyKey = Constants.CAPTCHA_CODE_KEY + uuid; String captcha = redisCache.getCacheObject(verifyKey); redisCache.deleteObject(verifyKey);
if (captcha == null) { log.error("账号为{},验证码失效!",username); throw new CaptchaExpireException(); } if (!code.equalsIgnoreCase(captcha)) { log.error("账号为{},验证码错误!",username); throw new CaptchaException(); }
QueryWrapper<AdminUserPO> queryWrapper = new QueryWrapper<>(); queryWrapper.eq("username",username);
AdminUserPO adminUserPO = loginMapper.selectOne(queryWrapper);
boolean passFlag = authcService.check(password, adminUserPO); if (!passFlag) { throw new ComFoundException("密码错误"); }
log.info("登录成功,正在生成token..."); String uuid1 = IdUtils.getUUID(); String jsonString = JSON.toJSONString(adminUserPO); ValueOperations<String, Object> redisString = template.opsForValue(); redisString.set(Constants.TOKEN + uuid1,jsonString,60 * 60, TimeUnit.SECONDS);
JSONObject jsonObject = new JSONObject(); jsonObject.put("name",username); jsonObject.put("id",adminUserPO.getId()); jsonObject.put(Constants.TOKEN,uuid1); return Result.succeed(jsonObject,"请求成功"); }
}
|
5、拦截器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| import io.swagger.models.HttpMethod; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.context.annotation.Configuration; import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;
@Configuration @Component @Slf4j public class MyInterceptorConfig implements HandlerInterceptor {
@Override public boolean preHandle(HttpServletRequest request, HttpServletResponse resp, Object handler) throws Exception { log.info("==============preHandle======start===================="); log.info("IP :" + request.getRemoteAddr()); log.info("URL :" + request.getRequestURL().toString()); log.info("HTTP Method :" + request.getMethod()); log.info("===============preHandle======End=====================");
if (HttpMethod.OPTIONS.toString().equals(request.getMethod())) { log.info("OPTIONS请求,放行"); return true; } log.info("------------ MyInterceptorConfig..................."); String token = request.getHeader("Authorization"); log.info("=============== MyInterceptorConfig token : {}", token); if (StringUtils.isBlank(token) ) { log.info("=============== 请求拦截 : {}", token); return false; } return true; } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
| package com.xiaoq.store.config;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import lombok.RequiredArgsConstructor;
@Component @RequiredArgsConstructor(onConstructor = @__(@Autowired)) public class WebConfig extends WebMvcConfigurationSupport {
private final MyInterceptorConfig myInterceptorConfig;
@Override protected void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(myInterceptorConfig) .addPathPatterns("/**")
.excludePathPatterns("/sys/login","/captchaImage") .excludePathPatterns("/doc.html") .excludePathPatterns("/swagger-resources/**") .excludePathPatterns("/webjars/**") .excludePathPatterns("/v2/**") .excludePathPatterns("/favicon.ico") .excludePathPatterns("/swagger-ui.html/**"); }
@Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOriginPatterns("*") .allowCredentials(true) .allowedMethods("GET", "POST", "DELETE", "PUT", "PATCH", "OPTIONS", "HEAD") .maxAge(3600 * 24); }
@Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/**").addResourceLocations( "classpath:/static/"); registry.addResourceHandler("swagger-ui.html").addResourceLocations( "classpath:/META-INF/resources/"); registry.addResourceHandler("/webjars/**").addResourceLocations( "classpath:/META-INF/resources/webjars/");
registry.addResourceHandler("doc.html").addResourceLocations( "classpath:/META-INF/resources/"); super.addResourceHandlers(registry); }
}
|
6、注解
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| package com.luck.backstage.tests;
import java.lang.annotation.*;
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface OperLog { String operModul() default ""; String operType() default ""; String operDesc() default ""; }
|
7、Utils
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| package com.qhzx.td.untils; import org.apache.commons.lang3.StringUtils;
import javax.servlet.http.HttpServletRequest;
public class HttpUtils {
public static String getIpAddress(HttpServletRequest request) { String Xip = request.getHeader("X-Real-IP"); String XFor = request.getHeader("X-Forwarded-For");
if (StringUtils.isNotEmpty(XFor) && !"unKnown".equalsIgnoreCase(XFor)) { int index = XFor.indexOf(","); if (index != -1) { return "0:0:0:0:0:0:0:1".equals(XFor.substring(0, index)) ? "127.0.0.1" : XFor.substring(0, index); } else { return "0:0:0:0:0:0:0:1".equals(XFor) ? "127.0.0.1" : XFor; } } XFor = Xip; if (StringUtils.isNotEmpty(XFor) && !"unKnown".equalsIgnoreCase(XFor)) { return "0:0:0:0:0:0:0:1".equals(XFor) ? "127.0.0.1" : XFor; } if (StringUtils.isBlank(XFor) || "unknown".equalsIgnoreCase(XFor)) { XFor = request.getHeader("Proxy-Client-IP"); } if (StringUtils.isBlank(XFor) || "unknown".equalsIgnoreCase(XFor)) { XFor = request.getHeader("WL-Proxy-Client-IP"); } if (StringUtils.isBlank(XFor) || "unknown".equalsIgnoreCase(XFor)) { XFor = request.getHeader("HTTP_CLIENT_IP"); } if (StringUtils.isBlank(XFor) || "unknown".equalsIgnoreCase(XFor)) { XFor = request.getHeader("HTTP_X_FORWARDED_FOR"); } if (StringUtils.isBlank(XFor) || "unknown".equalsIgnoreCase(XFor)) { XFor = request.getRemoteAddr(); } return "0:0:0:0:0:0:0:1".equals(XFor) ? "127.0.0.1" : XFor; } }
|
Token解析工具类(重要),它从Redis中取出对应的业务数据:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| package com.qhzx.td.untils;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.baomidou.mybatisplus.core.toolkit.Assert; import com.qhzx.td.constant.Constants; import com.qhzx.td.entity.AdminUserPO; import com.qhzx.td.entity.Users; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest; import java.util.List;
@Component public class TokenFactory {
private static RedisTemplate redisTemplate;
@Autowired public void setRedisTemplate(RedisTemplate redisTemplate) { TokenFactory.redisTemplate = redisTemplate; }
private static AdminUserPO get(String key) { Object token = redisTemplate.opsForValue().get(Constants.TOKEN+key.replace("Bearer ", "")); return token == null ? null : JSON.parseObject(token.toString(), AdminUserPO.class); }
private static List<Users> gets(String key) { Object token = redisTemplate.opsForValue().get("token:"+key); return token == null ? null : JSONArray.parseArray(token.toString(), Users.class); } public static AdminUserPO validateToken(HttpServletRequest request) throws Exception { String token = request.getHeader("Authorization"); Assert.notEmpty(token, "token不能为空!"); AdminUserPO userToken = get(token); Assert.notNull(userToken, "token无效!"); return userToken; }
}
|
8、切面
注意更换下面两个注解内容:
@Pointcut(“@annotation(com.qhzx.mad.backstage.config.OperLog)”)
@Pointcut(“execution(* com.qhzx.mad.backstage.controller...(..))”)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244
| package com.qhzx.td.config;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.URLUtil; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.serializer.PropertyFilter; import com.qhzx.td.annotation.OperLog; import com.qhzx.td.entity.AdminUserPO; import com.qhzx.td.entity.ExceptionLogPO; import com.qhzx.td.entity.OperationLogPO; import com.qhzx.td.services.common.ExceptionLogService; import com.qhzx.td.services.common.OperationLogService; import com.qhzx.td.untils.HttpUtils; import com.qhzx.td.untils.TokenFactory; import lombok.RequiredArgsConstructor; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.LocalVariableTableParameterNameDiscoverer; import org.springframework.core.env.Environment; import org.springframework.stereotype.Component; import org.springframework.validation.BindingResult; import org.springframework.web.context.request.RequestAttributes; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.multipart.MultipartFile;
import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; import java.util.Objects;
@Aspect @Component @RequiredArgsConstructor(onConstructor = @__(@Autowired)) public class OperLogAspect {
private final Environment env; private final ExceptionLogService exceptionLogService; private final OperationLogService operationLogService;
@Pointcut("@annotation(com.qhzx.td.annotation.OperLog)") public void operLogPoinCut() { }
@Pointcut("execution(* com.qhzx.td.controller..*.*(..))") public void operExceptionLogPoinCut() { }
@AfterReturning(value = "operLogPoinCut()", returning = "keys") public void saveOperLog(JoinPoint joinPoint, Object keys) throws Exception { RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); HttpServletRequest request = (HttpServletRequest) requestAttributes .resolveReference(RequestAttributes.REFERENCE_REQUEST); OperationLogPO operlog = new OperationLogPO(); MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = signature.getMethod(); OperLog opLog = method.getAnnotation(OperLog.class); String operDesc = null; if (opLog !=null){ String operModul = opLog.operModul(); String operType = opLog.operType(); operDesc = opLog.operDesc(); operlog.setOperModul(operModul); operlog.setOperType(operType); operlog.setOperDesc(operDesc); } String urlStr = request.getRequestURL().toString(); String className = joinPoint.getTarget().getClass().getName(); String methodName = method.getName(); methodName = className + "." + methodName; operlog.setOperMethod(methodName);
operlog.setOperRequParam(getReqestParams(request,joinPoint));
operlog.setOperRespParam(JSON.toJSONString(keys)); if (!Objects.equals(operDesc, "登录") && !Objects.equals(operDesc, "退出登录")){ AdminUserPO userToken = TokenFactory.validateToken(request); operlog.setOperUserId(userToken.getId()); operlog.setOperUserName(userToken.getUsername()); } operlog.setOperIp(HttpUtils.getIpAddress(request)); operlog.setOperUrl(request.getRequestURI()); operlog.setOperVer(env.getProperty("platform.operVer")); operlog.setBasePath(StrUtil.removeSuffix(urlStr, URLUtil.url(urlStr).getPath())); operationLogService.save(operlog);
}
@AfterThrowing(pointcut = "operExceptionLogPoinCut()", throwing = "e") public void saveExceptionLog(JoinPoint joinPoint, Throwable e) { RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); HttpServletRequest request = (HttpServletRequest) requestAttributes .resolveReference(RequestAttributes.REFERENCE_REQUEST);
ExceptionLogPO excepLog = new ExceptionLogPO(); try { MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = signature.getMethod(); String urlStr = request.getRequestURL().toString(); String className = joinPoint.getTarget().getClass().getName(); String methodName = method.getName(); methodName = className + "." + methodName;
excepLog.setExcRequParam(getReqestParams(request,joinPoint));
excepLog.setOperMethod(methodName); excepLog.setExcName(e.getClass().getName()); excepLog.setExcMessage(stackTraceToString(e.getClass().getName(), e.getMessage(), e.getStackTrace())); AdminUserPO userToken = TokenFactory.validateToken(request); excepLog.setOperUserId(userToken.getId()); excepLog.setOperUserName(userToken.getUsername()); excepLog.setOperUrl(request.getRequestURI()); excepLog.setOperIp(HttpUtils.getIpAddress(request)); excepLog.setOperVer(env.getProperty("platform.operVer")); excepLog.setBasePath(StrUtil.removeSuffix(urlStr, URLUtil.url(urlStr).getPath())); exceptionLogService.save(excepLog); } catch (Exception e2) { e2.printStackTrace(); } }
private String getReqestParams(HttpServletRequest request, JoinPoint joinPoint) { String httpMethod = request.getMethod(); String params = ""; if ("POST".equals(httpMethod) || "PUT".equals(httpMethod) || "PATCH".equals(httpMethod)) { Object[] paramsArray = joinPoint.getArgs(); Object[] arguments = new Object[paramsArray.length]; for (int i = 0; i < paramsArray.length; i++) { if (paramsArray[i] instanceof BindingResult || paramsArray[i] instanceof ServletRequest || paramsArray[i] instanceof ServletResponse || paramsArray[i] instanceof MultipartFile) { continue; } arguments[i] = paramsArray[i]; } PropertyFilter profilter = new PropertyFilter() { @Override public boolean apply(Object o, String name, Object value) { if(value!=null && value.toString().length()>500){ return false; } return true; } }; params = JSONObject.toJSONString(arguments, profilter); } else { MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = signature.getMethod(); Object[] args = joinPoint.getArgs(); LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer(); String[] paramNames = u.getParameterNames(method); if (args != null && paramNames != null) { for (int i = 0; i < args.length; i++) { params += " " + paramNames[i] + ": " + args[i]; } } } return params; }
public Map<String, String> converMap(Map<String, String[]> paramMap) { Map<String, String> rtnMap = new HashMap<String, String>(); for(String key : paramMap.keySet()) { rtnMap.put(key, paramMap.get(key)[0]); } return rtnMap; }
public String stackTraceToString(String exceptionName, String exceptionMessage, StackTraceElement[] elements) { StringBuffer strbuff = new StringBuffer(); for (StackTraceElement stet : elements) { strbuff.append(stet + "\n"); } String message = exceptionName + ":" + exceptionMessage + "\n\t" + strbuff.toString(); return message; } }
|
9、全局异常处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| @ControllerAdvice @ResponseBody public class DefaultExceptionAdvice { private static final Logger LOGGER = LoggerFactory.getLogger(DefaultExceptionAdvice.class);
@ExceptionHandler({MybatisPlusException.class}) public Result MybatisPlusException(MybatisPlusException e) { String message = e.getMessage(); return Result.fail(401,message); }
}
|
10、VO/DTO对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| package com.qhzx.td.vo;
import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import lombok.EqualsAndHashCode;
import javax.validation.constraints.NotNull;
@Data @EqualsAndHashCode @ApiModel(description= "用户登录对象") public class LoginBodyVO {
@ApiModelProperty(value = "用户账号", required = true) @NotNull(message = "不允许为空") private String username;
@ApiModelProperty(value = "用户密码", required = true) @NotNull(message = "不允许为空") private String password;
@ApiModelProperty(value = "验证码", required = true) @NotNull(message = "不允许为空") private String code;
@ApiModelProperty(value = "唯一标识", required = true) @NotNull(message = "不允许为空") private String uuid = "";
}
|
11、使用
在对应的方法上,加上注解。如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
| package com.qhzx.td.controller.system;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.github.xiaoymin.knife4j.annotations.ApiSupport; import com.qhzx.td.annotation.OperLog; import com.qhzx.td.entity.PermissionPO; import com.qhzx.td.entity.RolePO; import com.qhzx.td.services.system.SysRoleService; import com.qhzx.td.untils.Result; import com.qhzx.td.vo.RoleVO; import com.qhzx.td.vo.SysRoleQueryVO; import io.swagger.annotations.Api; import io.swagger.annotations.ApiImplicitParam; import io.swagger.annotations.ApiOperation; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*;
import java.util.List; import java.util.Set;
@Api(tags = "角色信息接口") @ApiSupport(author = "何锟") @RestController @RequestMapping("/role") @CrossOrigin @Slf4j @RequiredArgsConstructor(onConstructor = @__(@Autowired)) public class SysRoleController {
private final SysRoleService sysRoleService;
@ApiOperation("根据id获取单个角色信息") @OperLog(operModul = "后台系统-根据id获取单个角色信息", operDesc = "根据id获取单个角色信息") @GetMapping("/byId/{id}") @ApiImplicitParam(name = "id",value = "角色id",required = true) public Result<RolePO> getRoleById(@PathVariable Long id) { return sysRoleService.findById(id); }
@ApiOperation("根据角色编码获取单个角色信息") @OperLog(operModul = "后台系统-根据角色编码获取单个角色信息", operDesc = "根据角色编码获取单个角色信息") @GetMapping("/roleCode/{roleCode}") @ApiImplicitParam(name = "roleCode",value = "角色标识",required = true) public Result<RolePO> getRoleByRoleCode(@PathVariable String roleCode) { return sysRoleService.findByRoleCode(roleCode); }
@ApiOperation("获取所有角色信息") @OperLog(operModul = "后台系统-获取所有角色信息", operDesc = "获取所有角色信息") @GetMapping() public Result<List<RolePO>> getAllRole() { return sysRoleService.findAll(); }
@ApiOperation("根据条件查询返回角色分页列表") @OperLog(operModul = "后台系统-查询返回角色分页列表", operDesc = "查询返回角色分页列表") @PostMapping("/page") public Result<IPage<RoleVO>> getSysUserPage(@RequestBody SysRoleQueryVO sysRoleQueryVO) { return sysRoleService.page(sysRoleQueryVO); }
@ApiOperation("修改/添加角色信息") @PostMapping public Result<Result<RoleVO>> saveRole(@RequestBody RoleVO role) { if (role.getId() != 0) { return Result.succeed(sysRoleService.update(role),"请求成功"); } else { return Result.succeed(sysRoleService.create(role),"请求成功"); } }
@ApiOperation("删除角色信息") @OperLog(operModul = "后台系统-删除角色", operDesc = "删除角色") @DeleteMapping @ApiImplicitParam(name = "ids",value = "删除的角色的id,可以多个",required = true) public Result<Boolean> deleteRole(@RequestBody Set<Long> ids) { return sysRoleService.delete(ids); }
@ApiOperation("获取角色权限信息") @OperLog(operModul = "后台系统-获取角色权限信息", operDesc = "获取角色权限信息") @GetMapping("{roleId}/permission") public Result<List<PermissionPO>> getPermission(@PathVariable Long roleId) { return sysRoleService.getPermission(roleId); }
@ApiOperation("保存角色权限信息") @OperLog(operModul = "后台系统-保存角色权限信息", operDesc = "保存角色权限信息") @PostMapping("{roleId}/permission") public Result<Boolean> savePermission(@PathVariable Long roleId,@RequestBody Set<Long> menus) { return sysRoleService.savePermission(roleId,menus); }
}
|
这样就完成了 正常操作日志
的记录,而异常操作日志会根据在切面类中的 @Pointcut("execution(* com.qhzx.mad.backstage.controller..*.*(..))")
配置的类路径 进行操作。