Casbin is a powerful and efficient open-source access control library that supports various authorization models like ACL, RBAC, ABAC. It provides a unified way to handle authorization across different applications and frameworks.
Casbin Authorization Models
- ACL (Access Control List) - Basic user-resource permissions
- RBAC (Role-Based Access Control) - Permissions through roles
- ABAC (Attribute-Based Access Control) - Context-aware permissions
- RESTful Model - HTTP method and path based control
Dependencies and Setup
Maven Configuration:
<dependencies> <!-- Casbin Core --> <dependency> <groupId>org.casbin</groupId> <artifactId>jcasbin</artifactId> <version>1.39.0</version> </dependency> <!-- Spring Boot Integration --> <dependency> <groupId>org.casbin</groupId> <artifactId>jcasbin-spring-boot-starter</artifactId> <version>3.1.0</version> </dependency> <!-- Database Adapters --> <dependency> <groupId>org.casbin</groupId> <artifactId>jdbc-adapter</artifactId> <version>3.1.0</version> </dependency> <dependency> <groupId>org.casbin</groupId> <artifactId>redis-adapter</artifactId> <version>3.1.0</version> </dependency> <!-- Spring Security Integration --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <!-- Database (if using JDBC adapter) --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>runtime</scope> </dependency> </dependencies>
Core Configuration
CasbinProperties.java:
@Configuration
@ConfigurationProperties(prefix = "casbin")
@Data
public class CasbinProperties {
private ModelConfig model = new ModelConfig();
private AdapterConfig adapter = new AdapterConfig();
private WatcherConfig watcher = new WatcherConfig();
@Data
public static class ModelConfig {
private String path = "classpath:casbin/model.conf";
private boolean enabled = true;
}
@Data
public static class AdapterConfig {
private AdapterType type = AdapterType.FILE;
private String filePath = "classpath:casbin/policy.csv";
private JdbcConfig jdbc = new JdbcConfig();
private RedisConfig redis = new RedisConfig();
}
@Data
public static class JdbcConfig {
private String url;
private String username;
private String password;
private String driverClassName;
private String tableName = "casbin_rule";
}
@Data
public static class RedisConfig {
private String host = "localhost";
private int port = 6379;
private String password;
private int database = 0;
}
@Data
public static class WatcherConfig {
private boolean enabled = false;
private WatcherType type = WatcherType.REDIS;
}
public enum AdapterType {
FILE, JDBC, REDIS, MONGO
}
public enum WatcherType {
REDIS, ZOOKEEPER
}
}
Casbin Model Configuration
model.conf:
[request_definition] r = sub, obj, act
[policy_definition]
p = sub, obj, act
[role_definition]
g = _, _
[policy_effect]
e = some(where (p.eft == allow))
[matchers]
m = g(r.sub, p.sub) && keyMatch(r.obj, p.obj) && (r.act == p.act || p.act == "*")
Advanced RBAC with Domains:
[request_definition] r = sub, dom, obj, act
[policy_definition]
p = sub, dom, obj, act
[role_definition]
g = _, _, _
[policy_effect]
e = some(where (p.eft == allow))
[matchers]
m = g(r.sub, p.sub, r.dom) && r.dom == p.dom && keyMatch(r.obj, p.obj) && regexMatch(r.act, p.act)
ABAC Model:
[request_definition] r = sub, obj, act
[policy_definition]
p = sub_rule, obj_rule, act_rule
[policy_effect]
e = some(where (p.eft == allow))
[matchers]
m = eval(p.sub_rule) && eval(p.obj_rule) && eval(p.act_rule)
[role_definition]
g = _, _
Casbin Service Configuration
CasbinConfig.java:
@Configuration
@Slf4j
public class CasbinConfig {
private final CasbinProperties properties;
private final DataSource dataSource; // Optional, for JDBC adapter
public CasbinConfig(CasbinProperties properties,
@Autowired(required = false) DataSource dataSource) {
this.properties = properties;
this.dataSource = dataSource;
}
@Bean
@ConditionalOnMissingBean
public Enforcer enforcer() {
try {
// Create model
Model model = createModel();
// Create adapter
Adapter adapter = createAdapter();
// Create enforcer
Enforcer enforcer = new Enforcer(model, adapter);
// Enable auto-save
enforcer.enableAutoSave(true);
// Enable auto-build role links
enforcer.enableAutoBuildRoleLinks(true);
// Load policy
enforcer.loadPolicy();
// Add watcher if enabled
if (properties.getWatcher().isEnabled()) {
addWatcher(enforcer);
}
log.info("Casbin enforcer initialized successfully");
return enforcer;
} catch (Exception e) {
throw new CasbinConfigurationException("Failed to initialize Casbin enforcer", e);
}
}
private Model createModel() {
String modelPath = properties.getModel().getPath();
if (modelPath.startsWith("classpath:")) {
modelPath = modelPath.substring("classpath:".length());
return new Model("src/main/resources/" + modelPath);
} else {
return new Model(modelPath);
}
}
private Adapter createAdapter() {
switch (properties.getAdapter().getType()) {
case FILE:
return createFileAdapter();
case JDBC:
return createJdbcAdapter();
case REDIS:
return createRedisAdapter();
default:
throw new UnsupportedAdapterException(
"Unsupported adapter type: " + properties.getAdapter().getType());
}
}
private Adapter createFileAdapter() {
String filePath = properties.getAdapter().getFilePath();
if (filePath.startsWith("classpath:")) {
filePath = filePath.substring("classpath:".length());
try {
return new FileAdapter("src/main/resources/" + filePath);
} catch (Exception e) {
log.warn("Failed to load policy file, using in-memory adapter");
return new DefaultAdapter();
}
} else {
return new FileAdapter(filePath);
}
}
private Adapter createJdbcAdapter() {
if (dataSource == null) {
throw new CasbinConfigurationException("DataSource required for JDBC adapter");
}
JdbcAdapterConfig config = new JdbcAdapterConfig();
config.setDataSource(dataSource);
config.setTableName(properties.getAdapter().getJdbc().getTableName());
return new JdbcAdapter(config);
}
private Adapter createRedisAdapter() {
CasbinProperties.RedisConfig redisConfig = properties.getAdapter().getRedis();
RedisAdapterConfig config = new RedisAdapterConfig();
config.setHost(redisConfig.getHost());
config.setPort(redisConfig.getPort());
config.setPassword(redisConfig.getPassword());
config.setDatabase(redisConfig.getDatabase());
return new RedisAdapter(config);
}
private void addWatcher(Enforcer enforcer) {
// Implement watcher for distributed policy updates
// This would typically use Redis pub/sub or similar
log.info("Watcher enabled for Casbin enforcer");
}
}
Core Authorization Service
CasbinAuthService.java:
@Service
@Slf4j
public class CasbinAuthService {
private final Enforcer enforcer;
public CasbinAuthService(Enforcer enforcer) {
this.enforcer = enforcer;
}
// Basic permission check
public boolean checkPermission(String subject, String object, String action) {
try {
return enforcer.enforce(subject, object, action);
} catch (Exception e) {
log.error("Permission check failed for {}-{}-{}", subject, object, action, e);
return false;
}
}
// Permission check with domain (multi-tenant)
public boolean checkPermission(String subject, String domain, String object, String action) {
try {
return enforcer.enforce(subject, domain, object, action);
} catch (Exception e) {
log.error("Permission check failed for {}-{}-{}-{}", subject, domain, object, action, e);
return false;
}
}
// Get all roles for a user
public List<String> getRolesForUser(String username) {
try {
return enforcer.getRolesForUser(username);
} catch (Exception e) {
log.error("Failed to get roles for user: {}", username, e);
return Collections.emptyList();
}
}
// Get all roles for user in domain
public List<String> getRolesForUser(String username, String domain) {
try {
return enforcer.getRolesForUser(username, domain);
} catch (Exception e) {
log.error("Failed to get roles for user {} in domain {}", username, domain, e);
return Collections.emptyList();
}
}
// Get all users for a role
public List<String> getUsersForRole(String role) {
try {
return enforcer.getUsersForRole(role);
} catch (Exception e) {
log.error("Failed to get users for role: {}", role, e);
return Collections.emptyList();
}
}
// Check if user has role
public boolean hasRole(String username, String role) {
try {
return enforcer.hasRoleForUser(username, role);
} catch (Exception e) {
log.error("Failed to check role {} for user {}", role, username, e);
return false;
}
}
// Add role to user
public boolean addRoleForUser(String username, String role) {
try {
boolean result = enforcer.addRoleForUser(username, role);
if (result) {
log.info("Added role {} to user {}", role, username);
}
return result;
} catch (Exception e) {
log.error("Failed to add role {} to user {}", role, username, e);
return false;
}
}
// Add role to user in domain
public boolean addRoleForUser(String username, String role, String domain) {
try {
boolean result = enforcer.addRoleForUser(username, role, domain);
if (result) {
log.info("Added role {} to user {} in domain {}", role, username, domain);
}
return result;
} catch (Exception e) {
log.error("Failed to add role {} to user {} in domain {}", role, username, domain, e);
return false;
}
}
// Delete role for user
public boolean deleteRoleForUser(String username, String role) {
try {
boolean result = enforcer.deleteRoleForUser(username, role);
if (result) {
log.info("Deleted role {} from user {}", role, username);
}
return result;
} catch (Exception e) {
log.error("Failed to delete role {} from user {}", role, username, e);
return false;
}
}
// Add permission
public boolean addPermission(String subject, String object, String action) {
try {
boolean result = enforcer.addPolicy(subject, object, action);
if (result) {
log.info("Added permission {}-{}-{}", subject, object, action);
}
return result;
} catch (Exception e) {
log.error("Failed to add permission {}-{}-{}", subject, object, action, e);
return false;
}
}
// Delete permission
public boolean deletePermission(String subject, String object, String action) {
try {
boolean result = enforcer.removePolicy(subject, object, action);
if (result) {
log.info("Deleted permission {}-{}-{}", subject, object, action);
}
return result;
} catch (Exception e) {
log.error("Failed to delete permission {}-{}-{}", subject, object, action, e);
return false;
}
}
// Get all permissions for user
public List<List<String>> getPermissionsForUser(String username) {
try {
return enforcer.getPermissionsForUser(username);
} catch (Exception e) {
log.error("Failed to get permissions for user: {}", username, e);
return Collections.emptyList();
}
}
// Get all permissions
public List<List<String>> getAllPermissions() {
try {
return enforcer.getPolicy();
} catch (Exception e) {
log.error("Failed to get all permissions", e);
return Collections.emptyList();
}
}
// Batch add permissions
public boolean addPermissions(List<Permission> permissions) {
try {
List<List<String>> policies = permissions.stream()
.map(p -> Arrays.asList(p.getSubject(), p.getObject(), p.getAction()))
.collect(Collectors.toList());
boolean result = enforcer.addPolicies(policies);
if (result) {
log.info("Added {} permissions in batch", permissions.size());
}
return result;
} catch (Exception e) {
log.error("Failed to add batch permissions", e);
return false;
}
}
// Check with ABAC attributes
public boolean checkWithAttributes(String subject, Map<String, Object> subjectAttrs,
String object, Map<String, Object> objectAttrs,
String action, Map<String, Object> actionAttrs) {
try {
// For ABAC, you would use a custom function in the matcher
// This is a simplified example
ABACRequest request = new ABACRequest(subject, subjectAttrs, object, objectAttrs, action, actionAttrs);
return evaluateAbacRequest(request);
} catch (Exception e) {
log.error("ABAC permission check failed", e);
return false;
}
}
private boolean evaluateAbacRequest(ABACRequest request) {
// Implement ABAC evaluation logic
// This would typically use the eval() function in Casbin matchers
return false;
}
@Data
@AllArgsConstructor
public static class Permission {
private String subject;
private String object;
private String action;
}
@Data
@AllArgsConstructor
public static class ABACRequest {
private String subject;
private Map<String, Object> subjectAttributes;
private String object;
private Map<String, Object> objectAttributes;
private String action;
private Map<String, Object> actionAttributes;
}
}
Spring Security Integration
CasbinSecurityConfig.java:
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
@Slf4j
public class CasbinSecurityConfig {
private final CasbinAuthService casbinAuthService;
public CasbinSecurityConfig(CasbinAuthService casbinAuthService) {
this.casbinAuthService = casbinAuthService;
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authz -> authz
.requestMatchers("/public/**").permitAll()
.requestMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
)
.addFilterBefore(casbinAuthorizationFilter(), BasicAuthenticationFilter.class)
.formLogin(form -> form
.loginPage("/login")
.permitAll()
)
.logout(logout -> logout
.logoutSuccessUrl("/login?logout")
.permitAll()
)
.exceptionHandling(exceptions -> exceptions
.accessDeniedHandler(casbinAccessDeniedHandler())
);
return http.build();
}
@Bean
public CasbinAuthorizationFilter casbinAuthorizationFilter() {
return new CasbinAuthorizationFilter(casbinAuthService);
}
@Bean
public AccessDeniedHandler casbinAccessDeniedHandler() {
return (request, response, accessDeniedException) -> {
response.setStatus(HttpStatus.FORBIDDEN.value());
response.setContentType("application/json");
Map<String, Object> error = new HashMap<>();
error.put("timestamp", Instant.now());
error.put("status", HttpStatus.FORBIDDEN.value());
error.put("error", "Forbidden");
error.put("message", "Access denied");
error.put("path", request.getRequestURI());
ObjectMapper mapper = new ObjectMapper();
response.getWriter().write(mapper.writeValueAsString(error));
};
}
}
CasbinAuthorizationFilter.java:
@Component
@Slf4j
public class CasbinAuthorizationFilter extends OncePerRequestFilter {
private final CasbinAuthService casbinAuthService;
public CasbinAuthorizationFilter(CasbinAuthService casbinAuthService) {
this.casbinAuthService = casbinAuthService;
}
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null || !authentication.isAuthenticated()) {
filterChain.doFilter(request, response);
return;
}
String username = authentication.getName();
String path = request.getRequestURI();
String method = request.getMethod();
// Check permission using Casbin
boolean hasPermission = casbinAuthService.checkPermission(username, path, method);
if (hasPermission) {
filterChain.doFilter(request, response);
} else {
log.warn("Access denied for user {} to {} {}", username, method, path);
response.setStatus(HttpStatus.FORBIDDEN.value());
Map<String, Object> error = Map.of(
"error", "Forbidden",
"message", "Insufficient permissions",
"path", path,
"timestamp", Instant.now()
);
response.setContentType("application/json");
ObjectMapper mapper = new ObjectMapper();
response.getWriter().write(mapper.writeValueAsString(error));
}
}
@Override
protected boolean shouldNotFilter(HttpServletRequest request) {
String path = request.getRequestURI();
return path.startsWith("/public/") || path.equals("/login") || path.equals("/logout");
}
}
Method Security with Annotations
CasbinMethodSecurityService.java:
@Service
@Slf4j
public class CasbinMethodSecurityService {
private final CasbinAuthService casbinAuthService;
public CasbinMethodSecurityService(CasbinAuthService casbinAuthService) {
this.casbinAuthService = casbinAuthService;
}
@PreAuthorize("@casbinMethodSecurityService.hasPermission('read', #resource)")
public Resource readResource(String resource) {
log.info("Reading resource: {}", resource);
return new Resource(resource, "content");
}
@PreAuthorize("@casbinMethodSecurityService.hasPermission('write', #resource)")
public void updateResource(String resource, String content) {
log.info("Updating resource: {}", resource);
}
@PreAuthorize("@casbinMethodSecurityService.hasRole('ADMIN')")
public void adminOperation() {
log.info("Performing admin operation");
}
@PreAuthorize("@casbinMethodSecurityService.hasPermission(#domain, 'read', 'data')")
public List<Data> getDomainData(String domain) {
log.info("Getting data for domain: {}", domain);
return Collections.emptyList();
}
// Permission check method used in @PreAuthorize
public boolean hasPermission(String action, String resource) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null) {
return false;
}
String username = authentication.getName();
return casbinAuthService.checkPermission(username, resource, action);
}
// Domain-based permission check
public boolean hasPermission(String domain, String action, String resource) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null) {
return false;
}
String username = authentication.getName();
return casbinAuthService.checkPermission(username, domain, resource, action);
}
// Role check method
public boolean hasRole(String role) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null) {
return false;
}
String username = authentication.getName();
return casbinAuthService.hasRole(username, role);
}
@Data
@AllArgsConstructor
public static class Resource {
private String name;
private String content;
}
@Data
public static class Data {
private String id;
private String value;
}
}
REST API for Policy Management
CasbinPolicyController.java:
@RestController
@RequestMapping("/api/admin/casbin")
@PreAuthorize("hasRole('ADMIN')")
@Slf4j
public class CasbinPolicyController {
private final CasbinAuthService casbinAuthService;
private final Enforcer enforcer;
public CasbinPolicyController(CasbinAuthService casbinAuthService, Enforcer enforcer) {
this.casbinAuthService = casbinAuthService;
this.enforcer = enforcer;
}
@GetMapping("/policies")
public ResponseEntity<List<PolicyResponse>> getAllPolicies() {
List<List<String>> policies = casbinAuthService.getAllPermissions();
List<PolicyResponse> response = policies.stream()
.map(policy -> new PolicyResponse(policy.get(0), policy.get(1), policy.get(2)))
.collect(Collectors.toList());
return ResponseEntity.ok(response);
}
@PostMapping("/policies")
public ResponseEntity<PolicyResponse> addPolicy(@RequestBody PolicyRequest request) {
boolean success = casbinAuthService.addPermission(
request.getSubject(), request.getObject(), request.getAction());
if (success) {
PolicyResponse response = new PolicyResponse(
request.getSubject(), request.getObject(), request.getAction());
return ResponseEntity.ok(response);
} else {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}
@DeleteMapping("/policies")
public ResponseEntity<Void> deletePolicy(@RequestBody PolicyRequest request) {
boolean success = casbinAuthService.deletePermission(
request.getSubject(), request.getObject(), request.getAction());
return success ? ResponseEntity.noContent().build() :
ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
@GetMapping("/users/{username}/roles")
public ResponseEntity<List<String>> getUserRoles(@PathVariable String username) {
List<String> roles = casbinAuthService.getRolesForUser(username);
return ResponseEntity.ok(roles);
}
@PostMapping("/users/{username}/roles")
public ResponseEntity<Void> addUserRole(@PathVariable String username,
@RequestBody RoleRequest request) {
boolean success = casbinAuthService.addRoleForUser(username, request.getRole());
return success ? ResponseEntity.ok().build() :
ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
@DeleteMapping("/users/{username}/roles/{role}")
public ResponseEntity<Void> deleteUserRole(@PathVariable String username,
@PathVariable String role) {
boolean success = casbinAuthService.deleteRoleForUser(username, role);
return success ? ResponseEntity.noContent().build() :
ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
@GetMapping("/users/{username}/permissions")
public ResponseEntity<List<PolicyResponse>> getUserPermissions(@PathVariable String username) {
List<List<String>> permissions = casbinAuthService.getPermissionsForUser(username);
List<PolicyResponse> response = permissions.stream()
.map(permission -> new PolicyResponse(
permission.get(0), permission.get(1), permission.get(2)))
.collect(Collectors.toList());
return ResponseEntity.ok(response);
}
@PostMapping("/batch/policies")
public ResponseEntity<Void> addPoliciesBatch(@RequestBody List<PolicyRequest> requests) {
List<CasbinAuthService.Permission> permissions = requests.stream()
.map(req -> new CasbinAuthService.Permission(req.getSubject(), req.getObject(), req.getAction()))
.collect(Collectors.toList());
boolean success = casbinAuthService.addPermissions(permissions);
return success ? ResponseEntity.ok().build() :
ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
@PostMapping("/reload")
public ResponseEntity<Void> reloadPolicy() {
try {
enforcer.loadPolicy();
log.info("Casbin policy reloaded successfully");
return ResponseEntity.ok().build();
} catch (Exception e) {
log.error("Failed to reload Casbin policy", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}
// DTO classes
@Data
public static class PolicyRequest {
private String subject;
private String object;
private String action;
}
@Data
@AllArgsConstructor
public static class PolicyResponse {
private String subject;
private String object;
private String action;
}
@Data
public static class RoleRequest {
private String role;
}
}
Domain-Based Multi-Tenancy
DomainAwareCasbinService.java:
@Service
@Slf4j
public class DomainAwareCasbinService {
private final CasbinAuthService casbinAuthService;
public DomainAwareCasbinService(CasbinAuthService casbinAuthService) {
this.casbinAuthService = casbinAuthService;
}
public boolean checkDomainPermission(String username, String domain,
String resource, String action) {
return casbinAuthService.checkPermission(username, domain, resource, action);
}
public List<String> getUserDomains(String username) {
// Get all domains where user has any role
List<String> domains = new ArrayList<>();
// This would typically query the policy for domains where user has roles
// For simplicity, we'll return a hardcoded list
return Arrays.asList("domain1", "domain2");
}
public boolean addUserToDomain(String username, String domain, String role) {
return casbinAuthService.addRoleForUser(username, role, domain);
}
public boolean removeUserFromDomain(String username, String domain, String role) {
// Casbin doesn't have a direct method for this, so we need to remove the specific policy
// This is a simplified example
return casbinAuthService.deleteRoleForUser(username, role);
}
public Map<String, List<String>> getUserDomainRoles(String username) {
Map<String, List<String>> domainRoles = new HashMap<>();
// Get all roles for user across all domains
List<String> allDomains = getUserDomains(username);
for (String domain : allDomains) {
List<String> roles = casbinAuthService.getRolesForUser(username, domain);
domainRoles.put(domain, roles);
}
return domainRoles;
}
@PreAuthorize("@domainAwareCasbinService.hasDomainPermission(#domain, 'read', #resource)")
public DomainResource getDomainResource(String domain, String resource) {
log.info("Accessing resource {} in domain {}", resource, domain);
return new DomainResource(domain, resource, "content");
}
public boolean hasDomainPermission(String domain, String action, String resource) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null) {
return false;
}
String username = authentication.getName();
return checkDomainPermission(username, domain, resource, action);
}
@Data
@AllArgsConstructor
public static class DomainResource {
private String domain;
private String name;
private String content;
}
}
ABAC (Attribute-Based Access Control)
AbacService.java:
@Service
@Slf4j
public class AbacService {
private final Enforcer enforcer;
private final ObjectMapper objectMapper;
public AbacService(Enforcer enforcer) {
this.enforcer = enforcer;
this.objectMapper = new ObjectMapper();
registerCustomFunctions();
}
private void registerCustomFunctions() {
// Register custom functions for ABAC
enforcer.addFunction("timeInRange", (args) -> {
// Check if current time is within range
String startTime = args[0].toString();
String endTime = args[1].toString();
return isTimeInRange(startTime, endTime);
});
enforcer.addFunction("ipInRange", (args) -> {
// Check if IP is in range
String ip = args[0].toString();
String ipRange = args[1].toString();
return isIpInRange(ip, ipRange);
});
enforcer.addFunction("resourceOwner", (args) -> {
// Check if user owns the resource
String username = args[0].toString();
String resourceId = args[1].toString();
return isResourceOwner(username, resourceId);
});
}
public boolean checkAbacPermission(String subject, Map<String, Object> subjectAttrs,
String object, Map<String, Object> objectAttrs,
String action, Map<String, Object> actionAttrs) {
try {
// Convert attributes to JSON for Casbin evaluation
String subjectAttrsJson = objectMapper.writeValueAsString(subjectAttrs);
String objectAttrsJson = objectMapper.writeValueAsString(objectAttrs);
String actionAttrsJson = objectMapper.writeValueAsString(actionAttrs);
// Use Casbin with ABAC attributes
return enforcer.enforce(subject, subjectAttrsJson, object, objectAttrsJson,
action, actionAttrsJson);
} catch (Exception e) {
log.error("ABAC permission check failed", e);
return false;
}
}
public boolean checkTimeBasedPermission(String username, String resource, String action) {
Map<String, Object> subjectAttrs = Map.of("username", username, "department", "engineering");
Map<String, Object> objectAttrs = Map.of("name", resource, "sensitivity", "high");
Map<String, Object> actionAttrs = Map.of("name", action, "time", LocalTime.now().toString());
return checkAbacPermission(username, subjectAttrs, resource, objectAttrs, action, actionAttrs);
}
public boolean checkLocationBasedPermission(String username, String resource,
String action, String ipAddress) {
Map<String, Object> subjectAttrs = Map.of(
"username", username,
"ip_address", ipAddress,
"location", getLocationFromIp(ipAddress)
);
Map<String, Object> objectAttrs = Map.of("name", resource, "allowed_locations", "corporate");
Map<String, Object> actionAttrs = Map.of("name", action);
return checkAbacPermission(username, subjectAttrs, resource, objectAttrs, action, actionAttrs);
}
private boolean isTimeInRange(String startTime, String endTime) {
LocalTime now = LocalTime.now();
LocalTime start = LocalTime.parse(startTime);
LocalTime end = LocalTime.parse(endTime);
return !now.isBefore(start) && !now.isAfter(end);
}
private boolean isIpInRange(String ip, String ipRange) {
// Simplified IP range check
return ip.startsWith(ipRange);
}
private boolean isResourceOwner(String username, String resourceId) {
// Check if user owns the resource
// This would typically query a database
return true; // Simplified
}
private String getLocationFromIp(String ip) {
// Get location from IP (simplified)
return "office"; // Simplified
}
}
Application Configuration
application.yml:
casbin: model: path: "classpath:casbin/model.conf" enabled: true adapter: type: JDBC file-path: "classpath:casbin/policy.csv" jdbc: table-name: "casbin_rules" watcher: enabled: false type: REDIS spring: datasource: url: jdbc:h2:mem:testdb driverClassName: org.h2.Driver username: sa password: password jpa: database-platform: org.hibernate.dialect.H2Dialect h2: console: enabled: true logging: level: org.casbin: DEBUG com.example.casbin: INFO
Best Practices
- Policy Management - Use database adapter for production
- Caching - Implement policy caching for better performance
- Monitoring - Log authorization decisions for audit
- Testing - Comprehensive tests for policy rules
- Backup - Regular backup of policy data
- Performance - Use watchers for distributed policy updates
- Security - Secure policy management endpoints
Conclusion
Casbin in Java provides:
- Flexible authorization models (ACL, RBAC, ABAC)
- Multi-tenant support with domain-based policies
- Spring Security integration for seamless adoption
- High performance with efficient policy evaluation
- Extensible architecture with custom functions and adapters
By implementing Casbin for authorization, applications can achieve fine-grained access control that scales from simple permission checks to complex attribute-based policies across distributed systems.