1. 回顾

  上文讲解了自定义Feign。但是在某些场景下,前文自定义Feign的方式满足不了需求,此时可使用Feign Builder API手动创建Feign。

  本文围绕以下场景,为大家讲解如何手动创建Feign。

  • 用户微服务的接口需要登录后才能调用,并且对于相同的API,不同角色的用户有不同的行为。
  • 让电影微服务中的同一个Feign接口,使用不同的账号登录,并调用用户微服务的接口。

2. 修改用户微服务

  > 复制项目 microservice-provider-user,将 ArtifactId 修改为 microservice-provider-user-with-auth

  > 添加Spring Security依赖

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-security</artifactId>
  4. </dependency>

  > 创建 Spring Security的配置类

  1. package com.itmuch.cloud.microserviceprovideruserwithauth.security;
  2.  
  3. import org.springframework.security.core.GrantedAuthority;
  4. import org.springframework.security.core.authority.SimpleGrantedAuthority;
  5. import org.springframework.security.core.userdetails.UserDetails;
  6.  
  7. import java.util.ArrayList;
  8. import java.util.Collection;
  9.  
  10. public class SecurityUser implements UserDetails {
  11.  
  12. private static final long serialVersoinUID = 1L;
  13.  
  14. private Long id;
  15. private String username;
  16. private String password;
  17. private String role;
  18.  
  19. public SecurityUser() {
  20. }
  21.  
  22. public SecurityUser(String username, String password, String role) {
  23. this.username = username;
  24. this.password = password;
  25. this.role = role;
  26. }
  27.  
  28. @Override
  29. public Collection<? extends GrantedAuthority> getAuthorities() {
  30. Collection<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
  31. SimpleGrantedAuthority authority = new SimpleGrantedAuthority(this.role);
  32. authorities.add(authority);
  33. return authorities;
  34. }
  35.  
  36. @Override
  37. public String getPassword() {
  38. return password;
  39. }
  40.  
  41. @Override
  42. public String getUsername() {
  43. return username;
  44. }
  45.  
  46. @Override
  47. public boolean isAccountNonExpired() {
  48. return true;
  49. }
  50.  
  51. @Override
  52. public boolean isAccountNonLocked() {
  53. return true;
  54. }
  55.  
  56. @Override
  57. public boolean isCredentialsNonExpired() {
  58. return true;
  59. }
  60.  
  61. @Override
  62. public boolean isEnabled() {
  63. return true;
  64. }
  65.  
  66. public Long getId() {
  67. return id;
  68. }
  69.  
  70. public void setId(Long id) {
  71. this.id = id;
  72. }
  73.  
  74. public void setUsername(String username) {
  75. this.username = username;
  76. }
  77.  
  78. public void setPassword(String password) {
  79. this.password = password;
  80. }
  81.  
  82. public String getRole() {
  83. return role;
  84. }
  85.  
  86. public void setRole(String role) {
  87. this.role = role;
  88. }
  89. }
  1. package com.itmuch.cloud.microserviceprovideruserwithauth.security;
  2.  
  3. import org.springframework.security.core.userdetails.UserDetails;
  4. import org.springframework.security.core.userdetails.UserDetailsService;
  5. import org.springframework.security.core.userdetails.UsernameNotFoundException;
  6. import org.springframework.stereotype.Component;
  7.  
  8. @Component
  9. public class CustomUserDetailsService implements UserDetailsService {
  10.  
  11. /**
  12. * 模拟两个账户
  13. * ① 账号是user,密码是password1,角色是user-role
  14. * ② 账号时候admin,密码是password1,角色是admin-role
  15. * @param username
  16. * 用户名
  17. * @return
  18. *
  19. * @throws UsernameNotFoundException
  20. */
  21. @Override
  22. public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
  23. if ("user".equals(username)) {
  24. return new SecurityUser("user", "password1", "user-role");
  25. } else if ("admin".equals(username)) {
  26. return new SecurityUser("admin", "password2", "admin-role");
  27. } else {
  28. return null;
  29. }
  30. }
  31. }
  1. package com.itmuch.cloud.microserviceprovideruserwithauth.security;
  2.  
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.context.annotation.Bean;
  5. import org.springframework.context.annotation.Configuration;
  6. import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
  7. import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
  8. import org.springframework.security.config.annotation.web.builders.HttpSecurity;
  9. import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
  10. import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
  11. import org.springframework.security.crypto.password.NoOpPasswordEncoder;
  12. import org.springframework.security.crypto.password.PasswordEncoder;
  13.  
  14. @Configuration
  15. @EnableWebSecurity
  16. @EnableGlobalMethodSecurity(prePostEnabled = true)
  17. public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
  18.  
  19. @Autowired
  20. private CustomUserDetailsService userDetailsService;
  21.  
  22. @Override
  23. protected void configure(HttpSecurity http) throws Exception {
  24. // 所有的请求,都需要经过HTTP basic认证
  25. http
  26. .authorizeRequests()
  27. .anyRequest().authenticated()
  28. .and()
  29. .httpBasic();
  30. }
  31.  
  32. @Bean
  33. public PasswordEncoder passwordEncoder() {
  34. // 明文编码器。这个一个不做任何操作的密码编码器,是Spring提供给我们做明文测试的
  35. return NoOpPasswordEncoder.getInstance();
  36. }
  37.  
  38. @Override
  39. protected void configure(AuthenticationManagerBuilder auth) throws Exception {
  40. auth.userDetailsService(this.userDetailsService).passwordEncoder(this.passwordEncoder());
  41. }
  42. }

  > 修改Controller,在其中打印当前登录的用户信息

  1. package com.itmuch.cloud.microserviceprovideruserwithauth.controller;
  2.  
  3. import com.itmuch.cloud.microserviceprovideruserwithauth.dao.UserRepository;
  4. import com.itmuch.cloud.microserviceprovideruserwithauth.pojo.User;
  5. import org.slf4j.Logger;
  6. import org.slf4j.LoggerFactory;
  7. import org.springframework.beans.factory.annotation.Autowired;
  8. import org.springframework.security.core.GrantedAuthority;
  9. import org.springframework.security.core.context.SecurityContextHolder;
  10. import org.springframework.security.core.userdetails.UserDetails;
  11. import org.springframework.web.bind.annotation.GetMapping;
  12. import org.springframework.web.bind.annotation.PathVariable;
  13. import org.springframework.web.bind.annotation.RestController;
  14.  
  15. import java.util.Collection;
  16.  
  17. @RestController
  18. public class UserController {
  19.  
  20. private static final Logger LOGGER = LoggerFactory.getLogger(UserController.class);
  21.  
  22. @Autowired
  23. private UserRepository userRepository;
  24.  
  25. @GetMapping("/{id}")
  26. public User findById(@PathVariable Long id) {
  27. Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
  28. if (principal instanceof UserDetails) {
  29. UserDetails user = (UserDetails) principal;
  30. Collection<? extends GrantedAuthority> collections = user.getAuthorities();
  31. for (GrantedAuthority ga: collections) {
  32. // 打印当前登录用户的信息
  33. UserController.LOGGER.info("当前用户是{}, 角色是{}", user.getUsername(), ga.getAuthority());
  34. }
  35. } else {
  36. UserController.LOGGER.warn("ε=(´ο`*)))唉,出现问题了");
  37. }
  38. User findOne = userRepository.findById(id).get();
  39. return findOne;
  40. }
  41.  
  42. }

  > 启动 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注解

  1. package com.itmuch.cloud.microserviceconsumermoviefeignmanual.feign;
  2.  
  3. import com.itmuch.cloud.microserviceconsumermoviefeignmanual.pojo.User;
  4. import org.springframework.web.bind.annotation.GetMapping;
  5. import org.springframework.web.bind.annotation.PathVariable;
  6.  
  7. public interface UserFeignClient {
  8.  
  9. @GetMapping(value = "/{id}")
  10. User findById(@PathVariable("id") Long id);
  11.  
  12. }

  > 去掉启动类上的 @EnableFeignClients 注解

  1. package com.itmuch.cloud.microserviceconsumermoviefeignmanual;
  2.  
  3. import org.springframework.boot.SpringApplication;
  4. import org.springframework.boot.autoconfigure.SpringBootApplication;
  5. import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
  6. import org.springframework.cloud.openfeign.EnableFeignClients;
  7. import org.springframework.context.annotation.Bean;
  8. import org.springframework.web.client.RestTemplate;
  9.  
  10. @SpringBootApplication
  11. @EnableDiscoveryClient
  12. public class MicroserviceConsumerMovieFeignManualApplication {
  13.  
  14. public static void main(String[] args) {
  15. SpringApplication.run(MicroserviceConsumerMovieFeignManualApplication.class, args);
  16. }
  17.  
  18. }

  > 修改 Controller

  1. package com.itmuch.cloud.microserviceconsumermoviefeignmanual.controller;
  2.  
  3. import com.itmuch.cloud.microserviceconsumermoviefeignmanual.feign.UserFeignClient;
  4. import com.itmuch.cloud.microserviceconsumermoviefeignmanual.pojo.User;
  5. import feign.Client;
  6. import feign.Contract;
  7. import feign.Feign;
  8. import feign.auth.BasicAuthRequestInterceptor;
  9. import feign.codec.Decoder;
  10. import feign.codec.Encoder;
  11. import org.springframework.cloud.openfeign.FeignClientsConfiguration;
  12. import org.springframework.context.annotation.Import;
  13. import org.springframework.web.bind.annotation.GetMapping;
  14. import org.springframework.web.bind.annotation.PathVariable;
  15. import org.springframework.web.bind.annotation.RestController;
  16.  
  17. @Import(FeignClientsConfiguration.class) // Spring Cloud为Feign默认提供的配置类
  18. @RestController
  19. public class MovieController {
  20.  
  21. private UserFeignClient userUserFeignClient;
  22. private UserFeignClient adminUserFeignClient;
  23.  
  24. public MovieController(Decoder decoder, Encoder encoder, Client client, Contract contract) {
  25. this.userUserFeignClient = Feign.builder().client(client).encoder(encoder).decoder(decoder).contract(contract)
  26. .requestInterceptor(new BasicAuthRequestInterceptor("user", "password1"))
  27. .target(UserFeignClient.class, "http://microservice-provider-user/");
  28. this.adminUserFeignClient = Feign.builder().client(client).encoder(encoder).decoder(decoder).contract(contract)
  29. .requestInterceptor(new BasicAuthRequestInterceptor("admin", "password2"))
  30. .target(UserFeignClient.class, "http://microservice-provider-user/");
  31. }
  32.  
  33. @GetMapping("/user-user/{id}")
  34. public User findByIdUser(@PathVariable Long id) {
  35. return this.userUserFeignClient.findById(id);
  36. }
  37.  
  38. @GetMapping("/user-admin/{id}")
  39. public User findByIdAdmin(@PathVariable Long id) {
  40. return this.adminUserFeignClient.findById(id);
  41. }
  42.  
  43. }

  > 启动 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微服务架构与实战》

SpringCloud系列十二:手动创建Feign的更多相关文章

  1. SpringCloud系列十二:SpringCloudSleuth(SpringCloudSleuth 简介、SpringCloudSleuth 基本配置、数据采集)

    声明:本文来源于MLDN培训视频的课堂笔记,写在这里只是为了方便查阅. 1.概念:SpringCloudSleuth 2.具体内容 Sleuth 是一种提供的跟踪服务,也就是说利用 sleuth 技术 ...

  2. Web 前端开发精华文章推荐(jQuery、HTML5、CSS3)【系列十二】

    2012年12月12日,[<Web 前端开发人员和设计师必读文章>系列十二]和大家见面了.梦想天空博客关注 前端开发 技术,分享各种增强网站用户体验的 jQuery 插件,展示前沿的 HT ...

  3. SQL Server 2008空间数据应用系列十二:Bing Maps中呈现GeoRSS订阅的空间数据

    原文:SQL Server 2008空间数据应用系列十二:Bing Maps中呈现GeoRSS订阅的空间数据 友情提示,您阅读本篇博文的先决条件如下: 1.本文示例基于Microsoft SQL Se ...

  4. Alamofire源码解读系列(十二)之请求(Request)

    本篇是Alamofire中的请求抽象层的讲解 前言 在Alamofire中,围绕着Request,设计了很多额外的特性,这也恰恰表明,Request是所有请求的基础部分和发起点.这无疑给我们一个Req ...

  5. struts2官方 中文教程 系列十二:控制标签

    介绍 struts2有一些控制语句的标签,本教程中我们将讨论如何使用 if 和iterator 标签.更多的控制标签可以参见 tags reference. 到此我们新建一个struts2 web 项 ...

  6. 爬虫系列(十二) selenium的基本使用

    一.selenium 简介 随着网络技术的发展,目前大部分网站都采用动态加载技术,常见的有 JavaScript 动态渲染和 Ajax 动态加载 对于爬取这些网站,一般有两种思路: 分析 Ajax 请 ...

  7. 学习ASP.NET Core Razor 编程系列十二——在页面中增加校验

    学习ASP.NET Core Razor 编程系列目录 学习ASP.NET Core Razor 编程系列一 学习ASP.NET Core Razor 编程系列二——添加一个实体 学习ASP.NET ...

  8. 跟我学SpringCloud | 第十二篇:Spring Cloud Gateway初探

    SpringCloud系列教程 | 第十二篇:Spring Cloud Gateway初探 Springboot: 2.1.6.RELEASE SpringCloud: Greenwich.SR1 如 ...

  9. Alamofire源码解读系列(十二)之时间轴(Timeline)

    本篇带来Alamofire中关于Timeline的一些思路 前言 Timeline翻译后的意思是时间轴,可以表示一个事件从开始到结束的时间节点.时间轴的概念能够应用在很多地方,比如说微博的主页就是一个 ...

随机推荐

  1. scrapy生成csv文件空行、csv文件打开乱码(解决方案)

    一.scrapy生成csv文件会有多余的空行 当使用scrapy crawl testspider -o test.csv后,生成的默认csv文件每一行之间是有空行的,解决的方法是修改scrapy的源 ...

  2. 单调队列练习题(oj p1157 p1158 p1159)

    p1157是很气人的...自从评测机挂了后,速度就特别慢,cin已经过不了了,然而我不知道,就各种**的提交 惨兮兮惨兮兮,这还是开了小号(通过率堪忧.jpg...)... 思路就是单调队列维护,用队 ...

  3. 【莫比乌斯反演+分块】BZOJ1101-[POI2007]Zap

    [题目大意] 对于给定的整数a,b和d,有多少正整数对x,y,满足x<=a,y<=b,并且gcd(x,y)=d. [思路] 前面的思路同HDU1695 不过不同的是这道题中(a,b)和(b ...

  4. 【Treap模板详细注释】BZOJ3224-普通平衡树

    模板题:D错因见注释 #include<iostream> #include<cstdio> #include<cstring> #include<algor ...

  5. STL之search

    描述 使用STL中的search函数,判断一个序列是否是另一个序列的子序列. 部分代码已经给出,请补充完整,提交时请勿包含已经给出的代码. int main() { vector<int> ...

  6. hyxzc_背包九讲课件

    10 1 1 1 5 5 7 9 //体积 5 5 1 5 3 5 1//价值   01 完全 多重 分组 有依赖性 ... ------------------------------------- ...

  7. awk-使用

    http://www.cnblogs.com/ggjucheng/archive/2013/01/13/2858470.html 命令格式: awk [-F field-separator] 'pat ...

  8. NDK 调用对象属性

    JNIEXPORT jbyteArrayJNICALL Java_com_lanhetech_iso8583_nativeLib_Iso8583NativeLib_pubPack8583 (JNIEn ...

  9. WebServic dynamic url

    How to make your Web Reference proxy URL dynamic 开发环境和部署环境,Webservice 的URL不同 需将url 配置到 web.config文件中 ...

  10. Android 多线程之IntentService 完全详解

    关联文章: Android 多线程之HandlerThread 完全详解 Android 多线程之IntentService 完全详解 android多线程-AsyncTask之工作原理深入解析(上) ...