Spring Security – Individual Realm Access Control


Spring Security โ€“ Ensuring Each Customer Accesses Only Their Realm

Q: In a bank online app, every individual customer has their own sole realm. How can this requirement be satisfied with Spring’s role-based security mechanism?

Spring’s role-based mechanism controls access to functionality like user vs. admin, but doesn’t inherently control access to specific data objects (e.g. ensuring a user can only view their account).

To enforce this “realm” behavior, you need object-level security in addition to role checks.

โœ… Approach Overview

  • Use roles for functional access (`ROLE_USER`, `ROLE_ADMIN`).
  • Manually or declaratively enforce ownership checks on object access.
  • Leverage `@PreAuthorize` with a custom service.

// Example manual ownership check
if (!account.getOwnerUsername().equals(principal.getName())) {
    throw new AccessDeniedException("Unauthorized");
}
      

// Using @PreAuthorize with SpEL
@PreAuthorize("@accountSecurity.hasAccess(#accountId, principal.username)")
public Account getAccount(Long accountId) { ... }
      

This approach satisfies the individual realm isolation requirement.

Q: Can I see a full working Spring Boot example?

โœ… Full Spring Boot Example: Per-Customer Access Control

๐Ÿ“ Project Structure

bank-app/
โ”œโ”€โ”€ Account.java
โ”œโ”€โ”€ AccountController.java
โ”œโ”€โ”€ AccountService.java
โ”œโ”€โ”€ AccountSecurity.java
โ”œโ”€โ”€ SecurityConfig.java
โ””โ”€โ”€ BankAppApplication.java

1. Account.java

public class Account {
    private Long id;
    private String ownerUsername;
    private String details;

    public Account(Long id, String ownerUsername, String details) {
        this.id = id;
        this.ownerUsername = ownerUsername;
        this.details = details;
    }

    public Long getId() { return id; }
    public String getOwnerUsername() { return ownerUsername; }
    public String getDetails() { return details; }
}

2. AccountService.java

@Service
public class AccountService {
    private static final Map<Long, Account> accounts = new HashMap<>();

    static {
        accounts.put(1L, new Account(1L, "alice", "Alice's savings"));
        accounts.put(2L, new Account(2L, "bob", "Bob's checking"));
    }

    public Account findById(Long id) {
        return accounts.get(id);
    }
}

3. SecurityConfig.java

@Configuration
@EnableMethodSecurity
public class SecurityConfig {

    @Bean
    public UserDetailsService userDetailsService() {
        UserDetails alice = User.withUsername("alice")
                .password(passwordEncoder().encode("123"))
                .roles("USER")
                .build();
        UserDetails bob = User.withUsername("bob")
                .password(passwordEncoder().encode("456"))
                .roles("USER")
                .build();
        return new InMemoryUserDetailsManager(alice, bob);
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .csrf().disable()
            .authorizeHttpRequests(auth -> auth
                .anyRequest().authenticated())
            .formLogin();
        return http.build();
    }
}

4. AccountSecurity.java

@Component
public class AccountSecurity {

    @Autowired
    private AccountService accountService;

    public boolean hasAccess(Long accountId, String username) {
        Account account = accountService.findById(accountId);
        return account != null && account.getOwnerUsername().equals(username);
    }
}

5. AccountController.java

@RestController
@RequestMapping("/accounts")
public class AccountController {

    @Autowired
    private AccountService accountService;

    @PreAuthorize("@accountSecurity.hasAccess(#accountId, authentication.name)")
    @GetMapping("/{accountId}")
    public Account getAccount(@PathVariable Long accountId) {
        return accountService.findById(accountId);
    }
}

6. BankAppApplication.java

@SpringBootApplication
public class BankAppApplication {
    public static void main(String[] args) {
        SpringApplication.run(BankAppApplication.class, args);
    }
}

โœ… Test It

  • Login as alice / 123 โ†’ Access /accounts/1 โœ…
  • Login as bob / 456 โ†’ Access /accounts/1 โŒ


Leave a Comment

Your email address will not be published. Required fields are marked *