这篇博客的接着上一篇博客spring cloud(一)写的,使用的demo也是基于上一篇博客的demo继续演进,主要讲述了如何为服务提供方配置安全验证,以及Eureka的服务注册与发现,还会讲到Eureka的高可用。
一、为服务提供方配置安全验证
修改demo-provider-product项目的pom文件,加上springSecurity的依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
修改demo-provider-product项目的application.yml文件,这里需要注意不能有有两个同级的"spring:"或其他属性
spring:
security:
user:
name: admin # 认证用户名
password: nelson# 认证密码
roles:
- USER # 授权角色
服务提供方配置了安全验证之后服务消费方如果不做认证,就访问不了服务提供方了,如下结果
那么服务的消费方需要如何配置呢?
首先修改RestConfig配置类,在里面添加HttpHeaders头信息
@Bean
public HttpHeaders getHeaders() { // 要进行一个Http头信息配置
HttpHeaders headers = new HttpHeaders(); // 定义一个HTTP的头信息
String auth = "admin:nelson"; // 认证的原始信息
byte[] encodedAuth = Base64.getEncoder()
.encode(auth.getBytes(Charset.forName("US-ASCII"))); // 进行一个加密的处理
String authHeader = "Basic " + new String(encodedAuth);
headers.set("Authorization", authHeader);
return headers;
}
然后修改ConsumerProductController,在进行服务调用的时候加上这个头信息
package com.nelson.org.controller;
import com.nelson.org.entity.Product;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
import java.util.List;
/**
* @auther 1301913120@qq.com
* @create 2019-06-12 20:09
* @todo
*/
@RestController
@RequestMapping("/consumer")
public class ConsumerProductController {
public static final String PRODUCT_GET_URL = "http://localhost:8080/product/get/";
public static final String PRODUCT_LIST_URL="http://localhost:8080/product/list/";
public static final String PRODUCT_ADD_URL = "http://localhost:8080/product/add/";
@Resource
private RestTemplate restTemplate;
@Resource
private HttpHeaders httpHeaders;
@RequestMapping("/product/get")
public Object getProduct(long id) {
Product product = restTemplate.exchange(PRODUCT_GET_URL + id, HttpMethod.GET, new HttpEntity<Object>(httpHeaders), Product.class).getBody();
return product;
}
@RequestMapping("/product/list")
public Object listProduct() {
List<Product> list = restTemplate.exchange(PRODUCT_LIST_URL,HttpMethod.GET,new HttpEntity<Object>(httpHeaders), List.class).getBody();
return list;
}
@RequestMapping("/product/add")
public Object addPorduct(Product product) {
Boolean result = restTemplate.exchange(PRODUCT_ADD_URL, HttpMethod.POST, new HttpEntity<Object>(product, httpHeaders), Boolean.class).getBody();
return result;
}
}
调用测试
列表查询:http://localhost/consumer/product/list
然后调用其中一个接口试试,发现可以成功调用。
现在这里只有一个服务提供者,但是真实的项目中必然有多个服务提供方,绝大多数情况下这些服务都会用到安全验证,而且密码也会一样,如果每个服务都单独维护,每次密码变动都要对每个服务提供者重新配置,所以下面我们应该把安全验证模块单独抽离出一个模块。
新建一个demo-security模块,pom文件如下
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>demo-spring-cloud</artifactId>
<groupId>com.nelson.org</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>demo-security</artifactId>
<version>1.0.0</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
</dependencies>
</project>
简历一个统一的安全配置类,这个类负责用户及密码相关的配置
package com.nelson.org.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
/**
* @auther 1301913120@qq.com
* @create 2019-06-21 13:53
* @todo
*/
@Configuration
@EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter{
@Override
public void configure(AuthenticationManagerBuilder auth)
throws Exception {
auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder()).withUser("root").password(new BCryptPasswordEncoder().encode("nelson")).roles("USER").
and().withUser("admin").password(new BCryptPasswordEncoder().encode("nelson")).roles("USER", "ADMIN");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.httpBasic().and().authorizeRequests().anyRequest()
.fullyAuthenticated();
http.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
}
修改demo-provider-product模块的pom文件,把spring-boot-starter-security的依赖去掉,引入刚刚新建的安全认证模块demo-security
修改demo-provider-product的yml文件,把相关的安全配置项删掉
调用测试,认证模块添加成功(注意这不是单点登录,只是把安全认证模块单独抽离成独立模块)
列表查询:http://localhost/consumer/product/list
二、Eureka服务注册与发现
新建一个demo-eureka模块,pom文件如下
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>demo-spring-cloud</artifactId>
<groupId>com.nelson.org</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>demo-eureka</artifactId>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
</project>
注意:
如果是Edgware或之前的版本,用的是springboot 1.5.或者更低的版本
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>
为demo-eureka模块添加yml文件,并配置eureka相关信息
server:
port: 7001
eureka:
instance:
hostname: localhost
新增Eureka启动类,增加Eureka服务端注解
package com.nelson.org;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
/**
* @auther 1301913120@qq.com
* @create 2019-06-13 10:31
* @todo
*/
@SpringBootApplication
@EnableEurekaServer
public class EurekaApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaApplication.class, args);
}
}
运行main函数,虽然会报错,但先不用管,后面会解释什么原因,现在eureka已经启动起来了
然后访问localhost:7001,进入eureka控制台
Eureka启动起来了,下一步就是把服务提供方注册到eureka上
修改服务提供方demo-provider-product的pom文件,添加上eureka客户端的依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
如果是Edgware或之前的版本,用的是springboot 1.5.或者更低的版本则使用以下依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
修改服务提供方的yml文件,在里面定义要注册到的eureka服务的地址
修改服务提供方的启动类,在这个类增加eureka客户端的注解
加上eureka客户端注解之后启动服务提供方demo-provider-product,然后去eureka控制台刷新看一下,可以发现服务已注册到eureka上
虽然服务已成功注册到eureka上,但是可以看到Application和Status列的名字,UNKNOWN这样的名字毫无描述性,这个时候我们可以通过修改服务提供方的yml文件中的eureka配置改变这种情况。
修改后重启服务提供方,再刷新eureka控制台,可以看到Status列已发生改变
此时我们可以发现Application列还是显示UNKNOWN,也不具备描述性,这个时候我们需要为服务提供者配置application name
另外一般情况下,当鼠标焦点落在Status上查看的时候应该以IP作为链接项,这样当我们有多个服务提供方的时候可以容易看出是属于那台主机的
再次修改服务提供方的yml文件,新增一个eureka配置
然后我们点击Status列
点击Status列会显示如下信息,如果想看状态信息需要增加actuator模块
增加actuator模块,首先修改服务提供方的pom文件,在里面添加一个依赖
然后修改服务提供方demo-provider-product的yml文件
info:
app.name: microcloud-provider-product
company.name: nelson
build.artifactId: $project.artifactId$
build.modelVersion: $project.modelVersion$
注意:由于在yml文件中使用了$,这个时候启动是会报错的,因此还需要一个maven-resources-plugin插件的支持
接着修改父工程的pom文件
ps:最好所有新引入依赖的模块都Reimport all maven project一下
重新启动demo-eureka和demo-provider-product,再次点击Status,可以发现这次可以输出状态信息
三、通过发现服务来获取一些服务信息
然后访问http://localhost:8080/product/discover,可以发现通过DiscoveryClient可以拿到很多信息,但是注意是org.springframework.cloud.client.discovery.DiscoveryClient;而不是com.netflix.discovery.DiscoveryClient;
四、Eureka安全机制
一般情况下Eureka 和服务的提供注册者都会在一个内网环境中,但免不了在某些项目中需要让其他外网的服务注册到Eureka,这个时候就有必要让Eureka增加一套安全认证机制了,让所有服务提供者通过安全认证后才能注册进来。
首先在demo-eureka的pom文件中添加安全依赖
然后在demo-eureka的yml文件中配置账号密码
然后修改服务提供者的yml文件,修改eureka的请求url,加上账号密码。
注意:如果是Edgware或之前的版本,做到这一步就行了,但使用现在版本,你会发现启动demo-provider-product后服务注册不上去,如下提示
这时需要为demo-eureka模块新增配置类EurekaSecurityConfig,重写configure方法,把csrf劫持关闭
package com.nelson.org;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
/**
* @auther 1301913120@qq.com
* @create 2019-06-20 11:16
* @todo
*/
@Configuration
@EnableWebSecurity
public class EurekaSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
super.configure(http);
}
}
先重启服务demo-eureka再重启demo-provider-product,发现服务成功注册
五、Eureka高可用集群HA
我这里主要是基于本地搭建一个高可用Eureka集群,所以只是修改了本机的host文件,搭建多服务器之间的高可用其实是同理的。
Eureka单节点的情况下,如果Eureka出现问题将可能导致整个集群的不可用,这个时候就需要考虑注册中心的高可用解决方案了。
现在需要三个Eureka,每个Eureka都需要配置hostname,先修改本机的host文件,如下,分别为eureka1、eureka2、eureka3
接着把demo-eureka复制两份,把文件夹的名称改为demo-eureka-2和demo-eureka-3,把复制后的demo-eureka-2和demo-eureka-3中其他多余的文件删掉,只留下src目录和pom文件就可以了
把复制后的demo-eureka-2和demo-eureka-3配置成maven项目
修改demo-eureka的yml文件
然后修改demo-eureka-2和demo-eureka-3的yml文件,端口不能冲突(ps:yml文件的内容的拷贝,注意拷贝之后的缩进是否正确)
启动demo-eureka-2和demo-eureka-3项目可能会报“找不到主类”异常,这个时候先clean然后compile再install就可以了
使用yml文件中配置的eureka安全认证账号密码登录