java - 在 Java 8 中将 HttpServletRequest 与 ParallelStream 结合使用

标签 java spring-security parallel-processing java-8 java-stream

我有一个简单的应用程序,它将公开一个名为 'getAllDeviceData' 的 RESTFul GET 端点,它将简单地返回从数据库中的设备表中获取的所有设备数据的列表。

对于每个请求,我都会通过验证 HttpServletRequest.getUserPrincipal() 方法来验证用户身份。 为了加速这个过程,我使用了带有 lambda 表达式的并行流。

在 ParallelStream 中,我调用另一个名为 'getDeviceData' 的方法,在其中进行身份验证并从数据库获取数据。

问题是,当并行流进程调用 getDeviceDat 方法时,我收到 NullPointer 异常,并且无法完成并行流。

原因是,HttpServletRequest.getUserPrincipal() 在方法内为 null。但它实际上存在于 'getAllDeviceData' 中(lambda 表达式所在的位置)。

如果我将 'parallelStream()' 替换为 'stream()',则不会出现任何问题,但在这种情况下,并行性质不存在。

@Override
@ResponseBody
@RequestMapping(value = "getAllDeviceData", method = RequestMethod.GET, consumes = "*")
public List<List<Data>> getAllDeviceData(    
        @RequestParam(value = "recordLimit", required = false) final Integer recordLimit,         
        final HttpServletRequest request) {

    final List<Device> deviceList = deviceService.getAllDevices();   
    final List<List<Data>> dataList = deviceList.parallelStream().map(device -> getDeviceData(recordLimit, device.getDeviceId(), request)).collect(Collectors.toList());

    return alerts;
}

private List<Data> getDeviceData(@RequestParam(value = "recordLimit", required = false) Integer recordLimit, String deviceId, HttpServletRequest request) {
    if(request.getUserPrincipal() == null){
        logger.info("User Principle Null - 1");
    }else {
        logger.info("User Principle Not Null - 1");
    }
    authService.doAuthenticate(request);

    // if authrnticated proceed with following...
    List<Data> deviceData = deviceService.getGetDeviceData(deviceId);
    return deviceData;
}

但是,我观察到了一些事情。

查看上述应用程序的以下日志(不需要的部分已被省略)。

其中,主线程(例如:http-nio-7070-exec-2 等 - 这是该应用程序服务器线程池的主线程)工作正常,因为它打印出 '用户原则不为空 - 1',但是,在并行流的分解线程中,例如 ForkJoinPool.commonPool-worker-2 等。HTTPServletRequest.getUserPrincipal() 正在变为 null。

2018-01-15 15:28:06,897 INFO [http-nio-7070-exec-2] User Principle Not Null - 1 
2018-01-15 15:28:06,897 INFO [ForkJoinPool.commonPool-worker-2] User Principle Null - 1 
2018-01-15 15:28:06,906 INFO [ForkJoinPool.commonPool-worker-3] User Principle Null - 1 

2018-01-15 15:28:06,955 INFO [ForkJoinPool.commonPool-worker-2] User Principle Null - 1 
2018-01-15 15:28:06,955 INFO [ForkJoinPool.commonPool-worker-1] User Principle Null - 1

2018-01-15 15:28:06,957 INFO [ForkJoinPool.commonPool-worker-2] User Principle Null - 1 
2018-01-15 15:28:06,959 INFO [ForkJoinPool.commonPool-worker-3] User Principle Null - 1
2018-01-15 15:28:07,064 INFO [ForkJoinPool.commonPool-worker-2] User Principle Null - 1 

2018-01-15 15:28:07,076 INFO [http-nio-7070-exec-2] User Principle Not Null -1

2018-01-15 15:28:07,078 INFO [ForkJoinPool.commonPool-worker-1] User Principle Null - 1

我对 lambda 表达式和并行流还很陌生。 请帮助我了解这里的问题是什么。

Java 详细信息:

java version "1.8.0_121"
Java(TM) SE Runtime Environment (build 1.8.0_121-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.121-b13, mixed mode)

最佳答案

根本原因是 Spring 将 SecurityContextHolderAwareRequestWrapper 实例注入(inject)到您的方法中。当调用 equest.getUserPrincipal() 时,此包装器将调用以下几行:

private Authentication getAuthentication() {
    Authentication auth = SecurityContextHolder.getContext().getAuthentication();

SecurityContextHolder有不同的策略。默认情况下使用MODE_THREADLOCAL策略。这就是为什么你在主线程中有用户原则,但在 forkjoinpool 线程中没有用户原则。

-Dspring.security.strategy=MODE_INHERITABLETHREADLOCAL VM选项可以解决您的问题。 InheritableThreadLocal javadoc 和 InheritableThreadLocalSecurityContextHolderStrategy 源代码可能会给理解带来额外的值(value)。

关于java - 在 Java 8 中将 HttpServletRequest 与 ParallelStream 结合使用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48273368/

相关文章:

java - 审计检查员征求意见

java - 如何使用 Spring Security 将对象从 AJAX 传递到 Spring Controller

spring - spring security内部使用AOP吗?

parallel-processing - 这是 PLINQ 错误吗?

c# - 并行传递归约

带有土耳其字符的 Java 邮件问题

java - 字符串比较时的错误结果

jsp/servlet 应用程序中的 java 方法和竞争条件

java - Spring Security不支持请求方法 'POST'

r - 在 r 中同时(并行)在后台运行多个作业