本文最后更新于 2024-06-07,文章内容可能已经过时。

RuoYi-Cloud

eureka版本

eureka版本[弃]:

这个版本没找到对应的ui,找了ruoyi ant和vue调试了一下还是有点麻烦换了一个版本,当然还是学到不少eureka集群的知识

按启动顺序写:eureka->config->gateway->system....

eureka :

@EnableEurekaServer注解在启动类中
server: 
  port: 7001
 
eureka: 
  instance:
    hostname: eureka7001.com #eureka服务端的实例名称
  client: 
    register-with-eureka: false     #false表示不向注册中心注册自己。
    fetch-registry: false     #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
    service-url: 
      #单机 
      #设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址(单机)。
#      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
      defaultZone: http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/

配置,其中hostname换成ip会有一些奇怪的问题出现。而且要改的太多,尝试过一次意义不大,就没改了。

修改的配置,也能生效:server: 
  port: 7002
 
eureka: 
  instance:
    prefer-ip-address: true
    instance-id: eureka7003:7002
    ip-address: 127.0.0.1
    hostname: eureka7002 #eureka服务端的实例名称
  client: 
    register-with-eureka: false     #false表示不向注册中心注册自己。
    fetch-registry: false     #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
    service-url: 
      #单机 
      #defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/       
      #设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址(单机)。
      defaultZone: http://localhost:7001/eureka/,http://localhost:7002/eureka/,http://localhost:7003/eureka/
只需要引入eureka的依赖
其他服务引入依赖以后,通过下面的配置注册到服务中心eureka:
  client: #客户端注册进eureka服务列表内
    service-url: 
#       defaultZone: http://eureka7001.com:7001/eureka

Plain Text

config:

经典三步走->引依赖->写配置->写注解
@EnableDiscoveryClient@EnableConfigServer需要在启动类加上这两个注解
配置暂无需要详解的,就配置一个配置文件夹的目录即可。  # 配置中心
  cloud:
    config:
      server:
        native:
          search-locations: classpath:/config/依赖里需要eureka服务中心和config server的依赖:        <!--配置中心 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-config-server</artifactId>
        </dependency>
        <!--eureka 客户端 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

gateway:

@EnableDiscoveryClient@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)// 服务发现的注解。// 登录和验证码 以及swagger都在网关服务中// 做了Hystriix服务降级熔断:@Slf4j
@Component
public class HystrixFallbackHandler implements HandlerFunction<ServerResponse>
{
    @Override
    public Mono<ServerResponse> handle(ServerRequest serverRequest)
    {
        Optional<Object> originalUris = serverRequest.attribute(GATEWAY_ORIGINAL_REQUEST_URL_ATTR);
        originalUris.ifPresent(originalUri -> log.error("网关执行请求:{}失败,hystrix服务降级处理", originalUri));
        return ServerResponse.status(HttpStatus.INTERNAL_SERVER_ERROR.value()).contentType(MediaType.APPLICATION_JSON)
                .body(BodyInserters.fromValue(JSON.toJSONString(R.error("服务已被降级熔断"))));
    }
}
/**
 * 路由限流配置
 */
@Configuration
public class RateLimiterConfiguration
{
    @Bean(value = "remoteAddrKeyResolver")
    public KeyResolver remoteAddrKeyResolver()
    {
        return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getAddress().getHostAddress());
    }
}/*** 路由配置信息
 */
@Configuration
@AllArgsConstructor
public class RouterFunctionConfiguration
{
    private final HystrixFallbackHandler hystrixFallbackHandler;

    private final ImgCodeHandler         imgCodeHandler;

    @Bean
    public RouterFunction<?> routerFunction()
    {
        return RouterFunctions
                .route(RequestPredicates.path("/fallback").and(RequestPredicates.accept(MediaType.TEXT_PLAIN)),
                        hystrixFallbackHandler)
                .andRoute(RequestPredicates.GET("/code").and(RequestPredicates.accept(MediaType.TEXT_PLAIN)),
                        imgCodeHandler);
    }
}

另一eureka版本

config指定git的path设置: 增加search-paths就可以

其他没什么意外,可能因为和弃用的版本差不多,一路顺利

我的启动顺序->eureka->config->gateway->auth->system->monitor->file、job、gen

再npm install -> npm run dev启动前端

顺利进入后台:

至此部署完成.

和弃用版本区别

  1. 配置读取

弃用版本是通过读取本地文件配置文件,目前当前版本是通过git读取配置。

相比较而言,git可以进行版本控制和权限控制,并且可以通过分支进行不通的配置管理,缺点则是需要网络、根据git仓库可能存在一定延迟、敏感信息直接暴露在git中。不过以上几个缺点似乎可以通过本地部署一个git仓库进行解决?

读取本地文件的方式快速,且方便,直接修改文件即可,不需要再去提交git代码修改配置。缺点自然是 不易于管理、安全性也不够、没法做版本控制。

  1. 集群和单机

弃用版本单机部署还需要修改hosts配置 域名映射,这个版本使用localhost,本质是一样的。

集群可参考弃用版本的集群配置。

  1. 有ui....

  2. 此版本内存占用比 弃用版本的单机部署方式 更小,感知明显,我可以全部服务启动并开一堆后台服务,而之前,光是启动我都需要关掉很多东西,或者将服务拆分,部署到虚拟机上面运行。

配置中心对比搜文档: 配置中心nacos、Apollo、spring cloud config对比

接下来进行一些修改和学习:

todo:

  1. 权限有bug,当修改权限以后,另一账号只要不刷新,就可以进行刚修改权限的操作。

比如:在一个浏览器窗口登录管理员,另一无痕浏览器中登录测试账号,测试账号拥有查看和删除日志的权限,可正常操作。

管理员在后台去掉日志删除权限以后,测试账号在未刷新的页面进行删除日志操作,执行成功!

预期:应该是执行失败,提示无此权限。然后刷新页面。

解决思路:

找到对应接口所在:

@Log(title = "操作日志", businessType = BusinessType.DELETE)
@RequiresPermissions("system:operlog:remove")
@DeleteMapping("/{operIds}")
public AjaxResult remove(@PathVariable Long[] operIds)
{
    return toAjax(operLogService.deleteOperLogByIds(operIds));
}

不难看出 权限验证时通过自定义注解@RequiresPermissions,接下来去看注解是否有解析器或者AOP。

通过搜索RequiresPermissions.class找到

public class PreAuthorizeAspect
{
//....
   public void checkMethodAnnotation(Method method)
    {
//...
// 校验 @RequiresPermissions 注解
RequiresPermissions requiresPermissions = method.getAnnotation(RequiresPermissions.class);
if (requiresPermissions != null)
{
    AuthUtil.checkPermi(requiresPermissions);
}
// ....
}
}

找到鉴权的方法类AuthUtil.checkPermi(requiresPermissions); ,实现如下:

public static void checkPermi(RequiresPermissions requiresPermissions)
{
    authLogic.checkPermi(requiresPermissions);
}

authLogic.checkPermi(requiresPermissions); ,其中RequiresPermissions的logical属性默认 default Logical.AND

,实现如下:

    /**
     * 根据注解(@RequiresPermissions)鉴权, 如果验证未通过,则抛出异常: NotPermissionException
     * 
     * @param requiresPermissions 注解对象
     */
public void checkPermi(RequiresPermissions requiresPermissions)
{
    if (requiresPermissions.logical() == Logical.AND)
    {
        checkPermiAnd(requiresPermissions.value());
    }
    else
    {
        checkPermiOr(requiresPermissions.value());
    }
}
  /**
     * 获取当前账号的权限列表
     * 
     * @return 权限列表
     */
    public Set<String> getPermiList()
    {
        try
        {
            LoginUser loginUser = getLoginUser();
            return loginUser.getPermissions();
        }
        catch (Exception e)
        {
            return new HashSet<>();
        }
    }
    /**
     * 验证用户是否含有指定权限,必须全部拥有
     *
     * @param permissions 权限列表
     */
 public void checkPermiAnd(String... permissions)
    {
        Set<String> permissionList = getPermiList();
        for (String permission : permissions)
        {
            if (!hasPermi(permissionList, permission))
            {
                throw new NotPermissionException(permission);
            }
        }
    }
    /**
     * 获取当前用户缓存信息, 如果未登录,则抛出异常
     * 
     * @return 用户缓存信息
     */
    public LoginUser getLoginUser()
    {
        String token = SecurityUtils.getToken();
        if (token == null)
        {
            throw new NotLoginException("未提供token");
        }
        LoginUser loginUser = SecurityUtils.getLoginUser();
        if (loginUser == null)
        {
            throw new NotLoginException("无效的token");
        }
        return loginUser;
    }

不难看出,通过读取缓存中的登录用户信息,存在数据一致性问题。