Sử dụng React để gọi đến Cross Domain Server
Bài đăng này đã không được cập nhật trong 3 năm
Sử dụng React để gọi đến Cross Domain Server
Intro
Cùng tìm hiểu về Same Origin Policy, CORS và cách gọi từ ReactJS qua bài blog sau nhé
Same Origin Policy
Trong khoa học máy tính, same-origin policy là một khái niệm quan trọng trong mô hình bảo mật thông tin của web application. Dưới chính sách này, các trình duyệt web chỉ cho phép các đoạn script ở trang web thứ nhất truy cập vào dữ liệu ở trang web thứ hai (AJAX request), chỉ khi mà hai trang đó có cùng nguồn (same-origin). Việc cùng nguồn gốc hay không được định nghĩa thông qua một nhóm các yếu tố: đó là URI, hostname, port number. Lợi ích của việc này chính là ngăn cản các script độc hại ở một trang có thể có được các thông tin nhạy cảm ở một trang khác thông qua việc xử lý trên DOM (Document Object Model) (from Wikipedia). Tức là:
- Tức là ta có thể nhúng ảnh, video, css, javascript từ các trang khác vào
- Nhưng bị hạn chế khi thực hiện AJAX lên các domain khác (tưởng tượng sẽ nguy hiểm như thế nào nếu một đoạn script độc hại lấy thông tin của trang rồi POST thông tin đó lên một domain khác do hacker quản lý)
Chúng ta cùng xem qua hình sau để thấy rõ được trong những trường hợp URI nào thì ta có thể gọi được khi ta truy cập từ trang http://www.example.com/dir/page.html
Cần chú ý rằng Same Origin Policy là do các trình duyệt hỗ trợ chứ không phải là do phần nào khác. Các trình duyệt phổ biến hiện nay như: Firefox, Chrome, Safari,... đều có hỗ trợ tính năng này.
Tuy rằng nâng cao bảo mật nhưng chính sách này vẫn có một số hạn chế. Chẳng hạn sẽ có những nguồn tài nguyên mà mục đích được đưa lên trên Internet là để cho tất cả mọi người cùng truy cập một cách công khai, có thể kể đến là Google Web Font.
Trong bài này ta sẽ nói về một trong những phương pháp để làm lỏng chính sách này, đó là : Cross Origin Resource Sharing
Cross Origin Resource Sharing (CORS)
Để thực hiện được CORS trên các trình duyệt (CORS được hỗ trợ bởi hầu hết các trình duyệt phổ biến hiện nay: bắt đầu từ Firefox 3.5, Safari 4, và Chrome 3. Internet Explorer 10 hiện tại cũng đã hỗ trợ hoàn toàn) thì ta cần sử dụng thêm các header Origin request header và Access-Control-Allow-Origin header. Cùng xem qua ví dụ đơn giản sau để hiểu thêm.
- Chẳng hạn, một đoạn script từ trang http://example.com dùng AJAX để truy cập vào tài nguyên ở trang http://otherdomain.com/some-resource.
- Nếu là code JQuery ta sẽ viết như sau
$.ajax({
url: "http://otherdomain.com/some-resource",
type: "GET",
success: function(data) {
},
error: function(e) {
console.log(e);
}
});
- Lúc đó trình duyệt sẽ tự động sinh thêm header Origin có giá trị là domain của chúng ta. Header này chỉ được sinh ra khi đích đến của request là cross-origin.
GET http://otherdomain.com/some-resource/ HTTP/1.1
Referer: http://example.com/my-app/
Origin: http://example.com
- Nếu là một server chấp nhận CORS thì server sẽ trả về response như sau:
Access-Control-Allow-Origin: http://example.com
Content-Type: application/json
- Access-Control-Allow-Origin header chỉ định domain được phép truy cập. Trình duyệt kiểm tra, nếu trùng với domain hiện tại (http://example.com) sẽ cho phép xử lý response trả về.
Đối với các request phức tạp hơn, hãy tham khảo ở http://techblog.constantcontact.com/software-development/using-cors-for-cross-domain-ajax-requests/ và http://www.html5rocks.com/en/tutorials/cors/
CORS in ReactJS
Vậy CORS ở ReactJS sẽ như thế nào ? Có thể nói, nó hoàn toàn tương tự như đối với các thư viện khác. Hãy cùng làm thử thí nghiệm sau nhé:
- Ta sẽ chỉnh sửa lại tutorial Comment Box trên trang chủ tại https://facebook.github.io/react/docs/tutorial.html
- clone project về tại địa chỉ: https://github.com/reactjs/react-tutorial/
- Ở đây mình sẽ sử dụng backend là python. Ta chạy server:
[vigo@ubuntu ~/lab/reactjs/react-tutorial (tmp)]$ python server.py
* Running on http://127.0.0.1:3000/ (Press CTRL+C to quit)
- Truy cập vào http://127.0.0.1:3000/ và post thử 1 comment, everything seems OK !
- Trong ví dụ sử dụng JQuery AJAX để post dữ liệu:
$.ajax({
url: this.props.url,
dataType: 'json',
type: 'POST',
data: comment,
success: function(data) {
this.setState({data: data});
}.bind(this),
error: function(xhr, status, err) {
console.error(this.props.url, status, err.toString());
}.bind(this)
});
React.render(
<CommentBox url="comments.json" pollInterval={2000} />,
document.getElementById('content')
);
Nên địa chỉ đầy đủ của file comments.json
sẽ là http://locahost:3000/comments.json
. Như vậy cả phần URI, hostname, và port đều match nên request được chấp nhận.
Hãy thử giả lập 1 cross-site request nhé. Ta làm như sau:
- sửa lại file
server.py
server lắng nghe trên toàn bộ dải IP
if __name__ == '__main__':
app.run(host='0.0.0.0', port=3000)
- Ta sửa lại file
/etc/hosts
để giả lập 2 domain. Từ domainmydomain.com
ta sẽ request đến domainyourdomain.com
.
127.0.0.1 mydomain.com
127.0.0.1 yourdomain.com
- Sửa lại đường link đến file
comments.json
trong fileexample.js
React.render(
<CommentBox url="http://yourdomain.com:3000/comments.json" pollInterval={2000} />,
document.getElementById('content')
);
- Truy câp vào http://mydomain.com:3000/ và BANG !
-
Vậy là chúng ta request đến một domain khác với domain mà script đang chạy.
-
Chrome đã tự động thêm Origin header cho chúng ta nhưng server đơn giản là chưa chấp nhận cross site request, do dó ta không load được file
comments.json
cũng như post tạo comment mới. -
Để giải quyết tình huống này, ta cần sửa lại code server để chấp nhận CORS. Đối với Flask (frramework mà ví dụ sử dụng), đã có sẵn extension
Flask-CORS
(http://flask-cors.readthedocs.org/en/latest/)
sudo pip install flask-cors
- Sửa lại file
server.py
thêm decorator@cross_origin
để chấp nhận CORS:
import json
from flask import Flask, Response, request
from flask_cors import CORS, cross_origin
app = Flask(__name__, static_url_path='', static_folder='public')
app.add_url_rule('/', 'root', lambda: app.send_static_file('index.html'))
cors = CORS(app)
@cross_origin()
@app.route('/comments.json', methods=['GET', 'POST'])
def comments_handler():
with open('comments.json', 'r') as file:
comments = json.loads(file.read())
- Thử lại với
http://mydomain.com:3000/
, LGTM!
End
Đối với ReactJS, việc gọi CORS cũng hoàn toàn tương tự như đối với các framework khác,
vấn đề nằm ở vị trí thực hiện gọi mà thôi (componentDidMount
hoặc tại handle xử lý dữ liệu)
That's all.
Tham khảo
All rights reserved