从线程调用的 Feign Client + Spring 安全性
Feign Client called from a thread + Spring Security
我有打电话给假客户的情况。它工作正常,直到我尝试将假装客户端放入一个线程(简化版)中:
@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.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.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);
}
另外一个class,调用feign接口的……无所谓。我在几个不同的 classes 中尝试了它(所有这些都用 @RestController
或 @Service
注释)。如果我从线程中调用 UsuarioFeign.findByEmail
,它就会停止工作。
最后,问题与 Spring 安全上下文有关。按照设计,根据 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();
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();
我有打电话给假客户的情况。它工作正常,直到我尝试将假装客户端放入一个线程(简化版)中:
@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.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.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);
}
另外一个class,调用feign接口的……无所谓。我在几个不同的 classes 中尝试了它(所有这些都用 @RestController
或 @Service
注释)。如果我从线程中调用 UsuarioFeign.findByEmail
,它就会停止工作。
最后,问题与 Spring 安全上下文有关。按照设计,根据 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();
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();