自定义注释就是一个标记,一个信息收集器,如果配合SPRING的AOP使用,可以记录用户的操作行为。
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 新增的用法:@Audit(behaviour="新增了专题",
* value="#{args[0].colSubject}")
*
* 修改的用法:@Audit(behaviour="修改了专题", id="#{args[0].colSubject.id}",
* className="com.paul.program.colsubject.valueobject.ColSubject",
* value="#{args[0].colSubject}")
*
* 删除的用法:@Audit(behaviour="删除了专题", id="#{args[0].colSubject.id}"
* className="com.paul.program.colsubject.valueobject.ColSubject")
*
* @author PAUL
*
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Audit {
String id() default "";
String className() default "";
String collectionName() default "";
String value() default "";
String behaviour();
}
值对象
AuditData.java
package com.paul.common.audit;
import java.io.Serializable;
import java.util.Date;
import org.codehaus.jackson.annotate.JsonProperty;
import org.codehaus.jackson.map.annotate.JsonSerialize;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.Field;
import com.paul.common.util.jackson.CustomJsonDateSerializer;
@Document(collection = "auditdata")
public class AuditData implements Serializable{
private static final long serialVersionUID = -4011585863836336249L;
@Id
private String id;
@CreatedBy
@Field("userid")
@JsonProperty("userid")
private String userId;
@CreatedDate
@Field("createdate")
@JsonProperty("createdate")
@JsonSerialize(using = CustomJsonDateSerializer.class)
private Date createDate;
private String behaviour;
@Field("newvalue")
@JsonProperty("newvalue")
private String newValue;
@Field("oldvalue")
@JsonProperty("oldvalue")
private String oldValue;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public Date getCreateDate() {
return createDate;
}
public void setCreateDate(Date createDate) {
this.createDate = createDate;
}
public String getBehaviour() {
return behaviour;
}
public void setBehaviour(String behaviour) {
this.behaviour = behaviour;
}
public String getNewValue() {
return newValue;
}
public void setNewValue(String newValue) {
this.newValue = newValue;
}
public String getOldValue() {
return oldValue;
}
public void setOldValue(String oldValue) {
this.oldValue = oldValue;
}
public String toString() {
return "AuditData [id=" + id + ", userId=" + userId + ", createDate="
+ createDate + ", behaviour=" + behaviour + ", newValue="
+ newValue + ", oldValue=" + oldValue + "]";
}
}
RootObject.java
package com.paul.common.audit;
public class RootObject {
private final Object[] args;
private final Object invokedObject;
private final Object returned;
private final Throwable throwned;
public RootObject(Object invokedObject, Object[] args, Object returned, Throwable throwned) {
super();
this.invokedObject = invokedObject;
this.args = args;
this.returned = returned;
this.throwned = throwned;
}
public Object[] getArgs() {
return args;
}
public Object getInvokedObject() {
return invokedObject;
}
public Object getReturned() {
return returned;
}
public Throwable getThrowned() {
return throwned;
}
}
TemplateParserContext.java
package com.paul.common.audit;
import org.springframework.expression.ParserContext;
public class TemplateParserContext implements ParserContext {
public String getExpressionPrefix() {
return "#{";
}
public String getExpressionSuffix() {
return "}";
}
public boolean isTemplate() {
return true;
}
}
获取用户ID
package com.paul.common.audit.aware;
import javax.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.AuditorAware;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import com.paul.common.constant.Constants;
import com.paul.program.account.valueobject.Account;
@Component
public class MyAppAuditor implements AuditorAware<String> {
private static Logger logger = LoggerFactory.getLogger(MyAppAuditor.class);
// get your user name here
public String getCurrentAuditor() {
String result = "N/A";
try {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder
.getRequestAttributes()).getRequest();
Account account = (Account)request.getSession().getAttribute(Constants.USER_INFO);
result = account.getLoginName();
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
return result;
}
}
切面
package com.paul.common.audit.interceptor;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.ParseException;
import org.springframework.expression.ParserContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.stereotype.Component;
import com.paul.common.audit.AuditData;
import com.paul.common.audit.AuditDataRequest;
import com.paul.common.audit.RootObject;
import com.paul.common.audit.TemplateParserContext;
import com.paul.common.audit.annotation.Audit;
import com.paul.common.audit.service.AuditDataService;
import com.paul.common.util.StringUtils;
@Component
@Aspect
public class AuditAspect {
private static Logger logger = LoggerFactory.getLogger(AuditAspect.class);
@Autowired
private AuditDataService auditDataService;
@Autowired
private MongoTemplate mongoTemplate;
private SimpleDateFormat dateFormatPrototype = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
private Map<String, Expression> expressionCache = new ConcurrentHashMap<String, Expression>();
private ExpressionParser expressionParser = new SpelExpressionParser();
private ParserContext parserContext = new TemplateParserContext();
protected static void appendThrowableCauses(Throwable throwable, String separator, StringBuilder toAppendTo) {
List<Throwable> alreadyAppendedThrowables = new ArrayList<Throwable>();
while (throwable != null) {
// append
toAppendTo.append(throwable.toString());
alreadyAppendedThrowables.add(throwable);
// cause
Throwable cause = throwable.getCause();
if (cause == null || alreadyAppendedThrowables.contains(cause)) {
break;
} else {
throwable = cause;
toAppendTo.append(separator);
}
}
}
private String getValueByEl(String template, Object invokedObject, Object[] args, Object returned, Throwable throwned)
{
String result = "N/A";
if(StringUtils.isBlank(template))
return result;
try {
Expression expression = expressionCache.get(template);
if (expression == null) {
expression = expressionParser.parseExpression(template, parserContext);
expressionCache.put(template, expression);
}
Object evaluatedMessage = expression.getValue(new RootObject(invokedObject, args, returned, throwned), Object.class);
result = evaluatedMessage.toString();
} catch (ParseException e) {
logger.error(e.getMessage(), e);
} catch (EvaluationException e) {
logger.error(e.getMessage(), e);
}
return result;
}
protected AuditData buildAuditData(Audit auditAnnotation, Object invokedObject, Object[] args, Object returned, Throwable throwned, long durationInNanos) {
AuditData auditData = new AuditData();
auditData.setBehaviour(auditAnnotation.behaviour());
String id = this.getValueByEl(auditAnnotation.id(), invokedObject, args, returned, throwned);
auditData.setOldValue(this.getOldValueToString(id, auditAnnotation.className()));
try {
String newValue = this.getValueByEl(auditAnnotation.value(), invokedObject, args, returned, throwned);
auditData.setNewValue(newValue);
StringBuilder msg = new StringBuilder();
SimpleDateFormat simpleDateFormat = (SimpleDateFormat) dateFormatPrototype.clone();
msg.append(simpleDateFormat.format(new Date()));
// auditData.setCreateDate(simpleDateFormat.format(new Date()));
msg.append(" ").append(newValue);
if (throwned != null) {
msg.append(" threw '");
appendThrowableCauses(throwned, ", ", msg);
msg.append("'");
}
msg.append(" by ");
String user = this.getUser();
if (user == null) {
user = "anonymous";
}
msg.append(" in ") .append(TimeUnit.MILLISECONDS.convert(durationInNanos, TimeUnit.NANOSECONDS)).append(" ms");
return auditData;
} catch (Exception e) {
/*StringBuilder msg = new StringBuilder("Exception evaluating template '" + template + "': ");
appendThrowableCauses(e, ", ", msg);*/
return auditData;
}
}
private String getOldValueToString(/*ProceedingJoinPoint proceedingJoinPoint,*/ String id, String className)
{
String result = "N/A";
/*String id = "5385be613d2a47eec07c53d4";
try {
MethodSignature methodSig = (MethodSignature) proceedingJoinPoint.getSignature();
Object target = proceedingJoinPoint.getTarget();
Object newValue = proceedingJoinPoint.getArgs()[0];
String findMethodName = "findOne";
Object oldValue=null;
Method findMethod = target.getClass().getDeclaredMethod(findMethodName, (Class<?>[])null);
oldValue = findMethod.invoke(target, (Object[]) null);
} catch (Exception e) {
logger.error(e.getMessage(), e);
}*/
if(StringUtils.isBlank(id) || StringUtils.isBlank(className))
return result;
try {
Object object = mongoTemplate.findById(id, Class.forName(className));
result = ToStringBuilder.reflectionToString(object, ToStringStyle.SHORT_PREFIX_STYLE);
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
return result;
}
// @Around("execution(* com.paul..**.repository..*(..))")
@Around("@annotation(auditAnnotation)")
public Object aroundAdvice(ProceedingJoinPoint proceedingJoinPoint, Audit auditAnnotation) throws Throwable {
boolean ok = false;
Object o = null;
AuditData auditData = null;
try
{
auditData = buildAuditData(auditAnnotation, proceedingJoinPoint.getThis(),
proceedingJoinPoint.getArgs(),
o, null, 10);
o = proceedingJoinPoint.proceed();
ok = true;
return o;
}
finally
{
if (ok)
{
// String value = (MessageFormat.format(auditAnnotation.value(), proceedingJoinPoint.getArgs()));
try
{
AuditDataRequest auditDataRequest = new AuditDataRequest();
auditDataRequest.setAuditData(auditData);
auditDataService.addAuditData(auditDataRequest);
logger.info(auditData + "");
}
catch (Exception e)
{
logger.error(e.getMessage(), e);
}
}
}
}
private String getUser()
{
return "Paul";
}
}
保存至数据库
package com.paul.common.audit.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Service;
import com.paul.common.audit.AuditData;
import com.paul.common.audit.AuditDataRequest;
import com.paul.common.audit.annotation.Audit;
import com.paul.common.audit.repository.AuditDataRepository;
import com.paul.program.colsubject.valueobject.ColSubjectRequest;
@Service
public class AuditDataService {
@Autowired
private AuditDataRepository auditDataRepository;
// @Audited(message = "save(#{args[0].name}, #{args[0].email}): #{returned?.id}")
@Audit(behaviour="修改了审计", id="#{args[0].colSubject.id}",
className="com.paul.program.colsubject.valueobject.ColSubject",
value="#{args[0].colSubject}")
public void saveObject(ColSubjectRequest colSubjectRequest)
{
}
@Audit(behaviour="删除了专题", id="#{args[0]}",
className="com.paul.program.subject.valueobject.Subject")
public void delete(String id) {
}
@Audit(behaviour="新增了专题", value="#{args[0].colSubject}")
public void add(ColSubjectRequest colSubjectRequest) {
}
public AuditData addAuditData(AuditDataRequest auditDataRequest)
{
return auditDataRepository.save(auditDataRequest.getAuditData());
}
public Page<AuditData> findAll(AuditDataRequest auditDataRequest)
{
Page<AuditData> page = auditDataRepository.findAll(auditDataRequest.getPageable());
return page;
}
}
DAO
package com.paul.common.audit.repository;
import org.springframework.data.repository.PagingAndSortingRepository;
import com.paul.common.audit.AuditData;
/**
* 审计
*
*/
public interface AuditDataRepository extends PagingAndSortingRepository<AuditData, String>{
}