Loading [MathJax]/jax/output/CommonHTML/config.js
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >问答首页 >Envers: Spring项目上的错误审计表

Envers: Spring项目上的错误审计表
EN

Stack Overflow用户
提问于 2014-09-05 08:26:16
回答 1查看 1.8K关注 0票数 2

我正在将Spring Data JPA 1.6.4Hibernate 4.3.6.Final + envers一起用于Spring MVC 4.0.7 web应用程序,该应用程序使用Spring Security 3.2.5进行保护。web应用程序部署在使用JNDI数据源配置的Tomcat 7.0.52 web容器上:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<Resource 
              name="jdbc/appDB"
              auth="Container" 
              factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"
              type="javax.sql.DataSource" 
              initialSize="4"
              maxActive="8"
              maxWait="10000"
              maxIdle="8"
              minIdle="4"
              username="user"
              password="password" 
              driverClassName="com.mysql.jdbc.Driver" 
              url="jdbc:mysql://ip/schema?zeroDateTimeBehavior=convertToNull" 
              testOnBorrow="true" 
              testWhileIdle="true" 
              validationQuery="select 1"
              validationInterval="300000" />

数据库运行在MySql服务器版本5.5上,并具有InnoDB架构。

我对审计表Customers_H有一种奇怪的行为:注意到有时审计表被envers以错误的方式填充。大部分时间一切正常。

我不知道发生这种情况的原因和时间,但由于插入了如下的修订表:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
ID        ACTION TYPE        REV END        USER
23              0               256          U1
23              2               NULL        NULL
23              0               NULL         U2

奇怪的是,U1是id =6的实体的所有者(而不是id = 23的实体的所有者),而U2确实在实体ID 23上工作过。问题是修改表是不允许的,然后我有一个Hibernate断言失败。

似乎只有当envers创建了第三行时,它才会正常。但是,为什么它也创建了第一个(与动作创建)和第二个(使用操作删除)?

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
ERROR org.hibernate.AssertionFailure - HHH000099: an assertion failure occured (this may indicate a bug in Hibernate, but is more likely due to unsafe use of the session): java.lang.RuntimeException: Cannot update previous revision for entity Customer_H and id 23.

这禁止用户更新实体。

我的问题是调查这是怎么发生的!

以下是Customer域:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@SuppressWarnings("serial")
@Entity
@Audited
public class Customer extends AbstractDomain{

    @ManyToOne(optional=false)
    @JoinColumn(updatable=false, nullable=false)
    @JsonIgnore
    private Company company;

    @OneToMany(mappedBy="customer", cascade=CascadeType.REMOVE)
    private Set<Plant> plants = new HashSet<Plant>();

    @Enumerated(EnumType.STRING)
    @Column(nullable=false)
    private CustomerType customerType;

    private String code;

    // other basic fields + getter and settes
}

Company域与Customer有反向映射。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@OneToMany(mappedBy="company", cascade=CascadeType.REMOVE)
Set<Customer> customers = new HashSet<Customer>();

下面是AbstractDomain类:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@SuppressWarnings("serial")
@MappedSuperclass
@Audited
public abstract class AbstractDomain implements Auditable<String, Long>, Serializable {

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Long id;    

    @Version
    @JsonIgnore
    private int version;

    @JsonIgnore
    @Column(updatable=false)
    private String createdBy;

    @Type(type="org.jadira.usertype.dateandtime.joda.PersistentDateTime")
    @DateTimeFormat(iso=ISO.DATE_TIME)
    @JsonIgnore
    @Column(updatable=false)
    private DateTime createdDate;

    @JsonIgnore
    private String lastModifiedBy;

    @Type(type="org.jadira.usertype.dateandtime.joda.PersistentDateTime")
    @DateTimeFormat(iso=ISO.DATE_TIME)
    @JsonIgnore
    private DateTime lastModifiedDate;

    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }

    public int getVersion() {
        return version;
    }
    public void setVersion(int version) {
        this.version = version;
    }

    @Override
    public String getCreatedBy() {
        return createdBy;
    }
    @Override
    public void setCreatedBy(String createdBy) {
        this.createdBy = createdBy;
    }

    @Override
    public DateTime getCreatedDate() {
        return createdDate;
    }
    @Override
    public void setCreatedDate(DateTime createdDate) {
        this.createdDate = createdDate;
    }

    @Override
    public String getLastModifiedBy() {
        return lastModifiedBy;
    }
    @Override
    public void setLastModifiedBy(String lastModifiedBy) {
        this.lastModifiedBy = lastModifiedBy;
    }

    @Override
    public DateTime getLastModifiedDate() {
        return lastModifiedDate;
    }
    @Override
    public void setLastModifiedDate(DateTime lastModifiedDate) {
        this.lastModifiedDate = lastModifiedDate;
    }

    @Transient
    @Override
    public final boolean isNew() {
        if (id == null) {
            return true;
        } else {
            return false;
        }
    }   
}

这是CustomerService

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Service
@Repository
@Transactional(readOnly=true)
public class CustomerServiceImpl implements CustomerService{

    @Autowired
    private CustomerRepository customerRepository;

    @Override
    @PostAuthorize("@customerSecurityService.checkAuth(returnObject)")
    public Customer findById(Long id) {
        return customerRepository.findOne(id);
    }

    @Override
    @PreAuthorize("isAuthenticated()")
    @Transactional(readOnly=false)
    public Customer create(Customer entry) {
        entry.setCompany(SecurityUtils.getCustomer().getCompany());
        return customerRepository.save(entry);
    }

    @Override
    @PreAuthorize("@customerSecurityService.checkAuth(#entry)")
    @Transactional(readOnly=false)
    public Customer update(Customer entry) {
        return customerRepository.save(entry);
    }

    ....
}

这是我的CustomerRepository

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public interface CustomerRepository extends PagingAndSortingRepository<Customer, Long>,  QueryDslPredicateExecutor<Customer> {

}

在这里,我使用@PreAuthorize @PostAuthorize注释在CustomerService方法中执行安全检查的服务:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Component
@Transactional(readOnly=true)
public class CustomerSecurityService {

    Logger LOGGER = LoggerFactory.getLogger(CustomerSecurityService.class);

    @Autowired
    private CustomerRepository customerRepository;

    public boolean checkAuth(Customer customer) {
        if(customer == null) {
            LOGGER.error("customer NULL!");
            return false;
        }


        if (customer.getId()==null) {
            return true;
        }


        if (customer.getId()!=null) {
            Customer dbCustomer = customerRepository.findOne(customer.getId());

            if (dbCustomer.getCompany().getId().equals( SecurityUtils.getCustomer().getCompany().getId())){
                return true;
            }else {
                return false;
            }
        }
        return false;
    }

    public boolean checkPage(Page<Customer> pages) {
        for(Customer customer : pages.getContent()) {
            Customer dbCustomer = customerRepository.findOne(customer.getId());

            if (!dbCustomer.getCompany().getId().equals(SecurityUtils.getCustomer().getCompany().getId())){
                return false;
            }
        }
        return true;
    }
}

我的SecurityUtils课程

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class SecurityUtils {

    private SecurityUtils(){}

    private static Logger LOGGER = LoggerFactory.getLogger(SecurityUtils.class);

    public static Customer getCustomer() {
        Customer customer = null;
        if (SecurityContextHolder.getContext().getAuthentication()!=null) {
            customer = ((User)SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getCustomer();
            LOGGER.debug("Customer found: "+customer.getUserName());
        }else {
            LOGGER.debug("Customer not bound.");
        }
        return customer;        
    }


    public static boolean isUserInRole(String role) {
        for (GrantedAuthority grantedAuthority : SecurityContextHolder.getContext().getAuthentication().getAuthorities()) {
            if (grantedAuthority.getAuthority().equals(role)) {
                return true;
            }
        }
        return false;
    }
}

最后是xml配置:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:jpa="http://www.springframework.org/schema/data/jpa"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
        http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd">

    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="emf"/>
    </bean>

    <bean id="hibernateJpaVendorAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />

    <tx:annotation-driven transaction-manager="transactionManager" />

    <bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="jpaVendorAdapter" ref="hibernateJpaVendorAdapter" />

        <property name="packagesToScan" value="scan.domain"/>

        <property name="persistenceUnitName" value="persistenceUnit"/>
        <property name="jpaProperties">
            <props>
                <prop key="hibernate.dialect">${hibernate.dialect}</prop>
                <prop key="hibernate.ejb.naming_strategy">org.hibernate.cfg.ImprovedNamingStrategy</prop>
                <!--${hibernate.format_sql} -->
                <prop key="hibernate.format_sql">true</prop>
                <prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto}</prop>
                <!-- ${hibernate.show_sql} -->
                <prop key="hibernate.show_sql">false</prop> 

                <prop key="hibernate.connection.charSet">UTF-8</prop>

                <prop key="hibernate.max_fetch_depth">3</prop>
                <prop key="hibernate.jdbc.fetch_size">50</prop>
                <prop key="hibernate.jdbc.batch_size">20</prop>

                <prop key="jadira.usertype.databaseZone">jvm</prop>

                <prop key="org.hibernate.envers.audit_table_suffix">_H</prop>
                <prop key="org.hibernate.envers.revision_field_name">AUDIT_REVISION</prop>
                <prop key="org.hibernate.envers.revision_type_field_name">ACTION_TYPE</prop>
                <prop key="org.hibernate.envers.audit_strategy">org.hibernate.envers.strategy.ValidityAuditStrategy</prop>
                <prop key="org.hibernate.envers.audit_strategy_validity_end_rev_field_name">AUDIT_REVISION_END</prop>
                <prop key="org.hibernate.envers.audit_strategy_validity_store_revend_timestamp">True</prop>
                <prop key="org.hibernate.envers.audit_strategy_validity_revend_timestamp_field_name">AUDIT_REVISION_END_TS</prop>               
            </props>
        </property>
    </bean>

    <jpa:repositories base-package="scan.repository"
                      entity-manager-factory-ref="emf"
                      transaction-manager-ref="transactionManager"/>

    <jpa:auditing auditor-aware-ref="auditorAwareBean" />

    <bean id="auditorAwareBean" class="auditor.AuditorAwareBean"/>

</beans>

在这个项目中,我有大约50个域类,其中一些类带有继承SINGLE_TABLE

该应用程序现在被少数用户使用,这些用户没有同时连接。因此,我可以说,在给定的时间里,只有一个用户在使用我的应用程序。

我也不明白如何不安全地使用Session。我从不直接使用Hibernate会话。对于Spring数据存储库,我总是使用更高级别的抽象。有时,我需要扩展JpaRepository接口,以便调用saveAndFlush()或显式调用flush()。也许这就是原因?

我不能理解这种行为!如有任何建议,将不胜感激!

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2014-10-07 07:52:44

在经历了一些麻烦之后,我找到了解决办法:

mysql 5.5指南指出:

只要服务器运行,InnoDB就使用内存中的自动增量计数器.当服务器停止并重新启动时,如前面所述,InnoDB为表的第一个插入重新初始化每个表的计数器。

这对我来说是个大问题。我在用envers对实体进行审计。我得到的错误和我删除的“最后一行”一样多。

假设我开始将数据插入到空表中。假设插入10行。然后假设删除最后的8。在我的db中,我将作为结果2实体,分别具有id 1和2。在审计表中,我将拥有所有10个实体,id从1到10,而id从3到10的实体将有2个操作:创建操作和删除操作。

自动增量计数器现在设置为11。重新启动mysql服务自动增量计数器到3。因此,如果我插入一个新实体,它将使用id 3保存。但是在审计表中也有一个id = 3的实体。该实体已经标记为已创建和删除。它导致在update /delete操作期间断言失败,因为envers无法处理此不一致状态。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/25690217

复制
相关文章
Spring 项目启动错误提示 LoggingApplicationListener
上面的问题简单来说就是在你的 Spring 项目中有 2 个版本的 Spring ,你需要找到 Maven 的配置文件中确定只有一个 Spring Boot 的版本。
HoneyMoose
2022/08/29
1.4K0
Spring 项目启动错误提示 LoggingApplicationListener
Spring 项目运行提示错误 Not a managed type
https://www.ossez.com/t/spring-not-a-managed-type/14368
HoneyMoose
2023/04/01
8680
Spring 项目运行提示错误 Not a managed type
Spring Data Commons审计
Spring Data 提供了复杂的支持,以透明地跟踪谁创建或更改了实体以及更改发生的时间。要从功能中获益,您必须为实体类配置审计元数据,这些元数据可以使用注释或通过实现接口来定义。此外,必须通过注解配置或 XML 配置来进行审计以注册所需的基础架构组件。有关配置示例,请参见特定于商店的部分。
IT胶囊
2021/09/29
6710
Spring Data Commons审计
Spring Boot 项目编译时提示错误 cannot access WebApplicationInitializer 错误
因为在 Application 的代码中,我们继承了 SpringBootServletInitializer。
HoneyMoose
2020/04/13
1.5K0
Spring Boot 项目编译时提示错误 cannot access WebApplicationInitializer 错误
Spring项目启动错误处理方式
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/111273.html原文链接:https://javaforall.cn
全栈程序员站长
2022/07/05
9380
Github 上热门的 Spring Boot 项目实战推荐
最近经常被读者问到有没有 Spring Boot 实战项目可以学习,于是,我就去 Github 上找了 10 个我觉得还不错的实战项目。对于这些实战项目,有部分是比较适合 Spring Boot 刚入门的朋友学习的,还有一部分可能要求你对 Spring Boot 相关技术比较熟悉。需要的朋友可以根据个人实际情况进行选择。如果你对 Spring Boot 不太熟悉的话,可以看我最近开源的 springboot-guide:https://github.com/Snailclimb/springboot-gui
Guide哥
2020/05/08
1.3K0
Hadoop如何通过IT审计(上)?
内容: 1. 决策摘要 2. IT和企业风险环境 3. 越来越多的IT规范 4. Hadoop的职能 a. 安全 b. 灾难恢复和业务连续性 c. 资料管理:监督和法律要求 5. 额外要求 6. 关键要点 Hadoop最初并不是为企业环境所创造的,而是针对象Google, Yahoo, Facebook及Twitter等网络数据中心环境而产生的。这些公司拥有无论是架构,支持还是管理都完全不同于企业IT的网络环境
大数据文摘
2018/05/21
1.2K0
Spring 项目启动测试的时候错误:Unable to acquire JDBC Connection
出现这个问题的主要原因是我们在资源文件夹中还有一个 hibernate.properties 文件。
HoneyMoose
2022/10/05
1.5K0
Spring 项目启动测试的时候错误:Unable to acquire JDBC Connection
Spring Batch BATCH_JOB_INSTANCE 表不存在错误
java.sql.SQLSyntaxErrorException: (conn=12433) Table 'usrealestate.BATCH_JOB_INSTANCE' doesn't exist
HoneyMoose
2022/10/11
7320
Spring Batch BATCH_JOB_INSTANCE 表不存在错误
【JAVA代码审计】从零开始的Mybatis框架SQL注入审计(上)
上期分享了JDBC下的注入审计,今天开始分享mybatis框架下的SQL注入审计。
一名白帽的成长史
2022/11/11
1.3K0
【JAVA代码审计】从零开始的Mybatis框架SQL注入审计(上)
代码审计| Spring框架实例篇
Java代码审计Spring框架思路篇中,斗哥为大家讲述了如何得到Spring审计的Demo,审计源码,根据IDEA与Spring框架审计思路初步判定是否存在漏洞。
漏斗社区
2018/08/20
1.2K0
代码审计| Spring框架实例篇
DML错误日志表
前天写了篇文章《表中已存重复数据的情况,如何增加唯一性约束?》,提到了存在唯一约束前提下,重复数据的问题。
bisal
2019/01/29
1.2K0
Spring Cloud Security进行安全审计(一)
Spring Cloud Security是一个用于Spring Boot应用程序的安全框架,它提供了各种安全功能,例如身份验证、授权、密码管理和会话管理等。在现代应用程序中,安全审计是非常重要的一部分,它可以帮助开发人员识别应用程序中的潜在安全漏洞,并保证应用程序的安全性。在本文中,我们将讨论如何使用Spring Cloud Security进行安全审计,并提供一些示例。
堕落飞鸟
2023/04/14
5590
Linux系统实战项目——sudo日志审计
由于企业内部权限管理启用了sudo权限管理,但是还是有一定的风险因素,毕竟运维、开发等各个人员技术水平、操作习惯都不相同,也会因一时失误造成误操作,从而影响系统运行
民工哥
2020/09/16
1.8K0
Linkerd项目完成安全审计(pdf)
“Linkerd是Kubernetes和其他框架的服务网格。它通过提供运行时调试、可观察性、可靠性和安全性 - 所有这些都不需要对代码进行任何更改,从而使运行服务更容易、更安全。”
CNCF
2019/12/04
7630
Linkerd项目完成安全审计(pdf)
Java代码审计| Spring框架思路篇
相信小伙伴们通过Java代码审计入门篇对Java的环境和工具有了一定的了解,重点掌握了Tomcat部署使用、IDEA部署WEB项目与调试、Maven项目管理工具的使用。Java的WEB框架是Java进阶课程,当要进行Spring的漏洞分析,要有一定的Java代码知识储备。
漏斗社区
2018/07/26
2.4K0
Java代码审计| Spring框架思路篇
Spring Cloud Security进行安全审计(二)
现在,我们已经配置了Spring Boot应用程序的审计日志和安全过滤器。接下来,我们需要在代码中记录安全事件,以便后续的审计分析。
堕落飞鸟
2023/04/14
4510
Java代码审计| Spring框架知识篇
在上期的Java代码审计Spring框架思路篇中,斗哥为大家讲述了如何得到Spring审计的Demo,审计源码,根据Spring框架审计思路初步判定是否存在漏洞,剩下就是构造POC,动态调试分析修改POC。本期Java代码审计Spring框架知识篇将讲述Spring构造POC要必备的知识。
漏斗社区
2018/07/26
9860
Java代码审计| Spring框架知识篇
项目总览表
01 前言 分享一下以前做项目时用的一个表。对测试人员来说,这个表格的变种会更有意义。 02 项目总览 先上图 可以使用类似的方式来管理测试任务,有没有感觉高大上那么一点点,^_^。 03 缺陷情况
张树臣
2018/05/15
1.8K0
Spring(上)
参考:https://gitee.com/turnon/spring-tutorial
用户8126523
2023/04/12
6600

相似问题

Hibernate Envers:@子类上的审计

27

Hibernate Envers审计未经审计的实体

35

如何防止Hibernate Envers创建审计表?

11

条件Envers审计

58

使用Hibernate-Envers进行审计的表

14
添加站长 进交流群

领取专属 10元无门槛券

AI混元助手 在线答疑

扫码加入开发者社群
关注 腾讯云开发者公众号

洞察 腾讯核心技术

剖析业界实践案例

扫码关注腾讯云开发者公众号
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文