乐趣区

关于java:小心别被eureka坑了

Eureka 是 Netflix 开发的服务发现框架,自身是一个基于 REST 的服务,次要用于定位运行在 AWS 域中的中间层服务,以达到负载平衡和中间层服务故障转移的目标。SpringCloud 将它集成在其子我的项目 spring-cloud-netflix 中,以实现 SpringCloud 的服务发现性能。

Eureka 蕴含两个组件:Eureka Server 和 Eureka Client。具体怎么部署这里就不说了,间接说问题

Eureka 客户端注册时须要配置服务端地址,相似如下配置

eureka:
  instance:
    hostname: hello-service
    prefer-ip-address: true
    instance-id: ${eureka.instance.hostname}:${server.port}
  client:
    register-with-eureka: true 
    fetch-registry: true
    service-url: 
        defaultZone: "http://localhost:8761/eureka/" 

这种配置后客户端就会注册到 Eureka 注册核心,在 Eureka 界面就能看到:

然而这样把界面裸露到里面,会把注册信息透露,个别公司也不容许裸露没有平安认证的后盾界面

所以尝试把 Eureka 界面加密

引入 security

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

Eureka 服务端减少 basic 鉴权:

spring:
  application:
    name: eureka-server 
  security: 
    basic:
      enabled: true
    user:
      name: admin
      password: 123456

配置实现后发现当初拜访 eureka 界面须要用户名和明码登录了

然而登录进去后发现方才的 hello 服务并没有注册进来

呕吼,应该是客户端没配置鉴权信息的起因,在官网找到了客户端鉴权配置形式

https://cloud.spring.io/sprin…

于是在 hello 服务批改配置如下:

eureka:
  instance:
    hostname: hello-service
    prefer-ip-address: true
    instance-id: ${eureka.instance.hostname}:${server.port}
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://admin:123456@localhost:8761/eureka/

重启 hello 服务后,发现还是没有注册胜利,原来减少 basic 验证后,不反对跨域拜访了,我的天,你这个大坑,服务注册必定是跨域的了,

于是,迅速减少配置,去掉跨域拦挡

@EnableWebSecurity
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {// http.csrf().disable();// 一种形式间接敞开 csrf,另一种配置 url 后放行
        http.csrf().ignoringAntMatchers("/eureka/**");
        super.configure(http);
    }
}

终于在界面看到可可恶爱的 hello 服务了,然而呢,还有一个问题,当初不容许这种明文明码呈现在配置或代码中,怎么办呢?

首先想到的就是明码加密, 所以从 spring-securiry 中找到 PasswordEncoderFactories 加密

PasswordEncoderFactories.createDelegatingPasswordEncoder().encode("123456")

而后把加密后的后果放到 Eureka 服务端配置文件中:

security: 
  basic:
    enabled: true 
  user:
    name: admin
    password: '{bcrypt}$2a$10$mhH7ogkRB91YDUO3F883JugDMHz2o6miT95.8ukqEc6Ed4Z2xyHmm' // 必须有引号

那 Eureka 客户端怎么办?同样情理的嘛

client:
  register-with-eureka: true
  fetch-registry: true
  service-url:
    defaultZone: http://admin:{bcrypt}$2a$10$mhH7ogkRB91YDUO3F883JugDMHz2o6miT95.8ukqEc6Ed4Z2xyHmm@localhost:8761/eureka/

然而呢,启动间接报错,害,eureka 注册时并不会解密

解析 Eureka 服务端地址失败,即便是加上引号也是报雷同谬误

Eureka 客户端注册过去的音讯,服务端并不会给解密,那怎么办呢?

从上图能够看到如果要实现更简单的需要,须要通过注入 clientFilter 形式,so,搞起来

@Configuration
@Priority(Integer.MIN_VALUE)
public class UserCilentFilter extends ClientFilter {
    @Override
    public ClientResponse handle(ClientRequest clientRequest) throws ClientHandlerException {
        try {String originUrl = clientRequest.getURI().toString();
            if(originUrl.contains("@")){return this.getNext().handle(clientRequest);
            }
            String userNameAndPwd = "http://admin"+ jiemi("'{bcrypt}$2a$10$mhH7ogkRB91YDUO3F883JugDMHz2o6miT95.8ukqEc6Ed4Z2xyHmm'");
            String addUserInfoUrl = originUrl.replaceFirst("http://", userNameAndPwd);
            clientRequest.setURI(new URI(addUserInfoUrl));
        } catch (URISyntaxException e) {// FIXME: 2021/4/2}
        return this.getNext().handle(clientRequest);
    }

    private String jiemi(String pwd) {
        // FIXME: 解密
        return pwd;
    }


    @Bean
    public DiscoveryClient.DiscoveryClientOptionalArgs discoveryClientOptionalArgs() {DiscoveryClient.DiscoveryClientOptionalArgs discoveryClientOptionalArgs = new DiscoveryClient.DiscoveryClientOptionalArgs();
        discoveryClientOptionalArgs.setAdditionalFilters(Collections.singletonList(new UserCilentFilter()));
        return discoveryClientOptionalArgs;
    }
}

这样写的思路是让 其余客户端注册时去掉用户名和明码,而后在自定义过滤器中对没有用户名和明码时补充上 basic 验证的用户名和明码

而后开始测试,这样还是不行,其余服务注册过去时,会被其余平安过滤器拦挡都走不到自定义的拦截器就返回鉴权失败了,即便 @Priority(Integer.MIN_VALUE)最高优先级

那是不是能够在更后面的中央进行拦挡呢?减少 ServletRequest 拦截器可行否?

@Configuration
public class ServerRequestAuthFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
                         FilterChain filterChain) throws IOException, ServletException {
        // 业务实现,依据申请的 IP 或者参数判断是否能够执行注册或者拜访
//        String addUserInfoUrl = originUrl.replaceFirst("http://", "http://admin:123456@");
        filterChain.doFilter(request, response);
    }
}

然而假如这样批改后,登录的 web 界面也会走到这个拦截器,同样会减少鉴权

也就是说这样间接减少鉴权,无奈辨别是其余客户端注册还是从界面拜访

也没有什么太好的方法了,就间接在 security 的拦截器中把 eureka 注册相干的放掉,不进行鉴权操作

@Override
protected void configure(HttpSecurity http) throws Exception {http.authorizeRequests().antMatchers("/eureka/**").permitAll();
    super.configure(http);
}

这样设置后除了间接拜访的界面须要鉴权外,其余 eureka 相干注册、查问等不须要鉴权

都这样分层鉴权操作了,再找下是不是有其余形式达到雷同的目标,于是找到

eureka:
  dashboard:
    enabled: false

通过在启动脚本设置后,成果是相似的,在 eureka 主界面无法访问

下面所有的操作都是为了 信息安全 思考,还有一个常常遗记须要思考的组件是 Spring Boot Actuator,针对 Spring Boot Actuator 提供的 endpoint,采取以下几种措施,能够尽可能升高被平安攻打的危险

  1. 最小粒度裸露 endpoint。只开启并裸露真正用到的 endpoint,而不是配置:management.endpoints.web.exposure.include=*。
  2. 为 endpoint 配置独立的拜访端口,从而和 web 服务的端口分来到,防止裸露 web 服务时,误将 actuator 的 endpoint 也裸露进来。例:management.port=8099。
  3. 引入 spring-boot-starter-security 依赖,为 actuator 的 endpoint 配置访问控制。
  4. 谨慎评估是否须要引入 spring-boot-stater-actuator。以我集体的教训,我至今还没有遇到什么需要是肯定须要引入 spring-boot-stater-actuator 能力解决,如果你并不理解上文所述的平安危险,我倡议你先去除掉该依赖。

信息安全 曾经成为各大公司不得不思考的问题,所以精准的权限管制也是必不可少的,心愿本文对大家在应用 SpringCloud 相干组件安全控制上有启发作用。

如果感觉俺写的还能够,记得点赞,一键三连也不介意。

☞☞每周一篇,赛过神仙,看完点赞,养成习惯☜☜

退出移动版