【问题标题】:Feign Client called from a thread + Spring Security从线程调用的 Feign Client + Spring Security
【发布时间】:2017-12-14 11:11:03
【问题描述】:

我有打电话给假客户的情况。它工作正常,直到我尝试将 feign 客户端放入线程中(简化版):

@Autowired
UsuarioFeign feignUserClient;

....

final Runnable t = new Runnable() {
    @Override
    public void run() {
        try {
            feignUserClient.findByEmail("someEmail@address.com");
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
};
new Thread(t).start();

当我尝试这样做时,我得到(由真实版本打印,而不是上面的简化版本):

java.lang.NullPointerException
at br.alfa.tutoria.AlfaTutoriaApplication$1.apply(AlfaTutoriaApplication.java:46)
at feign.SynchronousMethodHandler.targetRequest(SynchronousMethodHandler.java:158)
at feign.SynchronousMethodHandler.executeAndDecode(SynchronousMethodHandler.java:88)
at feign.SynchronousMethodHandler.invoke(SynchronousMethodHandler.java:76)
at feign.ReflectiveFeign$FeignInvocationHandler.invoke(ReflectiveFeign.java:103)
at com.sun.proxy.$Proxy246.findByEmail(Unknown Source)
at br.alfa.tutoria.service.impl.TutoriaServiceImpl.atribuiFuncaoTutor(TutoriaServiceImpl.java:285)
at br.alfa.tutoria.service.impl.TutoriaServiceImpl$2.run(TutoriaServiceImpl.java:270)
at java.lang.Thread.run(Unknown Source)

feignUserClient 是 @Autowired(我尝试将其变为 final,并使用构造函数注入它,但没有任何区别 - 无论如何,代码执行时它的值不为 null)。

更多信息:

Feign 接口:

@FeignClient(name = "authUsers", url = br.alfa.tutoria.config.Url.AUTH_SERVER)
public interface UsuarioFeign {
    @RequestMapping(value = "/user-search-by-email", method = RequestMethod.POST)
    public User findByEmail(String email);
}

另一个类,调用feign接口……没关系。我在几个不同的类中尝试过(所有类都用@RestController@Service 注释)。如果我从线程中调用UsuarioFeign.findByEmail,它就会停止工作。

【问题讨论】:

  • 你能添加整个AlfaTutoriaApplication类吗?
  • 刚刚做了,@ryan。我正在努力追查问题。我用简单的线程构建了两个简单的 feign 应用程序。有效。如此简单的线程不是问题。我还在努力。现在我要更新 Spring boot,接下来,寻找嵌套线程。
  • 我从主题@ryan 中删除了AlfaTutoriaApplication 类。事实上,它给了我一个关于真正问题的重要线索(但感谢您提出这个问题 - 复制和粘贴的简单行为让我注意到了安全部分)。

标签: spring-security spring-cloud java-threads netflix-feign


【解决方案1】:
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

public class SecurityContextRunnable implements Runnable {
    private Runnable runnable;
    private RequestAttributes requestAttributes;
    private SecurityContext securityContext;
    private SecurityContext originalSecurityContext;

    private SecurityContextRunnable(Runnable runnable) {
        this(runnable, RequestContextHolder.getRequestAttributes());
    }

    private SecurityContextRunnable(Runnable runnable, RequestAttributes requestAttributes) {
        this(runnable, requestAttributes, null);
    }

    private SecurityContextRunnable(Runnable runnable, RequestAttributes requestAttributes, SecurityContext securityContext) {
        try {
            if (securityContext == null) {
                securityContext = SecurityContextHolder.getContext();
            }
        } catch (Exception e) {

        }

        this.runnable = runnable;
        this.requestAttributes = requestAttributes;
        this.securityContext = securityContext;
    }

    @Override
    public void run() {
        this.originalSecurityContext = SecurityContextHolder.getContext();
        try {
            SecurityContextHolder.setContext(securityContext);
            RequestContextHolder.setRequestAttributes(requestAttributes);
            runnable.run();
        } finally {
            SecurityContext emptyContext = SecurityContextHolder.createEmptyContext();
            if (emptyContext.equals(originalSecurityContext)) {
                SecurityContextHolder.clearContext();
            } else {
                SecurityContextHolder.setContext(originalSecurityContext);
            }
            this.originalSecurityContext = null;
        }
    }

    public static SecurityContextRunnable create(Runnable runnable) {
        return new SecurityContextRunnable(runnable);
    }

    public static SecurityContextRunnable create(Runnable runnable, RequestAttributes requestAttributes) {
        return new SecurityContextRunnable(runnable, requestAttributes);
    }

    public static SecurityContextRunnable create(Runnable runnable, RequestAttributes requestAttributes, SecurityContext securityContext) {
        return new SecurityContextRunnable(runnable, requestAttributes, securityContext);
    }

}


Runnable originalRunnable = new Runnable() {
    @Override
    public void run() {
        try {
            feignUserClient.findByEmail("someEmail@address.com");
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
};

SecurityContextRunnable wrappedRunnable = SecurityContextRunnable.create(originalRunnable);
new Thread(wrappedRunnable).start();

【讨论】:

  • 欢迎来到 Stack Overflow!请不要把你的源代码扔在这里。友善一点,并尝试对您的答案进行漂亮的描述,以便其他人会喜欢并支持它。见:How do I write a good answer?
【解决方案2】:

最后,问题与 Spring Security Context 有关。根据Spring Security docs 的设计,“安全性存储在每个线程的基础上”。这意味着如果您需要新线程中的安全上下文之类的东西,您将无法获得它。

在文档中,您可以找到一种将其作为参数传输到新线程的方法:

Runnable originalRunnable = new Runnable() {
    public void run() {
        // invoke secured service
    }
};

SecurityContext context = SecurityContextHolder.getContext();
DelegatingSecurityContextRunnable wrappedRunnable =
    new DelegatingSecurityContextRunnable(originalRunnable, context);

new Thread(wrappedRunnable).start();

【讨论】:

    猜你喜欢
    • 2018-02-01
    • 2015-10-12
    • 2021-12-17
    • 2015-11-12
    • 2017-09-22
    • 2018-01-29
    • 2023-02-20
    • 2019-09-25
    • 2021-09-04
    相关资源
    最近更新 更多