简体   繁体   English

在Apache CXF Interceptor中编写消息内容和响应代码

[英]Write message content and response code in Apache CXF Interceptor

I am trying to make my web service secure by making one of the methods require HTTP Basic authentication. 我试图通过使其中一个方法需要HTTP基本身份验证来确保我的Web服务安全。 In order to do this, I've implemented a custom Interceptor called LoginInterceptor that checks the requested URL, and if it corresponds to a method called open , it checks the message header for the username and password. 为了做到这一点,我实现了一个名为LoginInterceptor的自定义拦截器来检查请求的URL,如果它对应于一个名为open的方法,它会检查邮件头是否LoginInterceptor户名和密码。

If there are no authentication fields in the header, the response code is set to HTTP_UNAUTHORIZED , and if the username or password is incorrect, the response code is set to HTTP_FORBIDDEN . 如果标头中没有验证字段,则响应代码设置为HTTP_UNAUTHORIZED ,如果用户名或密码不正确,则响应代码将设置为HTTP_FORBIDDEN Here's the code: 这是代码:

public LoginInterceptor() {
     super(Phase.RECEIVE);
     addAfter(RequestInterceptor.class.getName()); //another custom interceptor, for some simple redirections.
}

public void handleMessage(Message message) throws Fault {
     String requestURI = message.get(Message.REQUEST_URI).toString();
     String methodKeyword = requestURI.substring("localhost".length()+1).split("/")[0];

     if(methodKeyword.equals("open")) {
          AuthorizationPolicy policy = message.get(AuthorizationPolicy.class);
          if(policy == null) {
              sendErrorResponse(message, HttpURLConnection.HTTP_UNAUTHORIZED);
              return;
          }

          //userPasswords is a hashmap of usernames and passwords.     
          String realPassword = userPasswords.get(policy.getUserName());
          if (realPassword == null || !realPassword.equals(policy.getPassword())) {
                    sendErrorResponse(message, HttpURLConnection.HTTP_FORBIDDEN);
          }
     }
}

//This is where the response code is set, and this is where I'd like to write the response message.
private void sendErrorResponse(Message message, int responseCode) {
    Message outMessage = getOutMessage(message);
    outMessage.put(Message.RESPONSE_CODE, responseCode);

    // Set the response headers
    Map responseHeaders = (Map) message.get(Message.PROTOCOL_HEADERS);

    if (responseHeaders != null) {
        responseHeaders.put("Content-Type", Arrays.asList("text/html"));
        responseHeaders.put("Content-Length", Arrays.asList(String.valueOf("0")));
    }

    message.getInterceptorChain().abort();

    try {
        getConduit(message).prepare(outMessage);
        close(outMessage);
    } catch (IOException e) {
        e.printStackTrace();
    }
}

private Message getOutMessage(Message inMessage) {
    Exchange exchange = inMessage.getExchange();
    Message outMessage = exchange.getOutMessage();

    if (outMessage == null) {
            Endpoint endpoint = exchange.get(Endpoint.class);
            outMessage = endpoint.getBinding().createMessage();
            exchange.setOutMessage(outMessage);
    }

    outMessage.putAll(inMessage);

    return outMessage;

}

//Not actually sure what this does. Copied from a tutorial online. Any explanation is welcome
private Conduit getConduit(Message inMessage) throws IOException {
    Exchange exchange = inMessage.getExchange();
    Conduit conduit = exchange.getDestination().getBackChannel(inMessage);
    exchange.setConduit(conduit);
    return conduit;
}

private void close(Message outMessage) throws IOException {
    OutputStream os = outMessage.getContent(OutputStream.class);
    os.flush();
    os.close();
}

This works fine, however, I want to also return a message in the response, something like "incorrect username or password". 这工作正常,但是,我想在响应中返回一条消息,例如“用户名或密码不正确”。 I've tried, from within the sendErrorResponse method, doing: 我已尝试从sendErrorResponse方法中执行以下操作:

outMessage.setContent(String.class, "incorrect username or password") and I set the content-length to "incorrect username or password".length() . outMessage.setContent(String.class, "incorrect username or password") ,我将content-length设置为"incorrect username or password".length() This doesn't work, I guess because the Apache CXF Messages use InputStream s and OutputStream s. 这不起作用,我猜是因为Apache CXF消息使用InputStreamOutputStream

So I tried: 所以我尝试过:

OutputStream os = outMessage.getContent(OutputStream.class);
try {
    os.write("incorrect username or password".getBytes() );
    outMessage.setContent(OutputStream.class, os);
 } catch (IOException e) {
    e.printStackTrace();
 }

This doesn't work either. 这也不起作用。 When stepping through with a debugger, I notice that os is null When testing with Postman, I get: 当使用调试器单步执行时,我注意到osnull当使用Postman进行测试时,我得到:

Could not get any response This seems to be like an error connecting to http://localhost:9090/launcher/open . 无法获得任何响应这似乎是连接到http://localhost:9090/launcher/open The response status was 0. Check out the W3C XMLHttpRequest Level 2 spec for more details about when this happens. 响应状态为0.有关何时发生此更多详细信息,请查看W3C XMLHttpRequest Level 2规范。

Pressing ctrl+shif+c (opening up dev tools) in Chrome, and checking the networks tab, I see: 在Chrome中按ctrl + shif + c(打开开发工具),然后检查网络标签,我看到:

"ERR_CONTENT_LENGTH_MISMATCH"

I've tried using an XMLStreamWriter, but that wans't any better. 我已经尝试过使用XMLStreamWriter,但是没有更好的。

Questions: 问题:

  • I can return the correct response code (401 Unauthorized and 403 forbidden), but how do I return a message in the response body? 我可以返回正确的响应代码(401 Unauthorized和403 forbidden),但是如何在响应正文中返回消息?

  • Do I need to specifically extend a particular OutInterceptor like JASRXOutInterceptor in order to modify the message content? 我是否需要专门扩展特定的OutInterceptor,如JASRXOutInterceptor才能修改消息内容?

  • I tried using a JAASInterceptor before, but I didn't manage to get that working. 我之前尝试过使用JAASInterceptor,但我没有设法让它工作。 Could someone show me how to implement it that way, if that's somehow easier? 有人可以告诉我如何以这种方式实现它,如果这在某种程度上更容易吗?

  • I could also just throw a fault like this: throw new Fault("incorrect username or password", Logger.getGlobal()); 我也可以像这样抛出一个错误: throw new Fault("incorrect username or password", Logger.getGlobal()); , but then the HTTP response code would be 500. I'd prefer to return a proper 401 or 403 response. ,但是HTTP响应代码将是500.我宁愿返回正确的401或403响应。

Note: 注意:

Right now I'm still using HTTP for the transport layer. 现在我仍在使用HTTP作为传输层。 Once I fix this, I'll change to HTTPS. 解决这个问题后,我将更改为HTTPS。

Basically, what I wanted to do is return a fault with a HTTP response code of 401 (unauthorized) or 403 (forbidden) instead of 500 (server error). 基本上,我想要做的是返回错误,HTTP响应代码为401(未授权)或403(禁止)而不是500(服务器错误)。 Turns out Apache CXF provides a simple way of doing that, using the Fault.setStatusCode(int) method , as I found from this question on Stack Overflow: how to throw a 403 error in Apache CXF - Java 事实证明Apache CXF提供了一种简单的方法,使用Fault.setStatusCode(int) method ,正如我在Stack Overflow上的这个问题中发现的: 如何在Apache CXF中抛出403错误 - Java

So this is what my handleMessage method looks like now: 所以这就是我的handleMessage方法现在的样子:

public void handleMessage(Message message) throws Fault {
        String requestURI = message.get(Message.REQUEST_URI).toString();
        String methodKeyword = requestURI.substring("localhost".length()+1).split("/")[0];

        if(methodKeyword.equals("open")) {
            AuthorizationPolicy policy = message.get(AuthorizationPolicy.class);

            if(policy == null) {
                Fault fault = new Fault("incorrect username or password", Logger.getGlobal());
                fault.setStatusCode(401);
                throw fault;
            }

            String realPassword = userPasswords.get(policy.getUserName());
            if (realPassword == null || !realPassword.equals(policy.getPassword())) {
                Fault fault = new Fault("incorrect username or password", Logger.getGlobal());
                fault.setStatusCode(403);
                throw fault;
            }
        }
    }

I removed the other methods, they were unnecessary. 我删除了其他方法,它们是不必要的。

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

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