Java Web Service 客户端基本身份验证

Posted

技术标签:

【中文标题】Java Web Service 客户端基本身份验证【英文标题】:Java Web Service client basic authentication 【发布时间】:2011-10-27 15:12:54 【问题描述】:

我在 Glassfish 之上创建了一个 JAX-WS Web 服务,它需要基本的 HTTP 身份验证。

现在我想为该 Web 服务创建一个独立的 Java 应用程序客户端,但我不知道如何传递用户名和密码。

它与 Eclipse 的 Web 服务资源管理器一起工作,并检查了我发现的线路:

POST /SnaProvisioning/SnaProvisioningV1_0 HTTP/1.1
Host: localhost:8080
Content-Type: text/xml; charset=utf-8
Content-Length: 311
Accept: application/soap+xml, application/dime, multipart/related, text/*
User-Agent: IBM Web Services Explorer
Cache-Control: no-cache
Pragma: no-cache
SOAPAction: ""
Authorization: Basic Z2VybWFuOmdlcm1hbg==
Connection: close

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:q0="http://ngin.ericsson.com/sna/types/v1.0" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <soapenv:Body>
    <q0:listServiceScripts/>
  </soapenv:Body>
</soapenv:Envelope>

如何使用 java 代码在此“授权”标头中传递用户名和密码?它是散列还是类似的东西?算法是什么?

在不涉及安全性的情况下,我有一个独立的 Java 客户端:

SnaProvisioning myPort = new SnaProvisioning_Service().getSnaProvisioningV10Port();
myPort.listServiceScripts();

【问题讨论】:

【参考方案1】:

基本认证的JAX-WS方式是

Service s = new Service();
Port port = s.getPort();

BindingProvider prov = (BindingProvider)port;
prov.getRequestContext().put(BindingProvider.USERNAME_PROPERTY, "myusername");
prov.getRequestContext().put(BindingProvider.PASSWORD_PROPERTY, "mypassword");

port.call();

【讨论】:

也感谢我!我会花 很长 时间来解决这个问题。 对于1.5级的,需要为BindingProvider添加这个依赖: javax.xmlwebservices-api1.5 很好的答案。非常感谢。【参考方案2】:

事实证明,有一种简单、标准的方法可以实现我想要的:

import java.net.Authenticator;
import java.net.PasswordAuthentication;

Authenticator myAuth = new Authenticator() 

    @Override
    protected PasswordAuthentication getPasswordAuthentication()
    
        return new PasswordAuthentication("german", "german".toCharArray());
    
;

Authenticator.setDefault(myAuth);

没有自定义“sun”类或外部依赖项,也没有手动编码任何东西。

我知道 BASIC 安全性并不安全,但我们也在使用 HTTPS。

【讨论】:

请注意Authenticator#setDefault 不是线程安全的,如果您的服务使用者访问多个端点,这不是一个好的解决方案。 是否有任何允许多个端点的线程安全替代方案? Authenticator 可用于任何类型的安全协议,但提供凭据的 JAX-WS 方式不需要此方法。 不幸的是,如果您的用户名是 unicode 而不是来自 Latin1 的用户名,则此解决方案将不起作用 :( 如果您以后想以编程方式更改凭据,您可能会遇到与this 错误/缺陷相关的this 问题。如果仅使用基本身份验证保护服务端口,最简单的解决方案是(再次)乔纳森的答案。如果 WSDL 本身受到保护,则只有其他线程中描述的丑陋解决方法。【参考方案3】:

对于Axis2 客户,这可能会有所帮助

...
serviceStub = new TestBeanServiceStub("<WEB SERVICE URL>"); // Set your value
HttpTransportProperties.Authenticator basicAuthenticator = new HttpTransportProperties.Authenticator();
List<String> authSchemes = new ArrayList<String>();
authSchemes.add(Authenticator.BASIC);
basicAuthenticator.setAuthSchemes(authSchemes); 
basicAuthenticator.setUsername("<UserName>"); // Set your value
basicAuthenticator.setPassword("<Password>"); // Set your value
basicAuthenticator.setPreemptiveAuthentication(true);
serviceStub._getServiceClient().getOptions().setProperty(org.apache.axis2.transport.http.HTTPConstants.AUTHENTICATE, basicAuthenticator);
serviceStub._getServiceClient().getOptions().setProperty(org.apache.axis2.transport.http.HTTPConstants.CHUNKED, "false");
...

【讨论】:

我不得不更改 authSchemes.add(Authenticator.BASIC);到 authSchemes.add(HttpTransportProperties.Authenticator.BASIC);让它编译。 嗨 Avil,我的 Axis2 客户端中没有存根类。对不起,我是初学者。我正在使用商业供应商的服务,客户由供应商提供。我做了wsimport,但没有存根类。所以我不能_getServiceClient().getOptions() 做这部分。执行wsimport 时是否需要添加任何其他属性?请帮我。感谢您的帮助。【参考方案4】:

一些关于基本身份验证的附加上下文,它包含在包含键/值对的标头中:

授权:基本 Z2VybWFuOmdlcm1hbg==

其中“Authorization”是标题键, 并且标头值有一个字符串(“Basic”字加上空格)连接到“ Z2VybWFuOmdlcm1hbg==",用户名和密码,base 64双点连接

String name = "username";
String password = "secret";
String authString = name + ":" + password;
String authStringEnc = new BASE64Encoder().encode(authString.getBytes());
...
objectXXX.header("Authorization", "Basic " + authStringEnc);

【讨论】:

【参考方案5】:

如果您为客户端使用 JAX-WS 实现,例如 Metro Web 服务,以下代码显示如何在 HTTP 标头中传递用户名和密码:

 MyService port = new MyService();
 MyServiceWS service = port.getMyServicePort();

 Map<String, List<String>> credentials = new HashMap<String,List<String>>();

 credentials.put("username", Collections.singletonList("username"));
 credentials.put("password", Collections.singletonList("password"));

 ((BindingProvider)service).getRequestContext().put(MessageContext.HTTP_REQUEST_HEADERS, credentials);

然后对服务的后续调用将被验证。请注意,密码仅使用 Base64 编码,因此我鼓励您使用其他附加机制(如客户端证书)来提高安全性。

【讨论】:

这对于手动添加任意标头和设置任意值很有用(通过手动计算凭据的 base64 编码),但还有更多标准选项【参考方案6】:

这对我有用:

 BindingProvider bp = (BindingProvider) port;
 Map<String, Object> map = bp.getRequestContext();
 map.put(BindingProvider.USERNAME_PROPERTY, "aspbbo");
 map.put(BindingProvider.PASSWORD_PROPERTY, "9FFFN6P");

【讨论】:

请使用英文【参考方案7】:

为了让您的生活更简单,您可能需要考虑使用 JAX-WS 框架,例如 Apache CXF 或 Apache Axis2。

这里是描述如何为 Apache CXF 设置 WS-Security 的链接 -> http://cxf.apache.org/docs/ws-security.html

编辑 顺便说一句,Authorization 字段只是使用简单的 Base64 编码。 据此(http://www.motobit.com/util/base64-decoder-encoder.asp),解码后的值为german:german

【讨论】:

Web 服务已经工作了很长一段时间,增加安全性只是向单个类添加单个注释 (@RolesAllowed) 的问题,我认为没有比这更简单的了尤其是当我查看 CXF 所需的大量配置时。除此之外,仅仅因为客户端无法发送(还)一个标头而改变框架是不值得的。 @German.从您的问题中不清楚您在客户端中使用的是 any 框架。看起来你自己的答案是赢家。 是的,也许不清楚我使用的框架,问题是我们没有任何 jar 或依赖项或任何引用特定实现的东西,但我想如果我们正在使用Glassfish,那么我们正在使用 Metro。【参考方案8】:

如果您使用 JAX-WS,以下对我有用:

    //Get Web service Port
    WSTestService wsService = new WSTestService();
    WSTest wsPort = wsService.getWSTestPort();

    // Add username and password for Basic Authentication
    Map<String, Object> reqContext = ((BindingProvider) 
         wsPort).getRequestContext();
    reqContext.put(BindingProvider.USERNAME_PROPERTY, "username");
        reqContext.put(BindingProvider.PASSWORD_PROPERTY, "password");

【讨论】:

【参考方案9】:

最简单的方法是在请求下包含用户名和密码。请参阅下面的示例。

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
    xmlns:typ="http://xml.demo.com/types" xmlns:ser="http://xml.demo.com/location/services"
    xmlns:typ1="http://xml.demo.com/location/types">
    <soapenv:Header>
        <typ:requestHeader>
            <typ:timestamp>?</typ:timestamp>
            <typ:sourceSystemId>TEST</typ:sourceSystemId>
            <!--Optional: -->
            <typ:sourceSystemUserId>1</typ:sourceSystemUserId>
            <typ:sourceServerId>1</typ:sourceServerId>
            <typ:trackingId>HYD-12345</typ:trackingId>
        </typ:requestHeader>

        <wsse:Security soapenv:mustUnderstand="1"
            xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
            <wsse:UsernameToken wsu:Id="UsernameToken-emmprepaid"
                xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
                <wsse:Username>your-username</wsse:Username>
                <wsse:Password
                    Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">your-password</wsse:Password>
            </wsse:UsernameToken>
        </wsse:Security>
    </soapenv:Header>
    <soapenv:Body>
        <ser:getLocation>
            <!--Optional: -->
            <ser:GetLocation>
                <typ1:locationID>HYD-GoldenTulipsEstates</typ1:locationID>
            </ser:GetLocation>
        </ser:getLocation>
    </soapenv:Body>
</soapenv:Envelope>

【讨论】:

以上是关于Java Web Service 客户端基本身份验证的主要内容,如果未能解决你的问题,请参考以下文章

使用 Java 进行基本和摘要式访问身份验证

web service001——web service简单认识

Java Restful Web Services (jax rs) 身份验证模式

怎样在java 中调用web service 传入参数返回xml?

web service基础知识

不同的.Net版本客户端软件调用Java Web Service区别