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