简体   繁体   中英

How to inject current HttpServletRequest into any Spring service?

I have a service that requires to read a special http query parameter. Therefore I have to access the current HttpServletRequest somehow. 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 . 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 ?

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. 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. So I created a filter in Spring Security chain and did use the SecurityContextHolder class. 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.

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. 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). 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 .

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. 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.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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