How to Integrate jCasbin with Spring Boot for Dynamic Permission Management
This article provides a step‑by‑step guide for backend developers to replace Shiro with jCasbin in a Spring Boot application, covering Maven dependencies, configuration files, Enforcer initialization, custom filter implementation, and runtime permission add/remove APIs, all with complete code examples.
Overview
This guide shows how to integrate jCasbin into a Spring Boot application for fine‑grained permission control. An Enforcer is created from a Casbin model file and a policy source (e.g., a MySQL database). A servlet filter delegates each request to enforcer.enforce(user, path, method), and policies can be added or removed at runtime via simple REST endpoints.
Dependencies
<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>Configuration
Create a @ConfigurationProperties class to hold database connection details and the path to the Casbin model file.
@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
}Add matching entries to application.properties (adjust values for your environment):
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.confEnforcer initialization
Implement a Spring component that creates the Enforcer when the application context starts. The component also provides static helper methods for adding, removing, and retrieving the enforcer.
@Component
public class EnforcerFactory implements InitializingBean {
private static Enforcer enforcer;
@Autowired
private EnforcerConfigProperties props;
@Override
public void afterPropertiesSet() throws Exception {
JDBCAdapter adapter = new JDBCAdapter(
props.getDriverClassName(),
props.getUrl(),
props.getUsername(),
props.getPassword(),
true);
enforcer = new Enforcer(props.getModelPath(), adapter);
enforcer.loadPolicy();
}
public static boolean addPolicy(Policy p) {
boolean ok = enforcer.addPolicy(p.getSub(), p.getObj(), p.getAct());
enforcer.savePolicy();
return ok;
}
public static boolean removePolicy(Policy p) {
boolean ok = enforcer.removePolicy(p.getSub(), p.getObj(), p.getAct());
enforcer.savePolicy();
return ok;
}
public static Enforcer getEnforcer() {
return enforcer;
}
}Define a simple POJO to represent a policy rule.
public class Policy {
private String sub; // user or role
private String obj; // resource path, e.g. /user/*
private String act; // HTTP method, 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
}Permission enforcement
Servlet filter
Register a @WebFilter that obtains the shared Enforcer and decides whether a request should proceed.
@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 resp, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
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 for user {} on {} {}", user, path, method);
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write("{\"code\":1001,\"msg\":\"User permission insufficient\",\"data\":null}");
}
}
// init and destroy methods omitted
}Dynamic policy management
Expose REST endpoints that call the static methods in EnforcerFactory to add or remove a policy without restarting the service.
@RestController
public class PolicyController {
@PutMapping("/anon/role/per")
public String addPer() {
EnforcerFactory.addPolicy(new Policy("alice", "/user/list", "*"));
return "success";
}
@DeleteMapping("/anon/role/per")
public String deletePer() {
EnforcerFactory.removePolicy(new Policy("alice", "/user/list", "*"));
return "success";
}
}Reference
Official jCasbin repository: https://github.com/casbin/jcasbin
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 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.
