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/