zoukankan      html  css  js  c++  java
  • 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微服务架构与实战》

  • 相关阅读:
    Linux常见命令
    CSS以及JQuery总是忽略掉的小问题
    HTML DOM appendChild() 方法
    WINUSB使用(附STM32Demo)
    STM32H7系列调试使用DCMI过程中若干注意事项
    godot初涉
    【转】使用objdump看内核源码
    scrcpy rk3399 交叉编译 meson
    gitlab hooks 钩子实现服务器代码同步至文件夹
    Linux编译笔记
  • 原文地址:https://www.cnblogs.com/jinjiyese153/p/8664370.html
Copyright © 2011-2022 走看看