在 Spring Boot 2 中集成 JCasbin 并实现 ClassPath 模型文件加载
概述
在现代Web应用开发中,权限管理和认证是不可或缺的一部分。JCasbin 是一个强大的、高效的开源访问控制库,它支持多种访问控制模型,并提供了灵活的策略管理机制。本文将介绍如何在 Spring Boot 2 应用程序中集成 JCasbin,并解决加载 classpath 中 model.conf
文件的问题。我们还将探讨 classpath:
和 classpath*:
的异同,以及如何正确地使用它们。
集成步骤
添加依赖
首先,在项目的 pom.xml
文件中添加 JCasbin 的相关依赖:
<!-- https://mvnrepository.com/artifact/org.casbin/jcasbin -->
<dependency>
<groupId>org.casbin</groupId>
<artifactId>jcasbin</artifactId>
<version>1.78.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.casbin/mybatis-adapter -->
<dependency>
<groupId>org.casbin</groupId>
<artifactId>jdbc-adapter</artifactId>
<version>2.10.0</version>
</dependency>
修改 Config
类以支持 ClassPath 加载
为了解决 JCasbin 的 Config
类默认不支持从 Java ClassPath 加载模型配置文件的问题,我们对 org.casbin.jcasbin.config.Config
类进行重写,修改了 parse
方法,使其能够首先尝试从 ClassPath 加载模型文件,如果失败,则回退到文件系统路径。以下是修改后的 parse
方法代码片段:
private void parse(String fname) {
lock.lock();
try {
// 尝试从classpath加载资源
InputStream is = this.getClass().getResourceAsStream(fname);
if (is == null) {
// 如果classpath中没有,则尝试从文件系统加载
try {
is = new FileInputStream(fname);
} catch (Exception e) {
throw new CasbinConfigException("无法找到文件: " + fname, e.getCause());
}
}
BufferedReader buf = new BufferedReader(new InputStreamReader(is));
parseBuffer(buf);
// 确保关闭流
buf.close();
is.close();
} catch (IOException e) {
throw new CasbinConfigException(e.getMessage(), e.getCause());
} finally {
lock.unlock();
}
}
配置 Casbin
接下来,创建一个配置类来初始化 JCasbin 的 Enforcer 实例,该实例会读取权限规则并执行权限检查。:
@Configuration
public class CasbinConfiguration {
@Bean
public Enforcer enforcer(CasbinProperties properties, DataSource dataSource) throws Exception {
JDBCAdapter adapter = new JDBCAdapter(dataSource);
CustomConfig config = new CustomConfig();
config.loadModelFromText(Files.readString(Paths.get(properties.getModelPath())));
Enforcer enforcer = new Enforcer(config, adapter);
enforcer.loadPolicy();
return enforcer;
}
}
请注意,上面的代码片段假设 properties.getModelPath()
返回的是一个可以直接读取文本的文件路径或 ClassPath 资源路径。根据实际情况,可能需要调整这段代码。dataSource
使用系统的数据源
处理 ClassPath 资源路径
为了正确加载位于 classpath 下的 model.conf
文件,我们需要理解 classpath:
和 classpath*:
之间的差异。getResourceAsStream()
方法是 Java 原生方法,不支持 classpath*:
语法,这是 Spring 框架特有的功能,用于扫描所有 classpath 下的资源,包括从多个 jar 文件中查找资源。
因此,当您需要通过 Java 原生方法加载资源时,请确保只使用 classpath:
或者直接指定文件路径。对于 Spring 框架提供的工具类,如 ResourceLoader
,可以使用 classpath*:
来尝试加载所有匹配的资源。
创建自定义属性类
定义一个配置属性类来接收和设置 application.properties
或 application.yml
中的相关配置。
@Configuration
@ConfigurationProperties(prefix = "casbin")
@Data
public class CasbinProperties {
private String modelPath;
}
实现过滤器进行权限校验
创建一个过滤器,在每次请求到达时,根据用户信息、请求路径和 HTTP 方法调用 Enforcer 进行权限验证。
@Component
@WebFilter(urlPatterns = "/*")
@Order(1)
public class CasbinAuthFilter implements Filter {
private final Enforcer enforcer;
public CasbinAuthFilter(Enforcer enforcer) {
this.enforcer = enforcer;
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
UserAccountVo userAccountVo = WebUtil.getCurUserAccountVo(httpRequest);
if (userAccountVo == null){
chain.doFilter(request, response);
return;
}
String user = userAccountVo.getUserId(); // 从请求头获取用户信息
String path = httpRequest.getRequestURI();
String method = httpRequest.getMethod();
// 进行权限校验
if (enforcer.enforce(user, path, method)) {
chain.doFilter(request, response);
} else {
httpResponse.setStatus(HttpServletResponse.SC_FORBIDDEN);
httpResponse.getWriter().write("没有访问权限");
}
}
}
model.conf配置示例
[request_definition]
r = sub, obj, act
[policy_definition]
p = sub, obj, act
[policy_effect]
e = some(where (p.eft == allow))
[matchers]
m = r.sub == p.sub && keyMatch2(r.obj, p.obj) && regexMatch(r.act, p.act)
CASBIN_RULE表示例
id,ptype,v0,v1,v2
1,p,1,/*,(GET)|(POST)
结论
通过上述步骤,我们成功地在 Spring Boot 2 应用中集成了 JCasbin,并解决了 model.conf
文件的加载问题。同时,我们也了解了 classpath:
和 classpath*:
之间的区别,并掌握了如何正确地在不同场景下使用它们。这对于任何希望在其应用程序中实现细粒度访问控制的开发者来说都是有价值的知识点。此外,通过重写 Config
类中的 parse
方法,我们实现了更加灵活的模型文件加载机制,使得应用程序更易于部署,特别是在打包为 JAR 文件后部署的情况下。