微服务中审核日志(用户活动)的最佳实践是什么?

时间:2018-12-27 04:10:13

标签: spring mongodb microservices

在我们的微服务架构中,我们正在将用户活动记录到mongo数据库表中?有什么好的方法来存储和检索审核日志吗?

1 个答案:

答案 0 :(得分:0)

通过使用DAO模式将AuditLogging存储到Mongo数据库中,您可以想到与以下类似的解决方案。

@Entity
@Table(name = "AuditLogging")

public class AuditLogging implements Serializable {
    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "auditid", updatable = false, nullable = false)
    private Long auditId;

    @Column(name = "event_type", length = 100)
    @Enumerated(EnumType.STRING)
    private AuditingEvent event;

    @Column(name = "event_creator", length = 100)
    @Enumerated(EnumType.STRING)
    private EventCreator eventCreator;

    @Column(name = "adminid", length = 20)
    private String adminId;

    @Column(name = "userid", length = 20)
    private String userId;

    @Column(name = "event_date")
    private Date eventDate;

}

public class Constants {

    public static final String EVENT_TYPE = "eventType";
    public static final String EVENT_CREATOR = "eventCreator";
    public static final String NEW_EMAIL_ID = "newEmailId";
    public static final String OLD_EMAIL_ID = "oldEmailId";
}
public enum AuditEvent {
    USER_REGISTRATION,
    USER_LOGIN,
    USER_LOGIN_FAIL,
    USER_ACCOUNT_LOCK,    
    USER_LOGOFF,
    USER_PASSWORD_CHANGE,
    USER_PASSWORD_CHANGE_FAIL,
    USER_FORGOT_PASSWORD,
    USER_FORGOT_PASSWORD_FAIL,
    ADMIN_LOGIN

    }

public enum EventCreator {
    ADMIN_FOR_SELF,
    USER_FOR_SELF,
    ADMIN_FOR_USER
}


public interface AuditingDao {
    /**
     * Stores the event into the DB/Mongo or Whatever
     * 
     * @param auditLogging
     * @return Boolean status
     */
    Boolean createAuditLog(final AuditLogging auditLogging);   

     /* Returns the log occurrence of a specific event
     * 
     * @param event
     * @return List of logged events by type
     */
    List<AuditLogging> getLogsForEvent(final AuditingEvent event);


}


public interface AuditingService {
    /**
     * Creates an Audit entry in the AuditLogging table using the
     * DAO layer
     * 
     * @param auditingEvent
     * @param eventCreator
     * @param userId
     * @param adminId     * 
     * @return {@link Boolean.TRUE} for success and {@link Boolean.FALSE} for
     *         failure
     */
    Boolean createUserAuditEvent(final AuditEvent auditEvent,
            final EventCreator eventCreator, final String userId, final String adminId,
            final String newEmailId,final String oldEmailId);
/**
    *
     * Returns all event for a user/admin based on the id
     * 
     * @param id
     * @return List of logged events for an id
     */
    List<AuditLogging> fetchLoggedEventsById(final String id);

    /***
     * Returns all event based on event type
     * 
     * @param eventName
     * @return List of logged events for an event
     */
    List<AuditLogging> fetchLoggedEventsByEventName(final String eventName);
}

@Service("auditingService")
public class AuditServiceImpl implements AuditingService {

    @Autowired
    private AuditingDao auditingDao;


    private static Logger log = LogManager.getLogger();

    @Override
    public Boolean createUserAuditingEvent(AuditEvent auditEvent,
            EventCreator eventCreator, String userId, String adminId,
            String newEmailId,String oldEmailId) {

        AuditLogging auditLogging = new AuditLogging();
        auditLogging.setEvent(auditingEvent);
        auditLogging.setEventCreator(eventCreator);
        auditLogging.setUserId(userId);
        auditLogging.setAdminId(adminId);
        auditLogging.setEventDate(new Date());

        return Boolean.TRUE;
    }

    @Override
    public List<AuditLogging> fetchLoggedEventsByEventName(
            final String eventName) {

        AuditEvent event = null;
        try {
            event = AuditingEvent.valueOf(eventName);
        } catch (Exception e) {
            log.error(e);
            return Collections.emptyList();
        }
        return auditingDao.getLogsForEvent(event);
    }

    public void setAuditingDao(AuditingDao auditingDao) {
        this.auditingDao = auditingDao;
    }


}

通过指向适当的控制器方法来触发事件,编写方面对于此类情况总是有利的。

@Aspect
@Component("auditingAspect")
public class AuditingAspect {

    @Autowired
    AuditingService auditingService;
     /* The below controllers you can think of your microservice endpoints 
    */
    @Pointcut("execution(* com.controller.RegistrationController.authenticateUser(..)) ||execution(* com.controller.RegistrationController.changeUserPassword(..)) || execution(* com.controller.RegistrationController.resetPassword(..)) ||execution(* com.controller.UpdateFunctionalityController.updateCustomerDetails(..))")
    public void aroundPointCut() {}

    @Around("aroundPointCut()")
    public Object afterMethodInControllerClass(ProceedingJoinPoint joinPoint)
            throws Throwable {
        joinPoint.getSignature().getName();
        joinPoint.getArgs();
        // auditingService
        Object result = joinPoint.proceed();
        ResponseEntity entity = (ResponseEntity) result;
        HttpServletRequest request =
                ((ServletRequestAttributes) RequestContextHolder
                        .getRequestAttributes()).getRequest();
        if(!((request.getAttribute(Constants.EVENT_TYPE).toString()).equalsIgnoreCase(AuditEvent.USER_LOGIN.toString()) || (((request.getAttribute(Constants.EVENT_TYPE).toString()).equalsIgnoreCase(AuditEvent.ADMIN_LOGIN.toString()))))){
        auditingService.createUserAuditEvent(
                (AuditingEvent) request.getAttribute(Constants.EVENT_TYPE),
                (EventCreator) request.getAttribute(Constants.EVENT_CREATOR),
                (request.getAttribute(Constants.USER_ID)!= null ? request.getAttribute(Constants.USER_ID).toString():""), null,
                (request.getAttribute(Constants.NEW_EMAIL_ID) == null ? ""
                        : request.getAttribute(Constants.NEW_EMAIL_ID).toString()),
                (request.getAttribute(Constants.OLD_EMAIL_ID) == null ? ""
                        : request.getAttribute(Constants.OLD_EMAIL_ID).toString()));
        }
        return entity;
    }
}

从REST控制器中,Aspect找到相应的事件时将被触发。

@RestController   
public class RegistrationController {
@RequestMapping(path = "/authenticateUser", method = RequestMethod.POST,
            produces = MediaType.APPLICATION_JSON_VALUE)
    /* This method call triggers the aspect */
    @ResponseBody
    public ResponseEntity<String> authenticateUser(HttpServletRequest request, @RequestBody User user)
            throws Exception {      

        request.setAttribute(Constants.EVENT_TYPE, AuditingEvent.USER_LOGIN);
        request.setAttribute(Constants.EVENT_CREATOR, EventCreator.USER_FOR_SELF);
        request.setAttribute(Constants.USER_ID, user.getUserId());

        ResponseEntity<String> responseEntity = null;       

        try {

           // Logic for authentication goes here
            responseEntity = new ResponseEntity<>(respData, HttpStatus.OK);
        } catch (Exception e) {
            request.setAttribute(Constants.EVENT_TYPE, AuditEvent.USER_LOGIN_FAIL);            
            responseEntity = new ResponseEntity<>(respData, HttpStatus.INTERNAL_SERVER_ERROR);            
        }

        return responseEntity;
    }
}

我希望这个答案有意义,并且您也可以为Mongo实现类似的功能。

干杯!