该组件支持mysql,Redis,可灵活配置
entity
@Getter
@Setter
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class PermissionUrL {
private String id;
private String url;
private String requestType;
private String serverName;
private Integer whiteList;
}
@Getter
@Setter
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class RoleUrL {
private String id;
private String roleCode;
private String url;
private String serverName;
}
annotation
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RbacPermission {
String[] role() default {};
boolean isWhiteList() default false;
}
constants
public class RbacPermissionConstants {
public static final String PRE_URL="prefix_url_set:";
public static final String PRE_ROLE_URL="prefix_role_url_set:";
}
context
@Data
public class RbacContext {
private Collection<Object> controllerList;
private String applicationName;
}
config
@Configuration
@EnableConfigurationProperties({RbacPermissionProperties.class})
public class RbacPermissionAutoConfig {
@Bean
@ConditionalOnProperty(prefix = "rbac.redis", value = { "host", "password", "port"})
public RbacDatasource redisDatasource(RbacPermissionProperties rbacPermissionProperties) {
return new RedisDatasource(rbacPermissionProperties.getRedis());
}
@Bean
@ConditionalOnProperty(prefix = "rbac.mysql", value = {"url", "username", "password"})
public RbacDatasource mysqlDatasource(RbacPermissionProperties rbacPermissionProperties) throws SQLException,InitException {
return new MysqlDatasource(rbacPermissionProperties.getMysql());
}
}
exception
public class InitException extends RuntimeException {
public InitException(String msg) {
super(msg);
}
}
datebase.mysql
public class RbacPermissionDaoImpl implements RbacPermissionDao {
SqlSessionFactory sqlSessionFactory
public RbacPermissionDaoImpl(DataSource dataSource) {
TransactionFactory transactionFactory = new JdbcTransactionFactory()
Environment environment = new Environment("rbac", transactionFactory, dataSource)
Configuration configuration = new Configuration(environment)
configuration.addMapper(RbacPermissionMapper.class)
sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration)
}
@Override
public boolean init() {
// testDB
return true
}
@Override
public boolean store(RbacContext rbacContext) {
//获取所有Controller
Collection<Object> values = rbacContext.getControllerList()
if (CollectionUtils.isEmpty(values)) {
return false
}
//获取服务名称
String serverName = rbacContext.getApplicationName()
for (Object controller : values) {
//获取controller的字节码对象
Class<?> clzz = controller.getClass()
Class<?> clzz1 = controller.getClass().getSuperclass()
List<Method> methods1 = Arrays.stream(clzz1.getDeclaredMethods()).collect(Collectors.toList())
List<Method> methods = Arrays.stream(clzz.getDeclaredMethods()).collect(Collectors.toList())
methods1.addAll(methods)
for (Method method : methods1) {
RbacPermission ann = method.getAnnotation(RbacPermission.class)
if (ann != null) {
List<String> roles = Arrays.asList(ann.role())
Integer isWhiteList = ann.isWhiteList()?1:2
//获取当前url
String path1 = ""
String path2 = ""
String methodType = ""
if (clzz.isAnnotationPresent(RequestMapping.class)) {
RequestMapping annotation = clzz.getAnnotation(RequestMapping.class)
path1 = annotation.value()[0]
}
if (clzz1.isAnnotationPresent(RequestMapping.class)) {
RequestMapping annotation1 = clzz1.getAnnotation(RequestMapping.class)
path1 = annotation1.value()[0]
}
if (method.isAnnotationPresent(RequestMapping.class)) {
path2 = method.getAnnotation(RequestMapping.class).value()[0]
}
if (method.isAnnotationPresent(GetMapping.class)) {
path2 = method.getAnnotation(GetMapping.class).value()[0]
methodType = "GET"
}
if (method.isAnnotationPresent(PostMapping.class)) {
path2 = method.getAnnotation(PostMapping.class).value()[0]
methodType = "POST"
}
if (method.isAnnotationPresent(DeleteMapping.class)) {
path2 = method.getAnnotation(DeleteMapping.class).value()[0]
methodType = "DELETE"
}
String url = "/" + serverName + path1 + path2
//处理url
dealPermissionUrls(serverName, isWhiteList, methodType, url)
//处理roleUrl,非白名单配置权限
if(isWhiteList!=1){
for (String role : roles) {
//根据role和url查
RoleUrl hasRoleUrl=getHasRoleUrl(role,url)
if (hasRoleUrl == null) {
RoleUrl roleUrl = new RoleUrl()
String roleUrlId = SnowflakeIdWorkerUtils.getNextId()
roleUrl.setId(roleUrlId)
roleUrl.setRoleCode(role)
roleUrl.setUrl(url)
roleUrl.setServerName(serverName)
this.saveRoleUrl(roleUrl)
}
}
}
}
}
}
return true
}
private void saveRoleUrl(RoleUrl roleUrl) {
SqlSession sqlSession = sqlSessionFactory.openSession()
try {
RbacPermissionMapper mapper = sqlSession.getMapper(RbacPermissionMapper.class)
mapper.saveRoleUrl(roleUrl)
sqlSession.commit()
}finally {
sqlSession.close()
}
}
@Override
public List<RoleUrl> selectRoleUrlByServerName(String serverName) {
SqlSession sqlSession = sqlSessionFactory.openSession()
try {
RbacPermissionMapper mapper = sqlSession.getMapper(RbacPermissionMapper.class)
return mapper.selectRoleUrlByServerName(serverName)
}finally {
sqlSession.close()
}
}
@Override
public List<PermissionUrl> selectByServerName(String serverName) {
SqlSession sqlSession = sqlSessionFactory.openSession()
try {
RbacPermissionMapper mapper = sqlSession.getMapper(RbacPermissionMapper.class)
return mapper.selectByServerName(serverName)
}finally {
sqlSession.close()
}
}
private RoleUrl getHasRoleUrl(String role, String url) {
SqlSession sqlSession = sqlSessionFactory.openSession()
try {
RbacPermissionMapper mapper = sqlSession.getMapper(RbacPermissionMapper.class)
return mapper.selectByRoleAndUrl(role,url)
}finally {
sqlSession.close()
}
}
private void dealPermissionUrls(String serverName, Integer isWhiteList, String methodType, String url) {
//根据url查询
PermissionUrl hasUrl =this.getHasUrl(serverName, url)
if(hasUrl==null){
PermissionUrl permissionUrl = new PermissionUrl()
String urlId = SnowflakeIdWorkerUtils.getNextId()
permissionUrl.setId(urlId)
permissionUrl.setServerName(serverName)
permissionUrl.setUrl(url)
permissionUrl.setRequestType(methodType)
permissionUrl.setWhiteList(isWhiteList)
this.saveUrl(permissionUrl)
}else{
if(hasUrl.getWhiteList().intValue()!= isWhiteList){
hasUrl.setWhiteList(isWhiteList)
this.updateUrl(hasUrl)
}
}
}
private void saveUrl(PermissionUrl permissionUrl) {
SqlSession sqlSession = sqlSessionFactory.openSession()
try {
RbacPermissionMapper mapper = sqlSession.getMapper(RbacPermissionMapper.class)
mapper.saveUrl(permissionUrl)
sqlSession.commit()
}finally {
sqlSession.close()
}
}
private void updateUrl(PermissionUrl hasUrl) {
SqlSession sqlSession = sqlSessionFactory.openSession()
try {
RbacPermissionMapper mapper = sqlSession.getMapper(RbacPermissionMapper.class)
mapper.updateUrl(hasUrl)
sqlSession.commit()
}finally {
sqlSession.close()
}
}
/**
* 根据服务名称查带注解的url
* @param serverName
* @return
*/
private PermissionUrL getHasUrl(String serverName, String url) {
SqlSession sqlSession = sqlSessionFactory.openSession()
try {
RbacPermissionMapper mapper = sqlSession.getMapper(RbacPermissionMapper.class)
return mapper.selectByServerNameAndUrl(serverName,url)
}finally {
sqlSession.close()
}
}
}
public interface RbacPermissionDao {
boolean init();
boolean store(RbacContext rbacContext);
List<PermissionUrl> selectByServerName(String serverName);
List<RoleUrl> selectRoleUrlByServerName(String serverName);
}
@Mapper
public interface RbacPermissionMapper {
@Results({
@Result(property = "id", column = "id"),
@Result(property = "url", column = "url"),
@Result(property = "requestType", column = "request_type"),
@Result(property = "serverName", column = "server_name"),
@Result(property = "whiteList", column = "white_list")
})
@Select("select * from rbac_permission_url where server_name=#{serverName} and url=#{url}")
PermissionUrl selectByServerNameAndUrl(@Param("serverName") String serverName, @Param("url") String url);
@Update("update rbac_permission_url set white_list=#{whiteList} where id=#{id}")
void updateUrl(PermissionUrl hasUrl);
@Results({
@Result(property = "id", column = "id"),
@Result(property = "url", column = "url"),
@Result(property = "roleCode", column = "role_code"),
@Result(property = "serverName", column = "server_name"),
})
@Select("select * from rbac_role_url where role_code=#{role} and url=#{url}")
RoleUrl selectByRoleAndUrl(@Param("role") String role, @Param("url") String url);
@Insert("insert into rbac_permission_url(id,url,request_type,server_name,white_list)" +
"values(#{id},#{url},#{requestType},#{serverName},#{whiteList})")
int saveUrl(PermissionUrl permissionUrl);
@Insert("insert into rbac_role_url(id,role_code,url,server_name)values(#{id},#{roleCode},#{url},#{serverName})")
int saveRoleUrl(RoleUrl roleUrl);
@Results({
@Result(property = "id", column = "id"),
@Result(property = "url", column = "url"),
@Result(property = "requestType", column = "request_type"),
@Result(property = "serverName", column = "server_name"),
@Result(property = "whiteList", column = "white_list")
})
@Select("select * from rbac_permission_url where server_name=#{serverName}")
List<PermissionUrl> selectByServerName(@Param("serverName") String serverName);
@Results({
@Result(property = "id", column = "id"),
@Result(property = "url", column = "url"),
@Result(property = "roleCode", column = "role_code"),
@Result(property = "serverName", column = "server_name"),
})
@Select("select * from rbac_role_url where server_name=#{serverName}")
List<RoleUrl> selectRoleUrlByServerName(@Param("serverName") String serverName);
}
public class MysqlDatasource extends DefaultRbacDatasource {
DruidDataSource dataSource;
RbacPermissionDao dao;
public MysqlDatasource(DataSourceProperties properties) throws SQLException, InitException {
dataSource = new DruidDataSource();
dataSource.setDriverClassName(properties.getDriverClassName());
dataSource.setUrl(properties.getUrl());
dataSource.setUsername(properties.getUsername());
dataSource.setPassword(properties.getPassword());
dataSource.init();
dao = new RbacPermissionDaoImpl(dataSource);
}
@Override
public Boolean init() {
return dao.init();
}
@Override
public boolean auth(List<String> roles, String url, String serverName) {
List<PermissionUrl> hasUrls = dao.selectByServerName(serverName);
if (CollectionUtils.isEmpty(hasUrls)) {
return true;
}
List<String> us = hasUrls.stream().map(x -> x.getUrl()).collect(Collectors.toList());
List<String> whiteListUs = hasUrls.stream().filter(y -> 1 == y.getWhiteList()).map(x -> x.getUrl()).collect(Collectors.toList());
if (!CollectionUtils.isEmpty(whiteListUs) && whiteListUs.contains(url)) {
return true;
}
if (!us.contains(url)) {
return true;
} else {
if (CollectionUtils.isEmpty(roles)) {
return false;
}
List<RoleUrl> hasRoleUrls = dao.selectRoleUrlByServerName(serverName);
return !hasRoleUrls.stream().noneMatch(x -> roles.contains(x.getRoleCode()) && url.equals(x.getUrl()));
}
}
@Override
public void store(RbacContext rbacContext) {
dao.store(rbacContext);
}
}
datebase.redis
public interface RbacPermissionClientService {
boolean store(RbacContext rbacContext);
List<PermissionUrl> getHasWithAnnotationUrls(String serverName);
List<RoleUrl> getHasRoleUrls(String serverName);
}
public class RbacPermissionClient implements RbacPermissionClientService {
private MyRedisUtil myRedisUtil
public RbacPermissionClient(MyRedisUtil myRedisUtil) {
this.myRedisUtil=myRedisUtil
}
@Override
public boolean store(RbacContext rbacContext) {
Collection<Object> values = rbacContext.getControllerList()
if (CollectionUtils.isEmpty(values)) {
return false
}
Map<String, Object> saveUrls = new HashMap<>()
Map<String, Object> saveRoleUrls = new HashMap<>()
//获取服务名称
String serverName = rbacContext.getApplicationName()
//已经存在的urls
List<PermissionUrl> hasUrls = this.getHasWithAnnotationUrls(serverName)
//已经存在的role-urls
List<RoleUrl> hasRoleUrls = getHasRoleUrls(serverName)
//获取所有Controller
for (Object controller : values) {
//获取controller的字节码对象
Class<?> clzz = controller.getClass()
Class<?> clzz1 = controller.getClass().getSuperclass()
List<Method> methods1 = Arrays.stream(clzz1.getDeclaredMethods()).collect(Collectors.toList())
List<Method> methods = Arrays.stream(clzz.getDeclaredMethods()).collect(Collectors.toList())
methods1.addAll(methods)
for (Method method : methods1) {
RbacPermission ann = method.getAnnotation(RbacPermission.class)
if (ann != null) {
List<String> roles = Arrays.asList(ann.role())
Integer isWhiteList = ann.isWhiteList() ? 1 : 2
//获取当前url
String path1 = ""
String path2 = ""
String methodType = ""
if (clzz.isAnnotationPresent(RequestMapping.class)) {
RequestMapping annotation = clzz.getAnnotation(RequestMapping.class)
path1 = annotation.value()[0]
}
if (clzz1.isAnnotationPresent(RequestMapping.class)) {
RequestMapping annotation1 = clzz1.getAnnotation(RequestMapping.class)
path1 = annotation1.value()[0]
}
if (method.isAnnotationPresent(RequestMapping.class)) {
path2 = method.getAnnotation(RequestMapping.class).value()[0]
}
if (method.isAnnotationPresent(GetMapping.class)) {
path2 = method.getAnnotation(GetMapping.class).value()[0]
methodType = "GET"
}
if (method.isAnnotationPresent(PostMapping.class)) {
path2 = method.getAnnotation(PostMapping.class).value()[0]
methodType = "POST"
}
if (method.isAnnotationPresent(DeleteMapping.class)) {
path2 = method.getAnnotation(DeleteMapping.class).value()[0]
methodType = "DELETE"
}
String url = "/" + serverName + path1 + path2
PermissionUrl hasPermissionUrl=null
if(!CollectionUtils.isEmpty(hasUrls)){
hasPermissionUrl = Optional.ofNullable(hasUrls.stream().filter(x -> url.equals(x.getUrl())).findFirst().get()).orElse(null)
}
if (CollectionUtils.isEmpty(hasUrls) || hasPermissionUrl == null) {
PermissionUrl permissionUrl = new PermissionUrl()
String urlId = SnowflakeIdWorkerUtils.getNextId()
permissionUrl.setId(urlId)
permissionUrl.setServerName(serverName)
permissionUrl.setUrl(url)
permissionUrl.setRequestType(methodType)
permissionUrl.setWhiteList(isWhiteList)
saveUrls.put(urlId, permissionUrl)
} else {
if (hasPermissionUrl.getWhiteList().intValue() != isWhiteList) {
hasPermissionUrl.setWhiteList(isWhiteList)
saveUrls.put(hasPermissionUrl.getId(), hasPermissionUrl)
}
}
for (String role : roles) {
if (!CollectionUtils.isEmpty(hasRoleUrls)) {
List<RoleUrl> collect = hasRoleUrls.stream().filter(x -> role.equals(x.getRoleCode()) && url.equals(x.getUrl())).collect(Collectors.toList())
if (CollectionUtils.isEmpty(collect)) {
RoleUrl roleUrl = new RoleUrl()
String roleUrlId = SnowflakeIdWorkerUtils.getNextId()
roleUrl.setId(roleUrlId)
roleUrl.setRoleCode(role)
roleUrl.setUrl(url)
roleUrl.setServerName(serverName)
saveRoleUrls.put(roleUrlId, roleUrl)
}
} else {
RoleUrl roleUrl = new RoleUrl()
String roleUrlId = SnowflakeIdWorkerUtils.getNextId()
roleUrl.setId(roleUrlId)
roleUrl.setRoleCode(role)
roleUrl.setUrl(url)
roleUrl.setServerName(serverName)
saveRoleUrls.put(roleUrlId, roleUrl)
}
}
}
}
}
myRedisUtil.hmset(RbacPermissionConstants.PRE_URL + serverName, saveUrls)
myRedisUtil.hmset(RbacPermissionConstants.PRE_ROLE_URL + serverName, saveRoleUrls)
return true
}
@Override
public List<RoleUrl> getHasRoleUrls(String serverName) {
Map<Object, Object> map1 = myRedisUtil.hmget(RbacPermissionConstants.PRE_ROLE_URL + serverName)
List<RoleUrl> hasRoleUrls = new ArrayList<>()
if (map1 != null) {
map1.forEach((k, v) -> {
ObjectMapper objectMapper = new ObjectMapper()
RoleUrl u = objectMapper.convertValue(v, RoleUrl.class)
hasRoleUrls.add(u)
})
}
return hasRoleUrls
}
@Override
public List<PermissionUrl> getHasWithAnnotationUrls(String serverName) {
Map<Object, Object> map = myRedisUtil.hmget(RbacPermissionConstants.PRE_URL + serverName)
List<PermissionUrl> hasUrls = new ArrayList<>()
if (map != null) {
map.forEach((k, v) -> {
ObjectMapper objectMapper = new ObjectMapper()
PermissionUrl us = objectMapper.convertValue(v, PermissionUrl.class)
hasUrls.add(us)
})
}
return hasUrls
}
}
public class RedisDatasource extends DefaultRbacDatasource {
RbacPermissionProperties.RedisConf redisConf;
RedisTemplate<String, Object> redisTemplate;
MyRedisUtil myRedisUtil;
RbacPermissionClientService permissionClientService;
public RedisDatasource(RbacPermissionProperties.RedisConf redisConf) {
this.redisConf = redisConf;
Jackson2JsonRedisSerializer<Object> valueSerializer = jackson2JsonRedisSerializer();
StringRedisSerializer keySerializer = new StringRedisSerializer();
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(connectionFactory());
redisTemplate.setKeySerializer(keySerializer);
redisTemplate.setValueSerializer(valueSerializer);
redisTemplate.setHashKeySerializer(keySerializer);
redisTemplate.setHashValueSerializer(valueSerializer);
this.redisTemplate = redisTemplate;
this.redisTemplate.afterPropertiesSet();
myRedisUtil=new MyRedisUtil(redisTemplate);
permissionClientService=new RbacPermissionClient(myRedisUtil);
}
@Override
public Boolean init() {
return true;
}
@Override
public boolean auth(List<String> roles, String url, String serverName) {
List<PermissionUrl> hasUrls = permissionClientService.getHasWithAnnotationUrls(serverName);
if (CollectionUtils.isEmpty(hasUrls)) {
return true;
}
List<String> us = hasUrls.stream().map(x -> x.getUrl()).collect(Collectors.toList());
List<String> whiteListUs = hasUrls.stream().filter(y -> 1 == y.getWhiteList()).map(x -> x.getUrl()).collect(Collectors.toList());
if (!CollectionUtils.isEmpty(whiteListUs)&&whiteListUs.contains(url)) {
return true;
}
if (!us.contains(url)) {
return true;
} else {
if (CollectionUtils.isEmpty(roles)) {
return false;
}
List<RoleUrl> hasRoleUrls = permissionClientService.getHasRoleUrls(serverName);
return !hasRoleUrls.stream().noneMatch(x -> roles.contains(x.getRoleCode()) && url.equals(x.getUrl()));
}
}
@Override
public void store(RbacContext rbacContext) {
permissionClientService.store(rbacContext);
}
public Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer() {
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json().build();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
return jackson2JsonRedisSerializer;
}
public LettuceConnectionFactory connectionFactory() {
GenericObjectPoolConfig<?> config = new GenericObjectPoolConfig<>();
config.setMaxTotal(redisConf.getLettuce().getPool().getMaxActive());
config.setMaxIdle(redisConf.getLettuce().getPool().getMaxIdle());
config.setMinIdle(redisConf.getLettuce().getPool().getMinIdle());
if (redisConf.getLettuce().getPool().getTimeBetweenEvictionRuns() != null) {
config.setTimeBetweenEvictionRunsMillis(redisConf.getLettuce().getPool().getTimeBetweenEvictionRuns().toMillis());
}
if (redisConf.getLettuce().getPool().getMaxWait() != null) {
config.setMaxWaitMillis(redisConf.getLettuce().getPool().getMaxWait().toMillis());
}
LettucePoolingClientConfiguration.LettucePoolingClientConfigurationBuilder
builder = LettucePoolingClientConfiguration.builder().
commandTimeout(redisConf.getTimeout());
builder.poolConfig(config);
LettuceClientConfiguration lettuceClientConfiguration = builder.build();
LettuceConnectionFactory lettuceConnectionFactory = createLettuceConnectionFactory(lettuceClientConfiguration);
lettuceConnectionFactory.afterPropertiesSet();
return lettuceConnectionFactory;
}
private LettuceConnectionFactory createLettuceConnectionFactory(LettuceClientConfiguration clientConfiguration) {
if (getSentinelConfig() != null) {
return new LettuceConnectionFactory(getSentinelConfig(), clientConfiguration);
}
if (getClusterConfiguration() != null) {
return new LettuceConnectionFactory(getClusterConfiguration(), clientConfiguration);
}
return new LettuceConnectionFactory(getStandaloneConfig(), clientConfiguration);
}
protected final RedisStandaloneConfiguration getStandaloneConfig() {
RedisStandaloneConfiguration config = new RedisStandaloneConfiguration();
if (StringUtils.hasText(redisConf.getUrl())) {
ConnectionInfo connectionInfo = parseUrl(redisConf.getUrl());
config.setHostName(connectionInfo.getHostName());
config.setPort(connectionInfo.getPort());
config.setPassword(RedisPassword.of(connectionInfo.getPassword()));
} else {
config.setHostName(redisConf.getHost());
config.setPort(redisConf.getPort());
config.setPassword(RedisPassword.of(redisConf.getPassword()));
}
config.setDatabase(redisConf.getDatabase());
return config;
}
protected final RedisSentinelConfiguration getSentinelConfig() {
RbacPermissionProperties.RedisConf.Sentinel sentinelProperties = redisConf.getSentinel();
if (sentinelProperties != null) {
RedisSentinelConfiguration config = new RedisSentinelConfiguration();
config.master(sentinelProperties.getMaster());
config.setSentinels(createSentinels(sentinelProperties));
if (redisConf.getPassword() != null) {
config.setPassword(RedisPassword.of(redisConf.getPassword()));
}
if (sentinelProperties.getPassword() != null) {
config.setSentinelPassword(RedisPassword.of(sentinelProperties.getPassword()));
}
config.setDatabase(redisConf.getDatabase());
return config;
}
return null;
}
protected final RedisClusterConfiguration getClusterConfiguration() {
if (redisConf.getCluster() == null) {
return null;
}
RbacPermissionProperties.RedisConf.Cluster clusterProperties = redisConf.getCluster();
RedisClusterConfiguration config = new RedisClusterConfiguration(clusterProperties.getNodes());
if (clusterProperties.getMaxRedirects() != null) {
config.setMaxRedirects(clusterProperties.getMaxRedirects());
}
if (redisConf.getPassword() != null) {
config.setPassword(RedisPassword.of(redisConf.getPassword()));
}
return config;
}
protected ConnectionInfo parseUrl(String url) {
try {
URI uri = new URI(url);
String scheme = uri.getScheme();
if (!"redis".equals(scheme) && !"rediss".equals(scheme)) {
throw new InitException("无法获取redis连接");
}
boolean useSsl = ("rediss".equals(scheme));
String password = null;
if (uri.getUserInfo() != null) {
password = uri.getUserInfo();
int index = password.indexOf(':');
if (index >= 0) {
password = password.substring(index + 1);
}
}
return new ConnectionInfo(uri, useSsl, password);
} catch (URISyntaxException ex) {
throw new InitException("无法获取redis连接");
}
}
private List<RedisNode> createSentinels(RbacPermissionProperties.RedisConf.Sentinel sentinel) {
List<RedisNode> nodes = new ArrayList<>();
for (String node : sentinel.getNodes()) {
try {
String[] parts = StringUtils.split(node, ":");
Assert.state(parts.length == 2, "Must be defined as 'host:port'");
nodes.add(new RedisNode(parts[0], Integer.parseInt(parts[1])));
} catch (RuntimeException ex) {
throw new IllegalStateException("Invalid redis sentinel property '" + node + "'", ex);
}
}
return nodes;
}
static class ConnectionInfo {
private final URI uri;
private final boolean useSsl;
private final String password;
ConnectionInfo(URI uri, boolean useSsl, String password) {
this.uri = uri;
this.useSsl = useSsl;
this.password = password;
}
boolean isUseSsl() {
return this.useSsl;
}
String getHostName() {
return this.uri.getHost();
}
int getPort() {
return this.uri.getPort();
}
String getPassword() {
return this.password;
}
}
}
database
public abstract class DefaultRbacDatasource extends ApplicationObjectSupport implements RbacDatasource, InitializingBean {
@Value("${spring.application.name}")
private String applicationName;
public Logger logger = LoggerFactory.getLogger(DefaultRbacDatasource.class);
@Override
public void afterPropertiesSet() {
if (init()) {
logger.info("Datasource Init Successfully");
} else {
throw new InitException("Datasource Init Fail");
}
Collection<Object> values = getApplicationContext().getBeansWithAnnotation(Controller.class).values();
RbacContext context = new RbacContext();
context.setControllerList(values);
context.setApplicationName(applicationName);
store(context);
}
}
public interface RbacDatasource {
Boolean init();
boolean auth(List<String> roles, String url, String serverName);
void store(RbacContext rbacContext);
}
properties
@ConfigurationProperties(prefix = "rbac")
@Configuration
@PropertySource("classpath:/db.properties")
public class RbacPermissionProperties {
private final RedisConf redis = new RedisConf();
private final DataSourceProperties mysql = new DataSourceProperties();
public RedisConf getRedis() {
return redis;
}
public DataSourceProperties getMysql() {
return mysql;
}
@Data
public static class RedisConf {
private int database = 0;
private String url;
private String host = "localhost";
private String password;
private int port = 6379;
private Duration timeout;
private String clientName;
private Sentinel sentinel;
private Cluster cluster;
private final Lettuce lettuce = new Lettuce();
public static class Pool {
private int maxIdle = 8;
private int minIdle = 0;
private int maxActive = 8;
private Duration maxWait = Duration.ofMillis(-1);
private Duration timeBetweenEvictionRuns;
public int getMaxIdle() {
return this.maxIdle;
}
public void setMaxIdle(int maxIdle) {
this.maxIdle = maxIdle;
}
public int getMinIdle() {
return this.minIdle;
}
public void setMinIdle(int minIdle) {
this.minIdle = minIdle;
}
public int getMaxActive() {
return this.maxActive;
}
public void setMaxActive(int maxActive) {
this.maxActive = maxActive;
}
public Duration getMaxWait() {
return this.maxWait;
}
public void setMaxWait(Duration maxWait) {
this.maxWait = maxWait;
}
public Duration getTimeBetweenEvictionRuns() {
return this.timeBetweenEvictionRuns;
}
public void setTimeBetweenEvictionRuns(Duration timeBetweenEvictionRuns) {
this.timeBetweenEvictionRuns = timeBetweenEvictionRuns;
}
}
public static class Cluster {
private List<String> nodes;
private Integer maxRedirects;
public List<String> getNodes() {
return this.nodes;
}
public void setNodes(List<String> nodes) {
this.nodes = nodes;
}
public Integer getMaxRedirects() {
return this.maxRedirects;
}
public void setMaxRedirects(Integer maxRedirects) {
this.maxRedirects = maxRedirects;
}
}
public static class Sentinel {
private String master;
private List<String> nodes;
private String password;
public String getMaster() {
return this.master;
}
public void setMaster(String master) {
this.master = master;
}
public List<String> getNodes() {
return this.nodes;
}
public void setNodes(List<String> nodes) {
this.nodes = nodes;
}
public String getPassword() {
return this.password;
}
public void setPassword(String password) {
this.password = password;
}
}
public static class Jedis {
private Pool pool;
public Pool getPool() {
return this.pool;
}
public void setPool(Pool pool) {
this.pool = pool;
}
}
public static class Lettuce {
private Duration shutdownTimeout = Duration.ofMillis(100);
private Pool pool;
private final Cluster cluster = new Cluster();
public Duration getShutdownTimeout() {
return this.shutdownTimeout;
}
public void setShutdownTimeout(Duration shutdownTimeout) {
this.shutdownTimeout = shutdownTimeout;
}
public Pool getPool() {
return this.pool;
}
public void setPool(Pool pool) {
this.pool = pool;
}
public Cluster getCluster() {
return this.cluster;
}
public static class Cluster {
private final Refresh refresh = new Refresh();
public Refresh getRefresh() {
return this.refresh;
}
public static class Refresh {
private Duration period;
private boolean adaptive;
public Duration getPeriod() {
return this.period;
}
public void setPeriod(Duration period) {
this.period = period;
}
public boolean isAdaptive() {
return this.adaptive;
}
public void setAdaptive(boolean adaptive) {
this.adaptive = adaptive;
}
}
}
}
}
}
util
public class SnowflakeIdWorkerUtils {
private final long twepoch = 1420041600000L;
private final long workerIdBits = 5L;
private final long datacenterIdBits = 5L;
private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
private final long sequenceBits = 12L;
private final long workerIdShift = sequenceBits;
private final long datacenterIdShift = sequenceBits + workerIdBits;
private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
private final long sequenceMask = -1L ^ (-1L << sequenceBits);
private long workerId;
private long datacenterId;
private long sequence = 0L;
private long lastTimestamp = -1L;
public SnowflakeIdWorkerUtils(long workerId, long datacenterId) {
if (workerId > maxWorkerId || workerId < 0) {
throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
}
if (datacenterId > maxDatacenterId || datacenterId < 0) {
throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
}
this.workerId = workerId;
this.datacenterId = datacenterId;
}
public synchronized long nextId() {
long timestamp = timeGen();
if (timestamp < lastTimestamp) {
throw new RuntimeException(
String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
}
if (lastTimestamp == timestamp) {
sequence = (sequence + 1) & sequenceMask;
if (sequence == 0) {
timestamp = tilNextMillis(lastTimestamp);
}
}
else {
sequence = 0L;
}
lastTimestamp = timestamp;
return ((timestamp - twepoch) << timestampLeftShift)
| (datacenterId << datacenterIdShift)
| (workerId << workerIdShift)
| sequence;
}
protected long tilNextMillis(long lastTimestamp) {
long timestamp = timeGen();
while (timestamp <= lastTimestamp) {
timestamp = timeGen();
}
return timestamp;
}
protected long timeGen() {
return System.currentTimeMillis();
}
static SnowflakeIdWorkerUtils idWorker = new SnowflakeIdWorkerUtils(0, 0);
public static String getNextId() {
long id = idWorker.nextId();
return String.valueOf(id);
}
}
public class MyRedisUtil {
private RedisTemplate<String, Object> redisTemplate;
public MyRedisUtil(RedisTemplate<String, Object> redisTemplate) {
this.redisTemplate=redisTemplate;
}
public boolean expire(String key, long time) {
try {
if (time > 0) {
redisTemplate.expire(key, time, TimeUnit.SECONDS);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
public long getExpire(String key) {
return redisTemplate.getExpire(key, TimeUnit.SECONDS);
}
public boolean hasKey(String key) {
try {
return redisTemplate.hasKey(key);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
public Object get(String key) {
return key == null ? null : redisTemplate.opsForValue().get(key);
}
public boolean set(String key, Object value) {
try {
redisTemplate.opsForValue().set(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
public boolean set(String key, Object value, long time) {
try {
if (time > 0) {
redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
} else {
set(key, value);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
public boolean set(String key, Object value, long time,TimeUnit t) {
try {
if (time > 0) {
redisTemplate.opsForValue().set(key, value, time, t);
} else {
set(key, value);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
public long incr(String key, long delta) {
if (delta < 0) {
throw new RuntimeException("递增因子必须大于0");
}
return redisTemplate.opsForValue().increment(key, delta);
}
public long decr(String key, long delta) {
if (delta < 0) {
throw new RuntimeException("递减因子必须大于0");
}
return redisTemplate.opsForValue().increment(key, -delta);
}
public Object hget(String key, String item) {
return redisTemplate.opsForHash().get(key, item);
}
public Map<Object, Object> hmget(String key) {
return redisTemplate.opsForHash().entries(key);
}
public boolean hmset(String key, Map<String, Object> map) {
try {
redisTemplate.opsForHash().putAll(key, map);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
public boolean hhset(String key, String hasKey,Map<String, Object> map) {
try {
redisTemplate.opsForHash().put(key,hasKey, map);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
public Object hhGet(String key, String hasKey) {
return redisTemplate.opsForHash().get(key, hasKey);
}
public boolean hmset(String key, Map<String, Object> map, long time) {
try {
redisTemplate.opsForHash().putAll(key, map);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
public boolean hset(String key, String item, Object value) {
try {
redisTemplate.opsForHash().put(key, item, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
public boolean hset(String key, String item, Object value, long time) {
try {
redisTemplate.opsForHash().put(key, item, value);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
public void hdel(String key, Object... item) {
redisTemplate.opsForHash().delete(key, item);
}
public boolean hHasKey(String key, String item) {
return redisTemplate.opsForHash().hasKey(key, item);
}
public double hincr(String key, String item, double by) {
return redisTemplate.opsForHash().increment(key, item, by);
}
public double hdecr(String key, String item, double by) {
return redisTemplate.opsForHash().increment(key, item, -by);
}
public Set<Object> sGet(String key) {
try {
return redisTemplate.opsForSet().members(key);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public boolean sHasKey(String key, Object value) {
try {
return redisTemplate.opsForSet().isMember(key, value);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
public long sSet(String key, Object... values) {
try {
return redisTemplate.opsForSet().add(key, values);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
public long sSetAndTime(String key, long time, Object... values) {
try {
Long count = redisTemplate.opsForSet().add(key, values);
if (time > 0) {
expire(key, time);
}
return count;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
public long sGetSetSize(String key) {
try {
return redisTemplate.opsForSet().size(key);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
public long setRemove(String key, Object... values) {
try {
Long count = redisTemplate.opsForSet().remove(key, values);
return count;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
public List<Object> lGet(String key, long start, long end) {
try {
return redisTemplate.opsForList().range(key, start, end);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public long lGetListSize(String key) {
try {
return redisTemplate.opsForList().size(key);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
public Object lGetIndex(String key, long index) {
try {
return redisTemplate.opsForList().index(key, index);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public boolean lSet(String key, Object value) {
try {
redisTemplate.opsForList().rightPush(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
public boolean lSet(String key, Object value, long time) {
try {
redisTemplate.opsForList().rightPush(key, value);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
public boolean lSet(String key, List<Object> value) {
try {
redisTemplate.opsForList().rightPushAll(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
public boolean lSet(String key, List<Object> value, long time) {
try {
redisTemplate.opsForList().rightPushAll(key, value);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
public boolean lUpdateIndex(String key, long index, Object value) {
try {
redisTemplate.opsForList().set(key, index, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
public long lRemove(String key, long count, Object value) {
try {
Long remove = redisTemplate.opsForList().remove(key, count, value);
return remove;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
}
META_INF.spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=xx.xx.RbacPermissionAutoConfig
db.properties
rbac.mysql.driverClassName=com.mysql.cj.jdbc.Driver
rbac.mysql.url=jdbc:mysql://xx.xx.xx.xx:3306/datebase_name?characterEncoding=UTF-8&useUnicode=true&useSSL=false&tinyInt1isBit=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
rbac.mysql.username=xxx
rbac.mysql.password=xxx
pom
<artifactId>rbac-permission-client</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.22</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure-processor</artifactId>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
</resource>
</resources>
</build>
README
#基于RBAC鉴权组件使用
## 1、准备工作
###引入rbac-permission-client,修改db.properties配置(支持mysql/redis),修改为存储鉴权数据的database(mysql:在数据库创建两张表rbac_permission_url,rbac_role_url)
```sql
CREATE TABLE `rbac_permission_url` (
`id` varchar(32) NOT NULL COMMENT 'id',
`url` varchar(128) NOT NULL COMMENT '路径url',
`request_type` varchar(64) DEFAULT NULL COMMENT '请求类型',
`server_name` varchar(64) DEFAULT NULL COMMENT '服务名称',
`white_list` tinyint(4) DEFAULT NULL COMMENT '是否白名单1:是2:否',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `rbac_role_url` (
`id` varchar(64) NOT NULL COMMENT 'id',
`role_code` varchar(128) DEFAULT NULL COMMENT '角色唯一编码',
`url` varchar(128) DEFAULT NULL COMMENT '权限url',
`server_name` varchar(64) DEFAULT NULL COMMENT '服务名称',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
```
## 2、使用
### 2.1 pom依赖引入rbac-permission-client
### 2.2 启动类添加扫描路径
### 2.3 controller中请求方法添加@RbacPermission(role={},isWhiteList=false)
####注解说明: @RbacPermission:基于RBAC接口鉴权注解,添加该注解说明该接口要进行鉴权,其中白名单优先级高于角色(若白名单配置true,则鉴权时直接放行)
####参数说明:role:授权的角色唯一编码集合(例如:role={"角色唯一编码",...});isWhiteList:是否白名单:默认false
## 3、鉴权
### 3.1 调用RbacDatasource的auth方法
####方法参数说明:入参:List<String> 角色唯一编码集合,String 访问的url,String 访问应用的applicationName
#### 出参:boolean (true:有权访问,false:无权访问)