spring aop 实现系统操作日志记录存储到数据库

1.引入依赖

<!--spring切面aop依赖-->
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

在application.properties文件里加这样一条配置
spring.aop.auto=true //这个配置我的例子中没有加 也正常运行

2.yml配置

spring:
  aop:
    auto: true    #这个配置我的例子中没有加 也正常运行

3.创建实体类

import lombok.Data;
import java.io.Serializable;
import java.util.Date;

/**
 * @author Mr.Wang
 * @create 2020/10/26
 */
@Data
public class SysLog implements Serializable {

    private Long id;

    private String username; //用户名

    private String operation; //操作

    private String method; //方法名

    private String params; //参数

    private String ip; //ip地址

    private Date createDate; //操作时间

    private String account;  //账户唯一标识
}

4.使用spring 的 aop 技术切到自定义注解上,创建一个自定义注解类

/**
 * @author Mr.Wang
 * @create 2020/10/26
 */

import java.lang.annotation.*;

/**
 * 自定义注解类
 */
@Target(ElementType.METHOD) //注解放置的目标位置,METHOD是可注解在方法级别上
@Retention(RetentionPolicy.RUNTIME) //注解在哪个阶段执行
@Documented //生成文档
public @interface MyLog {
    String value() default "";
}

5.创建aop切面实现类

package team.ggbond.hotel.component;

import com.alibaba.fastjson.JSON;
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.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import team.ggbond.hotel.entity.log.SysLog;
import team.ggbond.hotel.service.MyLog;
import team.ggbond.hotel.service.impl.SysLogService;
import team.ggbond.hotel.util.HttpContextUtils;
import team.ggbond.hotel.util.IPUtils;
import team.ggbond.hotel.util.JwtTokenUtil;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.lang.reflect.Method;
import java.util.Date;

/**
 * @author Mr.Wang
 * @create 2020/10/26
 */

@Aspect
@Component
public class SysLogAspect {

    @Autowired
    private SysLogService sysLogService;

    //定义切点 @Pointcut
    //在注解的位置切入代码
    @Pointcut("@annotation(team.ggbond.hotel.service.MyLog)")
    public void logPoinCut() {
    }

    //切面 配置通知
    @AfterReturning("logPoinCut()")
    public void saveSysLog(JoinPoint joinPoint) {
        //保存日志
        SysLog sysLog = new SysLog();

        //从切面织入点处通过反射机制获取织入点处的方法
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        //获取切入点所在的方法
        Method method = signature.getMethod();

        //获取操作
        MyLog myLog = method.getAnnotation(MyLog.class);
        if (myLog != null) {
            String value = myLog.value();
            sysLog.setOperation(value);//保存获取的操作
        }

        //获取请求的类名
        String className = joinPoint.getTarget().getClass().getName();
        //获取请求的方法名
        String methodName = method.getName();
        sysLog.setMethod(className + "." + methodName);

        //请求的参数
        Object[] args = joinPoint.getArgs();
        //将参数所在的数组转换成json
        String params = JSON.toJSONString(args);
        sysLog.setParams(params);

        sysLog.setCreateDate(new Date());
        //获取用户名
        //此例子用到了ShiroUtils框架来实现获取用户名,此处还可以用session来获取登录操作名
        //例:HttpSession session=((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest().getSession();
        HttpSession session=((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest().getSession();
//        session.get

        //获取用户ip地址
        HttpServletRequest request = HttpContextUtils.getHttpServletRequest();
        sysLog.setIp(IPUtils.getIpAddr(request));

        //调用service保存SysLog实体类到数据库
        //sysLogService.save(sysLog);
        String token = request.getHeader(JwtTokenUtil.TOKEN_HEADER).replace(JwtTokenUtil.TOKEN_PREFIX, "");

        String userRole = JwtTokenUtil.getUserRole(token);
        String count = JwtTokenUtil.getUsername(token);
        String username=null;    
        if(userRole.equals("ROLE_USER")){
            username =  sysLogService.findUsernameByOpenId(count);
        }else if(userRole.equals("ROLE_MERCHANT")){
            username =  sysLogService.findUsernameByTelephone(count);
        }

        sysLog.setUsername(username);
        sysLog.setAccount(count);
        sysLogService.insertSysLog(sysLog);
    }

}

注意:此处因为之前数据库设计不合理,在JWT中获取出来的用户名不正常,所以这里写多了写判断查询

6.接下来就可以在需要监控的方法上添加 aop的自定义注解

格式为 @+自定义注解的类名 @MyLog

eg:

//例如在contoller类的方法上加注解
	 @MyLog(value = "修改客户头像") 
    @PostMapping("customeravatar")
    public Result updateCustomerAvatar(@RequestBody  Customer customer) {
        Integer integer = customerService.updateCustomerAvatar(customer.getAvatar(),customer.getOpenId());
        if (integer != 0) {
            return new Result(200, null, "修改客户头像成功");
        } else {
            return new Result(400, null, "修改客户头像失败");
        }
    }

8.Mapper

package team.ggbond.hotel.mapper;

import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;
import team.ggbond.hotel.entity.log.SysLog;

/**
 * @author Mr.Wang
 * @create 2020/10/26
 */
public interface SysLogMapper {

    /**
     * 插入日志
     * @param sysLog
     * @return
     */
    @Insert("INSERT INTO syslog (username,operation,method,params,ip,create_date,account)\n" +
            "VALUES(#{username},#{operation},#{method},#{params},#{ip},#{createDate},#{account})")
    Integer insertSysLog(SysLog sysLog);


    /**
     * 通过openId查询用户名字
     * @param openId
     * @return
     */
    @Select("SELECT c_name  from customer WHERE open_id=#{openId}")
    String  findUsernameByOpenId(String openId);


    /**
     * 通过telephone查询商家名字
     * @param telephone
     * @return
     */
    @Select("SELECT m_name  from merchant WHERE telephone=#{openId}")
    String  findUsernameByTelephone(String telephone);
}

serviceImpl只是普通调用,此处省略

9.附带工具类

  1. HttpContextUtils

import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.util.Objects;

/**
 * @author Mr.Wang
 * @create 2020/10/26
 */
public class HttpContextUtils {
    public static HttpServletRequest getHttpServletRequest() {
        return ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
    }
}

  1. IPUtils
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.net.InetAddress;
import java.net.UnknownHostException;

public class IPUtils {
    private IPUtils() {
    }

    /**
     * 获取当前网络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;
    }

    /**
     * 获得MAC地址
     * @param ip
     * @return
     */
    public static String getMACAddress(String ip){
        String str = "";
        String macAddress = "";
        try {
            Process p = Runtime.getRuntime().exec("nbtstat -A " + ip);
            InputStreamReader ir = new InputStreamReader(p.getInputStream());
            LineNumberReader input = new LineNumberReader(ir);
            for (int i = 1; i < 100; i++) {
                str = input.readLine();
                if (str != null) {
                    if (str.indexOf("MAC Address") > 1) {
                        macAddress = str.substring(str.indexOf("MAC Address") + 14, str.length());
                        break;
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace(System.out);
        }
        return macAddress;
    }

}
  1. JwtTokenUtil
package team.ggbond.hotel.util;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtParser;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.stereotype.Component;
import team.ggbond.hotel.entity.Users;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * JwtToken生成的工具类
 * JWT token的格式:header.payload.signature
 * header的格式(算法、token的类型):
 * {"alg": "HS512","type": "JWT"}
 * payload的格式(用户名、创建时间、生成时间):
 * {"sub":"wang","created":1489079981393,"exp":1489684781}
 * signature的生成算法:
 * HMACSHA512(base64UrlEncode(header) + "." +base64UrlEncode(payload),secret)
 */
@Component
public class JwtTokenUtil {
    //header和prefix
    public static final String TOKEN_HEADER = "Authorization";
    public static final String TOKEN_PREFIX = "Bearer ";

    //主题
    public static final String SUBJECT = "congge";

    //时间(一天)
    public static final long EXPIRATION = 1000 * 24 * 60 * 60;

    //密钥(signature=HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), APPSECRET_KEY))
    public static final String APPSECRET_KEY = "zhe_shi_wo_de_mi_yao";

    //权限
    private static final String ROLE_CLAIMS = "rol";

    /**
     * 生成token
     * @param username
     * @param role
     * @return
     */
    public static String createToken(String username,String role) {
        //指定签名的时候使用的签名算法,也就是header那部分,jjwt已经将这部分内容封装好了。
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
        //过期时间(一天后)
        Date expireDate=new Date(System.currentTimeMillis()+EXPIRATION);
        //创建payload的私有声明()
        Map<String,Object> map = new HashMap<>();
        map.put(ROLE_CLAIMS, role);

//        System.out.println("当前时间:"+new Date());

        String token = Jwts.builder()//这里其实就是new一个JwtBuilder,设置jwt的body
                .setClaims(map)
//                .claim("username",username)
                .setAudience(username)//aud
                .setIssuedAt(new Date())
                //.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION))
                .signWith(signatureAlgorithm, APPSECRET_KEY).compact();

        System.out.println("token:"+token);

        return token;
    }

    /**
     * 解密Token
     *
     * @param token
     * @return
     * @throws Exception
     */
    public static Claims checkJWT(String token) {
        try {
            final Claims claims = Jwts.parser()//得到DefaultJwtParser
                    .setSigningKey(APPSECRET_KEY)//设置签名的秘钥
                    .parseClaimsJws(token).getBody();//设置需要解析的jwt
            return claims;
        } catch (Exception e) {
            e.printStackTrace();
            //token 校验失败, 抛出Token验证非法异常
            return null;
        }
    }

    /**
     * 获取用户名
     * @param token
     * @return
     */
    public static String getUsername(String token){
        Claims claims = Jwts.parser().setSigningKey(APPSECRET_KEY).parseClaimsJws(token).getBody();
        return claims.get("aud").toString();
    }

    /**
     * 获取用户角色
     * @param token
     * @return
     */
    public static String getUserRole(String token){
        Claims claims = Jwts.parser().setSigningKey(APPSECRET_KEY).parseClaimsJws(token).getBody();
        return claims.get("rol").toString();
    }

    /**
     * 是否过期
     * @param token
     * @return
     */
    public static boolean isExpiration(String token){
        Claims claims = Jwts.parser().setSigningKey(APPSECRET_KEY).parseClaimsJws(token).getBody();
        return claims.getExpiration().before(new Date());
    }

    /**
     * 获取到期时间
     * @param token
     * @return
     */
    public static String getExpTime(String token){
        Claims claims = Jwts.parser().setSigningKey(APPSECRET_KEY).parseClaimsJws(token).getBody();
        return claims.getExpiration().toString();
    }

//    public static void main(String[] args) {
//        String username = "wzj";
//        String rol = "admin";
//        String token = createToken(username,rol);
//        System.out.println(token);
//
        System.out.println(JWT.require(Algorithm.HMAC256(APPSECRET_KEY)).build().verify(token).getClaims());
//
        Claims claimss = Jwts.parser()//得到DefaultJwtParser
                .setSigningKey(APPSECRET_KEY)//设置签名的秘钥
                .parseClaimsJws(token)
                .getBody()
                ;
        System.out.println(claimss);
//
        Claims claims = checkJWT(token);
        System.out.println(claims);
        System.out.println(claims.get("aud"));

        System.out.println(getUsername(token));
        System.out.println(getUserRole(token));
        System.out.println(isExpiration(token));
//
//    }

//    public static String generateJsonWebToken(Users user) {
//
        if (user.getId() == null || user.getUserName() == null || user.getFaceImage() == null) {
//        if (user.getUsername() == null) {
//            return null;
//        }
//
//        Map<String,Object> map = new HashMap<>();
//        map.put(ROLE_CLAIMS, "rol");
//
//        String token = Jwts
//                .builder()
//                .setSubject(SUBJECT)
//                .setClaims(map)
                .claim("id", user.getId())
//                .claim("name", user.getUsername())
                .claim("img", user.getFaceImage())
//                .setIssuedAt(new Date())
//                .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION))
//                .signWith(SignatureAlgorithm.HS256, APPSECRET_KEY).compact();
//        return token;
//    }
}

10.运行效果

在这里插入图片描述

©️2020 CSDN 皮肤主题: 深蓝海洋 设计师:CSDN官方博客 返回首页