//1.注解类
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD) //注解放置的目标位置,METHOD是可注解在方法级别上
@Retention(RetentionPolicy.RUNTIME) //注解在哪个阶段执行
@Documented
public @interface OperLog {
String operModul()
default ""; // 操作模块
String operType()
default ""; // 操作类型
String operDesc()
default ""; // 操作说明
}
//2.aop切面 记录日志代码
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
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.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
@Aspect
@Component
public class OperLogAspect {
@Autowired
private OperateRecordService operateRecordService;
@Autowired
SecurityService securityService;
/**
* 设置操作日志切入点 记录操作日志 在注解的位置切入代码
*/
@Pointcut("@annotation(com.hengtiansoft.common.aop.OperLog)")
public void operLogPoinCut() {
}
/**
* 设置操作异常切入点记录异常日志 扫描所有controller包下操作
*/
@Pointcut("execution(* com.hengtiansoft.zjzj.controller..*.*(..))")
public void operExceptionLogPoinCut() {
}
/**
* 正常返回通知,拦截用户操作日志,连接点正常执行完成后执行, 如果连接点抛出异常,则不会执行
*
* @param joinPoint 切入点
* @param keys 返回结果
*/
@AfterReturning(value = "operLogPoinCut()", returning = "keys")
public void saveOperLog(JoinPoint joinPoint, Object keys) {
// 获取RequestAttributes
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
// 从获取RequestAttributes中获取HttpServletRequest的信息
HttpServletRequest request = (HttpServletRequest) requestAttributes
.resolveReference(RequestAttributes.REFERENCE_REQUEST);
OperateRecord operlog = new OperateRecord();
try {
// 从切面织入点处通过反射机制获取织入点处的方法
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
// 获取切入点所在的方法
Method method = signature.getMethod();
// 获取操作
OperLog opLog = method.getAnnotation(OperLog.class);
if (opLog != null) {
String operModul = opLog.operModul();
String operType = opLog.operType();
String operDesc = opLog.operDesc();
operlog.setModul(operModul); // 操作模块
operlog.setOperateType(operType); // 操作类型
operlog.setMemo(operDesc); // 操作描述
}
// 获取请求的类名
String className = joinPoint.getTarget().getClass().getName();
// 获取请求的方法名
String methodName = method.getName();
methodName = className + "." + methodName;
operlog.setOperMethod(methodName); // 请求方法
// 请求的参数
Map<String, String> rtnMap = converMap(request.getParameterMap());
// 将参数所在的数组转换成json
String params = JSON.toJSONString(rtnMap);
operlog.setOperRequParam(params); // 请求参数
operlog.setOperRespParam(JSON.toJSONString(keys)); // 返回结果
operlog.setCreator(String.valueOf(securityService.getCurrentUser().getId())); // 请求用户ID
operlog.setCreatorName(String.valueOf(securityService.getCurrentUser().getName())); // 请求用户名称
operlog.setOperateIp(IpUtil.getIpAddr(request)); // 请求IP
operlog.setOperUri(request.getRequestURI()); // 请求URI
operlog.setCreateDate(new Date()); // 创建时间
operateRecordService.insertOperLog(operlog);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 转换request 请求参数
*
* @param paramMap request获取的参数数组
*/
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;
}
}
//3. 访问者ip获取工具类
import javax.servlet.http.HttpServletRequest;
import java.net.InetAddress;
import java.net.UnknownHostException;
public class IpUtil {
/**
* 获取当前网络ip
* @param request
* @return
*/
public static String getIpAddr(HttpServletRequest request){
String ipAddress = request.getHeader("x-forwarded-for");
if(ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("Proxy-Client-IP");
}
if(ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("WL-Proxy-Client-IP");
}
if(ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getRemoteAddr();
if(ipAddress.equals("127.0.0.1") || ipAddress.equals("0:0:0:0:0:0:0:1")){
//根据网卡取本机配置的IP
InetAddress inet=null;
try {
inet = InetAddress.getLocalHost();
} catch (UnknownHostException e) {
e.printStackTrace();
}
ipAddress= inet.getHostAddress();
}
}
//对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
if(ipAddress!=null && ipAddress.length()>15){ //"***.***.***.***".length() = 15
if(ipAddress.indexOf(",")>0){
ipAddress = ipAddress.substring(0,ipAddress.indexOf(","));
}
}
return ipAddress;
}
}
//4.如何使用
在controller中想要记录操作行为的接口上加上下面这个注解
@OperLog(operModul = "业务", operType = "送审", operDesc = "业务申请列表送审操作")
数据库表结构:
//加密工具类
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.Security;
import java.util.Base64;
@Slf4j
public final class EncryptUtil {
private static final Logger LOGGER = LoggerFactory.getLogger(EncryptUtil.class);
/**
* 加密算法
*/
private static final String ENCRY_ALGORITHM = "AES";
/**
* 加密算法/加密模式/填充类型
* 本例采用AES加密,ECB加密模式,PKCS7Padding填充
*/
private static final String CIPHER_MODE = "AES/ECB/PKCS7Padding";
/**
* 设置加密字符集
* 本例采用 UTF-8 字符集
*/
private static final String CHARACTER = "UTF-8";
/**
* 设置加密密码处理长度。
* 不足此长度补0;
*/
private static final int PWD_SIZE = 16;
private static final String PASSWORD = "hengtiansoft@com";
private EncryptUtil() {
}
/**
* 密码处理方法
* 如果加解密出问题,
* 请先查看本方法,排除密码长度不足补"0",导致密码不一致
*
* @param password 待处理的密码
* @return
* @throws UnsupportedEncodingException
*/
private static byte[] pwdHandler(String password) throws UnsupportedEncodingException {
byte[] data = null;
StringBuffer sb = new StringBuffer(PWD_SIZE);
if (password != null) {
sb.append(password);
}
while (sb.length() < PWD_SIZE) {
sb.append("0");
}
if (sb.length() > PWD_SIZE) {
sb.setLength(PWD_SIZE);
}
data = sb.toString().getBytes(CHARACTER);
return data;
}
//======================>原始加密<======================
/**
* 原始加密
*
* @param clearTextBytes 明文字节数组,待加密的字节数组
* @param pwdBytes 加密密码字节数组
* @return 返回加密后的密文字节数组,加密错误返回null
*/
private static byte[] encrypt(byte[] clearTextBytes, byte[] pwdBytes) {
try {
// 1 获取加密密钥
SecretKeySpec keySpec = new SecretKeySpec(pwdBytes, ENCRY_ALGORITHM);
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
// 2 获取Cipher实例
Cipher cipher = Cipher.getInstance(CIPHER_MODE);
// 查看数据块位数 默认为16(byte) * 8 =128 bit
// 3 初始化Cipher实例。设置执行模式以及加密密钥
cipher.init(Cipher.ENCRYPT_MODE, keySpec);
// 4 执行
byte[] cipherTextBytes = cipher.doFinal(clearTextBytes);
// 5 返回密文字符集
return cipherTextBytes;
} catch (Exception e) {
LOGGER.error(e.getMessage());
}
return null;
}
/**
* 原始解密
*
* @param cipherTextBytes 密文字节数组,待解密的字节数组
* @param pwdBytes 解密密码字节数组
* @return 返回解密后的明文字节数组,解密错误返回null
*/
private static byte[] decrypt(byte[] cipherTextBytes, byte[] pwdBytes) {
try {
// 1 获取解密密钥
SecretKeySpec keySpec = new SecretKeySpec(pwdBytes, ENCRY_ALGORITHM);
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
// 2 获取Cipher实例
Cipher cipher = Cipher.getInstance(CIPHER_MODE);
// 查看数据块位数 默认为16(byte) * 8 =128 bit
// 3 初始化Cipher实例。设置执行模式以及加密密钥
cipher.init(Cipher.DECRYPT_MODE, keySpec);
// 4 执行
byte[] clearTextBytes = cipher.doFinal(cipherTextBytes);
// 5 返回明文字符集
return clearTextBytes;
} catch (Exception e) {
LOGGER.error(e.getMessage());
}
// 解密错误 返回null
return null;
}
//======================>BASE64<======================
/**
* BASE64加密
*
* @param clearText 明文,待加密的内容
* @param password 密码,加密的密码
* @return 返回密文,加密后得到的内容。加密错误返回null
*/
private static String encryptBase64(String clearText, String password) {
try {
// 1 获取加密密文字节数组
byte[] cipherTextBytes = encrypt(clearText.getBytes(CHARACTER), pwdHandler(password));
// 2 对密文字节数组进行BASE64 encoder 得到 BASE6输出的密文
Base64.Encoder base64Encoder = Base64.getEncoder();
String cipherText = base64Encoder.encodeToString(cipherTextBytes);
// 3 返回BASE64输出的密文
return cipherText;
} catch (Exception e) {
LOGGER.error(e.getMessage());
}
// 加密错误 返回null
return null;
}
public static String encryptBase64(String clearText) {
return encryptBase64(clearText, PASSWORD);
}
/**
* BASE64解密
*
* @param cipherText 密文,带解密的内容
* @param password 密码,解密的密码
* @return 返回明文,解密后得到的内容。解密错误返回null
*/
private static String decryptBase64(String cipherText, String password) {
try {
// 1 对 BASE64输出的密文进行BASE64 decodebuffer 得到密文字节数组
Base64.Decoder base64Decoder = Base64.getDecoder();
byte[] cipherTextBytes = base64Decoder.decode(cipherText);
// 2 对密文字节数组进行解密 得到明文字节数组
byte[] clearTextBytes = decrypt(cipherTextBytes, pwdHandler(password));
// 3 根据 CHARACTER 转码,返回明文字符串
return new String(clearTextBytes, CHARACTER);
} catch (Exception e) {
LOGGER.error(e.getMessage());
}
// 解密错误返回null
return null;
}
public static String decryptBase64(String cipherText) {
return decryptBase64(cipherText, PASSWORD);
}
/**
* 利用MD5进行加密
*
* @param str 待加密的字符串
* @return 加密后的字符串
* @throws NoSuchAlgorithmException 没有这种产生消息摘要的算法
* @throws UnsupportedEncodingException
*/
public static String encryptMd5(String str) {
String newstr = null;
try {
//确定计算方法
MessageDigest md5 = MessageDigest.getInstance("MD5");
Base64.Encoder base64Encoder = Base64.getEncoder();
//加密后的字符串
newstr = base64Encoder.encodeToString(md5.digest(str.getBytes(StandardCharsets.UTF_8)));
} catch (NoSuchAlgorithmException e) {
log.error("MD5加密失败,{}", e.getMessage());
}
return newstr;
}
}