# Permission
**Repository Path**: nepxion/Permission
## Basic Information
- **Project Name**: Permission
- **Description**: 🎯 Nepxion Permission is a permission system based on Spring Cloud with Nepxion Matrix AOP framework and Aquarius framework, and add permission control to microservice APIs 基于Spring Cloud的微服务注解式API权限框架
- **Primary Language**: Java
- **License**: Apache-2.0
- **Default Branch**: master
- **Homepage**: http://www.nepxion.com
- **GVP Project**: No
## Statistics
- **Stars**: 18
- **Forks**: 14
- **Created**: 2020-07-20
- **Last Updated**: 2025-09-23
## Categories & Tags
**Categories**: authority-management
**Tags**: None
## README
# Nepxion Permission
 [](https://github.com/Nepxion/Permission/blob/master/LICENSE) [](https://search.maven.org/artifact/com.nepxion/permission) [](http://www.javadoc.io/doc/com.nepxion/permission-aop) [](https://github.com/Nepxion/Permission/actions) [](https://www.codacy.com/gh/Nepxion/Permission/dashboard?utm_source=github.com&utm_medium=referral&utm_content=Nepxion/Permission&utm_campaign=Badge_Grade) [](https://github.com/Nepxion/Permission/stargazers) [](https://gitee.com/Nepxion/Permission/stargazers)
Nepxion Permission是一款基于Spring Cloud的微服务API权限框架,并通过Redis分布式缓存进行权限缓存。它采用Nepxion Matrix AOP框架进行切面实现,支持注解调用方式,也支持Rest调用方式
## 简介
- 实现权限自动扫描入库(可通过配置文件开启关闭)
- 实现权限验证从分布式缓存和API调用获取两种方式(缓存获取可通过配置文件开启关闭)
- 实现权限验证走UserId和Token两种方式,通过注解来决定
- 实现提供Feign接口,使用者实现到数据库和缓存数据交互的扩展
- 实现提供显式基于注解的权限验证,参数通过注解传递;实现提供基于Rest请求的权限验证,参数通过Header传递
- 实现根据Java8的特性来获取注解对应方法上的变量名(不是变量类型),支持标准反射和字节码CGLIG(ASM library)来获取,前者适用于接口代理,后者适用于类代理
标准反射的方式,需要在IDE和Maven里设置"-parameters"的Compiler Argument。参考如下:
- Eclipse加"-parameters"参数:https://www.concretepage.com/java/jdk-8/java-8-reflection-access-to-parameter-names-of-method-and-constructor-with-maven-gradle-and-eclipse-using-parameters-compiler-argument
- Idea加"-parameters"参数:http://blog.csdn.net/royal_lr/article/details/52279993
## 注意
Nepxion Permission提供简单易用的AOP框架(参考permission-springcloud-client-example),并非是全面的权限管理和调用系统,鉴于不同公司有不同权限架构,那么使用者需要自行去实现如下模块(参考permission-springcloud-service-example):
- 实现基于权限-角色-用户三层体系的数据库模型(Pojo类已在permission-entity里实现),并提供相关的增删改查接口
- 实现基于界面的权限-角色-用户的操作功能
- 实现和相关用户系统等多对接
- 实现基于权限验证的分布式缓存功能,例如验证缓存和失效(如果使用者有这样的需求)
- 实现基于Token的权限验证功能,和相关单点登录系统等做对接(如果使用者有这样的需求)
- 实现提供UI权限和API GATEWAY权限的接入(如果使用者有这样的需求)
## 兼容
版本兼容情况
- Spring Cloud F版,请采用3.x.x版本,具体代码参考master分支
- Spring Cloud E版,请采用2.x.x版本,具体代码参考2.0.x分支
## 依赖
AOP依赖
```xml
Title: Nepxion Permission
*Description: Nepxion Permission
*Copyright: Copyright (c) 2017-2050
*Company: Nepxion
* @author Haojun Ren * @version 1.0 */ import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import com.nepxion.aquarius.cache.annotation.EnableCache; import com.nepxion.permission.service.annotation.EnablePermissionSerivce; @SpringBootApplication @EnableDiscoveryClient @EnablePermissionSerivce @EnableCache public class PermissionApplication { public static void main(String[] args) { new SpringApplicationBuilder(PermissionApplication.class).run(args); } } ``` 需要实现permission-api的两个Feign接口PermissionResource和UserResource 模拟实现权限对数据库的相关接口,请自行实现相关和数据库,缓存等操作逻辑 ```java package com.nepxion.permission.service.impl; /** *Title: Nepxion Permission
*Description: Nepxion Permission
*Copyright: Copyright (c) 2017-2050
*Company: Nepxion
* @author Haojun Ren * @version 1.0 */ import java.util.List; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; import com.nepxion.permission.api.PermissionResource; import com.nepxion.permission.entity.PermissionEntity; // 该接口实现提供给调用端的Feign接口,需要实现的逻辑是权限数据入库,验证,以及缓存的操作 @RestController public class PermissionResourceImpl implements PermissionResource { private static final Logger LOG = LoggerFactory.getLogger(PermissionResourceImpl.class); // 权限列表入库 @Override public void persist(@RequestBody ListTitle: Nepxion Permission
*Description: Nepxion Permission
*Copyright: Copyright (c) 2017-2050
*Company: Nepxion
* @author Haojun Ren * @version 1.0 */ import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController; import com.nepxion.permission.api.UserResource; import com.nepxion.permission.entity.UserEntity; @RestController public class UserResourceImpl implements UserResource { private static final Logger LOG = LoggerFactory.getLogger(UserResourceImpl.class); // 根据Token获取User实体 @Override public UserEntity getUser(@PathVariable(value = "token") String token) { // 当前端登录后,它希望送token到后端,查询出用户信息(并以此调用authorize接口做权限验证,permission-aop已经实现,使用者并不需要关心) // 需要和单点登录系统,例如OAuth或者JWT等系统做对接 // 示例描述token为abcd1234对应的用户为lisi LOG.info("Token:{}", token); if (StringUtils.equals(token, "abcd1234")) { UserEntity user = new UserEntity(); user.setUserId("lisi"); user.setUserType("LDAP"); return user; } return null; } } ``` ### 模拟业务服务端 业务服务端配置 ``` # Spring cloud config spring.application.name=permission-springcloud-my-service-example server.port=1234 eureka.instance.metadataMap.owner=Haojun Ren eureka.client.serviceUrl.defaultZone=http://10.0.75.1:9528/eureka/ # Ribbon config ribbon.ReadTimeout=60000 ribbon.ConnectTimeout=60000 # Permission config # 权限拦截开启和关闭,不加这行,视为开启 permission.enabled=true # 权限系统的服务名,作为Feign的寻址名 permission.service.name=permission-springcloud-service-example # 扫描含有@Permission注解的接口或者类所在目录 permission.scan.packages=com.nepxion.permission.example.client.service # 如果开启,默认每次服务启动时候,会往权限系统的数据库插入权限(权限不存在则插入,权限存在则覆盖) permission.automatic.persist.enabled=true # 权限自动入库第一次失败后,还有重试的机会。下面配置项为重试的次数 permission.automatic.persist.retry.times=5 # 权限自动入库第一次失败后,还有重试的机会。下面配置项为每次重试的间隔时间 permission.automatic.persist.retry.interval=10000 # 权限系统验证拦截的用户类型白名单(例如用户类型是LDAP,那么对LDAP的用户做权限验证拦截),多个值以“;”分隔 permission.user.type.whitelist=LDAP # Cache config prefix=permission cache.enabled=true cache.type=redisCache # 当切面拦截出现异常,如果忽略该异常,则不影响当前业务方法调用,否则中断当前业务方法调用,缺省为true # cache.aop.exception.ignore=true # 全局缓存过期值,单位毫秒(小于等于零,表示永不过期),当注解上没配置该值的时候,以全局值为准,缺省为-1 # cache.expire=-1 # 扫描含有@Cacheable,@CacheEvict,@CachePut等注解的接口或者类所在目录 cache.scan.packages=com.nepxion.permission # Redis config spring.redis.host=localhost spring.redis.port=6379 spring.redis.password= spring.redis.database=0 spring.redis.pool.max-active=8 spring.redis.pool.max-wait=-1 spring.redis.pool.max-idle=8 spring.redis.pool.min-idle=0 # Frequent log print frequent.log.print=true ``` SpringCloud应用入口,需要加上@EnablePermission注解激活权限拦截功能(当然也可以在配置文件里面permission.enabled=false关闭它),@EnableCache从缓存获取权限数据 ```java package com.nepxion.permission.example.service; /** *Title: Nepxion Permission
*Description: Nepxion Permission
*Copyright: Copyright (c) 2017-2050
*Company: Nepxion
* @author Haojun Ren * @version 1.0 */ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.netflix.feign.EnableFeignClients; import org.springframework.context.ConfigurableApplicationContext; import com.nepxion.aquarius.cache.annotation.EnableCache; import com.nepxion.permission.annotation.EnablePermission; @SpringBootApplication @EnableDiscoveryClient @EnableFeignClients(basePackages = { "com.nepxion.permission.api" }) @EnablePermission @EnableCache public class MyApplication { private static final Logger LOG = LoggerFactory.getLogger(MyApplication.class); public static void main(String[] args) { ConfigurableApplicationContext applicationContext = SpringApplication.run(MyApplication.class, args); MyController myController = applicationContext.getBean(MyController.class); try { LOG.info("Result : {}", myController.doA("zhangsan", "LDAP", "valueA")); } catch (Exception e) { LOG.error("Error", e); } try { LOG.info("Result : {}", myController.doB("abcd1234", "valueB")); } catch (Exception e) { LOG.error("Error", e); } } } ``` 在RestController添加@Permission注解,实现API权限验证功能 ```java package com.nepxion.permission.example.service; /** *Title: Nepxion Permission
*Description: Nepxion Permission
*Copyright: Copyright (c) 2017-2050
*Company: Nepxion
* @author Haojun Ren * @version 1.0 */ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; import com.nepxion.permission.annotation.Permission; import com.nepxion.permission.annotation.Token; import com.nepxion.permission.annotation.UserId; import com.nepxion.permission.annotation.UserType; @RestController public class MyController { private static final Logger LOG = LoggerFactory.getLogger(MyController.class); // 显式基于UserId和UserType注解的权限验证,参数通过注解传递 @RequestMapping(path = "/doA/{userId}/{userType}/{value}", method = RequestMethod.GET) @Permission(name = "A-Permission", label = "A权限", description = "A权限的描述") public int doA(@PathVariable(value = "userId") @UserId String userId, @PathVariable(value = "userType") @UserType String userType, @PathVariable(value = "value") String value) { LOG.info("===== doA被调用"); return 123; } // 显式基于Token注解的权限验证,参数通过注解传递 @RequestMapping(path = "/doB/{token}/{value}", method = RequestMethod.GET) @Permission(name = "B-Permission", label = "B权限", description = "B权限的描述") public String doB(@PathVariable(value = "token") @Token String token, @PathVariable(value = "value") String value) { LOG.info("----- doB被调用"); return "abc"; } // 隐式基于Rest请求的权限验证,参数通过Header传递 @RequestMapping(path = "/doC/{value}", method = RequestMethod.GET) @Permission(name = "C-Permission", label = "C权限", description = "C权限的描述") public boolean doC(@PathVariable(value = "value") String value) { LOG.info("----- doC被调用"); return true; } } ``` ### 模拟业务客户端,基于Feign调用 业务客户端配置 ``` # Spring cloud config spring.application.name=permission-springcloud-my-client-example server.port=1212 eureka.client.serviceUrl.defaultZone=http://10.0.75.1:9528/eureka/ # Ribbon config ribbon.ReadTimeout=60000 ribbon.ConnectTimeout=60000 # Permission config # 权限Feign拦截开启和关闭,不加这行,视为开启 permission.feign.enabled=true ``` SpringCloud应用入口,需要加上@EnablePermissionFeign注解激活权限Feign拦截功能(当然也可以在配置文件里面permission.feign.enabled=false关闭它),该注解可以把Rest调用的Header数据传送到后端业务服务来 ```java package com.nepxion.permission.example.client; /** *Title: Nepxion Permission
*Description: Nepxion Permission
*Copyright: Copyright (c) 2017-2050
*Company: Nepxion
* @author Haojun Ren * @version 1.0 */ import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.netflix.feign.EnableFeignClients; import com.nepxion.permission.feign.annotation.EnablePermissionFeign; @SpringBootApplication @EnableDiscoveryClient @EnableFeignClients @EnablePermissionFeign public class MyApplication { public static void main(String[] args) { new SpringApplicationBuilder(MyApplication.class).run(args); } } ``` 基于Feign的调用 ```java package com.nepxion.permission.example.client; /** *Title: Nepxion Permission
*Description: Nepxion Permission
*Copyright: Copyright (c) 2017-2050
*Company: Nepxion
* @author Haojun Ren * @version 1.0 */ import org.springframework.cloud.netflix.feign.FeignClient; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @FeignClient(value = "permission-springcloud-my-service-example") public interface MyFeign { @RequestMapping(path = "/doA/{userId}/{userType}/{value}", method = RequestMethod.GET) int doA(@PathVariable(value = "userId") String userId, @PathVariable(value = "userType") String userType, @PathVariable(value = "value") String value); @RequestMapping(path = "/doB/{token}/{value}", method = RequestMethod.GET) String doB(@PathVariable(value = "token") String token, @PathVariable(value = "value") String value); @RequestMapping(path = "/doC/{value}", method = RequestMethod.GET) boolean doC(@PathVariable(value = "value") String value); } ``` ```java package com.nepxion.permission.example.client; /** *Title: Nepxion Permission
*Description: Nepxion Permission
*Copyright: Copyright (c) 2017-2050
*Company: Nepxion
* @author Haojun Ren * @version 1.0 */ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; @RestController public class MyController { @Autowired private MyFeign myFeign; @RequestMapping(path = "/doA/{userId}/{userType}/{value}", method = RequestMethod.GET) public int doA(@PathVariable(value = "userId") String userId, @PathVariable(value = "userType") String userType, @PathVariable(value = "value") String value) { return myFeign.doA(userId, userType, value); } @RequestMapping(path = "/doB/{token}/{value}", method = RequestMethod.GET) public String doB(@PathVariable(value = "token") String token, @PathVariable(value = "value") String value) { return myFeign.doB(token, value); } @RequestMapping(path = "/doC/{value}", method = RequestMethod.GET) public boolean doC(@PathVariable(value = "value") String value) { return myFeign.doC(value); } } ``` ### 基于注解调用结果 ``` permission 2018-01-18 17:18:33,382 INFO [main] c.n.p.a.PermissionInterceptor [PermissionInterceptor.java:103] - Intercepted for annotation - Permission [name=A-Permission, label=A权限, description=, proxyType=Reflective Aop Proxy, proxiedClass=com.nepxion.permission.service.MyServiceImpl, method=doA] permission 2018-01-18 17:18:33,442 INFO [main] c.n.a.c.a.CacheInterceptor [CacheInterceptor.java:120] - Intercepted for annotation - Cacheable [key=permission_cache_zhangsan_LDAP_A-Permission_SERVICE_permission-springcloud-example, expire=-1, proxyType=Cglib Aop Proxy, proxiedClass=com.nepxion.permission.aop.PermissionAuthorization, method=authorizeCache] permission 2018-01-18 17:18:33,582 INFO [main] c.n.a.c.r.i.RedisCacheDelegateImpl [RedisCacheDelegateImpl.java:54] - Before invocation, Cacheable key=permission_cache_zhangsan_LDAP_A-Permission_SERVICE_permission-springcloud-example, cache=true in Redis permission 2018-01-18 17:18:33,582 INFO [main] c.n.p.s.MyServiceImpl [MyServiceImpl.java:22] - ===== doA被调用 permission 2018-01-18 17:18:33,582 INFO [main] c.n.p.MyApplication [MyApplication.java:30] - Result : 123 permission 2018-01-18 17:18:33,582 INFO [main] c.n.p.a.PermissionInterceptor [PermissionInterceptor.java:103] - Intercepted for annotation - Permission [name=B-Permission, label=B权限, description=B权限的描述, proxyType=Reflective Aop Proxy, proxiedClass=com.nepxion.permission.service.MyServiceImpl, method=doB] permission 2018-01-18 17:18:33,584 INFO [main] c.n.a.c.a.CacheInterceptor [CacheInterceptor.java:120] - Intercepted for annotation - Cacheable [key=permission_cache_lisi_LDAP_B-Permission_SERVICE_permission-springcloud-example, expire=-1, proxyType=Cglib Aop Proxy, proxiedClass=com.nepxion.permission.aop.PermissionAuthorization, method=authorizeCache] permission 2018-01-18 17:18:33,585 INFO [main] c.n.a.c.r.i.RedisCacheDelegateImpl [RedisCacheDelegateImpl.java:54] - Before invocation, Cacheable key=permission_cache_lisi_LDAP_B-Permission_SERVICE_permission-springcloud-example, cache=false in Redis Exception in thread "main" com.nepxion.permission.exception.PermissionException: No permision to proceed method [name=doB, parameterTypes=java.lang.String,java.lang.String], permissionName=B-Permission, permissionLabel=B权限 at com.nepxion.permission.aop.PermissionInterceptor.invokePermission(PermissionInterceptor.java:131) at com.nepxion.permission.aop.PermissionInterceptor.invoke(PermissionInterceptor.java:73) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213) at com.sun.proxy.$Proxy64.doB(Unknown Source) at com.nepxion.permission.MyApplication.main(MyApplication.java:31) ``` ### 基于Rest调用结果 基于UserId和UserType的权限验证。如图所示,该用户对该API有权限  基于Token的权限验证。如图所示,该Token对应的用户对该API无权限  ## 请联系我 微信、钉钉、公众号和文档  ## Star走势图 [](https://starchart.cc/Nepxion/Permission)