springcloud Greenwich SR4版本筆記
本文只記錄實際版本,配置,pom,代碼以及注意事項。別的在其他springcloud 的F版本中已有詳述。
示例代碼地址:https://github.com/wjqhuaxia/springcloud-greenwich-sr4.git
目錄:
2、feign調用
3、網關zuul
3.1、過濾器實現權限校驗
3.2、智能路由
3.3、跨域處理
4、hystrix服務降級、熔斷、限流
4.2、zuul-hystrix服務降級
4.3、hystrix服務熔斷
5、ribbon客戶端負載勻衡
6、config
6.1、config server
6.2、config client
1. eureka server
詳細描述請查看另一文章。路徑【springcloud】Eureka服務注冊中心搭建
1.1 版本選擇
jdk: 1.8 springcloud: Greenwich.SR4 springboot: 2.1.9.RELEASE
1.2 pom
1.2.1 項目父pom
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.9.RELEASE</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.SR4</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/libs-milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
1.2.2 eureka server pom
<dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
</dependencies>
1.3 配置
1.3.1 單節點配置
application.yml中配置如下:
server:
port: 8761
eureka:
instance:
hostname: localhost
client:
registerWithEureka: false
fetchRegistry: false
serviceUrl:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
1.4 代碼
eureka server 中起動類代類如下:
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
1.5 安全校驗
1.5.1 pom增加項
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency>
1.5.2 eureka server
配置修改如下
server:
port: 8761
user: huaxia
password: huaxia
spring:
security:
user:
name: ${user}
password: ${password}
eureka:
instance:
hostname: localhost
client:
registerWithEureka: false
fetchRegistry: false
serviceUrl:
defaultZone: http://${user}:${password}@{eureka.instance.hostname}:${server.port}/eureka/
1.5.3 eureka client
配置時修改如下
eureka:
client:
service-url:
defaultZone:http://${user}:${password}@{eureka.instance.hostname}:${server.port}/eureka/
1.5.4 注意項
A、由於默認是開啟CSRF,所以需要將其關閉,不然會出現如下錯誤:
javax.ws.rs.WebApplicationException: com.fasterxml.jackson.databind.exc.MismatchedInputException:
Root name 'timestamp' does not match expected ('instance') for type [simple type, class com.netflix.appinfo.InstanceInfo]
在eureka服務端,創建一個WebSecurityConfig類,代碼如下:
// WebSecurityConfig.java
@EnableWebSecurity
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable(); //關閉csrf
super.configure(http); //開啟認證
}
}
B、spring-boot-starter-security包只能放在eureka server項目的pom中,如果放在其他服務的pom中,在調用接口時會報401,Unauthorized

1.5.5 總結
1、如果在eureka服務端增加上安全認證,客戶端無法注冊成功,先看看有沒有WebSecurityConfig。
2、客戶端也需要用戶名和密碼認證注冊的,服務端改成安全認證,客戶端不要忘了改。
3、如果服務端是安全認證的集群服務,客戶端注冊時每個地址都需要用戶名和密碼安全認證
安全配置參考:https://blog.csdn.net/wgh100817/article/details/101719042
2、feign調用
詳細請查看另一文章:【springcloud】模擬RPC調用(Feign)
2.1 pom文件修改
添加feign依賴
<!-- Feign 依賴 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
2.2 feign代碼實現
2.2.1 feign接口
@FeignClient(name = "stock-service", fallback = StockMsgClientFallback.class)
public interface StockMsgClient {
@GetMapping("/stock/getStockMsg")
public String getStockMsg();
}
2.2.2 feign接口調用
@RestController
public class HelloOrderController {
@Autowired
private StockMsgClient stockMsgClient;
@Autowired
private IntegralMsgClient integralMsgClient;
@GetMapping("/testStockRequest")
@ResponseBody
public String testStockRequest(){
return "order service: " + stockMsgClient.getStockMsg();
}
@GetMapping("/testIntegralRequest")
@ResponseBody
public String testIntegralRequest(){
return "order service: " + integralMsgClient.getIntegralMsg();
}
@RequestMapping(value = "/hello")
@ResponseBody
public String hello(){
return "Hello Order Controller!";
}
}
2.3 feign-hystrix服務降級
2.3.1 feignclient調用

2.3.2 fallback實現
@Component
public class StockMsgClientFallback implements StockMsgClient{
@Override
public String getStockMsg() {
return "getStockMsg 服務異常,請稍后再試!";
}
}
3、網關zuul
zuul相關詳細描述請移步:
【springcloud】API Gateway 的路由和過濾(Zuul--1)
【springcloud】Zuul高級配置(zuul--2)
【springcloud】Zuul高級配置(zuul--3)
3.1 pom配置
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-zuul</artifactId> </dependency>
3.2 智能路由實現
配置即可
zuul:
prefix: /api #zuul.prefix 配置項可以為所有的路由地址都添加一個前綴
routes:
order-service: /order-service/**
stock-service: /stock-service/**
訂單服務-服務降級處理
@Component
public class OrderServiceFallbackProvider implements FallbackProvider {
@Override
public String getRoute() {
return "order-service";
}
@Override
public ClientHttpResponse fallbackResponse(String route, final Throwable cause) {
if (cause instanceof HystrixTimeoutException) {
return response(HttpStatus.GATEWAY_TIMEOUT);
} else {
return response(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
private ClientHttpResponse response(final HttpStatus status) {
return new ClientHttpResponse() {
@Override
public HttpStatus getStatusCode() throws IOException {
return status;
}
@Override
public int getRawStatusCode() throws IOException {
return status.value();
}
@Override
public String getStatusText() throws IOException {
return status.getReasonPhrase();
}
@Override
public void close() {
}
@Override
public InputStream getBody() throws IOException {
return new ByteArrayInputStream("訂單服務暫時不可用,請稍后重試!".getBytes());
}
@Override
public HttpHeaders getHeaders() {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
return headers;
}
};
}
}
本示例中使用的是spring security安全框架實現安全管理。spring security的springboot集成可參考:springboot集成security
具體代碼已上傳github。以下記錄簡略步驟:
3.4.1 pom修改
<!-- security依賴 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
3.4.2 配置項
package cn.com.wjqhuaxia.config;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import cn.com.wjqhuaxia.service.impl.UserServiceImpl;
/**
* spring-security權限管理的核心配置
* @author wjqhuaxia
*
*/
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true) //全局
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
private final Logger logger = LoggerFactory.getLogger(getClass());
@Autowired
private UserServiceImpl userService;//實現了UserDetailsService接口
@Autowired
private MyFilterInvocationSecurityMetadataSource myFilterInvocationSecurityMetadataSource;//權限過濾器(當前url所需要的訪問權限)
@Autowired
private MyAccessDecisionManager myAccessDecisionManager;//權限決策器
@Autowired
private AuthenticationAccessDeniedHandler authenticationAccessDeniedHandler;//自定義錯誤(403)返回數據
/**
* 自定義的加密算法
* @return
*/
@Bean
public PasswordEncoder myPasswordEncoder() {
return new MyPasswordEncoder();
}
/**
* 配置userDetails的數據源,密碼加密格式
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService).passwordEncoder(myPasswordEncoder());
}
/**
* 配置放行的資源
*/
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring()
.antMatchers("/index.html", "/static/**","/loginPage","/register")
// 給 swagger 放行;不需要權限能訪問的資源
.antMatchers("/swagger-ui.html", "/swagger-resources/**", "/images/**", "/webjars/**", "/v2/api-docs",
"/configuration/ui", "/configuration/security", "/swagger-ui.html#!");
}
/**
* 這段配置,我認為就是配置Security的認證策略, 每個模塊配置使用and結尾。
authorizeRequests()配置路徑攔截,表明路徑訪問所對應的權限,角色,認證信息。
formLogin()對應表單認證相關的配置
logout()對應了注銷相關的配置
httpBasic()可以配置basic登錄
*/
/**
* HttpSecurity包含了原數據(主要是url)
* 1.通過withObjectPostProcessor將MyFilterInvocationSecurityMetadataSource和MyAccessDecisionManager注入進來
* 2.此url先被MyFilterInvocationSecurityMetadataSource處理,然后 丟給 MyAccessDecisionManager處理
* 3.如果不匹配,返回 MyAccessDeniedHandler
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
// authorizeRequests()配置路徑攔截,表明路徑訪問所對應的權限,角色,認證信息
http.authorizeRequests()
.withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
@Override
public <O extends FilterSecurityInterceptor> O postProcess(O o) {
o.setSecurityMetadataSource(myFilterInvocationSecurityMetadataSource);
o.setAccessDecisionManager(myAccessDecisionManager);
return o;
}
})
.and()
// formLogin()對應表單認證相關的配置
.formLogin()
//.loginPage("/loginPage.html")
.loginProcessingUrl("/login")
.usernameParameter("username")
.passwordParameter("password")
.permitAll()
.failureHandler(new AuthenticationFailureHandler() {
@Override
public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
logger.error("onAuthenticationFailure error. e: {}", e);
httpServletResponse.setContentType("application/json;charset=utf-8");
PrintWriter out = httpServletResponse.getWriter();
StringBuffer sb = new StringBuffer();
sb.append("{\"status\":\"error\",\"msg\":\"");
if (e instanceof UsernameNotFoundException || e instanceof BadCredentialsException) {
sb.append("用戶名或密碼輸入錯誤,登錄失敗!");
} else {
sb.append("登錄失敗!");
}
sb.append("\"}");
out.write(sb.toString());
out.flush();
out.close();
}
}).successHandler(new AuthenticationSuccessHandler() {
@Override
public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
httpServletResponse.setContentType("application/json;charset=utf-8");
PrintWriter out = httpServletResponse.getWriter();
String s = "{\"status\":\"success\",\"msg\":\"登陸成功\"}";
out.write(s);
out.flush();
out.close();
}
}).and()
// logout()對應了注銷相關的配置
.logout()
.permitAll()
.and()
.csrf()
.disable()
.exceptionHandling()
.accessDeniedHandler(authenticationAccessDeniedHandler);
}
}
3.4.3 權限數據源信息
詳細信息見github代碼以及springboot集成security,此處僅做簡略記錄。

4、springcloud config
4.1 config server
4.1.1 pom
添加 spring-cloud-config-server 依賴。
<parent>
<groupId>cn.com.wjqhuaxia</groupId>
<artifactId>greenwich-sr4-master</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>greenwich-sr4-config</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
</dependencies>
4.1.2 啟動類
添加 @EnableConfigServer 注解。
package cn.com.wjqhuaxia;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.config.server.EnableConfigServer;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableConfigServer
@EnableEurekaClient
public class ConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class, args);
}
}
4.1.3 配置項
application.yml,其中uri為git遠程庫地址。
spring:
application:
name: config-center
cloud:
config:
server:
git:
uri: https://github.com/wjqhuaxia/config.git
username: xx
password: xx
server:
port: 8000 # 端口號默認8080
# 服務注冊
eureka:
client:
serviceUrl:
defaultZone: http://huaxia:huaxia@localhost:8761/eureka/
4.2 config client
示例代碼中order項目,修改application.yml文件,並添加bootstrap.yml文件

4.2.1 pom
添加config-client依賴
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-config-client</artifactId> </dependency>
4.2.2 配置文件內容
spring:
application:
name: order-service
cloud:
config:
discovery:
enabled: true
service-id: config-center # 指定用於獲取配置的配置中心服務(應用)名稱
profile: test
label: master
eureka:
client:
serviceUrl:
defaultZone: http://huaxia:huaxia@localhost:8761/eureka/
