再见了 Shiro!

大家好,我是老赵

1.前言

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

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

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

github地址:https://github.com/casbin/jcasbin

2.准备

基于springboot1.5.10,但是和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.conf 和 policy.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

3.读取权限信息进行初始化

我们要对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 + ];
  }
  
}

4.使用

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

5.最后 

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

来源:blog.csdn.net/WayneLee0809/article/details/85702551

 
 

精彩推荐

1.IntelliJ IDEA终于支持对Redis 的可视化窗口操作了,真香!
2.学计算机的女生后来都怎么样了?
3.从阿里跳槽来的工程师,分享了三套干掉 “重复代码”方式,真的太绝了!
4.吹爆,Nginx 可视化!配置监控一条龙!
5.马斯克亲自组织Code Review,并晒出Twitter架构图,网友们低估其代码能力了!
6.Spring 6.0 正式发布,最强王者登基!!
7.这是我见过最好的微服务 SaaS 快速开发平台。功能完整,代码结构清晰!
8.我用一行log日志,搞崩公司线上系统,直接当着全公司人被批评!
8.SpringBoot超大文件上传,实现秒传!
10.公司新来一个同事,把优惠券系统设计的炉火纯青!
11.一个注解,轻松搞定 Redis 分布式锁!

436c6418e9fc0fa30562f77fd8dddafd.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值