Redian新闻
>
再见了 Shiro!

再见了 Shiro!

公众号新闻

原文:
blog.csdn.net/WayneLee0809/article/details/85702551


1、前言
作为一名后台开发人员,权限这个名词应该算是特别熟悉的了。就算是java里的类也有 public、private 等“权限”之分。之前项目里一直使用shiro作为权限管理的框架。说实话,shiro的确挺强大的,但是它也有很多不好的地方。shiro默认的登录地址还是login.jsp,前后端分离模式使用shiro还要重写好多类;手机端存储用户信息、保持登录状态等等,对shiro来说也是一个难题。
在分布式项目里,比如电商项目,其实不太需要明确的权限划分,说白了,我认为没必要做太麻烦的权限管理,一切从简。何况shiro对于springCloud等各种分布式框架来说,简直就是“灾难”。每个子系统里都要写点shiro的东西,慢慢的,越来越恶心。zuul网关就在这里大显身手了,控制用户的登录,鉴定用户的权限等等。zuul网关控制用户登录,鉴权以后再详说。以上拙见。
然后最近我发现了另一个权限框架jcasbin,虽然网上还没有很多关于博客,但是我看了一会就可以使用了。

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


2、准备


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即可,其他地方基本没啥区别。这个以后再写。


往期推荐:


再见,Navicat!!
警报炸锅,FastJson 又立功了。。
高德导航红绿灯为啥能读秒?
为什么 List 原生排序比 stream() 流效率更高?


微信扫码关注该文公众号作者

戳这里提交新闻线索和高质量文章给我们。
相关阅读
美东之行— 纽约的博物馆和画廊全球资管排名出炉,再见了BlackRock...“再见了,蚂蚁借呗!”Linux Shell 介绍:Bash、Zsh 和 Fish | Linux 中国再见,Guava!再见,Ehcache!这才是 Java 本地缓存之王!Shrinking Negative List Marks Shanghai FTZ’s Decade of Progress再见了!Coles Express宣布退出澳洲,正式成为历史再见了,德勤再见了,茅 (台)!再见了!奥特莱斯![汽车] 再见四明山,再见天马浙赛,再见我的小青春再见了,PwC再见!教师节!再见!伪读书人群体!小说:兰欣与乌茶 23再见了,钱德勒,让我们最后抱一下再见了R语言!生信套路大公开,学渣也能快速发表SCI!“你不该相信骗子的,再见了老婆”再见了,婚外恋人!再见了,Twitter再见李玟,再见月光爱人再见了iPhone?生成式AI呼唤新设备 | 熊彼特专栏光学追踪+裸手识别,是时候跟游戏手柄说再见了吗?再见了!奥特莱斯!!!深夜官宣,再见了!再见了,10G的PPT模板?秦刚再见了再见了!澳洲国民冰咖啡官宣全线停产!杂谈牛油果的前世今生和“翠涟虾韵”(三)今后只退休,不准备养老 (多图)酒局chì rè?zhì rè?千词万字“返场赛”来了!上海卖地上演消失的它:再见了,小马甲(待会删)再见了,美团app!!“再见了,蚂蚁花呗!”[电脑] 当 ROG 遇上 XPG——ROG Z790 HERO+XPG ROG 认证内存+太阳神装机展示
logo
联系我们隐私协议©2024 redian.news
Redian新闻
Redian.news刊载任何文章,不代表同意其说法或描述,仅为提供更多信息,也不构成任何建议。文章信息的合法性及真实性由其作者负责,与Redian.news及其运营公司无关。欢迎投稿,如发现稿件侵权,或作者不愿在本网发表文章,请版权拥有者通知本网处理。