SpringCloud
本文最后更新于 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启动前端
顺利进入后台:
至此部署完成.
和弃用版本区别
配置读取
弃用版本是通过读取本地文件配置文件,目前当前版本是通过git读取配置。
相比较而言,git可以进行版本控制和权限控制,并且可以通过分支进行不通的配置管理,缺点则是需要网络、根据git仓库可能存在一定延迟、敏感信息直接暴露在git中。不过以上几个缺点似乎可以通过本地部署一个git仓库进行解决?
读取本地文件的方式快速,且方便,直接修改文件即可,不需要再去提交git代码修改配置。缺点自然是 不易于管理、安全性也不够、没法做版本控制。
集群和单机
弃用版本单机部署还需要修改hosts配置 域名映射,这个版本使用localhost,本质是一样的。
集群可参考弃用版本的集群配置。
有ui....
此版本内存占用比 弃用版本的单机部署方式 更小,感知明显,我可以全部服务启动并开一堆后台服务,而之前,光是启动我都需要关掉很多东西,或者将服务拆分,部署到虚拟机上面运行。
配置中心对比搜文档: 配置中心nacos、Apollo、spring cloud config对比
接下来进行一些修改和学习:
todo:
权限有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;
}
不难看出,通过读取缓存中的登录用户信息,存在数据一致性问题。