How to Integrate jCasbin for Dynamic Permission Management in Spring Boot
This article explains how to replace Shiro with jCasbin in a Spring Boot application, covering Maven dependencies, configuration files, Enforcer initialization, policy definition, and practical usage through a servlet filter and REST endpoints for adding or removing permissions.
Preface
As a backend developer, the author finds Shiro cumbersome for distributed projects and introduces jCasbin as a lightweight alternative for permission management.
Preparation
The example uses Spring Boot 1.5.10, but any version works.
1. Maven Dependency
<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>2. Configuration Files
jCasbin stores roles and permissions in model.conf and policy.csv. These files can also be loaded from a database.
In application.properties configure the database and model path:
org.jcasbin.url=jdbc:mysql://localhost:3306/casbin?useSSL=false
org.jcasbin.driver-class-name=com.mysql.jdbc.Driver
org.jcasbin.username=root
org.jcasbin.password=root
org.jcasbin.model-path=conf/authz_model.conf2. Initialize Permission Information
Create a class that implements InitializingBean to load the Enforcer at container startup.
@Component
public class EnforcerFactory implements InitializingBean {
private static Enforcer enforcer;
@Autowired
private EnforcerConfigProperties enforcerConfigProperties;
@Override
public void afterPropertiesSet() throws Exception {
EnforcerConfigProperties config = enforcerConfigProperties;
JDBCAdapter jdbcAdapter = new JDBCAdapter(
config.getDriverClassName(), config.getUrl(), config.getUsername(),
config.getPassword(), true);
enforcer = new Enforcer(config.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; }
}The accompanying EnforcerConfigProperties class holds database and model configuration fields with getters and setters.
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 + "]";
}
}A simple Policy POJO encapsulates sub (subject), obj (object), and act (action).
public class Policy {
private String sub; // user or role
private String obj; // resource, e.g., /user/*
private String act; // HTTP method, e.g., GET
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
1. Permission Control
Implement a servlet filter that checks permissions with enforcer.enforce(user, path, method).
@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 req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
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("Access denied");
Map<String, Object> 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
}2. Add / Delete Permissions
Expose REST endpoints that call the factory methods to modify policies at runtime without restarting the server.
@PutMapping("/anon/role/per")
public ResultBO<Object> addPer() {
EnforcerFactory.addPolicy(new Policy("alice", "/user/list", "*"));
return ResultTool.success();
}
@DeleteMapping("/anon/role/per")
public ResultBO<Object> deletePer() {
EnforcerFactory.removePolicy(new Policy("alice", "/user/list", "*"));
return ResultTool.success();
}Conclusion
The author notes that jCasbin can also be combined with Spring Cloud Zuul by creating a custom ZuulFilter, achieving unified login and permission control across microservices.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Java High-Performance Architecture
Sharing Java development articles and resources, including SSM architecture and the Spring ecosystem (Spring Boot, Spring Cloud, MyBatis, Dubbo, Docker), Zookeeper, Redis, architecture design, microservices, message queues, Git, etc.
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.
