Integrating jCasbin Permission Management into Spring Boot Applications
This article demonstrates how to replace Shiro with jCasbin for fine‑grained permission control in a Spring Boot 1.5.10 project, covering Maven dependencies, configuration files, Enforcer initialization, a custom servlet filter, and dynamic policy add/remove APIs.
As a backend developer, the author finds Shiro cumbersome for distributed Spring Cloud projects and introduces jCasbin as a lightweight alternative for permission management.
1. Preparation
Add the following Maven dependencies to your 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>2. Configuration files
Place model.conf in a folder parallel to src and configure policy.csv (or load it from a database). Example application.properties entries:
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.conf3. Enforcer initialization
Create a configuration class EnforcerConfigProperties to bind the above properties, then implement an EnforcerFactory that implements InitializingBean to instantiate the Enforcer and load policies from the database:
@Component
public class EnforcerFactory implements InitializingBean {
private static Enforcer enforcer;
@Autowired
private EnforcerConfigProperties enforcerConfigProperties;
@Override
public void afterPropertiesSet() throws Exception {
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; }
}The Policy class simply holds sub , obj , and act fields with getters/setters.
4. Permission filter
Implement a servlet filter that obtains the current user, request path, and HTTP method, then calls 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 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 {
Map
result = new HashMap<>();
result.put("code", 1001);
result.put("msg", "用户权限不足");
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
}5. Dynamic policy management
Expose REST endpoints that call the static methods of EnforcerFactory to add or remove policies 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 jCasbin can be combined with Spring Cloud Zuul for unified login and permission control, which will be covered in a future article.
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.