集成SpringBootAdmin and Nacos and SpringSecurity

一世执手 提交于 2020-03-18 23:27:16

3 月,跳不动了?>>>

前言

本文约定:

  • SpringBootAdmin - SBA
  • SpringSecurity - SSE

以当前我在搜索引擎检索的情况来看,大家对于SBA的应用都比较局限,发散性使用很少,基本都是基于官网给的例子(eureka-SBA-SSE), 而且网上的各文章,出现了一些很容易让人迷惑的问题,就比如引入依赖的部分SpringCloudAlibaba在引入依赖的时候竟然出现了两种GroupId..., 为此我也被绕晕了,故基于最新的版本以及SpringCloudAlibaba推荐的方式,对SBA-SSE-Nacos进行了一下集成,并写了这篇文章,希望后面看到的人避免再淌这个坑
Ps.不得不说微服务各界的版本是真的很混乱。

开始

安装Nacos

具体安装教程,可以参考官网,这里不再赘述,安装很简单。 版本:1.4+(其实1.1以上应该都可以)

创建项目

一个maven项目即可,按一般的教程会创建两个项目,一个作为Admin Server,一个作为Admin Client, 这里为了减少大家的学习成本,本示例会直接将本项目在作为AdminServer的同时,同时配置以客户端的身份注册到AdminServer.

引入依赖

    <properties>
        <java.version>1.8</java.version>
        <spring-boot-admin.version>2.1.6</spring-boot-admin.version>
    </properties>
	
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>de.codecentric</groupId>
                <artifactId>spring-boot-admin-dependencies</artifactId>
                <version>${spring-boot-admin.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Greenwich.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>2.1.1.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
	
    <dependencies>
        <!--安全模块-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--Admin Server-->
        <dependency>
            <groupId>de.codecentric</groupId>
            <artifactId>spring-boot-admin-starter-server</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter</artifactId>
        </dependency>
        <!--nacos 配置中心客户端-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
            <version>0.9.0.RELEASE</version>
        </dependency>
        <!--nacos 注册中心客户端-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            <version>0.9.0.RELEASE</version>
        </dependency>
        <!--作为AdminServer的客户端时必须引入的依赖 start-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-actuator-autoconfigure</artifactId>
        </dependency>
        <!--用于开放JMX操作-->
        <dependency>
            <groupId>org.jolokia</groupId>
            <artifactId>jolokia-core</artifactId>
        </dependency>
        <!--作为AdminServer的客户端时必须引入的依赖 end-->
		
    </dependencies>

配置

创建AdminServerApp类

这么多代码,不慌,直接把这里面内容全丢进去即可,不需要改

/**
 * @author vate
 * @date 2019/12/18 23:03
 */
@SpringBootApplication
@EnableAdminServer // 作为AdminServer启动
@EnableDiscoveryClient // 开启服务发现
public class AdminServerApp {
    public static void main(String[] args) {
        SpringApplication springApplication = new SpringApplication(AdminServerApp.class);
        springApplication.run(args);
    }

    @Profile("dev")
    @Configuration()
    public static class SecurityPermitAllConfig extends WebSecurityConfigurerAdapter {

        private final String adminContextPath;

        public SecurityPermitAllConfig(AdminServerProperties adminServerProperties) {
            this.adminContextPath = adminServerProperties.getContextPath();
        }

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.authorizeRequests().anyRequest().permitAll().and().csrf()
                    .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()).ignoringRequestMatchers(
                    new AntPathRequestMatcher(this.adminContextPath + "/instances", HttpMethod.POST.toString()),
                    new AntPathRequestMatcher(this.adminContextPath + "/instances/*",
                            HttpMethod.DELETE.toString()),
                    new AntPathRequestMatcher(this.adminContextPath + "/actuator/**"));
        }
    }

    @Profile("pro")
    @Configuration()
    public static class SecuritySecureConfig extends WebSecurityConfigurerAdapter {

        private final String adminContextPath;

        public SecuritySecureConfig(AdminServerProperties adminServerProperties) {
            this.adminContextPath = adminServerProperties.getContextPath();
        }

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            // @formatter:off
            SavedRequestAwareAuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
            successHandler.setTargetUrlParameter("redirectTo");
            successHandler.setDefaultTargetUrl(this.adminContextPath + "/");

            http.authorizeRequests()
                    .antMatchers(this.adminContextPath + "/assets/**").permitAll()
                    .antMatchers(this.adminContextPath + "/login").permitAll()
                    .anyRequest().authenticated()
                    .and()
                    .formLogin().loginPage(this.adminContextPath + "/login").successHandler(successHandler).and()
                    .logout().logoutUrl(this.adminContextPath + "/logout").and()
                    .httpBasic().and()
                    .csrf()
                    .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
                    .ignoringRequestMatchers(
                            new AntPathRequestMatcher(this.adminContextPath + "/instances", HttpMethod.POST.toString()),
                            new AntPathRequestMatcher(this.adminContextPath + "/instances/*", HttpMethod.DELETE.toString()),
                            new AntPathRequestMatcher(this.adminContextPath + "/actuator/**")
                    );
            // @formatter:on
        }

    }

}

创建bootstrap.yaml文件

填入以下内容,配置nacos服务中心

spring:
  application:
    name: admin-server
  cloud:
    # nacos配置
    nacos:
      discovery:
        server-addr: xxx:8848

创建application.yaml文件

spring:
  profiles:
    active: pro,admin
server:
  port: 3333
---
spring:
  profiles: dev
---
spring:
  profiles: pro
  security:
    user:
      name: "user"
      password: "password"
  cloud:
    nacos:
      discovery:
        # 将本服务实例注册到服务中心时,在元信息中携带访问本服务使用的用户名和密码
        metadata:
          user.name: "user"         
          user.password: "password" 
# 如果采用eureka做注册中心时,将采用该配置
eureka:
  instance:
    # 将本服务实例注册到服务中心时,在元信息中携带访问本服务使用的用户名和密码
    metadata-map:
      user.name: "user"         #These two are needed so that the server
      user.password: "password"  #can access the protected client endpoints

在上面的配置文件中,不管是作为Nacos的客户端还是Eureka的客户端,我们都有一个metadata的配置:

nacos:
metadata:
  user.name: "user"         
  user.password: "password" 
eureka:
metadata-map:
user.name: "user"         
user.password: "password" 

这个配置极其重要, 对于集成了Security的项目来讲,我们的所有接口都是被保护起来的,试问当我将本实例注册到注册中心, 却不提供访问的帐号和密码,Admin Server如何去访问本实例的接口获取状态信息呢,所以该配置的含义就是将本实例定义的Security框架的用户名和密码作为元信息一并上传到注册中心, 以让AdminServer在从注册中心拉取注册服务列表的同时,能获取到访问每个服务的密钥,因此才可以让Admin Server的监控功能起作用。

创建application-admin.yaml文件

填入以下内容

management:
  endpoints:
    web:
      exposure:
        include: "*"
  endpoint:
    health:
      show-details: ALWAYS

这些配置我就不解释了,读者后续可以自行去搜索引擎检索SpringBoot Actuator配置详解。

启动应用

查看Nacos界面:

Nacos的默认用户名/密码为:nacos/nacos

登录:

首页:

Wallboard:

点击方块进去看看:

successfully.

后记

看到这里读者可能会有个疑惑,被监控的客户端呢?AdminServer不是服务端吗,Admin客户端的配置呢?

解答:当我们集成了Nacos的服务发现依赖starter后,当应用启动时Nacos客户端就会自动将我们的服务实例注册到Nacos注册中心。
而AdminServer和注册中心集成的原理, 其实就是把原来Client直接获取到AdminServer的地址并且手动将自身注册进AdminServer的方式,变成了AdminClient服务注册至注册中心, 而AdminServer直接去注册中心动态获取所有注册服务的方式来监控所有服务(当然前提是该客户端集成了Actuator,且正确配置了相关访问的元信息)。

到这里也不得不再提一遍如果AdminClient集成了Security则一定要在元信息中携带用户访问信息:
nacos:
metadata:
  user.name: "user"         
  user.password: "password" 
eureka:
metadata-map:
user.name: "user"        
user.password: "password"
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!