Chƣơng 3 : Bảo mật với dịch vụ RESTful
4.11 Cấu trúc REST API
REST API áp dụng trong hệ thống SMSGateway cũng gồm có ba tầng, tầng lưu trữ dữ liệu và tầng nghiệp vụ vẫn giữ nguyên, còn tầng giao tiếp với người dùng và máy tính được thay bằng REST API, cả người dùng và máy khách đều tương tác cùng một API, được mô tả trong hình sau:
Cấu trúc thiết kế của REST API được mô tả như trong hình 4.5 sau đây. Mục đính của cấu trúc này là tạo ra một cấu trúc chung, trừu tượng và chia thành các môđun nhỏ có thể, với cấu trúc REST API như đã trình bày thì rất phù hợp với nhiều loại hệ thống khác nhau dù sử dụng các công nghệ khác nhau.
Mỗi môđun xuất hiện trong hình 4.5 được mô tả như sau:
Dispatcher là môđun nhận các yêu cầu HTTP từ máy khách, phân tích yêu cầu và chuyển chúng tới môđun controller phù hợp, phụ thuộc vào yêu cầu gửi lên. Nếu yêu cầu gửi lên có dạng /MO/123 thì dispatcher sẽ chuyển yêu cầu này tới MO controller. Sau đó sẽ đợi controler gửi kết quả trở lại, nó sẽ xây dựng một response HTTP với kết quả đó và gửi lại cho máy khách.
Controller là môđun xác định tài nguyên, chúng nhận yêu cầu từ dispatcher và xác
CSDL
Tầng nghiệp vụ REST API
Máy chủ
phương thức có được gửi trong yêu cầu hay không, nếu nội dung của yêu cầu là đúng, v.v… Nếu yêu cầu không đúng nó sẽ gửi lại dispatcher mã lỗi HTTP phù hợp. Hay nói cách khác nó sẽ giúp REST điều khiển các luồng yêu cầu tương tác với tầng nghiệp vụ, nhận kết quả và trả kết quả lại dispatcher.
Xxx_resource cũng là một môđun xác định tài nguyên, một trong số chúng là loại tài nguyên trong hệ thống, chúng điều khiển các luồng nghiệp vụ cơ bản, mỗi luồng là một hành động được áp dụng đối với tài nguyên đó. Chúng được gọi từ
WEB SERVER DISPATCHER MO_CONTROLLER MT_CONTROLLER MO_RESOURCE MT_ RESOURCE BUSSINESS LAYER DATABASE SERVER REST API CACHE RENDER
controller, sau đó tương tác với tầng nghiệp vụ và thực hiện một số hành động với tầng lưu trữ dữ liệu và cuối cùng chúng trả kết quả lại cho controller.
Các lợi ích chính của việc mô tả cấu trúc:
Các môđun được chia nhỏ thành các chức năng nhỏ và mỗi chức năng có sự đặc biệt và mục đính định nghĩa riêng của nó
Nói chung và nói một cách trừu tượng thì khi có môđun dispatcher thì có thể thích ứng thêm được nhiều tài nguyên mới vào một cách dễ dàng.
Rất dễ dàng để thêm một loại tài nguyên mới vào hệ thống bằng cách thêm một controller và một môđun nhiệm vụ cho cho mỗi tài nguyên mới này.
Môđun controller có thể điều khiển được các luồng yêu cầu của HTTP, các luồng lỗi và nhiều loại nội khác nhau và cũng tương tác với tầng nghiệp vụ một cách dễ dàng.
4.12 Các lƣợc đồ tuần tự
Trong phần này sẽ mô tả các lược đồ tuần tự của hai loại tài nguyên đó là MO và MT để mô tả các luồng công việc chính, tức các use case chính.
4.12.1 Lấy danh sách MO bằng thao tác GET
Hình 4.6 chỉ ra luồng khi mà người dùng cần nhận danh sách tài nguyên MO được lưu trữ trong hệ thống, máy khách sẽ gửi một yêu cầu HTTP GET tới URI /MO. Khi đó yêu cầu sẽ được nhận bởi dispatcher, môđun này sẽ phân tích URI và các tham số header nếu có và chuyến yêu cầu đến một controller phù hợp. Trong trường hợp này controller được chọn chính là mo_controller, khi đó controller tiếp tục phân tích các tham số mà dispatcher chuyển tới từ đó đưa ra quyết định triệu gọi tài nguyên phù hợp, trong trường hợp này sẽ gọi tới mo_resource, môđun mo_resource này lại yêu cầu tầng lưu trữ dữ liệu trả lại danh sách dữ liệu mo. Danh sách sẽ được trả về môđun controller và ở môđun này sẽ yêu cầu môđun mo_render (nếu có) xuất một danh sách dữ liệu mo mà có kiểu dữ liệu như máy khách yêu cầu, cuối cùng một kết quả (response) sẽ được môđun mo_render gửi trả lại từ môđun controller cho dispatcher và môđun dispatcher sẽ tạo ra một kết quả HTTP trả lại cho người dùng.
4.12.2 Lấy một MO bằng thao tác GET
Hình 4.7 chi ra luồng công việc khi nguời dùng cần nhận dữ liệu dạng đơn, nghĩa là không phải danh sách tài nguyên, máy khách sẽ gửi một yêu cầu HTTP GET tới URI /MO/1234 với 1234 là định danh xác định tài nguyên mà người dùng muốn nhận. Yêu cầu sẽ được nhận bởi dispatcher, môđun này sẽ trích xuất các tham số trong URI và các yêu cầu trong header ra rồi chuyển chúng tới một controller phù hợp, trong trường hợp này controller được chọn là mo_controller, controller này sẽ phân tích các tham số URI và phương thức từ đó quyết định sẽ gọi môđun cache(nếu có) và môđun này sẽ check nếu mo đã thay đổi kể từ lúc lần cuối cùng máy khách nhận nó. Nếu không có thay đổi gì thì môđun cache sẽ trả về một thông điệp “Not Modified”, thông điệp sẽ được đưa đến môđun controller và môđun này sẽ yêu cầu môđun mo_render xuất ra thông báo phù hợp với loại nội dung yêu cầu của khách hàng. Cuối cùng một kết quả(response) sẽ được trả lại từ môđun controller tới dispatcher và dispatcher sẽ trả lại một kết quả dạng HTTP cho người dùng. Nếu tài nguyên có thay đổi thì controller sẽ quyết định gọi đến môđun mo_recource và môđun này sẽ gọi đến tầng lưu trữ dữ liệu để yêu cầu dữ liệu. Dữ liệu được chuyển đến môđun controller, môđun này yêu cầu mo_render tạo kiểu dữ liệu như người dùng yêu cầu. Cuối cùng một kết quả(response) sẽ được gửi từ môđun controller tới dispatcher và môđun này tạo ra một kết quả HTTP gửi lại cho người dùng.
4.12.3 Tạo mới một MO bằng thao tác POST
Trong hình 4.8 chỉ ra lược đồ của trường hợp này khi người dùng cần thêm mới một tài nguyên mo vào hệ thống. Khi đó máy khách sẽ gửi một yêu cầu HTTP POST tới URI /mo bao gồm cả dữ liệu tài nguyên mới cần phải tạo. Yêu cầu này sẽ được nhận bởi dispatcher, cũng như các trường hợp khác dispatcher sẽ lại phân tích các tham số trong URI, các yêu cầu trong header và nội dung tài nguyên mới đính kèm theo rồi chuyển cho một controller phù hợp, trong trường hợp này controller được chọn là mo_controller, môđun này sẽ gọi môđun mo_resource, môđun này sẽ tạo ra một đối tượng dữ liệu mo đẩy sang tầng lưu trữ dữ liệu ghi vào database, một đối tượng mo được chuyển đến controller yêu cầu môđun mo_render xuất ra dạng dữ liệu như máy khách yêu cầu. Controller sẽ gọi tiếp môđun cache, lưu cache để cache tài nguyên. Cuối cùng, một kết quả được gửi từ controller tới dispatcher từ đó tạo ra một kết quả HTTP và gửi lại cho người dùng.
4.12.4 Update một MO bằng thao tác PUT
Trong hình 4.9 cho ta thấy lược đồ cập nhật một tài nguyên trong hệ thống. Khi đó máy khách gửi một yêu cầu HTTP PUT tới URI /mo/1234 bao gồm cả tài nguyên đính kèm cần phải cập nhật, với mã 1234 là định danh xác định tài nguyên cần cập nhật. Yêu cầu được nhận bởi dispatcher phân tích các tham số cũng như nội dung đính kèm cần cập nhật sau đó chuyển sang một môđun controller phù hợp, trong trường hợp này controller được chọn là mo_controller, controller này sẽ gọi môđun mo_resource, môđun này sẽ cập nhật đối tượng mo và ghi vào database. Một đối tượng mo đã được cập nhật được chuyển tới môđun controller yêu cầu mo_render tạo ra một thông điệp như máy khách yêu cầu. Cuối cùng một kết quả được gửi từ controller tới dispatcher từ đây sẽ tạo ra một kết quả dạng HTTP trả lại cho người dùng.
4.12.5 Xóa MO bằng thao tác DELETE
Trong hệ thống SMSGateway không cho phép xóa tài nguyên nên thao tác DELETE sẽ không được ứng dụng trong luận văn này.
4.12.6 Tìm kiếm MO
Tìm kiếm tài nguyên trong hệ thống cũng giống như lấy danh sách tài nguyên(GET), chỉ khác một điều là tìm kiếm tài nguyên thì theo các tiêu chí tìm kiếm, dựa vào các tiêu chi tìm kiếm mà máy khách yêu cầu. Hình 4.10 chỉ ra lược đồ tìm kiếm tài nguyên theo các tiêu chí yêu cầu của người dùng, khi người dùng muốn nhận danh sách các tài nguyên mo thì máy khách sẽ gửi một yêu cầu dạng HTTP GET tới URI /mo. Yêu cầu này được nhận bởi dispatcher, cũng như các môđun khác, dispatcher sẽ phân tích các tham số trong yêu cầu cùng với các tiêu chí ràng buộc kèm theo trong yêu cầu rồi chuyển sang một môđun phù hợp, trong trường hợp này môđun được chọn là mo_controller, controller này sẽ tiếp tục phân tích URI và phương thức yêu cầu rồi từ đó đưa ra quyết định triệu gọi môđun mo_resource, môđun này sẽ lấy tài nguyên từ tầng lưu trữ dữ liệu. Một danh sách tài nguyên được truyền trả lại cho controller và yêu cầu mo_render tạo ra kiểu dữ liệu phù hợp với yêu cầu của máy khách. Cuối cùng một kết quả được gửi từ controller tới dispatcher và gửi tới máy khách dạng kết quả HTTP.
Chƣơng 5- Prototype
Chương này sẽ giới thiệu các công nghệ được sử dụng trong prototype để phát triển ứng dụng và lý do tại sao chúng được chọn, cũng trong chương này cũng mô tả các tiến trình phát triển code và cung cấp một vài đoạn code để chỉ ra quá trình thực hiện các yêu cầu REST API.
5.1 Giới thiệu
Các khái niệm kỹ thuật của REST khá là trừu tượng nếu là lý thuyết, bởi vậy cách tốt hơn để hiểu REST là tạo một số prototype nhỏ và riêng biệt và có thể hiểu REST qua các prototype đó để tránh các vấn đề không liên quan và chỉ tập trung vào các khái niệm thực sự mà ta cần kiểm tra. Phát triển các prototype theo xu hướng công nghệ không phải là mới, tuy nhiên tác giả đã sử dụng chúng để xây dựng các prototype như đã đề cập ở trên. Công nghệ javabean được sử dụng như là tầng lưu trữ dữ liệu, tomcat là một trong các webserver khá phổ biến và dễ sử dụng, ngoài ra nó khá nhẽ nhàng để có thể phát triển RESTful web service.
5.2 Một số đoạn code mô tả thực thi API
GET
Hình 5.1 chỉ ra đoạn code chương trình xử lý vấn để GET khi người dùng yêu cầu từ URI: http://localhost:8080/RFMOReceiver/restful/todos/MT?user_id=84932347686, sau khi dispatcher xử xý sẽ triệu gọi phương thức phù hợp với yêu cầu là GetMTByID() và trả về kết quả là một danh sách MT nếu thành công, danh sách rỗng nếu thất bại.
@GET
@Path("MT")
@Produces(MediaType.APPLICATION_XML)
public static List<ServiceData> GetMTByID(@QueryParam("user_id") String user_id)throws Exception{
return DBTool.getMTByID(user_id); }
Nếu thành công kết quả sẽ trả về một danh sách MT dạng dữ liệu XML như hình 5.2:
Hình 5.3 chỉ ra đoạn code chương trình xử lý vấn đề GET khi mà ứng dụng yêu cầu trả lại kết quả là kiểu dữ liệu JSON.
Và kết quả trả về dạng dữ liệu JSON như trong hình 5.4:
@GET
@Path("MT")
@Produces(MediaType.APPLICATION_JSON)
public static List<ServiceData> GetMTByID(@QueryParam("user_id") String user_id)throws Exception{
return DBTool.getMTByID(user_id); }
public static List<ServiceData> getMTByID(String user_id) throws Exception { Connection conn = null;
PreparedStatement preStt = null; ResultSet rs = null;
List<ServiceData> vt=new ArrayList<ServiceData>(); try {
conn = getConnection();
String sql ="SELECT * FROM SEND_LOG WHERE user_id='"+user_id+"'";
preStt = conn.prepareStatement(sql); rs=preStt.executeQuery();
ServiceData data =null; while (rs.next()){
data = new ServiceData();
data.setUserId(rs.getString("user_id")); data.setMessage(rs.getString("message")); data.setServiceId(rs.getString("service_id")); data.setCommandCode(rs.getString("command_code")); data.setMsgType(rs.getString("message_type")); data.setRequestId(rs.getString("request_id")); data.setTotalMsg(rs.getString("total_msg")); data.setMsgIndex(rs.getString("msg_index")); data.setIsMore(rs.getString("is_more")); data.setContentType(rs.getString("content_type")); vt.add(data); } return vt;
} catch (Exception ex) { throw ex;
} finally {
closeConnection(conn, preStt);
} }
POST
Hình 5.5 chỉ ra đoạn code chương trình xử lý vấn đề POST khi người dùng yêu cầu từ form dữ liệu, sau khi dispatcher xử lý sẽ triệu gọi phương thức phù hợp với yêu cầu là
messageReceiver() và trả về kết quả là 1 nếu thành công, -1 nếu thất bại.
@POST
@Path("form")
@Produces(MediaType.TEXT_HTML)
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public String messageReceiver(@FormParam("User_ID") String
User_ID,@FormParam("Service_ID") String Service_ID,@FormParam("Command_Code") String Command_Code,@FormParam("Message") String
Message,@FormParam("Request_ID") String Request_ID){ String MobileOperator=Util.getMobileOper(User_ID); String retvalue="-1";
ServiceData serviceData = new ServiceData(); serviceData.setRequestId(Request_ID); serviceData.setMobileOper(MobileOperator); serviceData.setUserId(User_ID); serviceData.setServiceId(Service_ID); serviceData.setCommandCode(Command_Code); serviceData.setMessage(Message); serviceData.setTimeStamp(Util.createTimestampFull()); try {
DBTool.createReceiveQueue(serviceData); retvalue="1"; } catch (Exception e) { retvalue="-1"; e.printStackTrace(); } finally { } return retvalue; }
Hình 5.5: Chức năng POST dữ liệu
{"serviceData":[{"commandCode":"LA","contentType":"0","isMore":"0","message":" Day la chuong trình spam thu cua dau so 6x66 - dang
pro","msgIndex":"1","msgType":"0","requestId":"111111111","serviceId":"6766"," totalMsg":"1","userId":"84932347686"},{"commandCode":"LA","contentType":"0","i sMore":"0","message":"Day la chuong trình spam thu cua dau so 6x66 - dang pro","msgIndex":"1","msgType":"0","requestId":"111111111","serviceId":"6766"," totalMsg":"1","userId":"84932347686"},
5.3 Test API với Google Chrome
Simple REST Client (SRC) là một gói mở rộng của Google Chrome để giúp người dùng xây dựng tùy chọn các yêu cầu HTTP trực tiếp để có thể kiểm tra trực tiếp được RESTful webservice của ta.
Sử dụng SRC rất đơn giản, chỉ cần lựa chọn URL, phương thức kiểm tra (là POST, GET, PUT hay DELETE) điền các giá trị header và body nếu cần thiết , tiến hành dùng Google Chrome kiểm tra dịch vụ với đầy đủ các yêu cầu như trên qua hình kiểm tra như sau:
POST
Với các tham số như trong hình 5.4 thì URL là địa chỉ chạy dịch vụ, method là POST để nhận các tham số từ form dữ liệu trong phần body bên dưới, sau khi người dùng kick nút
send request thì dữ liệu trong form sẽ được ánh xạ vào các tham số tương ứng và truyền vào hàm theo đường dẫn phân loại restful/todos/form. Nếu thành công sẽ có trạng thái (status code) là 200 và kết quả trả về là 1 trong phần body.
GET
Với các tham số như trong hình 5.5 thì URL là địa chỉ chạy dịch vụ, method là GET và nhận các tham số được truyền vào từ URL, sau khi người dùng kick nút send request thì dữ liệu trong URL sẽ được ánh xạ vào các tham số tương ứng và truyền vào hàm theo đường dẫn phân loại restful/todos/MT. Nếu thành công sẽ có trạng thái (status code) là 200 và kết quả trả về là 1 danh sách MT trong phần body
5.4 Test API với giao diện ứng dụng
POST
Hinh 5.8 chỉ ra giao diện ứng dụng đơn giản được dùng để kiểm tra dịch vụ với chức năng POST dữ liệu
Cũng như kiểm tra chức năng POST trong Google Chrome, dữ liệu được truyền vào theo form với các tham số tương ứng, nhấn nút invoke để thực hiện, kết quả trả về 1 là thành công, -1 là thất bại.
GET
Hinh 5.9 chỉ ra giao diện ứng dụng đơn giản được dùng để kiểm tra dịch vụ với chức năng GET dữ liệu, cột mã yêu cầu liệt kê danh sách các mã yêu cầu cùng với các liên kết đến tài nguyên khác, khi kick vào một trong các liên kết thì có nghĩa là thực hiện chức năng GET dữ liệu, với các tham số truyền theo kiểu query và cho kết quả như hình 5.10.
KẾT LUẬN
Hầu hết các hệ thống dựa trên nền web ngày nay có thể được truy cập từ bất kỳ nơi đâu trên thế giới thông qua web. Trong project demo này, dịch vụ RESTful được thực thi trong phần thiết kế RESTful API, và kết quả nhận được thật sự rất thỏa đáng. Do đó có thể nói rằng các hệ thống dựa trên nền web có nhiều yêu cầu phức tạp tuy nhiên có thể dễ dàng thực hiện với REST.
Sau một thời gian tìm hiểu và thiết kế REST API, thì tác giả có thể nói rằng kiểu kiến trúc REST là một giải pháp rất thích hợp đối với các hệ thống trên nền web. REST có thể