Integrating jCasbin Permission Management into Spring Boot Applications
This article demonstrates how to add jCasbin to a Spring Boot project, covering Maven dependencies, configuration files, Enforcer initialization, custom policy objects, a servlet filter for authorization, and dynamic add‑remove permission APIs, enabling lightweight, database‑driven access control.
As a backend developer, the author compares traditional Shiro permission management with the newer jCasbin library, noting Shiro's complexity in distributed Spring Cloud environments and introducing jCasbin as a simpler alternative.
1. Preparation
Add the jCasbin and JDBC adapter dependencies to your Maven pom.xml :
<dependency>
<groupId>org.casbin</groupId>
<artifactId>jcasbin</artifactId>
<version>1.1.0</version>
</dependency>
<dependency>
<groupId>org.casbin</groupId>
<artifactId>jdbc-adapter</artifactId>
<version>1.1.0</version>
</dependency>Create a configuration class to bind properties from application.properties :
@Configuration
@ConfigurationProperties(prefix = "org.jcasbin")
public class EnforcerConfigProperties {
private String url;
private String driverClassName;
private String username;
private String password;
private String modelPath;
// getters and setters omitted for brevity
@Override
public String toString() {
return "EnforcerConfigProperties [url=" + url + ", driverClassName=" + driverClassName + ", username=" + username + ", password=" + password + ", modelPath=" + modelPath + "]";
}
}Define the model and policy files ( model.conf and policy.csv ) and place them alongside the src directory. The policy can also be loaded from a database.
2. Initialize Permission Information
Implement a factory that creates a singleton Enforcer during Spring's startup by implementing InitializingBean :
@Component
public class EnforcerFactory implements InitializingBean {
private static Enforcer enforcer;
@Autowired
private EnforcerConfigProperties enforcerConfigProperties;
@Override
public void afterPropertiesSet() throws Exception {
// Load policy from DB via JDBCAdapter
JDBCAdapter jdbcAdapter = new JDBCAdapter(
enforcerConfigProperties.getDriverClassName(),
enforcerConfigProperties.getUrl(),
enforcerConfigProperties.getUsername(),
enforcerConfigProperties.getPassword(), true);
enforcer = new Enforcer(enforcerConfigProperties.getModelPath(), jdbcAdapter);
enforcer.loadPolicy();
}
public static boolean addPolicy(Policy policy) {
boolean added = enforcer.addPolicy(policy.getSub(), policy.getObj(), policy.getAct());
enforcer.savePolicy();
return added;
}
public static boolean removePolicy(Policy policy) {
boolean removed = enforcer.removePolicy(policy.getSub(), policy.getObj(), policy.getAct());
enforcer.savePolicy();
return removed;
}
public static Enforcer getEnforcer() { return enforcer; }
}Define a simple POJO to represent a policy entry:
public class Policy {
private String sub; // user or role
private String obj; // resource, e.g., /user/*
private String act; // action, e.g., GET, POST
public Policy() {}
public Policy(String sub, String obj, String act) {
this.sub = sub; this.obj = obj; this.act = act;
}
// getters and setters omitted
@Override
public String toString() {
return "Policy[sub=" + sub + ", obj=" + obj + ", act=" + act + "]";
}
}3. Usage
3.1 Permission Filter
Create a servlet filter that checks each request against the Enforcer :
@WebFilter(urlPatterns = "/*", filterName = "JCasbinAuthzFilter")
@Order(Ordered.HIGHEST_PRECEDENCE)
public class JCasbinAuthzFilter implements Filter {
private static final Logger log = LoggerFactory.getLogger(JCasbinAuthzFilter.class);
private static Enforcer enforcer;
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
String user = request.getParameter("username");
String path = request.getRequestURI();
String method = request.getMethod();
enforcer = EnforcerFactory.getEnforcer();
if (path.contains("anon")) {
chain.doFilter(request, response);
} else if (enforcer.enforce(user, path, method)) {
chain.doFilter(request, response);
} else {
log.info("Unauthorized access");
Map
result = new HashMap<>();
result.put("code", 1001);
result.put("msg", "User lacks permission");
result.put("data", null);
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json");
response.getWriter().write(JSONObject.toJSONString(result, SerializerFeature.WriteMapNullValue));
}
}
// init and destroy omitted
}3.2 Adding and Removing Permissions at Runtime
Expose REST endpoints that call the factory methods, allowing policies to be added or removed without restarting the service:
@PutMapping("/anon/role/per")
public ResultBO
addPer() {
EnforcerFactory.addPolicy(new Policy("alice", "/user/list", "*"));
return ResultTool.success();
}
@DeleteMapping("/anon/role/per")
public ResultBO
deletePer() {
EnforcerFactory.removePolicy(new Policy("alice", "/user/list", "*"));
return ResultTool.success();
}Finally, the author notes that the same approach can be combined with Spring Cloud Zuul by implementing a custom ZuulFilter , achieving unified login and permission control across micro‑services.
Java Architect Essentials
Committed to sharing quality articles and tutorials to help Java programmers progress from junior to mid-level to senior architect. We curate high-quality learning resources, interview questions, videos, and projects from across the internet to help you systematically improve your Java architecture skills. Follow and reply '1024' to get Java programming resources. Learn together, grow together.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.