服务对外提供接口以供不同站点之间使用:Spring Cloud Feign使用记录及携带token请求

  在开发 Spring Cloud 微服务的时候,我们知道,服务之间都是以 HTTP 接口的形式对外提供服务的,因此消费者在进行调用的时候,底层就是通过 HTTP Client 的这种方式进行访问。当然我们可以使用JDK原生的 URLConnection、Apache 的 HTTP Client、Netty 异步 Http Client,Spring 的 RestTemplate 去实现服务间的调用。但是最方便、最优雅的方式是通过 Spring Cloud Open Feign 进行服务间的调用。Spring Cloud 对 Feign 进行了增强,使 Feign 支持 Spring Mvc 的注解,并整合了 Ribbon 等,从而让 Feign 的使用更加方便。

  Feign是一个声明式的Web服务客户端,使用Feign可使得Web服务客户端的写入更加方便。 它具有可插拔注释支持,包括Feign注解和JAX-RS注解、Feign还支持可插拔编码器和解码器、Spring Cloud增加了对Spring MVC注释的支持,并HttpMessageConverters在Spring Web中使用了默认使用的相同方式。Spring Cloud集成了Ribbon和Eureka,在使用Feign时提供负载平衡的http客户端。

  Feign作用:可以解决不同服务器接口之间的相互调用,即跨域请求!feign结合eureka注册中心,把不同的服务项目注册到eureka中,通过feign客户端进行调用,可以解决负载均衡问题。

  接下来就简单讲述一下Feign的入门使用

一、引入依赖及配置编写

1、引入依赖

<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-gson</artifactId>
    <version>10.2.3</version>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
    <version>2.1.1.RELEASE</version>
</dependency>

2、编写配置文件

@Configuration
public class FeignConfiguration {

    @Bean
    public Contract feignContract() {
        return new Contract.Default();
    }

    @Bean
    Logger.Level feignLoggerLevel() {
        return Logger.Level.FULL;
    }

    @Bean
    Decoder feignDecoder() {
        return new GsonDecoder();
    }

    @Bean
    Encoder feignEncoder() {
        return new GsonEncoder();
    }
}

3、在应用主类Application里,通过@EnableFeignClients注解开启Spring Cloud Feign的支持功能。

// 在启动类上加上注解
// 开启 Feign 扫描支持
@EnableFeignClients

二、编写Feign接口及使用

1、编写Feign接口

  @FeignClient注解指定服务名来绑定服务,然后再使用Spring MVC的注解来绑定具体该服务提供的REST接口。

@FeignClient(name = "myApi", url = "http://localhost:8080")
public interface MyService {
    // 调用另外一个服务的接口
    @RequestLine("GET /getUsers?searchString={searchString}")
    @Headers("Content-Type: application/json")
    List<User> getUsers(@Param("searchString") String searchString);
}

2、使用接口

  Feign接口不需要实现类,可直接调用

    private MyService myService;

    @GetMapping("/userList")
    public List<User> getUsers(@RequestParam String searchString){
        List<User> userList = myService.getUsers(searchString);
        return userList;
    }

三、携带token请求

  为了安全考虑要访问的服务的接口需要token验证才能访问,因此需要携带token才能访问。关于新的服务搭建安全框架,使用与要访问的平台一致的token生成和验证机制,这里就不赘述了。

1、方案一:直接在@Headers注解中加token

  这种方案可以用来测试,因为,这种方式token是写死的,不能根据浏览器携带的token进行验证。

@FeignClient(name = "myApi", url = "http://localhost:8080")
public interface MyService {
    @RequestLine("GET /getUsers?searchString={searchString}")
    // 直接在@Headers注解中加token
    @Headers({"Content-Type: application/json", "Authorization: Bearer eyJh..."})
    List<User> getUsers(@Param("searchString") String searchString);
}

2、方案二:根据浏览器动态获取token

如何从浏览器中拿到token?

  可以看到javax.servlet.http包下有个getHeader的方法,可以获得当前浏览器Header中的信息。

如何将token放到跨域请求中?

  在fegin包中的请求拦截器RequestInterceptor有个apply方法,该方法的默认实现如下:

  可以看到,默认的Authorization是通过用户名和密码进行base64加密得到的,跟我们的token生成方式不一样,所以直接使用默认的是无法验证通过的,因此,只需实现RequestInterceptor,重写apply方法即可

解决方案

  编写配置类,实现RequestInterceptor,重写apply方法,把浏览器header拿到的token放进去。

@Slf4j
@Configuration
@AllArgsConstructor
public class NimBusRequestInterceptor implements RequestInterceptor {

    private HttpServletRequest req;
    private static final String HEADER_STRING = "Authorization";

    @Override
    public void apply(RequestTemplate requestTemplate) {
        // 如果header没有auth头,从cookie获取token
        String token = req.getHeader(HEADER_STRING);
        Cookie[] cookies = req.getCookies();
        if (cookies != null && cookies.length > 0) {
            for (Cookie cookie : cookies) {
                if (Objects.equals(cookie.getName(), "token")) {
                    try {
                        token = URLDecoder.decode(cookie.getValue(), StandardCharsets.UTF_8.name());
                    } catch (UnsupportedEncodingException e) {
                        log.error(LogUtil.getStack(e));
                    }
                }
            }
        }
        requestTemplate.header(HEADER_STRING, token);
    }
}

  以上就实现了Feign基本使用与携带token请求。

原文地址:https://www.cnblogs.com/goloving/p/13667100.html