我又被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界面就能看到:

image.png

但是这样把界面暴露到外面,会把注册信息泄漏,一般公司也不允许暴露没有安全认证的后台界面


所以尝试把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界面需要用户名和密码登录了

image.png

但是登录进去后发现刚才的hello服务并没有注册进来

image.png

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

cloud.spring.io/spring-clou…

image.png

于是在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注册时并不会解密

image.png

解析Eureka服务端地址失败,即使是加上引号也是报相同错误

Eureka客户端注册过来的消息,服务端并不会给解密,那怎么办呢?

image.png

从上图可以看到如果要实现更复杂的需求,需要通过注入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主界面无法访问

image.png

上面所有的操作都是为了信息安全考虑,还有一个经常忘记需要考虑的组件是Spring Boot Actuator,针对 Spring Boot Actuator 提供的 endpoint,采取以下几种措施,可以尽可能降低被安全攻击的风险


  1. 最小粒度暴露 endpoint。只开启并暴露真正用到的 endpoint,而不是配置:management.endpoints.web.exposure.include=*。


  1. 为 endpoint 配置独立的访问端口,从而和 web 服务的端口分离开,避免暴露 web 服务时,误将 actuator 的 endpoint 也暴露出去。例:management.port=8099。


  1. 引入 spring-boot-starter-security 依赖,为 actuator 的 endpoint 配置访问控制。


  1. 慎重评估是否需要引入 spring-boot-stater-actuator。以我个人的经验,我至今还没有遇到什么需求是一定需要引入spring-boot-stater-actuator 才能解决,如果你并不了解上文所述的安全风险,我建议你先去除掉该依赖。


信息安全已经成为各大公司不得不考虑的问题,所以精准的权限控制也是必不可少的,希望本文对大家在使用SpringCloud相关组件安全控制上有启发作用。


目录
相关文章
|
算法 API 数据安全/隐私保护
电商 API 双平台实战:淘宝 item.get + 京东 item_detail 对接指南(附可复用代码 + 问题排查)
本文详细解析了淘宝和京东双平台API对接的核心流程,涵盖资质申请、凭证获取、签名生成、高频接口调用及常见问题解决方案,助力开发者高效实现商品数据同步与管理。
|
11月前
|
Java Go 开发工具
【Java】(8)正则表达式的使用与常用类分享
正则表达式定义了字符串的模式。正则表达式并不仅限于某一种语言,但是在每种语言中有细微的差别。
607 1
|
JSON 算法 Java
微服务Token鉴权设计的几种方案
【8月更文挑战第18天】在微服务架构中,Token鉴权是确保服务安全性的重要环节。本文将详细介绍几种常见的微服务Token鉴权设计方案,旨在帮助大家在工作和学习中更好地理解和应用这些技术。
1169 2
|
缓存 算法 NoSQL
短信验证码登录接口,如何防止恶意攻击
该文讨论了移动应用中常见的手机短信验证码登录方式,后端实现通常涉及两个API:获取短信验证码和短信验证码登录。在设计时,为增强短信验证码接口的安全性,提出了几种无需使用Redis等存储介质的方案:1) 使用数字签名,基于时间戳或随机数生成唯一签名进行验证;2) 基于时间的有效期验证,通过加密或修改时间戳形式确保安全性;3) 应用TOTP算法,按时间生成动态码进行比对;4) 利用JWTToken生成带有限期的签名进行验证。这些方法旨在防止恶意攻击并优化登录接口性能。
1408 1
|
Linux 数据库
ntp如何配置同步服务器
【5月更文挑战第19天】ntp如何配置同步服务器
2841 2
|
Java Spring
@PostConstruct注解的使用
@PostConstruct注解的使用
547 0
Java 正则表达式总结
Java 正则表达式总结
|
Java 关系型数据库 数据库
Spring Boot 学习研究笔记(七) -使用SpringData JPA访问PostgreSql数据库
Spring Boot 学习研究笔记(七) -使用SpringData JPA访问PostgreSql数据库
1856 0
|
Java Linux iOS开发
8 种 Java- 内存溢出之五 -Unable to create new native thread
8 种 Java- 内存溢出之五 -Unable to create new native thread
|
XML 缓存 JSON
SpringCloud组件(二):注册中心eureka
`Eureka` 是一个服务注册与发现框架,是 Netflix 开源的**注册中心**组件,分为 Eureka **Client** 和 Eureka **Server**
666 0
SpringCloud组件(二):注册中心eureka

热门文章

最新文章