2015年7月11日星期六

JBoss_009:Fuse 快速上手之九:secure-rest

环境:JBoss Fuse 6.2.0

1. 学习重点
(1)开发 JAX-WS Web Service,实现CRUD。
(2)使用 CXF 的 JaxWsProxyFactoryBean 创建 Client 端 proxy,调用远程 Web Service。
(3)在配置文档中增加安全。

2. blueprint.xml 
<?xml version="1.0" encoding="UTF-8"?>
<!--
    JBoss, Home of Professional Open Source
    Copyright 2014, Red Hat, Inc. and/or its affiliates, and individual
    contributors by the @authors tag. See the copyright.txt in the
    distribution for a full listing of individual contributors.

    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at
    http://www.apache.org/licenses/LICENSE-2.0
    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.
-->
<!--
   This is the OSGi Blueprint XML file defining the CXF JAX-RS beans.  Because the file is in the
   OSGI-INF/blueprint directory inside our JAR, it will be automatically activated as soon as the artifact is installed.

   The root element for any OSGi Blueprint file is 'blueprint' - you also see the namespace definitions for both the Blueprint
   and the CXF JAX-RS namespaces.
-->
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:jaxrs="http://cxf.apache.org/blueprint/jaxrs"
           xsi:schemaLocation="
      http://www.osgi.org/xmlns/blueprint/v1.0.0 http://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd
      http://cxf.apache.org/blueprint/jaxrs http://cxf.apache.org/schemas/blueprint/jaxrs.xsd">

    <!--
      The <jaxrs:server/> element is used to set up our JAX-RS services.  It defines:
      - the server's address, '/securecrm', relative to the default CXF servlet URI
        with the default settings, the server will be running on 'http://localhost:8181/cxf/securecrm'
      - a list of service beans
        in this example, we refer to another bean defined in this Blueprint XML file with a <ref/> element
      - a list of interceptors that are being applied to the inbound request
        in this example, we refer to another bean defined in this Blueprint XML file with a <ref/> element
    -->
    <jaxrs:server id="customerService" address="/securecrm">
        <jaxrs:serviceBeans>
            <ref component-id="customerSvc"/>
        </jaxrs:serviceBeans>
        <jaxrs:providers>
            <ref component-id="authenticationFilter"/>
        </jaxrs:providers>
    </jaxrs:server>

    <!--
       We are using the OSGi Blueprint XML syntax to define a bean that we referred to in our JAX-RS server setup before.
       This bean carries a set of JAX-RS annotations that allow its methods to be mapped to incoming requests.
    -->
    <bean id="customerSvc" class="io.fabric8.quickstarts.rest.secure.CustomerService"/>

    <!--
       We are using the OSGi Blueprint XML syntax to define a bean that we referred to in our JAX-RS server setup before.
       This bean is a CXF interceptor that ensures that a request has been authenticated before allowing it to pass. For
       performing the authentication, this interceptor will delegate to JAAS, using the realm name 'karaf'.  This will allow
       it to reuse the same authentication mechanism that is being used to secure other ESB facilities, such as the remote
       SSH shell and the webconsole.
    -->

    <bean id="authenticationFilter" class="org.apache.cxf.jaxrs.security.JAASAuthenticationFilter">
        <!-- Name of the JAAS Context -->
        <property name="contextName" value="karaf"/>
    </bean>
</blueprint>

3. CrmSecureTest.java
/*
 * JBoss, Home of Professional Open Source
 * Copyright 2014, Red Hat, Inc. and/or its affiliates, and individual
 * contributors by the @authors tag. See the copyright.txt in the
 * distribution for a full listing of individual contributors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.fabric8.quickstarts.rest.secure;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.UsernamePasswordCredentials;
import org.apache.commons.httpclient.auth.AuthScheme;
import org.apache.commons.httpclient.auth.AuthScope;
import org.apache.commons.httpclient.auth.BasicScheme;
import org.apache.commons.httpclient.methods.FileRequestEntity;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.PutMethod;
import org.apache.commons.httpclient.methods.RequestEntity;
import org.junit.Before;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;

import static org.junit.Assert.*;

/**
 * The client class has a main method that accesses a few of the resources defined in our JAX-RS example using
 * the Apache Commons HttpClient classes.
 */
public final class CrmSecureTest {

    public static final String CUSTOMER_TEST_URL = "http://localhost:8181/cxf/securecrm/customerservice/customers/123";
    public static final String PRODUCT_ORDER_TEST_URL =
        "http://localhost:8181/cxf/securecrm/customerservice/orders/223/products/323";
    public static final String CUSTOMER_SERVICE_URL = "http://localhost:8181/cxf/securecrm/customerservice/customers";
    private static final Logger LOG = LoggerFactory.getLogger(CrmSecureTest.class);
    private URL url;
    private InputStream in;
    private HttpClient httpClient;
    private AuthScheme scheme;

    /*
     * Just a simple helper method to read bytes from an InputStream and return the String representation.
     */
    private static String getStringFromInputStream(InputStream in) throws Exception {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        int c = 0;
        while ((c = in.read()) != -1) {
            bos.write(c);
        }
        in.close();
        bos.close();
        return bos.toString();
    }

    @Before
    public void beforeTest() {
        // Now we need to use the basic authentication to send the request
        httpClient = new HttpClient();
        httpClient.getState().setCredentials(AuthScope.ANY, new UsernamePasswordCredentials("admin", "admin"));
        // Use basic authentication
        scheme = new BasicScheme();

    }

    /**
     * HTTP GET http://localhost:8181/cxf/crm/customerservice/customers/123
     * returns the XML document representing customer 123
     *
     * On the server side, it matches the CustomerService's getCustomer() method
     *
     * @throws Exception
     */
    @Test
    public void getCustomerTest() throws Exception {
        String res = "";

        LOG.info("============================================");
        LOG.info("Sent HTTP GET request to query customer info");

        GetMethod get = new GetMethod(CUSTOMER_TEST_URL);
        get.getHostAuthState().setAuthScheme(scheme);
        try {
            httpClient.executeMethod(get);
            res = get.getResponseBodyAsString();
            LOG.info(res);
        } catch (IOException e) {
            LOG.error("Error connecting to {}", CUSTOMER_SERVICE_URL);
            LOG.error("You should build the 'rest' quick start and deploy it to a local Fabric8 before running this test");
            LOG.error("Please read the README.md file in 'rest' quick start root");
            fail("Connection error");
        } finally {
            get.releaseConnection();
        }
        assertTrue(res.contains("123"));
    }

    /**
     * HTTP GET http://localhost:8181/cxf/crm/customerservice/orders/223/products/323
     * returns the XML document representing product 323 in order 223
     *
     * On the server side, it matches the Order's getProduct() method
     *
     * @throws Exception
     */
    @Test
    public void getProductOrderTest() throws Exception {
        String res = "";

        LOG.info("============================================");
        LOG.info("Sent HTTP GET request to query sub resource product info");
        GetMethod get = new GetMethod(PRODUCT_ORDER_TEST_URL);

        get.getHostAuthState().setAuthScheme(scheme);
        try {
            httpClient.executeMethod(get);
            res = get.getResponseBodyAsString();
            LOG.info(res);
        } catch (IOException e) {
            LOG.error("Error connecting to {}", PRODUCT_ORDER_TEST_URL);
            LOG.error("You should build the 'rest' quick start and deploy it to a local Fabric8 before running this test");
            LOG.error("Please read the README.md file in 'rest' quick start root");
            fail("Connection error");
        } finally {
            get.releaseConnection();
        }
        assertTrue(res.contains("product 323"));
    }

    /**
     * HTTP POST http://localhost:8181/cxf/crm/customerservice/customers is used to upload the contents of
     * the add_customer.xml file to add a new customer to the system.
     *
     * On the server side, it matches the CustomerService's addCustomer() method
     *
     * @throws Exception
     */
    @Test
    public void postCustomerTest() throws IOException {
        LOG.info("============================================");
        LOG.info("Sent HTTP POST request to add customer");
        String inputFile = this.getClass().getResource("/add_customer.xml").getFile();
        File input = new File(inputFile);
        PostMethod post = new PostMethod(CUSTOMER_SERVICE_URL);
        post.getHostAuthState().setAuthScheme(scheme);
        post.addRequestHeader("Accept", "text/xml");
        RequestEntity entity = new FileRequestEntity(input, "text/xml; charset=ISO-8859-1");
        post.setRequestEntity(entity);

        String res = "";

        try {
            int result = httpClient.executeMethod(post);
            LOG.info("Response status code: " + result);
            LOG.info("Response body: ");
            res = post.getResponseBodyAsString();
            LOG.info(res);
        } catch (IOException e) {
            LOG.error("Error connecting to {}", CUSTOMER_SERVICE_URL);
            LOG.error("You should build the 'rest' quick start and deploy it to a local Fabric8 before running this test");
            LOG.error("Please read the README.md file in 'rest' quick start root");
            fail("Connection error");
        } finally {
            // Release current connection to the connection pool once you are
            // done
            post.releaseConnection();
        }
        assertTrue(res.contains("Jack"));

    }

    /**
     * HTTP PUT http://localhost:8181/cxf/crm/customerservice/customers is used to upload the contents of
     * the update_customer.xml file to update the customer information for customer 123.
     *
     * On the server side, it matches the CustomerService's updateCustomer() method
     *
     * @throws Exception
     */
    @Test
    public void putCustomerTest() throws IOException {

        LOG.info("============================================");
        LOG.info("Sent HTTP PUT request to update customer info");

        String inputFile = this.getClass().getResource("/update_customer.xml").getFile();
        File input = new File(inputFile);
        PutMethod put = new PutMethod(CUSTOMER_SERVICE_URL);
        put.getHostAuthState().setAuthScheme(scheme);
        RequestEntity entity = new FileRequestEntity(input, "text/xml; charset=ISO-8859-1");
        put.setRequestEntity(entity);

        int result = 0;
        try {
            result = httpClient.executeMethod(put);
            LOG.info("Response status code: " + result);
            LOG.info("Response body: ");
            LOG.info(put.getResponseBodyAsString());
        } catch (IOException e) {
            LOG.error("Error connecting to {}", CUSTOMER_SERVICE_URL);
            LOG.error("You should build the 'rest' quick start and deploy it to a local Fabric8 before running this test");
            LOG.error("Please read the README.md file in 'rest' quick start root");
            fail("Connection error");
        } finally {
            // Release current connection to the connection pool once you are
            // done
            put.releaseConnection();
        }

        assertEquals(result, 200);
    }

} 

5. 编译、部署、卸载
(1)cd /Users/maping/Redhat/fuse/jboss-fuse-6.2.0.redhat-133/quickstarts/cxf/secure-rest
(2)mvn clean install
(3)./fuse (确认 etc/user.properties 文件中 admin 用户已经打开注释)
(4)osgi:install -s mvn:org.jboss.quickstarts.fuse/cxf-secure-rest/6.2.0.redhat-133
(5)osgi:list
(6)http://localhost:8181/cxf/ 
(7)http://localhost:8181/cxf/securecrm/
(8)http://localhost:8181/cxf/securecrm/customerservice/customers/123
(9)mvn -Ptest
(10)Create a customer
curl --basic -u admin:admin -X POST -T src/test/resources/add_customer.xml -H "Content-Type: text/xml" http://localhost:8181/cxf/securecrm/customerservice/customers
(11)Retrieve the customer instance with id 123
curl --basic -u admin:admin http://localhost:8181/cxf/securecrm/customerservice/customers/123
(12)Update the customer instance with id 123
curl --basic -u admin:admin -X PUT -T src/test/resources/update_customer.xml -H "Content-Type: text/xml" http://localhost:8181/cxf/securecrm/customerservice/customers
(13)Delete the customer instance with id 123
curl --basic -u admin:admin -X DELETE http://localhost:8181/cxf/securecrm/customerservice/customers/123
(14)osgi:uninstall <id>

7. 管理用户密码
增加用户到 JAAS realm 有两种方法:
(1)修改 etc/users.properties 增加一行,比如:
myuser = mysecretpassword,roles
(2)使用 jaas: 命令
jaas:manage --realm karaf --index 1
jaas:useradd myuser mysecretpassword
jaas:update

没有评论: