如何序列化 java.security.Principal?

Posted

技术标签:

【中文标题】如何序列化 java.security.Principal?【英文标题】:How to serialize a java.security.Principal? 【发布时间】:2020-01-13 07:53:20 【问题描述】:

我已经写了这段代码:

@RestController
public class UserController 

    private final static Logger LOGGER = LoggerFactory.getLogger(UserController.class);

    @RequestMapping(path = "/", produces = "application/json")
    public @ResponseBody Principal getUser(HttpServletRequest request) throws Exception 
        try 
            Principal principal = request.getUserPrincipal();
            LOGGER.debug("User found" + principal.toString());
            return principal;
           
        catch (Exception e) 
            LOGGER.debug("User not found : " + e.getMessage());
            throw e;
        
    

但它会抛出:

java.lang.IllegalStateException: getOutputStream() 已经 呼吁作出回应

如何序列化该对象?是什么让 Spring Boot 的对象可序列化?在执行过程中,实际的对象类型是Principalorg.apache.catalina.realm.GenericPrincipal实现

根据要求,这里是完整的堆栈跟踪:

#ERROR#org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/loggedinusertest].[dispatcherServlet]##anonymous#https-jsse-nio-8041-exec-5#na# ab54cfe76#loggedinusertest#web#ab54cfe76#na#na#na#na#Servlet.service() 对于 servlet [dispatcherServlet] 抛出异常 java.lang.IllegalStateException: getOutputStream() 已经 呼吁在 org.apache.catalina.connector.Response.getWriter(Response.java:624) 在 org.apache.catalina.connector.ResponseFacade.getWriter(ResponseFacade.java:211) 在 org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration$SpelView.render(ErrorMvcAutoConfiguration.java:218) 在 org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1257) 在 org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1037) 在 org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:980) 在 org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:897) 在 org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970) 在 org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861) 在 javax.servlet.http.HttpServlet.service(HttpServlet.java:635) 在 org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846) 在 javax.servlet.http.HttpServlet.service(HttpServlet.java:742) 在 org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) 在 org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) 在 org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52) 在 org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) 在 org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) 在 org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99) 在 org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) 在 org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) 在 org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) 在 org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:101) 在 org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) 在 org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) 在 org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:101) 在 org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) 在 org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) 在 org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:101) 在 org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) 在 org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) 在 org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:101) 在 org.springframework.boot.web.support.ErrorPageFilter.doFilter(ErrorPageFilter.java:112) 在 org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) 在 org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) 在 org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:728) 在 org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:470) 在 org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:395) 在 org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:316) 在 org.springframework.boot.web.support.ErrorPageFilter.handleErrorStatus(ErrorPageFilter.java:153) 在 org.springframework.boot.web.support.ErrorPageFilter.doFilter(ErrorPageFilter.java:121) 在 org.springframework.boot.web.support.ErrorPageFilter.access$000(ErrorPageFilter.java:61) 在 org.springframework.boot.web.support.ErrorPageFilter$1.doFilterInternal(ErrorPageFilter.java:94) 在 org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) 在 org.springframework.boot.web.support.ErrorPageFilter.doFilter(ErrorPageFilter.java:112) 在 org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) 在 org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) 在 org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198) 在 org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) 在 com.sap.core.connectivity.jco.session.ext.RequestTracker.invoke(RequestTracker.java:55) 在 org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:610) 在 com.sap.cloud.runtime.impl.bridge.security.AbstractAuthenticator.invoke(AbstractAuthenticator.java:206) 在 org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140) 在 org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:650) 在 org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:650) 在 com.sap.core.tenant.valve.TenantValidationValve.invokeNextValve(TenantValidationValve.java:182) 在 com.sap.core.tenant.valve.TenantValidationValve.invoke(TenantValidationValve.java:97) 在 com.sap.js.statistics.tomcat.valve.RequestTracingValve.callNextValve(RequestTracingValve.java:113) 在 com.sap.js.statistics.tomcat.valve.RequestTracingValve.invoke(RequestTracingValve.java:59) 在 com.sap.core.js.monitoring.tomcat.valve.RequestTracingValve.invoke(RequestTracingValve.java:27) 在 org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81) 在 org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87) 在 org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342) 在 org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:800) 在 org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) 在 org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:800) 在 org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1471) 在 org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) 在 java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) 在 java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) 在 org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) 在 java.lang.Thread.run(Thread.java:836)

【问题讨论】:

看起来这不是java.security.Principal 的可序列化问题。也许某种过滤器(或拦截器)在此方法已经编写 request.outputStream 之前处理请求。我建议调整日志记录级别以跟踪并查看结果。 当我返回 String 时它工作正常,这就是为什么我认为它与序列化有关 @cassiomolin 我在问题的结尾提到了它 我现在看到了 :) 此代码不会抛出该异常,并且该异常发生在序列化之前,因此与序列化无关。请跟踪堆栈。 【参考方案1】:

通过在您的 Web API 中公开 Principal 实例(例如来自 Apache Tomcat 的 GenericPrincipal 实例),您最终可能会泄露一些您无意泄露的敏感细节。

更好的方法是创建一个 DTO,然后仅将您要公开的主要细节映射到此 DTO。


尽管存在安全漏洞,但让我澄清一下序列化本身可能有什么问题。

如果您创建GenericPrincipal 的实例,然后尝试直接使用ObjectMapper 将其序列化为JSON,您会发现它不会成功:

ObjectMapper mapper = new ObjectMapper();
Principal principal = new GenericPrincipal("john.doe", "secret", Arrays.asList("admin"));
String json = mapper.writeValueAsString(principal);

例外是:

Exception in thread "main" com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Direct self-reference leading to cycle (through reference chain: org.apache.catalina.realm.GenericPrincipal["userPrincipal"])

如果您查看GenericPrincipal 源代码,您会看到userPrincipal 字段:

protected final Principal userPrincipal;

好吧,您可以尝试使用mix-in annotations,但这可能不值得。只需创建一个 DTO 并仅公开您需要的内容。

【讨论】:

它实际上是一个存根,用于测试目的,所以我不介意暴露任何敏感内容。 好的,我明白了,它引用了自己,所以它抛出一个异常来防止无限递归序列化。感谢您的见解!

以上是关于如何序列化 java.security.Principal?的主要内容,如果未能解决你的问题,请参考以下文章

c# sorket序列化反序列化如何如何传接Object?

什么是序列化,在java中如何实现序列化?

如何序列化一个 lambda?

什么是序列化? 如何实现(反)序列化 序列化的应用

什么是序列化? 如何实现(反)序列化 序列化的应用

什么是序列化? 如何实现(反)序列化 序列化的应用