再见了 shiro

我是程序汪

共 11219字,需浏览 23分钟

 ·

2023-01-12 20:47

来源:blog.csdn.net/WayneLee0809/
article/details/85702551
  • 前言
  • 一、准备
  • 二、读取权限信息进行初始化
  • 三、使用
  • 写在后面的话

前言

作为一名后台开发人员,权限这个名词应该算是特别熟悉的了。就算是java里的类也有 public、private 等“权限”之分。之前项目里一直使用shiro作为权限管理的框架。说实话,shiro的确挺强大的,但是它也有很多不好的地方。shiro默认的登录地址还是login.jsp,前后端分离模式使用shiro还要重写好多类;手机端存储用户信息、保持登录状态等等,对shiro来说也是一个难题。

在分布式项目里,比如电商项目,其实不太需要明确的权限划分,说白了,我认为没必要做太麻烦的权限管理,一切从简。何况shiro对于springCloud等各种分布式框架来说,简直就是“灾难”。每个子系统里都要写点shiro的东西,慢慢的,越来越恶心。zuul网关就在这里大显身手了,控制用户的登录,鉴定用户的权限等等。zuul网关控制用户登录,鉴权以后再详说。以上拙见。

然后最近我发现了另一个权限框架jcasbin,虽然网上还没有很多关于博客,但是我看了一会就可以使用了。

顺手贴上github地址:https://github.com/casbin/jcasbin


一、准备

基于springboot1.5.10,但是和springboot关系不太大,所以版本可以忽略,用你熟悉的springboot版本就行。

1、mavan仓库引入

<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、配置文件

jcasbin把用户的角色、权限信息(访问路径)放置在配置文件里,然后通过输入流读取配置文件。主要有两个配置文件:model.confpolicy.csv。简单的使用GitHub里都讲了,在此就不再赘述了。

其实也可以读取数据库的角色权限配置。所以我们可以把关于数据库的信息提取出来,可以进行动态设置。

@Configuration
@ConfigurationProperties(prefix = "org.jcasbin")
public class EnforcerConfigProperties {
 
 private String url;
 
 private String driverClassName;
 
 private String username;
 
 private String password;
 
 private String modelPath;
 
 public String getUrl() {
  return url;
 }
 
 public void setUrl(String url) {
  this.url = url;
 }
 
 public String getDriverClassName() {
  return driverClassName;
 }
 
 public void setDriverClassName(String driverClassName) {
  this.driverClassName = driverClassName;
 }
 
 public String getUsername() {
  return username;
 }
 
 public void setUsername(String username) {
  this.username = username;
 }
 
 public String getPassword() {
  return password;
 }
 
 public void setPassword(String password) {
  this.password = password;
 }
 
 public String getModelPath() {
  return modelPath;
 }
 
 public void setModelPath(String modelPath) {
  this.modelPath = modelPath;
 }
 
 @Override
 public String toString() {
  return "EnforcerConfigProperties [url=" + url + ", driverClassName=" + driverClassName + ", username="
    + username + ", password=" + password + ", modelPath=" + modelPath + "]";
 }
 
}

这样我们就可以在application.properties里进行相关配置了。model.conf是固定的文件,之间复制过来放在新建的和src同级的文件夹下即可。policy.csv的内容是可以从数据库读取的。

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.conf

二、读取权限信息进行初始化

我们要对Enforcer这个类初始化,加载配置文件里的信息。所以我们写一个类实现InitializingBean,在容器加载的时候就初始化这个类,方便后续的使用。

@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.
 }
 
 /**
  * 添加权限
  * @param policy
  * @return
  */

 public static boolean addPolicy(Policy policy){
  boolean addPolicy = enforcer.addPolicy(policy.getSub(),policy.getObj(),policy.getAct());
  enforcer.savePolicy();
  
  return addPolicy;
 }
 
 /**
  * 删除权限
  * @param policy
  * @return
  */

 public static boolean removePolicy(Policy policy){
  boolean removePolicy = enforcer.removePolicy(policy.getSub(),policy.getObj(),policy.getAct());
  enforcer.savePolicy();
  
  return removePolicy;
 }
 
 public static Enforcer getEnforcer(){
  return enforcer;
 }
 
 
}

在这个类里,我们注入写好的配置类,然后转为静态的,在afterPropertiesSet方法里实例化Enforcer并加载policy(策略,角色权限/url对应关系)。

同时又写了两个方法,用来添加和删除policy,Policy是自定的一个类,对官方使用的集合/数组进行了封装。

public class Policy {
 /**想要访问资源的用户 或者角色*/
 private String sub;
 
 /**将要访问的资源,可以使用  * 作为通配符,例如/user/* */
 private String obj;
 
 /**用户对资源执行的操作。HTTP方法,GET、POST、PUT、DELETE等,可以使用 * 作为通配符*/
 private String act;
 
 public Policy() {
  super();
 }
 
 /**
  * 
  * @param sub 想要访问资源的用户 或者角色
  * @param obj 将要访问的资源,可以使用  * 作为通配符,例如/user/*
  * @param act 用户对资源执行的操作。HTTP方法,GET、POST、PUT、DELETE等,可以使用 * 作为通配符
  */

 public Policy(String sub, String obj, String act) {
  super();
  this.sub = sub;
  this.obj = obj;
  this.act = act;
 }
 
 public String getSub() {
  return sub;
 }
 
 public void setSub(String sub) {
  this.sub = sub;
 }
 
 public String getObj() {
  return obj;
 }
 
 public void setObj(String obj) {
  this.obj = obj;
 }
 
 public String getAct() {
  return act;
 }
 
 public void setAct(String act) {
  this.act = act;
 }
 
 @Override
 public String toString() {
  return "Policy [sub=" + sub + ", obj=" + obj + ", act=" + act + "]";
 }
 
}

三、使用

1、权限控制

jcasbin的权限控制非常简单,自定义一个过滤器,if判断就可以搞定,没错,就这么简单。

@WebFilter(urlPatterns = "/*" , filterName = "JCasbinAuthzFilter")
@Order(Ordered.HIGHEST_PRECEDENCE)//执行顺序,最高级别最先执行,int从小到大
public class JCasbinAuthzFilter implements Filter {
 
 private static final Logger log = LoggerFactory.getLogger(JCasbinAuthzFilter.class);
 
 private static Enforcer enforcer;
 
 @Override
 public void init(FilterConfig filterConfig) throws ServletException {
 }
 
 @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("无权访问");
   Map<String, Object> result = new HashMap<String, Object>();
            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));
     }
  
 }
 
 @Override
 public void destroy() {
  
 }
 
}

主要是用enforcer.enforce(user, path, method)这个方法对用户、访问资源、方式进行匹配。这里的逻辑可以根据自己的业务来实现。在这个过滤器之前还可以添加一个判断用户是否登录的过滤器。

2、添加删除权限

对于权限的操作,直接调用上面写好的EnforcerFactory里对应的方法即可。并且,可以达到同步的效果。就是不用重启服务器或者其他任何操作,添加或删除用户权限后,用户对应的访问就会收到影响。

 @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();
 }

写在后面的话

其实可以把jcasbin和SpringCloud的zuul结合来实现用户的统一登录和权限控制。自定义一个过滤器继承ZuulFilter即可,其他地方基本没啥区别。

程序汪资料链接

程序汪接的7个私活都在这里,经验整理

Java项目分享  最新整理全集,找项目不累啦 07版

堪称神级的Spring Boot手册,从基础入门到实战进阶

卧槽!字节跳动《算法中文手册》火了,完整版 PDF 开放下载!

卧槽!阿里大佬总结的《图解Java》火了,完整版PDF开放下载!

字节跳动总结的设计模式 PDF 火了,完整版开放下载!

欢迎添加程序汪个人微信 itwang009  进粉丝群或围观朋友圈

浏览 24
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报