[英]How to inject current HttpServletRequest into any Spring service?
我有一项服务,需要读取特殊的http查询参数。 因此,我必须以某种方式访问当前的HttpServletRequest
。 由于无法将请求作为参数传递,因此我必须以某种方式注入或读取它。
有两种可能性:从RequestContextHolder
获取RequestContextHolder
,或直接注入HttpServletRequest
。 什么是正确的? 也许甚至还有第三种选择?
@Service
public class MyUserDetailsService implements UserDetailsService {
//TODO is that correct? scope?
@Autowired
private HttpServletRequest req;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
HttpServletRequest req = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
req.getParameter("myparam");
}
}
是线程安全的,因为MyUserDetailsService
显然是单例的,而HttpServletRequest
应该是@RequestScope
吗?
我不知道您到底要训练什么,但是您需要知道:在服务类中使用HttpServletRequest时,您将遇到错误,因为servelet请求和类似Services的Spring组件位于不同的范围内,您不能当HttpServletRequest不在同一线程中时访问它。 我有几乎相同的问题,就我而言,我需要在JWT标头中获取用户ID并保存以进行一些查询。 因此,我在Spring Security链中创建了一个过滤器,并确实使用了SecurityContextHolder类。 这是我的代码:
@Slf4j
public class JWTAuthenticationFilter extends GenericFilterBean {
private String enviroment;
private JWTAuthenticationService jwtAuthenticationService;
public JWTAuthenticationFilter(String enviroment,
JWTAuthenticationService jwtAuthenticationService) {
this.enviroment = enviroment;
this.jwtAuthenticationService = jwtAuthenticationService;
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
Claims jwtClaims = this.jwtAuthenticationService.getJWTClaims((HttpServletRequest) request);
try {
this.jwtAuthenticationService.checkJWT(enviroment, jwtClaims);
} catch (UnauthorizedException ex){
log.error("Token JWT Inválido");
((HttpServletResponse) response).sendError(401, ex.getMessage());
return;
}
Authentication authentication = this.jwtAuthenticationService.getAuthentication(jwtClaims);
SecurityContextHolder.getContext().setAuthentication(authentication);
filterChain.doFilter(request, response);
}
}
@Service
public class AuthRequestService {
public String getAuthorizationKey() {
Claims claims = (Claims)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
return claims.get(KEY_CLAIM).toString();
}
}
通过这种方法,我避免了线程错误,因为我正在使用SecurityContextHolder管理器的当前上下文。
直接注入应该可以,但是它将是“请求范围”,而不是“会话范围”。
注入请求范围的Bean将导致创建代理。 从不是源自DispatcherServlet
的线程访问该代理上的方法将导致IllegalStateException
,因为根据该代理没有可用的请求(实际上可能有零个或多个并发请求,但是无法确定性地绑定到一个)。 但是,当从实际上是HTTP请求的一部分的线程访问代理的方法时(在您的情况下,可能是在Controller-> Service中),它的行为就像是当前的HttpServletRequest
。
我不确定服务层是否是绑定HttpServletRequest
的正确位置,尽管我不确定您的用例,但该服务可能是从其他异步上下文访问的。 无论如何,只要您的体系结构定义正确并且依赖于HttpServletRequest
bean的类仅在请求线程/上下文中使用它,它就可以正常工作。
因此,总而言之,您应该做什么实际上取决于您的用例。 听起来您只会从DispatcherServlet
内部访问它,因此很安全,但是也许您应该抽象化(分开考虑)将数据获取到另一个提供给Service类的类。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.