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

The software you build is only as secure as the code that powers it. Learn how malicious code creeps into your software supply chain.

Apache Cassandra combines the benefits of major NoSQL databases to support data management needs not covered by traditional RDBMS vendors.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

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

Related

  • Keep Your Application Secrets Secret
  • How to Use Java to Build Single Sign-on
  • Java's Quiet Revolution: Thriving in the Serverless Kubernetes Era
  • Leverage Amazon BedRock Chat Model With Java and Spring AI

Trending

  • Simplifying Multi-LLM Integration With KubeMQ
  • Endpoint Security Controls: Designing a Secure Endpoint Architecture, Part 1
  • Navigating Change Management: A Guide for Engineers
  • Integration Isn’t a Task — It’s an Architectural Discipline
  1. DZone
  2. Software Design and Architecture
  3. Security
  4. Implementing Row-Level Security

Implementing Row-Level Security

A practical guide for multi-tenant applications for businesses that need to efficiently serve multiple clients or organizations through a single application.

By 
Humashankar Vellathur Jaganathan user avatar
Humashankar Vellathur Jaganathan
·
Nov. 21, 24 · Tutorial
Likes (3)
Comment
Save
Tweet
Share
5.1K Views

Join the DZone community and get the full member experience.

Join For Free

In today's interconnected world, businesses often need to serve multiple clients or organizations through a single application. This approach, known as multi-tenancy, brings unique challenges in data security and isolation. One powerful technique to address these challenges is Row-Level Security (RLS). Let's dive into how RLS can be implemented effectively using a real-world business case.

The Business Case: CloudHealth Medical Records System

Imagine you're developing CloudHealth, a cloud-based medical records system for multiple hospitals and clinics. Each healthcare provider needs to access only their patients' data, ensuring strict compliance with privacy regulations like HIPAA.

Understanding Row-Level Security

RLS is a database feature that restricts which rows a user can access in a database table. It's like having an invisible filter on every query, ensuring users only see data they're authorized to view.

Implementing RLS in CloudHealth

Let's walk through the process of implementing RLS in our CloudHealth system using Java, Spring Boot, and PostgreSQL.

Step 1: Database Schema Design

First, we'll create our database schema:

SQL
 
CREATE TABLE patients (

    id SERIAL PRIMARY KEY,

    name VARCHAR(100),

    dob DATE,

    medical_history TEXT,

    hospital_id INTEGER

);

CREATE TABLE hospitals (

    id SERIAL PRIMARY KEY,

    name VARCHAR(100)

);


Step 2: Enable Row-Level Security

Now, let's enable RLS on the patients table:

SQL
 
ALTER TABLE patients ENABLE ROW LEVEL SECURITY;


Step 3: Create a Policy

We'll create a policy that restricts access based on the hospital_id:

SQL
 
CREATE POLICY hospital_isolation_policy ON patients

    USING (hospital_id = current_setting('app.current_hospital_id')::INTEGER);


Step 4: Java Entity Classes

Let's create our Java entity classes:

Java
 
@Entity

@Table(name = "patients")

public class Patient {

    @Id

    @GeneratedValue(strategy = GenerationType.IDENTITY)

    private Long id;

    private String name;

    private LocalDate dob;

    private String medicalHistory;

    private Integer hospitalId;

    // Getters and setters

}

 @Entity

@Table(name = "hospitals")

public class Hospital {

    @Id

    @GeneratedValue(strategy = GenerationType.IDENTITY)

    private Long id;

    private String name;

    // Getters and setters

}


Step 5: Custom DataSource

We need a custom DataSource to set the current hospital ID for each database connection:

Java
 
public class HospitalAwareDataSource extends AbstractDataSource {

    private final DataSource targetDataSource;

    public HospitalAwareDataSource(DataSource targetDataSource) {

        this.targetDataSource = targetDataSource;

    }

 ------------------------------------------------------------------

    @Override

    public Connection getConnection() throws SQLException {

        Connection connection = targetDataSource.getConnection();

        setHospitalId(connection);

        return connection;

    }

    private void setHospitalId(Connection connection) throws SQLException {

        try (Statement stmt = connection.createStatement()) {

            Long hospitalId = HospitalContext.getCurrentHospitalId();

            stmt.execute("SET app.current_hospital_id = " + hospitalId);

        }
    }


    // Other methods...

}


Step 6: Hospital Context

We'll create a HospitalContext to store the current hospital ID:

Java
 
public class HospitalContext {

    private static final ThreadLocal<Long> currentHospitalId = new ThreadLocal<>();

    public static void setCurrentHospitalId(Long hospitalId) {

         currentHospitalId.set(hospitalId);

    }

    public static Long getCurrentHospitalId() {

        return currentHospitalId.get();

    }

    public static void clear() {

         currentHospitalId.remove();

    }

}


Step 7: Spring Security Configuration

We'll configure Spring Security to set the hospital ID based on the authenticated user:

Java
 
@Configuration

@EnableWebSecurity

public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override

    protected void configure(HttpSecurity http) throws Exception {

        http

            .authorizeRequests()

                .anyRequest().authenticated()

            .and()

            .formLogin()

            .and()

            .addFilterAfter(new HospitalIdFilter(), UsernamePasswordAuthenticationFilter.class);

    }

    // Other configurations...

}


public class HospitalIdFilter extends OncePerRequestFilter {

    @Override

    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)

            throws ServletException, IOException {

        Authentication auth = SecurityContextHolder.getContext().getAuthentication();

        if (auth != null && auth.isAuthenticated()) {

            UserDetails userDetails = (UserDetails) auth.getPrincipal();

            Long hospitalId = // Logic to get hospital ID from UserDetails

            HospitalContext.setCurrentHospitalId(hospitalId);

        }

        try {

            filterChain.doFilter(request, response);

        } finally {

            HospitalContext.clear();

        }

    }

}


Step 8: Repository and Service Layer

Now, we can create our repository and service layers as usual:

Java
 
@Repository

public interface PatientRepository extends JpaRepository<Patient, Long> {

}

@Service

public class PatientService {

    @Autowired

    private PatientRepository patientRepository;


    public List<Patient> getAllPatients() {

        return patientRepository.findAll();

    }

    // Other methods...

}


The Magic of RLS

With this setup, when a user from Hospital A logs in and tries to access patient data, they'll only see patients associated with Hospital A. The same applies to users from Hospital B, C, and so on. The beauty of RLS is that it happens at the database level, providing an additional layer of security even if there's a bug in the application code.

Performance Considerations

While RLS provides robust security, it's important to consider its impact on performance. For large datasets, you might need to optimize your queries and indexes to maintain good performance. Regular testing and monitoring are crucial to ensure your system scales well as the number of tenants and data volume grows.

The Bottom Line

Implementing RLS in a multi-tenant application like CloudHealth provides a powerful way to ensure data isolation and compliance with privacy regulations. By leveraging database-level security features and integrating them seamlessly with Spring Boot, we can create secure, scalable applications that meet the complex needs of modern businesses. 

Remember, security is an ongoing process. Regularly review and update your security measures, conduct thorough testing, and stay informed about the latest best practices and potential vulnerabilities in your chosen technologies. By following these steps and principles, you can build robust, secure multi-tenant applications that your clients can trust with their most sensitive data.

Java (programming language) Spring Boot security

Opinions expressed by DZone contributors are their own.

Related

  • Keep Your Application Secrets Secret
  • How to Use Java to Build Single Sign-on
  • Java's Quiet Revolution: Thriving in the Serverless Kubernetes Era
  • Leverage Amazon BedRock Chat Model With Java and Spring AI

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!