【问题标题】:Feign Client Throwing Unauthorized Exception for Url, where authentication is not neededFeign 客户端为 Url 抛出未经授权的异常,不需要身份验证
【发布时间】:2020-02-23 21:58:39
【问题描述】:

我关注了blog 并创建了几个微服务:Eureka-server、Auth-service、Zuul-service、Gallery-service、Image-service。 从画廊服务中,我想使用 Feign-Client 调用 auth-service API 该 url 不需要身份验证,但客户端抛出 FeignException$Unauthorized 我正在使用 JWT 令牌进行身份验证。

//AuthServerProxy.java

@FeignClient(name = "auth-service")
@RibbonClient(name = "auth-service")
public interface AuthServiceProxy {

    @PostMapping("/auth/authenticate")
    public ResponseEntity<?> authenticate(@RequestBody UserEntity userEntity);

    @GetMapping("/auth/register")
    public String test();
}

控制器 - 图库服务

@Autowired
    AuthServiceProxy authServiceProxy;
    @GetMapping("/test")
    public String test(){
        UserEntity userEntity = new UserEntity();
        userEntity.setUsername("admin");
        userEntity.setPassword("admin");
        ResponseEntity<?> responseEntity = authServiceProxy.authenticate(userEntity);
        System.out.println(responseEntity.getStatusCode());
        return responseEntity.toString();

    }

    @GetMapping("/test/str")
    public String testStr(){
        return authServiceProxy.test();
    }

安全配置 - ZuulServer, Auth-Service

.antMatchers(HttpMethod.POST, "/auth/authenticate").permitAll()

这是错误日志

ERROR 1123 --- [nio-8100-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is feign.FeignException$Unauthorized: status 401 reading AuthServiceProxy#authenticate(UserEntity)] with root cause

feign.FeignException$Unauthorized: status 401 reading AuthServiceProxy#authenticate(UserEntity)
at feign.FeignException.errorStatus(FeignException.java:94) ~[feign-core-10.2.3.jar:na]
    at feign.FeignException.errorStatus(FeignException.java:86) ~[feign-core-10.2.3.jar:na]
    at feign.codec.ErrorDecoder$Default.decode(ErrorDecoder.java:93) ~[feign-core-10.2.3.jar:na]
    at feign.SynchronousMethodHandler.executeAndDecode(SynchronousMethodHandler.java:149) ~[feign-core-10.2.3.jar:na]
    at feign.SynchronousMethodHandler.invoke(SynchronousMethodHandler.java:78) ~[feign-core-10.2.3.jar:na]
    at feign.ReflectiveFeign$FeignInvocationHandler.invoke(ReflectiveFeign.java:103) ~[feign-core-10.2.3.jar:na]
    at com.sun.proxy.$Proxy101.authenticate(Unknown Source) ~[na:na]
    at com.test.gallery.Controller.test(Controller.java:47) ~[classes/:na]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_201]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_201]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_201]
    at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_201]
    at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:190) ~[spring-web-5.1.9.RELEASE.jar:5.1.9.RELEASE]
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138) ~[spring-web-5.1.9.RELEASE.jar:5.1.9.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:104) ~[spring-webmvc-5.1.9.RELEASE.jar:5.1.9.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:892) ~[spring-webmvc-5.1.9.RELEASE.jar:5.1.9.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:797) ~[spring-webmvc-5.1.9.RELEASE.jar:5.1.9.RELEASE]
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-5.1.9.RELEASE.jar:5.1.9.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1039) ~[spring-webmvc-5.1.9.RELEASE.jar:5.1.9.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:942) ~[spring-webmvc-5.1.9.RELEASE.jar:5.1.9.RELEASE]
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1005) ~[spring-webmvc-5.1.9.RELEASE.jar:5.1.9.RELEASE]
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:897) ~[spring-webmvc-5.1.9.RELEASE.jar:5.1.9.RELEASE]
...

非常感谢任何帮助。 TIA

【问题讨论】:

  • 你确定 Feign 调用了正确的 URL 吗?您可以打开日志并检查吗?您可以通过网络浏览器或 Postman 客户端访问此 URL 吗?
  • 是的,我可以通过 Advanced Rest Client 访问 url,但是通过 Feign Client 访问时失败

标签: java spring-boot microservices feign


【解决方案1】:

看起来身份验证标头没有通过FeignClient 传递

尝试添加此配置:

@Bean
public RequestInterceptor requestInterceptor() {

    return requestTemplate -> {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

        if (authentication != null && authentication.getDetails() instanceof OAuth2AuthenticationDetails) {
            OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails) authentication.getDetails();
            requestTemplate.header(HttpHeaders.AUTHORIZATION, String.format("Bearer %s", details.getTokenValue()));
        }
    };
}

【讨论】:

    【解决方案2】:

    Feign 不知道应该传递给目标服务的授权。不幸的是,您需要自己处理。下面是一个可以提供帮助的 java 类

    @Component
    public class FeignClientInterceptor implements RequestInterceptor {
    
         private static final String AUTHORIZATION_HEADER = "Authorization";
           private static final String BEARER_TOKEN_TYPE = "Bearer";
        
        
           @Override
            public void apply(RequestTemplate template) {
                SecurityContext securityContext = SecurityContextHolder.getContext();
                Authentication authentication = securityContext.getAuthentication();
    
                if (authentication != null && authentication.getDetails() instanceof OAuth2AuthenticationDetails) {
                    OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails) authentication.getDetails();
                    template.header(AUTHORIZATION_HEADER, String.format("%s %s", BEARER_TOKEN_TYPE, details.getTokenValue()));
                }
            }
    

    【讨论】:

      【解决方案3】:

      听起来问题可能是您没有将 @EnableResourceServer 附加到您的 Auth-Service。

      如果没有该注释,任何不属于 spring 安全包的端点(例如 /oauth/token、/oauth/check_token)都将自动需要授权。

      此外,您可能需要添加与此类似的 ResourceServerConfigurerAdapter,以确保资源端点配置为允许所有这些:

      @Configuration
      @EnableResourceServer
      public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
      
          private final TokenStore tokenStore;
      
          public ResourceServerConfig(TokenStore tokenStore) {
              this.tokenStore = tokenStore;
          }
      
          @Override
          public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
              resources.tokenStore(tokenStore);
          }
      
          @Override
          public void configure(HttpSecurity http) throws Exception {
              http
                      .authorizeRequests()
                      .antMatchers(HttpMethod.POST).permitAll()
                      .and()
                      .logout().disable()
                      .csrf().disable();
          }
      }
      

      *********编辑*********

      如果您能够从浏览器中的请求中获得 ok 响应但不是 feign,那么您的问题很可能是您的 Feign 客户端没有指向正确的端点。通常你会期待一个 404 错误,但由于 API 是安全的,你会得到一个 401,因为它甚至不允许你知道什么是有效端点,除非你经过身份验证或者它是一个不安全的端点

      如果您的 AuthServiceProxy feign 客户端使用您的 zuul-server 而不是 auth-service,那么您可以将日志记录添加到您的 zuul 过滤器以查看成功和不成功请求的样子。从那里进行必要的更改,以使您的代理请求与您从浏览器发出的请求相匹配,您应该一切顺利

      【讨论】:

      • 我没有资源服务器,估计需要为其添加一些依赖。这是 GitHub 存储库的链接:github.com/harshasridhar/microservice-example 请建议我需要进行的更改我已经在 auth-service 中进行了另一个 Url GET /auth/register,当从没有令牌的浏览器访问它时它可以工作,但是从Feign Client,还是抛出Unauthorized Exception
      • 我将代理feign客户端更改为使用zuul-server,请求流程如图所示!Image不知道为什么zuul-server重定向到/error of auth-service
      • 即使代理使用 auth-service,也会调用 /error
      • 你有运行尤里卡服务器吗?我问是因为您的 git 存储库中没有命名服务器,而您的画廊服务 application.properties 预计在localhost:8761 有一个
      • 是的,我正在运行尤里卡服务器。我没有将服务器代码添加到 repo,因为我正在重用另一个项目的代码,只是为了 eureka 服务器
      猜你喜欢
      • 2018-10-16
      • 1970-01-01
      • 2017-07-17
      • 2012-09-24
      • 1970-01-01
      • 1970-01-01
      • 2011-06-22
      • 1970-01-01
      • 2021-09-28
      相关资源
      最近更新 更多