DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workkloads.

Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • Enterprise RIA With Spring 3, Flex 4 and GraniteDS
  • Spring Data: Data Auditing Using JaVers and MongoDB
  • Spring Boot - Microservice- JaxRS Jersey - HATEOAS - JerseyTest - Integration
  • Upgrade Guide To Spring Boot 3.0 for Spring Data JPA and Querydsl

Trending

  • A Guide to Developing Large Language Models Part 1: Pretraining
  • How to Build Scalable Mobile Apps With React Native: A Step-by-Step Guide
  • How to Convert Between PDF and TIFF in Java
  • *You* Can Shape Trend Reports: Join DZone's Software Supply Chain Security Research
  1. DZone
  2. Coding
  3. Java
  4. Spring Data, Spring Security and Envers integration

Spring Data, Spring Security and Envers integration

Learn about pros, cons, and basics of Spring security and data, plus Envers integration.

By 
Nicolas Fränkel user avatar
Nicolas Fränkel
DZone Core CORE ·
Aug. 20, 12 · Analysis
Likes (1)
Comment
Save
Tweet
Share
24.8K Views

Join the DZone community and get the full member experience.

Join For Free

Spring Data JPA, Spring Security and Envers are libraries that I personally enjoy working with (and I tend to think they are considered best-of-breed in their respective category). Anyway, I wanted to implement what I consider a simple use-case: entities have to be Envers-audited but the revision has to contain the identity of the user that initiated the action. Although it seem simple, I had some challenges to overcome to achieve this. This article lists them and provide a possible solution.

Software architecture

I used Spring MVC as the web framework, configured to use Spring Security. In order to ease my development, Spring Security was configured with the login/password to recognize. It's possible to connect it to a more adequate backend easily. In the same spirit, I use a datasource wrapper around a driver manager connection on a H2 memory database. This is simply changed by Spring configuration.A typical flow is handled by my Spring MVC Controller, and passed to the injected service, which manages the Spring Data JPA repository (the component that accesses the database).

Facts

Here are the facts that have proven to be a hindrance (read obstacles to overcome) during this development:

  • Spring Security 3.1.1.RELEASE (latest) uses Spring 3.0.7.RELEASE (not latest)
  • Spring Data JPA uses (wait for it) JPA 2. Thus, you have to parameterize your Spring configuration with LocalContainerEntityManagerFactoryBean, then configure the EntityManager factory bean with the implementation to use (Hibernate in our case). Some passed parameters are portable across different JPA implementations, other address Envers specifically and are thus completely non-portable.
    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="jpaDialect">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" />
        </property>
    </bean>
    <bean class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                <property name="generateDdl" value="true" />
                <property name="database" value="H2" />
                <property name="showSql" value="true" />
            </bean>
        </property>
        <property name="jpaProperties">
            <props>
                <prop key="org.hibernate.envers.auditTablePrefix"></prop>
                <prop key="org.hibernate.envers.auditTableSuffix">_HISTORY</prop>
            </props>
        </property>
    </bean>
  • Spring Data JPA does provide some auditing capabilities in the form of the Auditable interface. Auditables have createdBy, createdDate, modifiedBy and modifiedDateproperties. Unfortunately, the library doesn't store previous state of the entities: we do have to use Envers that provide this feature
  • After having fought like mad, I realized integrating Envers in Spring Data JPA was no small potatoes and stumbled upon the Spring Data Envers module, which does exactly that job. I did founf no available Maven artifacts in any repository, so I cloned the Git repo and built the current version (0.1.0.BUILD-SNAPSHOT then). It provided me with the opportunity to give a very modest contribution to the project.

On the bright side, and despite all examples I googled, the latest versions of Hibernate not only are shipped with Envers, but there's no configuration needed to register Envers listeners, thanks to a smart service provider use. You only need to provide the JAR on the classpath, and the JAR itself takes care of registration. This makes Envers integration much simpler.

How To

Beyond adding Spring Data Envers, there's little imagination to have, as it's basic Envers use.

  • Create a revision entiy that has the needed attribute (user):
    @RevisionEntity(AuditingRevisionListener.class)
    @Entity
    public class AuditedRevisionEntity extends DefaultRevisionEntity {
    
        private static final long serialVersionUID = 1L;
    
        private String user;
    
        public String getUser() {
    
            return user;
        }
    
        public void setUser(String user) {
    
            this.user = user;
        }
    }
  • Create the listener to get the identity from the Spring Secuirty context and set is on the revision entity:
    public class AuditingRevisionListener implements RevisionListener {
    
        @Override
        public void newRevision(Object revisionEntity) {
    
            AuditedRevisionEntity auditedRevisionEntity = (AuditedRevisionEntity) revisionEntity;
    
            String userName = SecurityContextHolder.getContext().getAuthentication().getName();
    
            auditedRevisionEntity.setUser(userName);
        }
    }

Presto, you're done...

Bad News

... Aren't you? The aforementioned solution works perfectly when writing to the database. The rub comes from trying to get the information once it's there, because the Spring Data API doesn't allow that (yet?). So, there are basically three options (assuming you care about retrieval: this kind of information may not be for the user to see, and you can always connect to the database to query when -if - you need to access the data):

  • Create code to retrieve the data. Obviously, you cannot use Spring Data JPA (your entity is missing the userfield, it's "created" at runtime). The best way IMHO would be to create a listener that get data on read queries for the audited entity and returns an enhanced entity. Performance as well as design considerations aren't in favor of this one.
  • Hack the API, using reflection that make it dependent on internals of Spring Data. This is as bad an option as the one before, but I did it for educational purposes:
    Revisions<Integer, Stuff> revisions = stuffRepository.findRevisions(stuff.getId());
    
    List<AuditedRevisionEntity> auditedRevisionEntities = new ArrayList<AuditedRevisionEntity>();
    
    for (Revision<Integer, Stuff> revision : revisions.getContent()) {
    
        Field field = ReflectionUtils.findField(Revision.class, "metadata");
    
        // Oh, it's ugly!
        ReflectionUtils.makeAccessible(field);
    
        @SuppressWarnings("rawtypes")
        RevisionMetadata metadata = (RevisionMetadata) ReflectionUtils.getField(field, revision);
    
        AuditedRevisionEntity auditedRevisionEntity = (AuditedRevisionEntity) metadata.getDelegate();
    
        // Do what your want with auditedRevisionEntity...
    }
  • Last but not least, you can contribute to the code of Spring Data. Granted, you'll need knowledge of the API and the internals, skills and time, but it's worth it :-)

Conclusion

Integrating heterogeneous libraries is hardly a walkover. In the case of Spring Data JPA and Envers, it's as easy as pie thanks to the Spring Data Envers library. However, if you need to make your audit data accessible, you need to integrate further.

The sources for this article are here, in Eclipse/Maven format.

From http://blog.frankel.ch/spring-data-spring-security-and-envers-integration

To go further:

  • The Spring Data Envers project on Github
  • Hibernate Envers site
  • Spring Data JPA site
  • Finally, Spring Security
Spring Framework Spring Data Spring Security Data (computing) Integration

Opinions expressed by DZone contributors are their own.

Related

  • Enterprise RIA With Spring 3, Flex 4 and GraniteDS
  • Spring Data: Data Auditing Using JaVers and MongoDB
  • Spring Boot - Microservice- JaxRS Jersey - HATEOAS - JerseyTest - Integration
  • Upgrade Guide To Spring Boot 3.0 for Spring Data JPA and Querydsl

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!