在 Spring Boot 2 中集成 JCasbin 并实现 ClassPath 模型文件加载

在 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.propertiesapplication.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 文件后部署的情况下。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值