如何通过Java Print API更改将CUPS注册为打印作业发起者的用户?
我正在使用Java打印API [1],通过CUPS打印到一台打印机。
我们在Java客户端使用Java 8,CUPS的版本是2.2.6。
Java进程与CUPS守护进程在同一台服务器上,后者注册了所有打印机。
我当前面临的问题是,无法更改在CUPS中注册的请求打印作业的用户。
更具体地说,我无法改变以下命令返回的第二列的值:lpstat -W all -o PRINTERDNS
从Java API出发,我指定了 RequestingUserName 属性(ipp: requesting-user-name)[3]。
我已经尝试指定 JobOriginatingUserName 属性 [1],但在Java客户端请求中无法实现。它只能在监听CUPS发送的关于打印作业状态的事件的监听器中定义。
我想能够使用 RequestingUserName 属性来改变用户。
但如果我像这样指定用户 "john":
PrintRequestAttributeSet aset = new HashPrintRequestAttributeSet();
aset.add(new Copies(numberOfCopies));
aset.add(new JobName("Document", new Locale("en", "US")));
aset.add(new RequestingUserName("john", Locale.US));
在CUPS的作业列表中(lpstat -W all -o PRINTERDNS)看到,发起打印作业的用户是启动Java客户端进程的Linux用户,而不是RequestingUserName属性中指定的用户。
有没有办法在CUPS中指定用户,使其使用通过RequestingUserName发送的用户,而不是启动Java客户端进程的Linux用户?
如果我使用 lp 命令(例如 lp -d PRINTERDNS -U john document.ps),确实可以改变该值,但john用户在Linux服务器上并不存在,而是在CUPS的作业详情中注册。
因此,我理解必须有一种方法能够在Java中实现与使用 lp 命令相同的效果。
关于链接 [2],请注意以下这句话: 如果可能,打印机将此属性设定为从提交Print Request的认证服务中所能获取到的、经过认证的可打印用户名。如果没有这样的可用信息,打印机将使用客户端在Print Request的属性集中提供的RequestingUserName属性值。
[1] https://docs.oracle.com/javase/8/docs/api/javax/print/package-summary.html [2] https://docs.oracle.com/javase/8/docs/api///javax/print/attribute/standard/JobOriginatingUserName.html
解决方案
JRE打印API的实现始终依赖于操作系统集成(因此也取决于操作系统的版本)。问题在于 javax.print 包看起来像IPP,但通常并不使用IPP。在IPP中,job-originating-user-name 是服务器在IPP作业响应中返回的只读属性(另见 RFC 8011模型与语义)。
将打印作业交给底层打印管理实现的过程非常困难。据我所知,Java运行时会执行lp命令——即使在Linux上,CUPS和 IPP都可用。你可能需要修复JVM的系统特定源代码。
使用john的 userid
为了欺骗实现,你可以使用john的 userid启动一个用于交接打印作业的进程。这需要具备sudo权限,并且系统中也必须存在john的 userid。
使用原生IPP
为了解决javax实现的限制,你可以直接通过IPP与 CUPS通信。ipp-client库的2.x版本 仍应支持Java 8。之后你可以决定要使用哪种身份验证级别。若仅进行用户名识别,可以在ippPrinter对象使用的ippConfig中设置用户名。IppPrinter的实现是线程安全的,但对所有方法执行共用ippConfig。因此如果每个打印作业的用户标识动态变化,你需要为每个用户创建一个IppPrinter实例。