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.附带工具类
- 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();
}
}
- 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;
}
}
- 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;
// }
}