2015-05-16 15 views
6

Hãy tưởng tượng một vấn đề: Tôi có dịch vụ REST, được triển khai bằng cách sử dụng công nghệ Java/MySQL/Spring và HTTP/JSON. Các khách hàng của dịch vụ REST là các ứng dụng di động. Vì vậy, có thể ai đó sẽ dịch ngược mã và sẽ nhận được API của dịch vụ REST. (Có, mã bị làm mờ, v.v.).Dịch vụ REST và điều kiện chủng tộc

Sự cố: có phương thức POST để gửi tiền cho người dùng khác của ứng dụng. Tôi lo lắng rằng ai đó có thể lấy API, viết bot và thực hiện yêu cầu POST này 500 hoặc 5.000 hoặc thậm chí 50.000 lần mỗi giây. Kết quả là, anh ta có thể gửi nhiều tiền hơn anh ta thực sự có, bởi vì nếu 1000 yêu cầu được xử lý đồng thời thì kiểm tra số dư có thể là thành công cho tất cả 1000 yêu cầu, tuy nhiên số tiền thực trên tài khoản có thể chỉ đủ, cho phép nói, 50 yêu cầu.

Vì vậy, về cơ bản, nó giống với điều kiện "đua" chuẩn với nhiều chuỗi. Vấn đề là, tôi có nhiều máy chủ và chúng không liên quan với nhau. Vì vậy, 300 yêu cầu có thể đến máy chủ A, 300 yêu cầu có thể đến máy chủ B và các yêu cầu còn lại có thể đến máy chủ C.

Ý tưởng hay nhất là sử dụng cái gì đó như "CHỌN ... CẬP NHẬT" và đồng bộ hóa ở cấp cơ sở dữ liệu. Tuy nhiên, tôi muốn xem xét các giải pháp khác.

Bất kỳ ý tưởng hoặc đề xuất nào?

+1

Đừng bạn có thông tin đăng nhập, phiên và tokens chống CSRF để đảm bảo rằng các yêu cầu chuyển giao chỉ có thể đến từ một đăng nhập, người dùng được ủy quyền? Bạn không có kiểm tra ủy quyền để đảm bảo rằng chỉ yêu cầu chuyển tiền của riêng mình được tôn trọng? Bạn không có một ứng dụng ba tầng vì vậy giao diện người dùng chỉ xử lý lớp trình bày và logic nghiệp vụ được xử lý đằng sau hậu trường? Bạn không có khả năng phân đoạn xử lý cho các yêu cầu như vậy (cùng một nhà tài trợ, cùng một mục tiêu, vv) tất cả trong một máy chủ logic nghiệp vụ duy nhất? – atk

+0

Cách đăng nhập/phiên có thể ngăn điều này? Nếu ai đó hack API, anh ta có thể hack đăng nhập/phiên và gửi yêu cầu này bằng cơ chế xác thực hợp lệ. Nhân tiện, xác thực là dựa trên mã thông báo, tức là OAuth. Đó là một dịch vụ REST và tôi không sử dụng thẻ csrf. – user3489820

+0

Nếu bạn có người đăng nhập, và hạn chế mọi thứ để mọi người chỉ có thể tiêu tiền của riêng mình, thì bạn ngăn chặn các cuộc tấn công mà kẻ tấn công sẽ tiêu tiền của người khác. Nó không ngăn cản họ rút tiền từ tài khoản của chính họ, nhưng nó ngăn họ rút tiền * tài khoản * của người khác. – atk

Trả lời

2

Bạn có một vài lựa chọn:

  1. Dựa vào thực hiện ACID của cơ sở dữ liệu (MySQL trong trường hợp của bạn). Giả sử bạn đang sử dụng công cụ InnoDB, bạn cần chọn mức cô lập giao dịch phù hợp (SET TRANSACTION syntax) kết hợp với cơ chế khóa đọc đúng (SELECT ... FOR UPDATE and SELECT ... LOCK IN SHARE MODE Locking Reads). Bạn cần phải hiểu những khái niệm này để có thể lựa chọn đúng. Nó có thể là có thể chỉ đơn giản bằng cách sử dụng mức độ cô lập đúng sẽ đã ngăn chặn các điều kiện chủng tộc ngay cả khi không có khóa đọc. Các nhược điểm là bạn đang giao dịch hết tính nhất quán cho khả năng mở rộng và buộc ứng dụng của bạn vào cơ sở dữ liệu RDBMS do đó sẽ khó khăn hơn cho bạn để chuyển sang NoSQL chẳng hạn.

  2. Phân hủy phần cuối của bạn thành tầng web và tầng dịch vụ (tùy chọn được đề xuất bởi atk trong các nhận xét). Điều này sẽ cho phép bạn mở rộng các cá thể tầng web một cách độc lập trong khi vẫn giữ một cá thể tầng dịch vụ duy nhất. Có một cá thể tầng dịch vụ duy nhất làm cho nó có thể sử dụng các cơ chế đồng bộ hóa Java như các khối synchronised hoặc ReadWriteLock. Mặc dù giải pháp này sẽ làm việc tôi sẽ không khuyên bạn nên nó vì nó làm giảm khả năng mở rộng của tầng dịch vụ của bạn.

  3. Đây là tùy chọn nâng cao của tùy chọn trước đó. Bạn có thể sử dụng Distributed lock manager thay vì các cơ chế đồng bộ hóa java tích hợp sẵn. Nó sẽ cho phép bạn mở rộng tầng web và tầng dịch vụ của bạn một cách độc lập.

+0

Cảm ơn bạn đã trả lời :) Trình quản lý khóa phân tán có thể là một ý tưởng tồi cho khả năng mở rộng. Tôi sẽ cố gắng chơi với RDBMS. – user3489820

0

Đối với các ứng dụng quan trọng, tốt nhất là nên có nhiều cấp độ cơ chế khóa.

"CHỌN ... CHO CẬP NHẬT" là một cách hay để làm như vậy, nhưng chúng khá tốn kém và khi bạn cố gắng kích hoạt điều này với Charles, bạn sẽ thấy rằng API trên của bạn sẽ bị ảnh hưởng, và cơ chế đơn giản đó sẽ làm tê liệt cơ sở hạ tầng của bạn khá dễ dàng, giống như một sự kiện DDoS.

Triển khai nó ở lớp Load Balancer/Proxy trước, để tăng tốc N số yêu cầu trong một khoảng thời gian xác định từ một địa chỉ IP duy nhất.

Sau đó, áp dụng khóa lớp bộ nhớ cache dùng chung, trong đó tất cả các hộp của bạn sẽ đồng bộ hóa trên các phím nhất định tùy thuộc vào giao dịch quan trọng nào bạn muốn khóa. Ví dụ: bạn có thể sử dụng chức năng Redis GETSET hoặc INCR để đặt cờ một cách nguyên tử, trước khi nhập đường dẫn mã quan trọng. Từ chối bất cứ điều gì khác một cách nhanh chóng để tránh những diễn viên xấu từ giữ CPU/bộ nhớ.

Bạn cũng có thể thực hiện những thứ như bộ nhớ cache APC (trước khi nhấn cụm Redis/Memcache) để thực hiện khóa tương tự trên cơ sở mỗi hộp. Điều này nhanh hơn vì không có độ trễ mạng nào liên quan.

Trên đây là cần thiết trên đầu trang của việc sử dụng "SELECT ... FOR UPDATE"

Các vấn đề liên quan