基于RBAC鉴权组件

41 阅读6分钟

该组件支持mysql,Redis,可灵活配置

entity

@Getter
@Setter
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class PermissionUrL {

    private String id;
    /**
     * url
     */
    private String url;
    /**
     * 请求类型
     */
    private String requestType;
    /**
     * 服务名称
     */
    private String serverName;
    /**
     * 是否白名单(1:是,2:否)
     */
    private Integer whiteList;
}

/**
 * @author fanchunling
 * @date 2023/05/29 14:00
 */
@Getter
@Setter
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class RoleUrL {

    private String id;
    /**
     * 角色编码
     */
    private String roleCode;
    /**
     * 权限url
     */
    private String url;
    /**
     * 服务名称
     */
    private String serverName;

}

annotation

/**
 * @author fanchunling
 */
//贴有该注解 表示 该方法须有该url权限才能访问
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RbacPermission {
    //角色唯一标识
     String[] role() default {};
     //是否白名单
    boolean isWhiteList() default false;
}

constants

/**
 * @author fanchunling
 * @date 2023/05/29 14:09
 */
public class RbacPermissionConstants {
    public static final String PRE_URL="prefix_url_set:";
    public static final String PRE_ROLE_URL="prefix_role_url_set:";
}

context

/**
 * @author fanchunling
 */
@Data
public class RbacContext {
    /**
     * controller-beans
     */
    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();

    /**
     * 存储数据
     * @param rbacContext
     */
    boolean store(RbacContext rbacContext);

    /**
     * 根据服务名查url
     * @param serverName
     * @return
     */
    List<PermissionUrl> selectByServerName(String serverName);

    /**
     * 根据服务名查role-url
     * @param serverName
     * @return
     */
    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();

        // Config Dao
        dao = new RbacPermissionDaoImpl(dataSource);
    }

    @Override
    public Boolean init() {
        // 测试db是否初始化成功
        return dao.init();
    }

    @Override
    public boolean auth(List<String> roles, String url, String serverName) {
        List<PermissionUrl> hasUrls = dao.selectByServerName(serverName);
        //没有带注解的url直接放行
        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());
       //url在白名单内,放行
        if (!CollectionUtils.isEmpty(whiteListUs) && whiteListUs.contains(url)) {
            return true;
        }
        //不在带注解的url内,放行
        if (!us.contains(url)) {
            return true;
        } else {
            //角色为空,不放行
            if (CollectionUtils.isEmpty(roles)) {
                return false;
            }
            //查询有权限的role,判断是否放行
            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

/**
 * @author fanchunling
 */
public interface RbacPermissionClientService {
    boolean store(RbacContext rbacContext);

    /**
     * 根据服务名查url
     * @param serverName
     * @return
     */
    List<PermissionUrl> getHasWithAnnotationUrls(String serverName);

    /**
     * 根据服务名查role-url
     * @param serverName
     * @return
     */
    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() {
        // 测试redis是否连接成功

        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;
            }
            //查询有权限的role
            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;
    }

    /**
     * 连接配置
     *
     * @return
     */
    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());
        }

        //redis客户端配置
        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");
        }
        // 扫描Bean注解,存放数据至context
        Collection<Object> values = getApplicationContext().getBeansWithAnnotation(Controller.class).values();
        RbacContext context = new RbacContext();
        context.setControllerList(values);
        context.setApplicationName(applicationName);
        // store负责具体的数据存储
        store(context);
    }
}
public interface RbacDatasource {

    Boolean init();

    boolean auth(List<String> roles, String url, String serverName);

    void store(RbacContext rbacContext);
}

properties

/**
 *
 * @author fanchunling
 */
@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 {

        /**
         * Database index used by the connection factory.
         */
        private int database = 0;

        /**
         * Connection URL. Overrides host, port, and password. User is ignored. Example:
         * redis://user:password@example.com:6379
         */
        private String url;

        /**
         * Redis server host.
         */
        private String host = "localhost";

        /**
         * Login password of the redis server.
         */
        private String password;

        /**
         * Redis server port.
         */
        private int port = 6379;

        /**
         * Connection timeout.
         */
        private Duration timeout;

        /**
         * Client name to be set on connections with CLIENT SETNAME.
         */
        private String clientName;

        private Sentinel sentinel;

        private Cluster cluster;

        private final Lettuce lettuce = new Lettuce();

        /**
         * Pool properties.
         */
        public static class Pool {

            /**
             * Maximum number of "idle" connections in the pool. Use a negative value to
             * indicate an unlimited number of idle connections.
             */
            private int maxIdle = 8;

            /**
             * Target for the minimum number of idle connections to maintain in the pool. This
             * setting only has an effect if both it and time between eviction runs are
             * positive.
             */
            private int minIdle = 0;

            /**
             * Maximum number of connections that can be allocated by the pool at a given
             * time. Use a negative value for no limit.
             */
            private int maxActive = 8;

            /**
             * Maximum amount of time a connection allocation should block before throwing an
             * exception when the pool is exhausted. Use a negative value to block
             * indefinitely.
             */
            private Duration maxWait = Duration.ofMillis(-1);

            /**
             * Time between runs of the idle object evictor thread. When positive, the idle
             * object evictor thread starts, otherwise no idle object eviction is performed.
             */
            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;
            }

        }

        /**
         * Cluster properties.
         */
        public static class Cluster {

            /**
             * Comma-separated list of "host:port" pairs to bootstrap from. This represents an
             * "initial" list of cluster nodes and is required to have at least one entry.
             */
            private List<String> nodes;

            /**
             * Maximum number of redirects to follow when executing commands across the
             * cluster.
             */
            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;
            }

        }

        /**
         * Redis sentinel properties.
         */
        public static class Sentinel {

            /**
             * Name of the Redis server.
             */
            private String master;

            /**
             * Comma-separated list of "host:port" pairs.
             */
            private List<String> nodes;

            /**
             * Password for authenticating with sentinel(s).
             */
            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;
            }

        }

        /**
         * Jedis client properties.
         */
        public static class Jedis {

            /**
             * Jedis pool configuration.
             */
            private Pool pool;

            public Pool getPool() {
                return this.pool;
            }

            public void setPool(Pool pool) {
                this.pool = pool;
            }

        }

        /**
         * Lettuce client properties.
         */
        public static class Lettuce {

            /**
             * Shutdown timeout.
             */
            private Duration shutdownTimeout = Duration.ofMillis(100);

            /**
             * Lettuce pool configuration.
             */
            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 {

                    /**
                     * Cluster topology refresh period.
                     */
                    private Duration period;

                    /**
                     * Whether adaptive topology refreshing using all available refresh
                     * triggers should be used.
                     */
                    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 {
    // ==============================Fields===========================================
    /**
     * 开始时间截 (2015-01-01)
     */
    private final long twepoch = 1420041600000L;

    /**
     * 机器id所占的位数
     */
    private final long workerIdBits = 5L;

    /**
     * 数据标识id所占的位数
     */
    private final long datacenterIdBits = 5L;

    /**
     * 支持的最大机器id,结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数)
     */
    private final long maxWorkerId = -1L ^ (-1L << workerIdBits);

    /**
     * 支持的最大数据标识id,结果是31
     */
    private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);

    /**
     * 序列在id中占的位数
     */
    private final long sequenceBits = 12L;

    /**
     * 机器ID向左移12位
     */
    private final long workerIdShift = sequenceBits;

    /**
     * 数据标识id向左移17位(12+5)
     */
    private final long datacenterIdShift = sequenceBits + workerIdBits;

    /**
     * 时间截向左移22位(5+5+12)
     */
    private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;

    /**
     * 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095)
     */
    private final long sequenceMask = -1L ^ (-1L << sequenceBits);

    /**
     * 工作机器ID(0~31)
     */
    private long workerId;

    /**
     * 数据中心ID(0~31)
     */
    private long datacenterId;

    /**
     * 毫秒内序列(0~4095)
     */
    private long sequence = 0L;

    /**
     * 上次生成ID的时间截
     */
    private long lastTimestamp = -1L;

    //==============================Constructors=====================================

    /**
     * 构造函数
     *
     * @param workerId     工作ID (0~31)
     * @param datacenterId 数据中心ID (0~31)
     */
    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;
    }

    // ==============================Methods==========================================

    /**
     * 获得下一个ID (该方法是线程安全的)
     *
     * @return SnowflakeId
     */
    public synchronized long nextId() {
        long timestamp = timeGen();

        //如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常
        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;
        }

        //上次生成ID的时间截
        lastTimestamp = timestamp;

        //移位并通过或运算拼到一起组成64位的ID
        return ((timestamp - twepoch) << timestampLeftShift) //
                | (datacenterId << datacenterIdShift) //
                | (workerId << workerIdShift) //
                | sequence;
    }

    /**
     * 阻塞到下一个毫秒,直到获得新的时间戳
     *
     * @param lastTimestamp 上次生成ID的时间截
     * @return 当前时间戳
     */
    protected long tilNextMillis(long lastTimestamp) {
        long timestamp = timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = timeGen();
        }
        return timestamp;
    }

    /**
     * 返回以毫秒为单位的当前时间
     *
     * @return 当前时间(毫秒)
     */
    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;
    }


    /**
     * 指定缓存失效时间
     * @param key  键
     * @param time 时间(秒)
     * @return
     */
    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;
        }
    }

    /**
     * 根据key 获取过期时间
     * @param key 键 不能为null
     * @return 时间(秒) 返回0代表为永久有效
     */
    public long getExpire(String key) {
        return redisTemplate.getExpire(key, TimeUnit.SECONDS);
    }

    /**
     * 判断key是否存在
     * @param key 键
     * @return true 存在 false不存在
     */
    public boolean hasKey(String key) {
        try {
            return redisTemplate.hasKey(key);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }


    // ============================String=============================

    /**
     * 普通缓存获取
     * @param key 键
     * @return 值
     */
    public Object get(String key) {
        return key == null ? null : redisTemplate.opsForValue().get(key);
    }

    /**
     * 普通缓存放入
     * @param key   键
     * @param value 值
     * @return true成功 false失败
     */
    public boolean set(String key, Object value) {
        try {
            redisTemplate.opsForValue().set(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }

    }

    /**
     * 普通缓存放入并设置时间
     * @param key   键
     * @param value 值
     * @param time  时间(秒) time要大于0 如果time小于等于0 将设置无限期
     * @return true成功 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;
        }
    }
    /**
     * 普通缓存放入并设置时间
     * @param key   键
     * @param value 值
     * @param time  时间(秒) time要大于0 如果time小于等于0 将设置无限期
     * @return true成功 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;
        }
    }

    /**
     * 递增
     * @param key   键
     * @param delta 要增加几(大于0)
     * @return
     */
    public long incr(String key, long delta) {
        if (delta < 0) {
            throw new RuntimeException("递增因子必须大于0");
        }
        return redisTemplate.opsForValue().increment(key, delta);
    }

    /**
     * 递减
     * @param key   键
     * @param delta 要减少几(小于0)
     * @return
     */
    public long decr(String key, long delta) {
        if (delta < 0) {
            throw new RuntimeException("递减因子必须大于0");
        }
        return redisTemplate.opsForValue().increment(key, -delta);
    }

    // ================================Map=================================

    /**
     * HashGet
     * @param key  键 不能为null
     * @param item 项 不能为null
     * @return 值
     */
    public Object hget(String key, String item) {
        return redisTemplate.opsForHash().get(key, item);
    }

    /**
     * 获取hashKey对应的所有键值
     * @param key 键
     * @return 对应的多个键值
     */
    public Map<Object, Object> hmget(String key) {
        return redisTemplate.opsForHash().entries(key);
    }

    /**
     * HashSet
     * @param key 键
     * @param map 对应多个键值
     * @return true 成功 false 失败
     */
    public boolean hmset(String key, Map<String, Object> map) {
        try {
            redisTemplate.opsForHash().putAll(key, map);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
    /**
     * HashSet
     * @param key 键
     * @param map 对应多个键值
     * @return true 成功 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;
        }
    }
    /**
     * HashSet
     * @param key 键
     * @param hasKey 对应多个键值
     * @return object
     */
    public Object hhGet(String key, String hasKey) {
        return redisTemplate.opsForHash().get(key, hasKey);

    }

    /**
     * HashSet 并设置时间
     * @param key  键
     * @param map  对应多个键值
     * @param time 时间(秒)
     * @return true成功 false失败
     */
    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;
        }
    }

    /**
     * 向一张hash表中放入数据,如果不存在将创建
     * @param key   键
     * @param item  项
     * @param value 值
     * @return true 成功 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;
        }
    }

    /**
     * 向一张hash表中放入数据,如果不存在将创建
     * @param key   键
     * @param item  项
     * @param value 值
     * @param time  时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间
     * @return true 成功 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;
        }
    }

    /**
     * 删除hash表中的值
     * @param key  键 不能为null
     * @param item 项 可以使多个 不能为null
     */
    public void hdel(String key, Object... item) {
        redisTemplate.opsForHash().delete(key, item);
    }

    /**
     * 判断hash表中是否有该项的值
     * @param key  键 不能为null
     * @param item 项 不能为null
     * @return true 存在 false不存在
     */
    public boolean hHasKey(String key, String item) {
        return redisTemplate.opsForHash().hasKey(key, item);
    }

    /**
     * hash递增 如果不存在,就会创建一个 并把新增后的值返回
     * @param key  键
     * @param item 项
     * @param by   要增加几(大于0)
     * @return
     */
    public double hincr(String key, String item, double by) {
        return redisTemplate.opsForHash().increment(key, item, by);
    }

    /**
     * hash递减
     * @param key  键
     * @param item 项
     * @param by   要减少记(小于0)
     * @return
     */
    public double hdecr(String key, String item, double by) {
        return redisTemplate.opsForHash().increment(key, item, -by);
    }

    // ============================set=============================

    /**
     * 根据key获取Set中的所有值
     * @param key 键
     * @return
     */
    public Set<Object> sGet(String key) {
        try {
            return redisTemplate.opsForSet().members(key);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 根据value从一个set中查询,是否存在
     * @param key   键
     * @param value 值
     * @return true 存在 false不存在
     */
    public boolean sHasKey(String key, Object value) {
        try {
            return redisTemplate.opsForSet().isMember(key, value);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 将数据放入set缓存
     * @param key    键
     * @param values 值 可以是多个
     * @return 成功个数
     */
    public long sSet(String key, Object... values) {
        try {
            return redisTemplate.opsForSet().add(key, values);
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    /**
     * 将set数据放入缓存
     * @param key    键
     * @param time   时间(秒)
     * @param values 值 可以是多个
     * @return 成功个数
     */
    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;
        }
    }

    /**
     * 获取set缓存的长度
     * @param key 键
     * @return
     */
    public long sGetSetSize(String key) {
        try {
            return redisTemplate.opsForSet().size(key);
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    /**
     * 移除值为value的
     * @param key    键
     * @param values 值 可以是多个
     * @return 移除的个数
     */
    public long setRemove(String key, Object... values) {
        try {
            Long count = redisTemplate.opsForSet().remove(key, values);
            return count;
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }
    // ===============================list=================================

    /**
     * 获取list缓存的内容
     * @param key   键
     * @param start 开始
     * @param end   结束 0 到 -1代表所有值
     * @return
     */
    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;
        }
    }

    /**
     * 获取list缓存的长度
     * @param key 键
     * @return
     */
    public long lGetListSize(String key) {
        try {
            return redisTemplate.opsForList().size(key);
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    /**
     * 通过索引 获取list中的值
     * @param key   键
     * @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推
     * @return
     */
    public Object lGetIndex(String key, long index) {
        try {
            return redisTemplate.opsForList().index(key, index);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 将list放入缓存
     * @param key   键
     * @param value 值
     * @return
     */
    public boolean lSet(String key, Object value) {
        try {
            redisTemplate.opsForList().rightPush(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 将list放入缓存
     * @param key   键
     * @param value 值
     * @param time  时间(秒)
     * @return
     */
    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;
        }
    }

    /**
     * 将list放入缓存
     * @param key   键
     * @param value 值
     * @return
     */
    public boolean lSet(String key, List<Object> value) {
        try {
            redisTemplate.opsForList().rightPushAll(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 将list放入缓存
     * @param key   键
     * @param value 值
     * @param time  时间(秒)
     * @return
     */
    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;
        }
    }

    /**
     * 根据索引修改list中的某条数据
     * @param key   键
     * @param index 索引
     * @param value 值
     * @return
     */
    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;
        }
    }

    /**
     * 移除N个值为value
     * @param key   键
     * @param count 移除多少个
     * @param value 值
     * @return 移除的个数
     */
    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

#rbac.redis.database=1
#rbac.redis.host=xx.xx.xx.xx
#rbac.redis.port=6379
#rbac.redis.password=xxxx
#rbac.redis.timeout=15
#rbac.redis.lettuce.pool.maxActive=50
#rbac.redis.lettuce.pool.maxWait=15
#rbac.redis.lettuce.pool.maxIdle=50
#rbac.redis.lettuce.pool.minIdle=0
##如果是集群,不需要配置host和port,请配置cluster
#rbac.redis.cluster.nodes=xx.xx.xx.xx:port1,xx.xx.xx.xx:port2
#rbac.redis.cluster.maxRedirects=3

pom

<artifactId>rbac-permission-client</artifactId>
<dependencies>
    <!--redis相关的依赖-->
    <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:无权访问)