Simplify Spring Boot Permission Management with jCasbin
This guide shows how to replace Shiro with jCasbin in a Spring Boot microservice, covering Maven dependencies, configuration files, Enforcer initialization, policy handling, and a custom filter for runtime permission checks, enabling dynamic role‑based access control without server restarts.
Preface
As a backend developer, permission management is familiar; Java has public, private, etc. Shiro is powerful but cumbersome in distributed Spring Cloud projects, requiring many customizations. jCasbin offers a lightweight alternative that can be quickly integrated.
1. 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 role‑permission mappings in model.conf and policy.csv. These files can also be loaded from a database.
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 Enforcer
Create a configuration properties class and a factory that implements InitializingBean to load the policy at startup.
@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 + "]";
}
} @Component
public class EnforcerFactory implements InitializingBean {
private static Enforcer enforcer;
@Autowired
private EnforcerConfigProperties enforcerConfigProperties;
private static EnforcerConfigProperties config;
@Override
public void afterPropertiesSet() throws Exception {
config = enforcerConfigProperties;
JDBCAdapter jdbcAdapter = new JDBCAdapter(config.getDriverClassName(), config.getUrl(), config.getUsername(), config.getPassword(), true);
enforcer = new Enforcer(config.getModelPath(), jdbcAdapter);
enforcer.loadPolicy(); // Load the policy from DB.
}
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;
}
}Policy POJO
public class Policy {
/** User or role that wants to access a resource */
private String sub;
/** Resource, can use * as wildcard, e.g., /user/* */
private String obj;
/** Action, HTTP method, can use * as wildcard */
private String act;
public Policy() {}
public Policy(String sub, String obj, String act) {
this.sub = sub;
this.obj = obj;
this.act = act;
}
// getters and setters omitted for brevity
@Override
public String toString() {
return "Policy [sub=" + sub + ", obj=" + obj + ", act=" + act + "]";
}
}3. Usage
1. Permission Control
Implement a servlet filter that uses enforcer.enforce(user, path, method) to decide whether to allow the request.
@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("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 for brevity
}2. Add / Remove Permissions
Expose REST endpoints that call the factory methods to modify policies at runtime; changes take effect immediately without restarting the service.
@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
Combining jCasbin with Spring Cloud (e.g., Zuul) enables unified login and fine‑grained permission control. By defining a custom filter or extending ZuulFilter, you can integrate dynamic RBAC into any microservice architecture.
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.
