简体   繁体   English

如何将当前的HttpServletRequest注入任何Spring服务?

[英]How to inject current HttpServletRequest into any Spring service?

I have a service that requires to read a special http query parameter. 我有一项服务,需要读取特殊的http查询参数。 Therefore I have to access the current HttpServletRequest somehow. 因此,我必须以某种方式访问​​当前的HttpServletRequest As I cannot hand the request down as a parameter, I have to inject or read it somehow. 由于无法将请求作为参数传递,因此我必须以某种方式注入或读取它。

There are two possibilities: either get the request from RequestContextHolder , or directly inject HttpServletRequest . 有两种可能性:从RequestContextHolder获取RequestContextHolder ,或直接注入HttpServletRequest What is correct? 什么是正确的? Or maybe there is even a 3rd option? 也许甚至还有第三种选择?

@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");
   }
}

Is that threadsafe, as MyUserDetailsService is obviously as singleton, and HttpServletRequest is supposed to be @RequestScope ? 是线程安全的,因为MyUserDetailsService显然是单例的,而HttpServletRequest应该是@RequestScope吗?

I don't know what exactly are you train to do, but you need to know: With HttpServletRequest in a service class, you will have an error because servelet request and Spring Components like Services was in a different scope, and you can't access it when HttpServletRequest was not in the same thread. 我不知道您到底要训练什么,但是您需要知道:在服务类中使用HttpServletRequest时,您将遇到错误,因为servelet请求和类似Services的Spring组件位于不同的范围内,您不能当HttpServletRequest不在同一线程中时访问它。 I have almost the same issue, in my case, I needed to get a user ID in JWT header and save to make some queries. 我有几乎相同的问题,就我而言,我需要在JWT标头中获取用户ID并保存以进行一些查询。 So I created a filter in Spring Security chain and did use the SecurityContextHolder class. 因此,我在Spring Security链中创建了一个过滤器,并确实使用了SecurityContextHolder类。 Here my code: 这是我的代码:

@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();
  }

}

With this approach, I avoid the thread error because I'm using the current context that SecurityContextHolder manager. 通过这种方法,我避免了线程错误,因为我正在使用SecurityContextHolder管理器的当前上下文。

Injecting it directly should work, but it will be Request Scope, not Session Scope. 直接注入应该可以,但是它将是“请求范围”,而不是“会话范围”。

Injecting the Request-scoped bean will result in a proxy being created. 注入请求范围的Bean将导致创建代理。 Accessing methods on that proxy from a thread that is not originating from the DispatcherServlet will result in an IllegalStateException being thrown, because there is no available request according to the proxy (in actuality there might be zero or many concurrent requests, but it's impossible to deterministically bind to one). 从不是源自DispatcherServlet的线程访问该代理上的方法将导致IllegalStateException ,因为根据该代理没有可用的请求(实际上可能有零个或多个并发请求,但是无法确定性地绑定到一个)。 However, when accessing the proxy's method from a thread that is actually part of an HTTP request, in your case probably coming in the Controller->Service, it will act just as if it was the current HttpServletRequest . 但是,当从实际上是HTTP请求的一部分的线程访问代理的方法时(在您的情况下,可能是在Controller-> Service中),它的行为就像是当前的HttpServletRequest

I'm not sure that the Service Layer is the right place to be binding HttpServletRequest as it might be possible that the service is access from other asynchronous contexts, although I'm not sure of your use-case. 我不确定服务层是否是绑定HttpServletRequest的正确位置,尽管我不确定您的用例,但该服务可能是从其他异步上下文访问的。 In any case, as long as your architecture is well defined and the class depending on the HttpServletRequest bean is only using it in a request thread/context, it will work fine. 无论如何,只要您的体系结构定义正确并且依赖于HttpServletRequest bean的类仅在请求线程/上下文中使用它,它就可以正常工作。

So in summary, what you should do really depends on your use-case. 因此,总而言之,您应该做什么实际上取决于您的用例。 It sounds like you will only be accessing it from within the DispatcherServlet and would thus be safe, but perhaps you should abstract (separate the concern of) the acquiring of the data to another class that provides it to the Service class. 听起来您只会从DispatcherServlet内部访问它,因此很安全,但是也许您应该抽象化(分开考虑)将数据获取到另一个提供给Service类的类。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM