Springcloud security+Nacos+Spring Boot Admin+Gateway框架搭建

当前架构不断演进,分布式架构的重要性越开越高,本文就记录一下整个搭建过程,本次主要利用springcloud自带的分布式特性,由于nacos可以支持动态刷新以及拥有可视化界面,方便服务上下线管理,故采用nacos提代eureka以及config,利用springboot admin配合acturaror对各微服务进行监控,同时利用nacos的动态刷新配合gateway实现动态更新路由,为了保证数据安全,采用Springcloud security对admin模块进行加密,防止数据泄露,具体技术选型大家自行判断。

Nacos服务搭建

版本选择

您可以在Nacos的release notes中找到每个版本支持的功能的介绍,当前使用的稳定版本为2.0.3。

预备环境准备

Nacos 依赖Java环境来运行,需要安装JDK8+

您可以从 最新稳定版本 下载编译好的安装包如nacos-server-2.0.3.tar.gz。

下载后解压安装包

tar -xvf nacos-server-2.0.3.tar.gz

进入解压目录nacos

cd nacos

解压后目录结构如截图

Springcloud security+Nacos+Spring Boot Admin+Gateway框架搭建_第1张图片

配置

按照官方文档配置启动默认是不需要登录的这样会导致你的配置中心对外直接暴露,如果需要在微服务注册时进行认证,则需要修改conf目录下的application.properties,改为如下配置:

nacos.core.auth.enabled=true

启动&关闭服务服务

Linux/Unix/Mac

进入bin目录,启动命令(standalone代表着单机模式运行,非集群模式):

sh startup.sh -m standalone

关闭命令为

sh shutdown.sh

nacos默认密码为nacos/nacos。如果需要修改密码,则进入页面可以修改密码

页面地址:http://ip:8848/nacos 

将ip改为你部署的实际IP

具体nacos官网说明地址为Nacos 快速开始

SpringCloud 工程搭建

使用springcloud多模块搭建,父工程pom文件如下:




    4.0.0
    com.test
    springcloud-demo
    1.0.0
    pom
    springcloud-demo

    
        org.springframework.boot
        spring-boot-starter-parent
        2.2.10.RELEASE
    

    
        gateway
        user
        producer
        consumer
        admin
    

    
        UTF-8
        UTF-8
        1.8
        4.12
        2.6
        1.3.1
        2.5
        1.10
        1.2.78
        2.9.9
        1.18.4
        2.7.0
        Hoxton.SR8
    

    
        
            junit
            junit
            ${junit.version}
            test
        

        
            org.springframework.boot
            spring-boot-starter-test
            test
        

        
        
            org.springframework.boot
            spring-boot-starter-logging
            
                
                    org.apache.logging.log4j
                    log4j-api
                
            
        

        
            com.alibaba
            fastjson
            ${fastjson.version}
        

        
            org.projectlombok
            lombok
            ${lombok.version}
        

        
            com.alibaba.cloud
            spring-cloud-starter-alibaba-nacos-discovery
        

        
        
            com.alibaba.cloud
            spring-cloud-starter-alibaba-nacos-config
        

        
            org.springframework.cloud
            spring-cloud-starter-openfeign
        

        
        
            org.springframework.boot
            spring-boot-starter-actuator
        

    

    
    
        
            
            
                org.springframework.cloud
                spring-cloud-dependencies
                ${spring-cloud.version}
                pom
                import
            

            
                com.alibaba.cloud
                spring-cloud-alibaba-dependencies
                2.2.5.RELEASE
                pom
                import
            

            
            
                io.springfox
                springfox-swagger2
                ${swagger.version}
            
            
                io.springfox
                springfox-swagger-ui
                ${swagger.version}
            

            
                joda-time
                joda-time
                ${joda.time.version}
            

            
                commons-lang
                commons-lang
                ${commons.lang.version}
            
            
                commons-fileupload
                commons-fileupload
                ${commons.fileupload.version}
            
            
                commons-io
                commons-io
                ${commons.io.version}
            
            
                commons-codec
                commons-codec
                ${commons.codec.version}
            

        

    

         
               
                   
                       
                       org.apache.maven.plugins
                       maven-compiler-plugin
                       3.1
                       
                           
                           1.8 
                           1.8 
                           UTF-8
                       
                   

               
           

    
    
        
            public
            aliyun nexus
            http://maven.aliyun.com/nexus/content/groups/public/
            
                true
            
        
    
    
        
            public
            aliyun nexus
            http://maven.aliyun.com/nexus/content/groups/public/
            
                true
            
            
                false
            
        
    

搭建Spring Admin

pom文件如下:




    
        com.test
        springcloud-demo
        1.0.0
    

    4.0.0
    com.example
    admin
    1.0.0
    admin
    admin for Spring Boot

    
        
            org.springframework.boot
            spring-boot-starter-web
        

        
            de.codecentric
            spring-boot-admin-starter-server
            2.2.1
        

        
            org.springframework.boot
            spring-boot-starter-security
        

        
            de.codecentric
            spring-boot-admin-server-ui
            2.2.1
        

    

    
        
            
                org.apache.maven.plugins
                maven-compiler-plugin
                3.8.1
                
                    1.8
                    1.8
                    UTF-8
                
            
            
                org.springframework.boot
                spring-boot-maven-plugin
                
                    com.example.demo.AdminApplication
                
                
                    
                        repackage
                        
                            repackage
                        
                    
                
            
        
    


在resources目录下删除application.yml,新建bootstrap.yml,文件内容如下:

# 应用名称
spring:
  security:
    user:
      name: admin
      password: admin
  application:
    name: admin
  cloud:
    nacos:
      username: nacos
      password: XXX
      config:
        server-addr: nacos.com:8848
        namespace: 4712216e-5fb1-4855-b57e-d57fae45809b
        file-extension: yaml
      discovery:
        server-addr: nacos.com:8848
        namespace: 4712216e-5fb1-4855-b57e-d57fae45809b
        metadata:
          user.name: ${spring.security.user.name}
          user.password: ${spring.security.user.password}
  profiles:
    active: dev
  jmx:
    enabled: true

server:
  port: 9000

其中,spring.security.user.name为admin界面的用户名配置,spring.security.user.password为admin界面的密码配置,经测试,若通过nacos下发配置文件用户名和密码不生效,在启动日志中会输出默认密码。spring.cloud.nacos.username为nacos设置的用户名,spring.cloud.nacos.password为nacos设置的相应的密码。另外注意的是,spring.cloud.nacos.discovery.metadata.user.name 的配置是因为引入security后若不配置相应的密码,会导致无法在注册中心注册。另外如果在nacos上新建了命名空间的话则需要上述的spring.cloud.nacos.discovery.namespace以及spring.cloud.nacos.config.namespace配置,如果在默认命名空间则不需要该配置

在admin server工程中新建SecuritySecureConfig类,具体内容如下

import de.codecentric.boot.admin.server.config.AdminServerProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;

@Configuration
public 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(adminContextPath + "/");

        http.authorizeRequests()
                //授予对所有静态资产和登录页面的公共访问权限
                .antMatchers(adminContextPath + "/assets/**").permitAll()
                .antMatchers(adminContextPath + "/login").permitAll()
                //必须对每个其他请求进行身份验证
                .anyRequest().authenticated()
                .and()
                //配置登录和注销
                .formLogin().loginPage(adminContextPath + "/login").successHandler(successHandler).and()
                .logout().logoutUrl(adminContextPath + "/logout").and()
                //启用HTTP-Basic支持。这是Spring Boot Admin Client注册所必需的
                .httpBasic().and()
                .csrf()
                .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
                .ignoringAntMatchers(
                        //	禁用CRSF保护Spring引导管理客户端用来注册的端点。
                        adminContextPath + "/instances",
                        // 禁用执行器端点的CRSF保护
                        adminContextPath + "/actuator/**"
                );
    }


}

在admin server工程中的启动类上添加@EnableAdminServer 以及@EnableDiscoveryClient注解。并在resources目录下新建logback-spring.xml,具体内容配置如下:



    
    
    
    
    
    ${APP_Name}
    
    

    
    
    
    
    
    

    
    
        
            ${CONSOLE_LOG_PATTERN}
            utf8
        
    

    
    
        ${LOG_HOME}/admin.log
        
            
            ${LOG_HOME}/output-%d{yyyy-MM-dd}.log
            
            30
        

        
            
            %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}:%L - %msg%n
        
    

    
    
        
        
    

在nacos上创建配置admin-dev.yaml(具体命名规则见官网说明),配置如下:

#### 暴露端点
management:
  endpoints:
    web:
      exposure:
        include: '*'
  endpoint:
    loggers:
      enabled: true
    logfile: 
      external-file: /apps/logs/admin/admin.log
    health:
      show-details: always

spring:
  jmx:
    enabled: true

其中,management.endpoint.logfile.external-file参数为logback-spring.xml中配置的日志文件,这样在admin的管理界面可以动态查看日志记录

至此admin server搭建完成,大家可以启动看一下是否注册成功,并登录admin server的管理界面进行查看监控数据,地址一般为http://ip:port/login ,其中ip为部署admin server的地址,port为yml文件中配置的地址,输入用户名和密码即可查看。

搭建gateway网关

pom文件如下:



    4.0.0
    
        com.test
        springcloud-demo
        1.0.0
    

    gateway
    gateway网关
    1.0.0

    

        
            org.springframework.cloud
            spring-cloud-starter-gateway
        

        
            de.codecentric
            spring-boot-admin-starter-client
            2.2.1
        

        
        
            com.alibaba.cloud
            spring-cloud-starter-alibaba-sentinel
        
        
            com.alibaba.cloud
            spring-cloud-alibaba-sentinel-gateway
        

        
            org.jolokia
            jolokia-core
        

        
        
            io.springfox
            springfox-swagger2
        

        
        
            io.github.openfeign
            feign-okhttp
        
        
            org.springframework.cloud
            spring-cloud-starter-openfeign
        
    


    
        ${project.artifactId}-${project.version}
        
            
                org.springframework.boot
                spring-boot-maven-plugin
            
        
    

在resources目录下删除application.yml,新建bootstrap.yml,文件内容如下:

#端口号
server:
  port: 9100

# nacos 注册
spring:
  jmx:
    enabled: true
  application:
    name: gateway-zuul #服务名
  cloud:
    nacos:
      username: nacos
      password: Haha135790
      config:
        server-addr: nacos.com:8848
        namespace: 4712216e-5fb1-4855-b57e-d57fae45809b
        file-extension: yaml
        group: DEFAULT_GROUP
      discovery:
        server-addr: nacos.com:8848
        namespace: 4712216e-5fb1-4855-b57e-d57fae45809b
        metadata:
          management:
            context-path: /actuator
  profiles:
    active: dev

具体密码配置与admin server类似,主要是配置nacos的密码,只要相应的通知nacos即可注册成功,同目录下也需要配置logback-spring.xml。

大家可以参考openfeign的使用配置一些服务间调用,这个比较基础,这里就不详细展开了,后续可以进行测试。

在工程启动类上配置@EnableDiscoveryClient以及@EnableFeignClients(basePackages = "com.test.demo.gateway.feign"),并在nacos上配置配置文件gateway-zuul-dev.yaml,具体内容如下:

test: 
  name: shen1 

cors-config: 
  origin: "*"

session-filter: 
  ignored-path: /login
  

#### 暴露端点
management:
  endpoints:
    web:
      exposure:
        include: '*'
  endpoint:
    loggers:
      enabled: true
    logfile: 
      external-file: /apps/logs/zuul/zuul.log
    health:
      show-details: always

spring:
  cloud:
    gateway:
      routes:
      - id: demo-producer
        uri: lb://demo-producer
        predicates:
          - Path=/producer/**
      - id: user
        uri: lb://user-api
        predicates:
          - Path=/user/**

    discovery:
      locator:
        enabled: true
  jmx:
    enabled: true

日志文件路径也需要与logback-spring.xml中设置的一致,lb配置后面跟的是服务名,用于转发,spring.cloud.discovery.locator.enable需要设置为true,用于通过服务名进行查找相关微服务。

若想实现动态刷新路由,还需要增加一个配置文件NacosDynamicRouteConfig,具体内容如下:

import com.alibaba.cloud.nacos.NacosConfigProperties;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;
import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;

/**
 * nacos动态路由配置
 */
@Component
@Slf4j
@RefreshScope
public class NacosDynamicRouteConfig implements ApplicationEventPublisherAware {

    @Value("${spring.application.name}"+"-"+"${spring.profiles.active}")
    private String dataId;

    @Value("${spring.cloud.nacos.config.group}")
    private String group;

    @Value("${spring.cloud.nacos.config.server-addr}")
    private String serverAddr;

    @Autowired
    private NacosConfigProperties nacosConfigProperties;

    @Autowired
    private RouteDefinitionWriter routeDefinitionWriter;

    private ApplicationEventPublisher applicationEventPublisher;

    private static final List ROUTE_LIST = new ArrayList<>();

    @PostConstruct
    public void dynamicRouteByNacosListener() {
        try {
            log.info("dataId:{}",dataId);
            ConfigService configService =  NacosFactory.createConfigService(nacosConfigProperties.assembleConfigServiceProperties());
            String configInfo = configService.getConfig(dataId, group, 5000);
            log.info("configInfo:{}",configInfo);
            configService.addListener(dataId, group, new Listener() {
                @Override
                public void receiveConfigInfo(String configInfo) {
                    clearRoute();
                    try {
                        List gatewayRouteDefinitions = JSONObject.parseArray(configInfo, RouteDefinition.class);
                        for (RouteDefinition routeDefinition : gatewayRouteDefinitions) {
                            addRoute(routeDefinition);
                        }
                        publish();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }

                @Override
                public Executor getExecutor() {
                    return null;
                }
            });
        } catch (NacosException e) {
            e.printStackTrace();
        }
    }

    private void clearRoute() {
        for(String id : ROUTE_LIST) {
            this.routeDefinitionWriter.delete(Mono.just(id)).subscribe();
        }
        ROUTE_LIST.clear();
    }

    private void addRoute(RouteDefinition definition) {
        try {
            routeDefinitionWriter.save(Mono.just(definition)).subscribe();
            ROUTE_LIST.add(definition.getId());
        } catch (Exception e) {
            log.error("添加路由异常!",e);
        }
    }

    private void publish() {
        this.applicationEventPublisher.publishEvent(new RefreshRoutesEvent(this.routeDefinitionWriter));
    }


    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.applicationEventPublisher = applicationEventPublisher;
    }
}

需要注意的是,因为我们nacos设置了密码,所以获取ConfigService时需要参照示例,否则会出现认证问题,这样网关模块也搭建完成,只需要动态更新nacos的配置就可以实时更新路由,当然网关的一些个人配置大家可以自行配置,这里只是主要说明如何搭建整体框架。

搭建用户微服务模块

这里的用户微服务模块在我搭建过程中只是为了给网关提供feign调用,需要注意的是,当微服务配置了server.servlet.context-path参数后,采集也需要配置,否则admin展示采集数据会有问题,也就是文件中相应的配置了spring.cloud.nacos.discovery.metadata.management.context-path参数。user模块的bootstrap.yml配置文件如下:

# nacos 注册
spring:
  application:
    name: user-api #服务名
  cloud:
    nacos:
      username: nacos
      password: Haha135790
      config:
        server-addr: nacos.com:8848
        namespace: 4712216e-5fb1-4855-b57e-d57fae45809b
        file-extension: yaml
      discovery:
        server-addr: nacos.com:8848
        namespace: 4712216e-5fb1-4855-b57e-d57fae45809b
        metadata:
          management:
            context-path: ${server.servlet.context-path}/actuator
  profiles:
    active: dev

server:
  port: 8090
  servlet:
    context-path: /user

其他具体设置就不写了,也没有什么特别的。

总结

整体框架就是这样,注意的是,nacos要想实现动态刷新,需要在引入配置文件的地方加上@RefreshScope注解,这样就整体搭建了一套微服务,可实现服务的注册、配置文件的下发与动态更新,同时通过admin实现微服务的监控,整个过程都通过Spring Security设置了用户名以及密码防止安全问题,其实密码可以通过加密算法加密来实现,这样基本上可以保证一些安全性,如果大家有更好的想法,也欢迎随时沟通。

程序之路漫漫,吾将上下而求索 

你可能感兴趣的:(spring,cloud,spring,boot,spring,cloud,微服务)