⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 blog.csdn.net/WayneLee0809/article/details/85702551 「红藕香残玉簟秋」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注**微信公众号:【芋道源码】**有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言将得到认真回复。甚至不知道如何读源码也可以请教噢
  4. 新的源码解析文章实时收到通知。每周更新一篇左右
  5. 认真的源码交流微信群。

前言

作为一名后台开发人员,权限这个名词应该算是特别熟悉的了。就算是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即可,其他地方基本没啥区别。

文章目录
  1. 1. 前言
  2. 2. 一、准备
    1. 2.0.1. 1、mavan仓库引入
    2. 2.0.2. 2、配置文件
  • 3. 二、读取权限信息进行初始化
  • 4. 三、使用
    1. 4.0.1. 1、权限控制
    2. 4.0.2. 2、添加删除权限
  • 5. 写在后面的话