SpringCloud系列十二:手動創建Feign


1. 回顧

  上文講解了自定義Feign。但是在某些場景下,前文自定義Feign的方式滿足不了需求,此時可使用Feign Builder API手動創建Feign。

  本文圍繞以下場景,為大家講解如何手動創建Feign。

  • 用戶微服務的接口需要登錄后才能調用,並且對於相同的API,不同角色的用戶有不同的行為。
  • 讓電影微服務中的同一個Feign接口,使用不同的賬號登錄,並調用用戶微服務的接口。

2. 修改用戶微服務

  > 復制項目 microservice-provider-user,將 ArtifactId 修改為 microservice-provider-user-with-auth

  > 添加Spring Security依賴

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-security</artifactId>
</dependency>

  > 創建 Spring Security的配置類

package com.itmuch.cloud.microserviceprovideruserwithauth.security; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import java.util.ArrayList; import java.util.Collection; public class SecurityUser implements UserDetails { private static final long serialVersoinUID = 1L; private Long id; private String username; private String password; private String role; public SecurityUser() { } public SecurityUser(String username, String password, String role) { this.username = username; this.password = password; this.role = role; } @Override public Collection<? extends GrantedAuthority> getAuthorities() { Collection<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>(); SimpleGrantedAuthority authority = new SimpleGrantedAuthority(this.role); authorities.add(authority); return authorities; } @Override public String getPassword() { return password; } @Override public String getUsername() { return username; } @Override public boolean isAccountNonExpired() { return true; } @Override public boolean isAccountNonLocked() { return true; } @Override public boolean isCredentialsNonExpired() { return true; } @Override public boolean isEnabled() { return true; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public void setUsername(String username) { this.username = username; } public void setPassword(String password) { this.password = password; } public String getRole() { return role; } public void setRole(String role) { this.role = role; } }
package com.itmuch.cloud.microserviceprovideruserwithauth.security; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Component; @Component public class CustomUserDetailsService implements UserDetailsService { /** * 模擬兩個賬戶 * ① 賬號是user,密碼是password1,角色是user-role * ② 賬號時候admin,密碼是password1,角色是admin-role * @param username * 用戶名 * @return * * @throws UsernameNotFoundException */ @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { if ("user".equals(username)) { return new SecurityUser("user", "password1", "user-role"); } else if ("admin".equals(username)) { return new SecurityUser("admin", "password2", "admin-role"); } else { return null; } } }
package com.itmuch.cloud.microserviceprovideruserwithauth.security; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; 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.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.crypto.password.NoOpPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; @Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private CustomUserDetailsService userDetailsService; @Override protected void configure(HttpSecurity http) throws Exception { // 所有的請求,都需要經過HTTP basic認證
 http .authorizeRequests() .anyRequest().authenticated() .and() .httpBasic(); } @Bean public PasswordEncoder passwordEncoder() { // 明文編碼器。這個一個不做任何操作的密碼編碼器,是Spring提供給我們做明文測試的
        return NoOpPasswordEncoder.getInstance(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(this.userDetailsService).passwordEncoder(this.passwordEncoder()); } }

  > 修改Controller,在其中打印當前登錄的用戶信息

package com.itmuch.cloud.microserviceprovideruserwithauth.controller; import com.itmuch.cloud.microserviceprovideruserwithauth.dao.UserRepository; import com.itmuch.cloud.microserviceprovideruserwithauth.pojo.User; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController; import java.util.Collection; @RestController public class UserController { private static final Logger LOGGER = LoggerFactory.getLogger(UserController.class); @Autowired private UserRepository userRepository; @GetMapping("/{id}") public User findById(@PathVariable Long id) { Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal(); if (principal instanceof UserDetails) { UserDetails user = (UserDetails) principal; Collection<? extends GrantedAuthority> collections = user.getAuthorities(); for (GrantedAuthority ga: collections) { // 打印當前登錄用戶的信息
                UserController.LOGGER.info("當前用戶是{}, 角色是{}", user.getUsername(), ga.getAuthority()); } } else { UserController.LOGGER.warn("ε=(´ο`*)))唉,出現問題了"); } User findOne = userRepository.findById(id).get(); return findOne; } }

  > 啟動 microservice-discovery-eureka

  > 啟動 microservice-provider-user-with-auth

  > 訪問 http://localhost:8000/1,彈出登錄對話框

 

  > 使用 user/password1 登錄,可在控制台看到如下日志

  > 使用 admin/password2 登錄,可在控制台看到如下日志

3. 修改電影微服務

  > 復制項目 microservice-consumer-movie-feign,將 ArtifactId 修改為 microservice-consumer-movie-feign-manual

  > 去掉Feign接口 UserFeignClient上的@FeignClient注解

package com.itmuch.cloud.microserviceconsumermoviefeignmanual.feign; import com.itmuch.cloud.microserviceconsumermoviefeignmanual.pojo.User; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; public interface UserFeignClient { @GetMapping(value = "/{id}") User findById(@PathVariable("id") Long id); }

  > 去掉啟動類上的 @EnableFeignClients 注解

package com.itmuch.cloud.microserviceconsumermoviefeignmanual; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.context.annotation.Bean; import org.springframework.web.client.RestTemplate; @SpringBootApplication @EnableDiscoveryClient public class MicroserviceConsumerMovieFeignManualApplication { public static void main(String[] args) { SpringApplication.run(MicroserviceConsumerMovieFeignManualApplication.class, args); } }

  > 修改 Controller

package com.itmuch.cloud.microserviceconsumermoviefeignmanual.controller; import com.itmuch.cloud.microserviceconsumermoviefeignmanual.feign.UserFeignClient; import com.itmuch.cloud.microserviceconsumermoviefeignmanual.pojo.User; import feign.Client; import feign.Contract; import feign.Feign; import feign.auth.BasicAuthRequestInterceptor; import feign.codec.Decoder; import feign.codec.Encoder; import org.springframework.cloud.openfeign.FeignClientsConfiguration; import org.springframework.context.annotation.Import; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController; @Import(FeignClientsConfiguration.class) // Spring Cloud為Feign默認提供的配置類
@RestController public class MovieController { private UserFeignClient userUserFeignClient; private UserFeignClient adminUserFeignClient; public MovieController(Decoder decoder, Encoder encoder, Client client, Contract contract) { this.userUserFeignClient = Feign.builder().client(client).encoder(encoder).decoder(decoder).contract(contract) .requestInterceptor(new BasicAuthRequestInterceptor("user", "password1")) .target(UserFeignClient.class, "http://microservice-provider-user/"); this.adminUserFeignClient = Feign.builder().client(client).encoder(encoder).decoder(decoder).contract(contract) .requestInterceptor(new BasicAuthRequestInterceptor("admin", "password2")) .target(UserFeignClient.class, "http://microservice-provider-user/"); } @GetMapping("/user-user/{id}") public User findByIdUser(@PathVariable Long id) { return this.userUserFeignClient.findById(id); } @GetMapping("/user-admin/{id}") public User findByIdAdmin(@PathVariable Long id) { return this.adminUserFeignClient.findById(id); } }

  > 啟動 microservice-discovery-eureka

  > 啟動 microservice-provider-user-with-auth

  > 啟動 microservice-consumer-movie-feign-manual

  > 訪問 http://localhost:8010/user-user/1,可正常獲取結果,並且在用戶微服務控制台打印如下日志

  > 訪問 http://localhost:8010/user-admin/1,可正常獲取結果,並且在用戶微服務控制台打印如下日志

4. 總結

  由測試不難發現,手動創建Feign的方式更加靈活。

  下文將講解Feign對集成的支持、對壓縮的支持、日志、構造多參數請求等。敬請期待~~~

5. 參考

  周立 --- 《Spring Cloud與Docker微服務架構與實戰》


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM