过去微软.NET的ASMX Web Service已被大家广泛应用﹐但在信息安全日愈重视之下﹐微软有意以WCF取代原有的 ASMX Web Service。WCF 具有许多先进的技术﹐而跨平台作业已是现在不可避免的问题﹐同样是微软的 Solution之下如何使用WCF应该不是什么问题﹐但在不同的平台上是否有那么容易呢?因此这里以 Java 实作如何来调用具有使用身份验证的 WCF﹐并以WCF 预设的wsHttpBinding 及一般常使用的 basicHttpBinding 的系结方式实作。
我本身并非专研 Java﹐但既然日后使用了 WCF 也势必面临 Java 或其它平台的呼叫﹐Java 是Open Source 具有多种 Framework﹐且有多种开发工具﹐参考了网络上许多范例与讨论﹐Java 对于WCF比较常用的是 ASIX 和 Metro 套件﹐因此这里主要使用 NetBeans IDE 搭配 Metro﹐eclipse 搭配asix 这两种﹐不过因为不是专研 Java﹐故仍有些地方不是实作的很完全﹐这里就抛砖引玉﹐期待高手来解惑了。
这里使用的 NetBeans 版本为 NetBeans IDE 7.2 (Build 201207301726)﹐JDK 是1.6.0_37。NetBeans可至Oracle官网(http://netbeans.org/downloads/index.html)下载。
在 Viusal Studio 上建立WCF 项目时﹐预设所产生的就是使用 wsHttpBinding 的系结。wsHttpBinding 预设的安全性模式为 Message(讯息模式加密)﹐在 WCF 中也是普遍的被使用。当我想以 Java 调用时却发现在网络上有许多人询问﹐但很少看到一个完整的范例。同时所看到的讨论回复都很片断﹐因此实作过程并不容易。在这里分别以 Java Application 与 Web应用程序当Client程序调用WCF为范例。
这一篇是搭配使用者认证的WCF服务—wsHttpBinding系结所建置的WCF 服务。
1. 自订使用者账号/密码﹐Java Application client 不以Glassfish为container
1.1. 汇入凭证档
因为WCF自订使用者账号/密码认证是需要X.509凭证﹐因此在开始之前必须先取得凭证放置到Java可以读取的位置。
这里的凭证继续延用WCF自定义用户账号密码之使用者认证的WCF服务wsHttpBinding总结这一篇中的凭证﹐其凭证主体为 MyWCFCert。首先使用windows凭证管理将之前制作WCF时所制作的凭证先汇出。
将汇出的凭证档档名命名为 MyWCFCert.cer﹐接着使用 JDK 所提供的工具 keytool 指令建立放置凭证的 keystore 或汇入已存在的 keystore(金钥库)。Keytool.exe是java的凭证管理工具。
指令:
把一个凭证档导入到指定的keystore www.it165.net
keytool -import -file MyWCFCert.cer -keystore my.TrustStore -alias wcfsvrkey
用-keystore 参数指定 keystore 档案﹐my.TrustStore 是我自己建立的 keystore﹐如果不存在﹐会自动建立同时会询问keystore的密码。
-alias则是为这个汇入的凭证建立一个别名。
汇入成功后应该做一下检查﹐同样使用keytool指令
指令:
列出keystore中的内容信息
keytool -list -v -keystore my.TrustStore
执行之后可以检视这个keystore所有的凭证档。
![\](http://www.it165.net/uploadfile/2012/1226/20121226110046921.png?x-oss-process=image/resize,w_1400/format,webp)
如果有删除凭证的需要时同样使用keytool
指令:
删除指定的keystore中的凭证
keytool -delete -alias wcfsvrkey -keystore my.TrustStore
1.2. 下载 METRO 2.2.1
我由Oracle 官网下载使用的NetBeans IDE是7.2 版﹐内附 METRO 是 2.0 版﹐对于要使用具有使用者账号密码认证的WCF服务在国外论坛上有不少﹐有许多人都说必须将Metro更新到2.0版才行﹐不过经过我实测后发现必须使用 2.2.1版才行。
Metro 2.2.1 版可至 http://metro.java.net/2.2.1/ 此处下载。将下载的metro-standalone-2.2.1.zip档案解压至自订的路径之下﹐例如以我个人放置到D:\Java\metro-2_2_1。然后开启NetBeans﹐到工具/链接库将 METRO 2.2.1 加入链接库。
![\](http://www.it165.net/uploadfile/2012/1226/20121226110117994.png?x-oss-process=image/resize,w_1400/format,webp)
在链接库管理器中﹐按下[加入JAR/数据夹]指向Metor 2.2.1的位置﹐之后在NetBeans 中就可以直接选择了。
![\](http://www.it165.net/uploadfile/2012/1226/20121226110156807.png?x-oss-process=image/resize,w_1400/format,webp)
1.3. 建立项目
开启NetBeans新增项目﹐左侧选择Java﹐右侧选择Java Application﹐[下一步]继续。
![\](http://www.it165.net/uploadfile/2012/1226/20121226110240295.png?x-oss-process=image/resize,w_1400/format,webp)
输入项目名称﹑项目位置﹑Class文件等各项信息﹐按下[完成]。
![\](http://www.it165.net/uploadfile/2012/1226/20121226110311451.png?x-oss-process=image/resize,w_1400/format,webp)
专案名称:wsHttpClient
Class名称:JavaClient
1.4. 加入 Web Service Client
项目建立后﹐在该项目的名称上以鼠标右键选择 New/Web Service Client。
![\](http://www.it165.net/uploadfile/2012/1226/20121226110347992.png?x-oss-process=image/resize,w_1400/format,webp)
完成后回到NetBeans IDE接口等一下﹐NetBeans正在产生WSDL档及自动产生一些相关的设定档和程序代码。NetBeans跑完后大约如下图。
![\](http://www.it165.net/uploadfile/2012/1226/20121226110418452.png?x-oss-process=image/resize,w_1400/format,webp)
1.5. 档案转换为 UT-8 格式
检视项目之下有个Generated Source﹐将其展开后会如下﹐在Generated Source是NetBeans依WSDL所自动产生的class檔。
![\](http://www.it165.net/uploadfile/2012/1226/20121226110452521.png?x-oss-process=image/resize,w_1400/format,webp)
点选开启 GetProduct.java﹐画面会出现警告﹐这是因为NetBeans自动Generated的档案为ANSI格式﹐但这个档案中有中文﹐而NetBeans要读取的档案为UTF-8﹐故这里NetBeans跳出了警告。
![\](http://www.it165.net/uploadfile/2012/1226/20121226110524202.png?x-oss-process=image/resize,w_1400/format,webp)
因为档案带有中文字﹐因此必须要先将档案改存为UTF-8才行﹐不然后续做 Builde Project 时是会出错的。可以用记事本开启档案﹐再以另存新档方式变更格式再回存。不过因为档案不只一个﹐这样改比较慢﹐可以去网络找一个老牌的工具 ConverZ 做批次转换。 www.it165.net
1.6. 编辑 Web 服务属性
接着在Web服务参照下的MyProducts按下鼠标右键﹐选择[编辑Web服务属性]。
![\](http://www.it165.net/uploadfile/2012/1226/20121226110555882.png?x-oss-process=image/resize,w_1400/format,webp)
开出的是Web服务属性的画面﹐这里我们只要设定Quality Of Service页签下关于安全的部分。
![\](http://www.it165.net/uploadfile/2012/1226/20121226110629958.png?x-oss-process=image/resize,w_1400/format,webp)
在编辑Web服务属性的设定画面上有一个[使用开发默认值]的复选框﹐点击一下﹐画面会跳出一些讯息。
![\](http://www.it165.net/uploadfile/2012/1226/20121226110711906.png?x-oss-process=image/resize,w_1400/format,webp)
这个讯息是询问我们,是否使用METRO Library﹐如果要使用则会移除JAX-WS library﹐因为JAX-WS已被包含于METRO之中。按下Yes则NetBeans会帮我们项目加上METRO。如下图﹐回到Project中展开链接库﹐就可以看到METRO 2.0已被加入。
![\](http://www.it165.net/uploadfile/2012/1226/20121226110743510.png?x-oss-process=image/resize,w_1400/format,webp)
再回到编辑Web服务属性设定画面﹐刚刚所点击的[使用开发默认值]的复选框如果已经有被勾选了﹐请将勾选取消。然后先离开编辑Web服务属性设定画面。
1.7. 加入CallbackHandler 档案
这里需要加入一个继承CallbackHandler的档案
TrustStoreCallbackHandler.java
01.
public
class
TrustStoreCallbackHandler implements CallbackHandler {
02.
KeyStore keyStore =
null
;
03.
String password =
"123456"
;
// keystore的密码
04.
public
TrustStoreCallbackHandler() {
05.
System.
out
.println(
"Truststore CBH.CTOR Called.........."
);
06.
InputStream
is
=
null
;
07.
try
{
08.
keyStore = KeyStore.getInstance(
"JKS"
);
09.
String keystoreURL =
"C:\\Java\\Certificate\\myTrustStore"
;
//放置凭证档的keystore
10.
is
=
new
FileInputStream(keystoreURL);
11.
keyStore.load(
is
, password.toCharArray());
12.
}
catch
(IOException ex) {
13.
Logger.getLogger(TrustStoreCallbackHandler.
class
.getName()).log(Level.SEVERE,
null
, ex);
14.
throw
new
RuntimeException(ex);
15.
}
catch
(NoSuchAlgorithmException ex) {
16.
Logger.getLogger(TrustStoreCallbackHandler.
class
.getName()).log(Level.SEVERE,
null
, ex);
17.
throw
new
RuntimeException(ex);
18.
}
catch
(CertificateException ex) {
19.
Logger.getLogger(TrustStoreCallbackHandler.
class
.getName()).log(Level.SEVERE,
null
, ex);
20.
throw
new
RuntimeException(ex);
21.
}
catch
(KeyStoreException ex) {
22.
Logger.getLogger(TrustStoreCallbackHandler.
class
.getName()).log(Level.SEVERE,
null
, ex);
23.
throw
new
RuntimeException(ex);
24.
}
finally
{
25.
try
{
26.
is
.close();
27.
}
catch
(IOException ex) {
28.
Logger.getLogger(TrustStoreCallbackHandler.
class
.getName()).log(Level.SEVERE,
null
, ex);
29.
}
30.
}
31.
}
32.
33.
public
void
handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
34.
System.
out
.println(
"Truststore CBH.handle() Called.........."
);
35.
for
(
int
i = 0; i < callbacks.length; i++) {
36.
if
(callbacks[i] instanceof KeyStoreCallback) {
37.
KeyStoreCallback cb = (KeyStoreCallback) callbacks[i];
38.
print(cb.getRuntimeProperties());
39.
cb.setKeystore(keyStore);
40.
}
else
{
41.
throw
new
UnsupportedCallbackException(callbacks[i]);
42.
}
43.
}
44.
}
45.
46.
private
void
print(Map context) {
47.
Iterator it = context.keySet().iterator();
48.
while
(it.hasNext()) {
49.
System.
out
.println(
"Prop "
+ it.next());
50.
}
51.
}
52.
}
1.8. 重回编辑 Web 服务属性
重新开启 编辑Web服务属性
![\](http://www.it165.net/uploadfile/2012/1226/20121226110848267.png?x-oss-process=image/resize,w_1400/format,webp)
将[认证凭证]选择动态。变更完后画面会改变。
![\](http://www.it165.net/uploadfile/2012/1226/20121226110917830.png?x-oss-process=image/resize,w_1400/format,webp)
在[使用者名称回呼处理程序]及[密码回呼处理程序]后方的浏览键按下后选择刚刚所建立的CallbackHandler档案TrustStoreCallbackHandler.java。
接下来点选画面上的[信任库]按键。
![\](http://www.it165.net/uploadfile/2012/1226/20121226110953596.png?x-oss-process=image/resize,w_1400/format,webp)
画面将出现信任库配置的画面。
![\](http://www.it165.net/uploadfile/2012/1226/20121226111023932.png?x-oss-process=image/resize,w_1400/format,webp)
位置请选择凭证文件所在的keystor﹐同时在信任库密码输入keystore的密码。这时候如果要去选择别名是选不到的﹐请先按下「OK」键﹐并离开编辑Web服务属性的画面回到NetBeans IDE画面。
观察Project之下在原始码套件/META-INF之下多了两个档案﹐MyProducts.svc.xml及wsit-client.xml﹐这是刚刚的设定之后产生的。
![\](http://www.it165.net/uploadfile/2012/1226/20121226111055526.png?x-oss-process=image/resize,w_1400/format,webp)
现在必须先开启MyProducts.svc.xml做些修改。修改<sc:TrustStore>下的type属性﹐type 必须改为 JKS。
MyProducts.svc.xml 取需要修改的部分
01.
<
wsp1:Policy
wsu:Id
=
"WSHttpBinding_IProductServicePolicy"
>
02.
<
wsp1:ExactlyOne
>
03.
<
wsp1:All
>
04.
<
sc:TrustStore
wspp:visibility
=
"private"
type
=
"JKS"
storepass
=
"123456"
location
=
"C:\Java\Certificate\myTrustStore"
/>
05.
<
sc:CallbackHandlerConfiguration
wspp:visibility
=
"private"
>
06.
<
sc:CallbackHandler
name
=
"usernameHandler"
classname
=
"wshttpclient.TrustStoreCallbackHandler"
/>
07.
<
sc:CallbackHandler
name
=
"passwordHandler"
classname
=
"wshttpclient.TrustStoreCallbackHandler"
/>
08.
</
sc:CallbackHandlerConfiguration
>
09.
</
wsp1:All
>
10.
</
wsp1:ExactlyOne
>
11.
</
wsp1:Policy
>
上述修改之后﹐再次回到Web服务属性编辑画面并点选信任库﹐这时再去拉选别名﹐就可以选择到凭证档的别名了﹐按下OK后并离开Web服务属性编辑画面﹐再次检视刚刚刚的MyProducts.svc.xml可以发现设定多了peeralais的设定。
<sc:TrustStore wspp:visibility="private" type="JKS" storepass="123456"
location="C:\Java\Certificate\myTrustStore" peeralias="win7svrkey"/>
1.9. 撰写 Client 程序
前面的设定已完成了工作中的大部分﹐剩下client程序。
wcfClient.java
01.
public
class
wcfClient {
02.
public
static
void
main(String[] args) {
03.
org.tempuri.ProductService client;
04.
org.tempuri.IProductService port;
05.
06.
Product product;
07.
try
{
08.
client=
new
org.tempuri.ProductService();
09.
port=client.getWSHttpBindingIProductService();
10.
11.
//设定呼叫WCF的使用者账号/密码
12.
((BindingProvider)port).getRequestContext().put(BindingProvider.USERNAME_PROPERTY,
"testman"
);
13.
((BindingProvider)port).getRequestContext().put(BindingProvider.PASSWORD_PROPERTY,
"a0987"
);
14.
15.
String result= port.saySomething(
"Hello~~"
);
16.
System.
out
.println(result);
17.
System.
out
.println();
18.
19.
product=port.getProduct(
"P-001"
);
20.
System.
out
.println(
"产品编号:"
+product.getNo().getValue());
21.
System.
out
.println(
"产品名称:"
+product.getName().getValue());
22.
System.
out
.println(
"单价:"
+product.getPrice());
23.
System.
out
.println(
"数量:"
+product.getQuantity());
24.
}
catch
(Exception er){
25.
System.
out
.println(er.getMessage());
26.
}
27.
}
28.
}
1.10. 测试程序
执行Build Project﹐如果没有任何错误﹐那么就直接执行程序。结果﹐失败~~
01.
java.util.logging.ErrorManager: 5
02.
java.lang.NullPointerException
03.
at java.util.PropertyResourceBundle.handleGetObject(PropertyResourceBundle.java:136)
04.
at java.util.ResourceBundle.getObject(ResourceBundle.java:368)
05.
at java.util.ResourceBundle.getString(ResourceBundle.java:334)
06.
at java.util.logging.Formatter.formatMessage(Formatter.java:108)
07.
at java.util.logging.SimpleFormatter.format(SimpleFormatter.java:63)
08.
at java.util.logging.StreamHandler.publish(StreamHandler.java:177)
09.
at java.util.logging.ConsoleHandler.publish(ConsoleHandler.java:88)
10.
at java.util.logging.Logger.log(Logger.java:478)
11.
at java.util.logging.Logger.doLog(Logger.java:500)
12.
at java.util.logging.Logger.log(Logger.java:589)
13.
at com.sun.xml.ws.security.impl.policy.CertificateRetriever.digestBST(CertificateRetriever.java:136)
14.
at com.sun.xml.wss.jaxws.impl.SecurityClientTube.processRequest(SecurityClientTube.java:211)
15.
at com.sun.xml.ws.api.pipe.Fiber.__doRun(Fiber.java:629)
16.
at com.sun.xml.ws.api.pipe.Fiber._doRun(Fiber.java:588)
17.
at com.sun.xml.ws.api.pipe.Fiber.doRun(Fiber.java:573)
18.
com.sun.org.apache.xml.
internal
.security.exceptions.Base64DecodingException: It should be divisible by four
19.
at com.sun.xml.ws.api.pipe.Fiber.runSync(Fiber.java:470)
20.
at com.sun.xml.ws.client.Stub.process(Stub.java:319)
21.
at com.sun.xml.ws.client.sei.SEIStub.doProcess(SEIStub.java:157)
22.
at com.sun.xml.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.java:109)
23.
at com.sun.xml.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.java:89)
24.
at com.sun.xml.ws.client.sei.SEIStub.invoke(SEIStub.java:140)
25.
at $Proxy43.saySomething(Unknown Source)
26.
at wshttpclient.wcfClient.main(wcfClient.java:31)
接着在[链接库]上按鼠标右键选择[Add Library…]﹐点选之前所加入的Metro 2.2.1按下[加入链接库]就可以了。
![\](http://www.it165.net/uploadfile/2012/1226/20121226111400148.png?x-oss-process=image/resize,w_1400/format,webp)
![\](http://www.it165.net/uploadfile/2012/1226/20121226111414306.png?x-oss-process=image/resize,w_1400/format,webp)
![\](http://www.it165.net/uploadfile/2012/1226/20121226111430992.png?x-oss-process=image/resize,w_1400/format,webp)
重新再Build一次程序后﹐再一次执行。结果﹐再次失败~~~
错误的讯息
2012/12/22 上午 10:44:09 [com.sun.xml.ws.policy.parser.PolicyConfigParser] parse
信息: WSP5018: 已从档案 file:/D:/MyProject/WCF/WCFSite/WCFSolution/Java/NetBeansProjects/wsHttpClient/build/classes/META-INF/wsit-client.xml 载入 WSIT 组态.
Truststore CBH.CTOR Called..........
Truststore CBH.CTOR Called..........
2012/12/22 上午 10:44:12 com.sun.xml.wss.jaxws.impl.SecurityClientTube processClientResponsePacket
严重的: WSSTUBE0025: 验证输入讯息中的安全性时发生错误.
com.sun.xml.wss.impl.PolicyViolationException: ERROR: No security header found in the message
at com.sun.xml.wss.impl.policy.verifier.MessagePolicyVerifier.verifyPolicy(MessagePolicyVerifier.java:138)
at com.sun.xml.ws.security.opt.impl.incoming.SecurityRecipient.createMessage(SecurityRecipient.java:1016)
at com.sun.xml.ws.security.opt.impl.incoming.SecurityRecipient.validateMessage(SecurityRecipient.java:252)
at com.sun.xml.wss.jaxws.impl.SecurityTubeBase.verifyInboundMessage(SecurityTubeBase.java:455)
at com.sun.xml.wss.jaxws.impl.SecurityClientTube.processClientResponsePacket(SecurityClientTube.java:434)
at com.sun.xml.wss.jaxws.impl.SecurityClientTube.processResponse(SecurityClientTube.java:362)
at com.sun.xml.ws.api.pipe.Fiber.__doRun(Fiber.java:1074)
at com.sun.xml.ws.api.pipe.Fiber._doRun(Fiber.java:979)
at com.sun.xml.ws.api.pipe.Fiber.doRun(Fiber.java:950)
at com.sun.xml.ws.api.pipe.Fiber.runSync(Fiber.java:825)
at com.sun.xml.ws.client.Stub.process(Stub.java:443)
at com.sun.xml.ws.client.sei.SEIStub.doProcess(SEIStub.java:174)
at com.sun.xml.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.java:119)
at com.sun.xml.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.java:102)
WSSTUBE0025: 验证输入讯息中的安全性时发生错误.
at com.sun.xml.ws.client.sei.SEIStub.invoke(SEIStub.java:154)
at $Proxy41.saySomething(Unknown Source)
at wshttpclient.wcfClient.main(wcfClient.java:31)
成功建置 (总时间:3 秒)
失败的原因是什么呢?这里必须回到 WCF 的设定﹐检视 WCF 下的 wcf.config 中的 Binding 设定
修改前
1: <bindings>
2: <wsHttpBinding>
3: <binding name="Product.wsHttpBinding">
4: <security>
5: <message clientCredentialType="UserName" />
6: </security>
7: </binding>
8: </wsHttpBinding>
9: </bindings>
修改后
1: <bindings>
2: <wsHttpBinding>
3: <binding name="Product.wsHttpBinding">
4: <security>
5: <message clientCredentialType="UserName"
6: negotiateServiceCredential="false"
7: algorithmSuite="Basic128"
8: establishSecurityContext="false" />
9: </security>
10: </binding>
11: </wsHttpBinding>
12: </bindings>
在message标签中negotiateServiceCredential预设是true﹐这个属性和Windows认证有关﹐可以参考MSDN的文章http://msdn.microsoft.com/zh-tw/library/system.servicemodel.messagesecurityoverhttp.negotiateservicecredential.aspx 及http://paper.dic123.com/lunwen_234540427/ ﹐不是很好了解﹐这里Java要呼叫必须将 negotiateServiceCredential 设为 false。
另外algorithmSuite原本默认值为Basic256必须要修改为Basic128﹐理由是什么…我记得曾在一篇国外讨论区看到说Java不支持到256的长度﹐文章已遗落在茫茫网海中﹐这给Java高手去回答吧。establishSecurityContext默认值为true也必须改为false。
注意﹐WCF的web.config一旦有改变﹐原本呼叫此WCF的Client程序必须跟着修改设定。
之后重新执行Build Project﹐那么就直接执行程序。结果﹐这次总算成功~~
网络上有许多人询问Java可否调用wsHttpBinding的WCF呢?根据实作后的经验﹐当然是可以﹐不过为什么会有人有疑惑?首先﹐WCF具有多项认证技术﹐例如Windows认证﹑SQL Membership﹑使用者账号密码…等﹐而在wsHttpBinding其预设是使用Windows认证﹐这在微软各项Solution中不会有什么太大的问题﹐但是像Java就不一定。在这一小节里提到negotiateServiceCredential这个属性必须改为false﹐就是因为这个设定和 Windows 认证有关﹐当然可以在WCF组态设定变更认证方式﹐但一奱更之后就跟随着必须提供凭证﹐例如本次的范例实作﹐这一部分是常被大家所混淆的。
2. 自订使用者账号/密码﹐Java Client 以Glassfish为container
2.1. 建立项目
档案/New Project/Java Web/Web应用程序﹐建立一个Web应用程序。这次就不再示范无认证的WCF﹐直接跑有认证的WCF。专案名称:wsHttpWebAppUseAuth
![\](http://www.it165.net/uploadfile/2012/1226/20121226111633196.png?x-oss-process=image/resize,w_1400/format,webp)
![\](http://www.it165.net/uploadfile/2012/1226/20121226111648129.png?x-oss-process=image/resize,w_1400/format,webp)
下一步之后这里要选择所使用的容器﹐这里选择的是GlassFish Server 3.1.2。
![\](http://www.it165.net/uploadfile/2012/1226/20121226111720521.png?x-oss-process=image/resize,w_1400/format,webp)
按下[完成]后﹐可以看到所产生的项目架构。
![\](http://www.it165.net/uploadfile/2012/1226/20121226111752214.png?x-oss-process=image/resize,w_1400/format,webp)
2.2. 加入 Web Service Client
在项目名称上鼠标右键选择New/Web Service Client。
![\](http://www.it165.net/uploadfile/2012/1226/20121226111824206.png?x-oss-process=image/resize,w_1400/format,webp)
选择WSDL URL并输入具有认证的WCF URL。
WSDL URL:http://10.0.100.101:85/wsHttpUserAuth.webhost/MyProducts.svc?wsdl
[完成]之后在项目中可以看到同样产生了Generated Sources还有服务参照。请记得Generated出来的class档带有中文﹐要先将档案格式转换成UTF-8﹐不然在最后做builder时会失败。
![\](http://www.it165.net/uploadfile/2012/1226/20121226111935596.png?x-oss-process=image/resize,w_1400/format,webp)
2.3. 编辑 Web 服务属性
在Web服务参照之下的MyProducts按下鼠标右键选择[编辑Web服务属性]。
![\](http://www.it165.net/uploadfile/2012/1226/20121226112012580.png?x-oss-process=image/resize,w_1400/format,webp)
到此和之前的Java Application的做法都相同﹐但这次是搭配GlassFish做container﹐因此不需要METRO﹐故接下来直接点[信任库]设定keystore就可以。
![\](http://www.it165.net/uploadfile/2012/1226/20121226112103729.png?x-oss-process=image/resize,w_1400/format,webp)
将位置改选择放置凭证的keystore档案﹐并输入keystore的密码。同样的现在是选不到别名的。请按下[OK]回到前一个画面后也按下[OK]离开Web服务属性的编辑。
![\](http://www.it165.net/uploadfile/2012/1226/20121226112134481.png?x-oss-process=image/resize,w_1400/format,webp)
重新检视项目﹐在原始码套件/META-INF之下多了MyProducts.svc.xml档案。
![\](http://www.it165.net/uploadfile/2012/1226/20121226112218475.png?x-oss-process=image/resize,w_1400/format,webp)
开启MyProducts.svc.dml﹐并修改<sc:TrustStrore>下的Type属性改为JKS。
修改后
1: <wsp1:Policy wsu:Id="WSHttpBinding_IProductServicePolicy">
2: <wsp1:ExactlyOne>
3: <wsp1:All>
4: <sc:TrustStore wspp:visibility="private" storepass="123456" type="JKS" location="C:\Java\Certificate\myTrustStore"/>
5: </wsp1:All>
6: </wsp1:ExactlyOne>
7: </wsp1:Policy>
再重新回到之前的Web服务属性的编辑并进入信任库中﹐这时就已经可以选择别名了。指定别名后回头看MyProducts.svc.xml的设定就会发现已有修改。如果指令很熟悉的人就不需要这么麻烦了﹐直接编写设定档即可。
2.4. 建立 Servlet
在原始码套件上以鼠标右键﹐选择New/Servlet﹐然后输入ClassName和Package﹐按下[完成]。
![\](http://www.it165.net/uploadfile/2012/1226/20121226112310161.png?x-oss-process=image/resize,w_1400/format,webp)
然后在所产生的wsService.java中输入以下程序代码
wsService.java
01.
@WebServlet(name =
"wsService"
, urlPatterns = {
"/wsService"
})
02.
public
class
wsService extends HttpServlet {
03.
/**
04.
* Processes requests for both HTTP
05.
* <code>GET</code> and
06.
* <code>POST</code> methods.
07.
*
08.
* @param request servlet request
09.
* @param response servlet response
10.
* @throws ServletException if a servlet-specific error occurs
11.
* @throws IOException if an I/O error occurs
12.
*/
13.
protected
void
processRequest(HttpServletRequest request, HttpServletResponse response)
14.
throws ServletException, IOException {
15.
response.setContentType(
"text/html;charset=UTF-8"
);
16.
PrintWriter
out
= response.getWriter();
17.
org.tempuri.ProductService client=
null
;
18.
org.tempuri.IProductService port=
null
;
19.
Product product;
20.
try
{
21.
client=
new
org.tempuri.ProductService();
22.
port=client.getWSHttpBindingIProductService();
23.
BindingProvider bp=(BindingProvider)port;
24.
bp.getRequestContext().put(BindingProvider.USERNAME_PROPERTY,
"testman"
);
25.
bp.getRequestContext().put(BindingProvider.PASSWORD_PROPERTY,
"a0987"
);
26.
27.
out
.println(
"<html>"
);
28.
out
.println(
"<head>"
);
29.
out
.println(
"<title>Servlet wsService</title>"
);
30.
out
.println(
"</head>"
);
31.
out
.println(
"<body>"
);
32.
String result=port.saySomething(
"Hello~~"
);
33.
out
.println(
"<h1>Servlet wsService at "
+ result +
"</h1>"
);
34.
out
.println(
"<br/>"
);
35.
36.
product=port.getProduct(
"P-001"
);
37.
out
.println(
"产品编号:"
+product.getNo().getValue()+
"<br>"
);
38.
out
.println(
"产品名称:"
+product.getName().getValue()+
"<br>"
);
39.
out
.println(
"单价:"
+product.getPrice()+
"<br>"
);
40.
out
.println(
"数量:"
+product.getQuantity()+
"<br>"
);
41.
42.
out
.println(
"</body>"
);
43.
out
.println(
"</html>"
);
44.
}
finally
{
45.
out
.close();
46.
}
47.
}
48.
}
将项目builder之后执行应该可以得到如下的结果。
![\](http://www.it165.net/uploadfile/2012/1226/20121226112413573.png?x-oss-process=image/resize,w_1400/format,webp)
在撰写过程﹐花了最多时间是在Java Application 如何调用WCF 的部分﹐目前我所成功的就是 NetBeans + Metro 及 GlassFish﹐至于使用ASIX 2 还没成功过﹐不过如果是 basicHttpBinding 以 Transport 方式加密﹐使用 ASIX 倒是可以﹐这留待下一篇再继续了。