Thoát ra: NHẬN hoặc POST?


433

Câu hỏi này không phải là về thời điểm sử dụng GET hoặc POST nói chung; đó là về một trong những đề xuất để xử lý đăng xuất khỏi ứng dụng web. Tôi đã tìm thấy nhiều thông tin về sự khác biệt giữa GET và POST theo nghĩa chung, nhưng tôi không tìm thấy câu trả lời chắc chắn cho kịch bản cụ thể này.

Là một người theo chủ nghĩa thực dụng, tôi có xu hướng sử dụng GET, bởi vì việc thực hiện nó đơn giản hơn POST; chỉ cần thả một liên kết đơn giản và bạn đã hoàn tất. Điều này dường như là trường hợp với phần lớn các trang web tôi có thể nghĩ ra, ít nhất là từ đỉnh đầu của tôi. Ngay cả Stack Overflow cũng xử lý đăng xuất bằng GET.

Điều khiến tôi ngần ngại là lập luận (mặc dù cũ) rằng một số trang tăng tốc / proxy trang web bộ đệm trước bằng cách truy cập và truy xuất mọi liên kết họ tìm thấy trong trang, vì vậy người dùng nhận được phản hồi nhanh hơn khi cô nhấp vào chúng. Tôi không chắc liệu điều này có còn áp dụng hay không, nhưng nếu đây là trường hợp, thì theo lý thuyết, người dùng có một trong những máy gia tốc này sẽ bị đuổi khỏi ứng dụng ngay khi cô đăng nhập, bởi vì máy gia tốc của cô sẽ tìm và lấy ra đăng xuất liên kết ngay cả khi cô ấy không bao giờ nhấp vào nó.

Tất cả mọi thứ tôi đã đọc cho đến nay đều đề xuất rằng POST nên được sử dụng cho "hành động phá hoại", trong khi các hành động không làm thay đổi trạng thái bên trong của truy vấn giống như ứng dụng và nên được xử lý bằng GET . Dựa trên điều này, câu hỏi thực sự ở đây là:

Việc đăng xuất khỏi ứng dụng có được coi là hành động phá hoại / nó có làm thay đổi trạng thái bên trong của ứng dụng không?


Chà, giả sử bạn đang truy cập trang web lần đầu tiên và liên kết đăng xuất không có mặt, bạn sẽ đăng xuất khi đăng nhập. Sẽ ổn thôi sau khi bạn đăng nhập lần thứ hai, vì url đăng xuất đã được lưu vào bộ nhớ cache. Nhưng người ta có thể cho rằng bất kỳ máy gia tốc tốt nào cũng có thể lọc ra hầu hết các url đăng xuất.
HyperCas

2
HyperCas, máy gia tốc lọc ra các URL đăng xuất là một lý thuyết tôi đang xem xét và một trong những lý do khiến tôi quyết định đăng câu hỏi. Tôi cảm thấy hơi miễn cưỡng khi chỉ tin vào logic máy gia tốc và một ngày nọ, có một người dùng với máy gia tốc tào lao phàn nàn rằng cô ấy không thể đăng nhập. Bạn có biết nếu họ tuân theo một tiêu chuẩn, hoặc nếu tiêu chuẩn đó tồn tại?
Daniel Liuzzi

Bất kỳ Trình tăng tốc nào tự động gửi biểu mẫu (ví dụ) sẽ là phần mềm độc hại IMO ... hoàn toàn phi logic khi nghĩ rằng trình tăng tốc sẽ tự động gửi biểu mẫu. Hãy tưởng tượng bạn ghé thăm Google. Làm thế nào nó có thể gửi mẫu tìm kiếm? Không ai có thể giải thích cho Phần mềm độc hại vì nó quá khó đoán và không tuân theo các quy tắc.
Alex

3
@AlexW - Tôi nghĩ bạn đã hiểu nhầm câu hỏi của tôi. Kịch bản máy gia tốc mà tôi đề xuất là hiển thị một vấn đề có thể xảy ra khi sử dụng GET, không phải POST, do đó sẽ không có hình thức để đăng, chỉ có các liên kết đơn giản mà máy gia tốc sẽ không gặp vấn đề gì sau đây.
Daniel Liuzzi

1
Tôi nhận ra tôi đã quá muộn cho việc này, nhưng Alex, đó không phải là điều Daniel đang hỏi. Anh ta nói rằng nếu người dùng nhấp vào liên kết đăng xuất và trình tăng tốc trả về trang đăng xuất được lưu trong bộ nhớ cache mà không nhấn ứng dụng thì người dùng sẽ vẫn đăng nhập. Không có gì phải làm với phần mềm độc hại, mặc dù FYI kiểm tra chuỗi User-Agent sẽ không khắc phục được bất cứ điều gì
Rob Grant

Câu trả lời:


474

Sử dụng POST.

Trong năm 2010, sử dụng GETcó lẽ là một câu trả lời chấp nhận được. Nhưng hôm nay (năm 2013), các trình duyệt sẽ tìm nạp trước các trang mà họ "nghĩ" bạn sẽ truy cập tiếp theo.

Đây là một trong những nhà phát triển StackOverflow nói về vấn đề này trên twitter:

Tôi muốn cảm ơn ngân hàng của mình vì đã đăng xuất yêu cầu GET và nhóm Chrome đã tìm nạp URL tiện dụng.- Nick Craver ( @Nick_Craver ) ngày 29 tháng 1 năm 2013

sự thật thú vị: StackOverflow được sử dụng để xử lý đăng xuất thông qua GET, nhưng không còn nữa.


2
Cảm ơn về bản cập nhật này, Dave. Tôi thậm chí còn không nhận thấy SO đã chuyển đăng xuất của họ sang POST và tôi thực sự không có manh mối nào Chrome đi kèm với tính năng tìm nạp trước được tích hợp sẵn. Cuối cùng, twit mà bạn trích dẫn có thể chưa bao giờ đưa ra một ví dụ tốt hơn cho vấn đề tôi mô tả trong câu hỏi của tôi và xác nhận sự nghi ngờ của tôi. Tôi đang bỏ phiếu cho câu trả lời của bạn và làm cho nó là câu trả lời được chấp nhận.
Daniel Liuzzi

4
Trong trình duyệt của tôi, đăng xuất Stackoverflow trông giống như <li> <a href="https://stackoverflow.com/users/logout"> đăng xuất </a> </ li> là một GET, không phải là POST
Boatcoder 22/213

9
@ Mark0978, nhấp vào liên kết.
David Murdoch

2
Hấp dẫn. Đó có lẽ là một trong những tính năng yêu thích nhất của tôi, đăng xuất sau đó hỏi tôi có chắc không. Đoán rằng nó giữ cho việc tìm nạp trước không đăng xuất bạn, nhưng Amazon, Ebay và Gmail đều sử dụng GET để đăng xuất mà không có trang lừa ở giữa những gì người dùng được thông báo là đăng xuất và sự kiện đăng xuất thực tế. Tôi sẽ tưởng tượng rằng ở giữa trang sẽ dẫn đến rất nhiều người lầm tưởng rằng họ đã đăng xuất. Các vấn đề với điều đó trên SO là tối thiểu, không liên quan đến tiền và 99% mọi thứ đều được công khai.
thuyền viên

7
@Red Theo tiêu chuẩn HTTP / 1.1, đây là lỗi của máy chủ, không phải của trình duyệt. GET dự kiến ​​sẽ không có tác dụng phụ ở phía máy chủ. Tiêu chuẩn thậm chí còn nói rằng "người dùng không yêu cầu các tác dụng phụ, do đó không thể chịu trách nhiệm cho họ".
Eyuelt

45

Trong REST không nên có phiên, do đó không có gì để hủy. Một khách hàng REST xác thực trên mọi yêu cầu. Đăng nhập hoặc thoát ra, đó chỉ là ảo ảnh.

Những gì bạn thực sự yêu cầu là trình duyệt nên tiếp tục gửi thông tin xác thực trên mỗi yêu cầu.

Có thể cho rằng, nếu ứng dụng của bạn tạo ra ảo ảnh khi đăng nhập, thì bạn sẽ có thể "đăng xuất" bằng cách sử dụng javascript. Không yêu cầu chuyến đi khứ hồi.


Luận án Fielding - Phần 5.1.3

mỗi yêu cầu từ máy khách đến máy chủ phải chứa tất cả thông tin cần thiết để hiểu yêu cầu và không thể tận dụng bất kỳ bối cảnh được lưu trữ nào trên máy chủ. Do đó, trạng thái phiên được giữ hoàn toàn trên máy khách


1
Tôi thực sự không nhận thức được điều này. Sau đó, tôi đoán ứng dụng của mình sẽ không quá RESTful, vì tôi đang sử dụng ASP.NET MVC với FormsAuthentication và nó phụ thuộc vào các phiên ...
Daniel Liuzzi

18
nhưng trong thực tế, thông tin đăng nhập được giữ trong một cookie được đánh dấu bằng httponlythuộc tính để ngăn ngừa một số rủi ro xss, điều đó có nghĩa là nó chỉ có thể được đặt lại từ máy chủ (viết tắt là xóa thủ công cookie)
Remus Rusanu

6
'Thủ công' như trong phần người dùng sẽ chuyển đến cài đặt Trình duyệt và chọn tùy chọn 'Xóa cookie'. Hầu như không phải là một cách chấp nhận để 'đăng xuất' một trang web.
Remus Rusanu

1
@Remus Ahhh, làm thế nào trình duyệt web lừng lẫy khiến việc viết các ứng dụng web trở nên đau đớn như vậy.
Darrel Miller

1
@DarrelMiller có tuy nhiên việc không thu hồi JWT ở phía máy chủ là một lỗ hổng bảo mật. Ngay cả khi mã thông báo không được lưu trữ trên máy chủ, chúng vẫn nên được đưa vào danh sách đen khi người dùng đăng xuất / thay đổi mật khẩu / thay đổi vai trò / thoát / vv để tránh lạm dụng (ít nhất là cho đến khi chúng hết hạn).
java-nghiện602

38

Một cách GETcó thể bị lạm dụng ở đây là một người (có lẽ là đối thủ cạnh tranh :) đã đặt thẻ hình ảnh với src="<your logout link>"MỌI NƠI trên internet và nếu người dùng trang web của bạn tình cờ thấy trang đó, anh ta sẽ vô tình đăng xuất.


4
Không, điều này không đúng. Liên kết đăng xuất sẽ chỉ hoạt động nếu dữ liệu cookie chính xác được gửi, mà nó sẽ không đến từ một tên miền khác. Và ngay cả khi id phiên được lưu trữ trong url, điều đó sẽ không hoạt động vì những thay đổi này cho mỗi phiên.
Richard H

4
Wow, tôi chưa bao giờ nghĩ về điều đó! Vì vậy, một lý do khác để không sử dụng GET và một lý do khác khiến tôi không hiểu tại sao mọi người lại làm như vậy. Chết tiệt, bây giờ tôi đã cố gắng đưa một stackoverflow.com/users/logout "hình ảnh" vào bài đăng của mình và xem điều gì xảy ra :-D
Daniel Liuzzi

24
src = là một yêu cầu trình duyệt đơn giản, nó không đến từ phía máy chủ, mà từ máy khách. Nó mang tất cả các cookie và đến từ IP người dùng. Đó là lý do tại sao pixel theo dõi quảng cáo hoạt động. Cách duy nhất để xác định khai thác như vậy sẽ là kiểm tra người giới thiệu.
raveren

12
SuperLogout.com thực hiện chính xác điều đó (tải /logoutURL trong các hình ảnh ẩn) và nó hoạt động.
Dan Dascalescu

9
re: SuperLogout ... Tôi không biết tại sao tôi lại nhấp vào đó.
MI Wright

21

Để chính xác, GET / POST (hoặc các động từ khác) là các hành động trên một số tài nguyên (được giải quyết bằng URL) - vì vậy, nói chung là về trạng thái của tài nguyên chứ không phải về trạng thái ứng dụng như vậy. Vì vậy, trong tinh thần thực sự, bạn nên có một URL như [host name]\[user name]\session, sau đó 'XÓA' sẽ là động từ chính xác cho hành động đăng xuất.

Sử dụng [host name]\bla bla\logoutnhư URL không thực sự là một cách đầy đủ REST (IMO), vậy tại sao lại tranh luận về việc sử dụng GET / POST chính xác trên nó?

Tất nhiên, tôi cũng sử dụng GET để một url đăng xuất trong các ứng dụng của mình :-)


2
Trong trường hợp đó, sau đó tôi sẽ lập luận rằng việc có phần [tên người dùng] trong URL có vẻ không cần thiết, vì người dùng luôn đăng xuất từ ​​(tức là XÓA) phiên của chính họ ; không bao giờ người dùng khác ':-)
Daniel Liuzzi

1
Không thực sự - chúng tôi đang nói rằng phiên là một tài nguyên và chúng tôi muốn xóa nó. Vì vậy, để giải quyết thống nhất bất kỳ phiên nào, bạn cần phải có tên người dùng như một phần của URL. Đối số của bạn cũng tốt như việc phát hành hành động PUT trên [thư viện ảnh] \ hình ảnh có nghĩa là bạn đang thêm vào ảnh của mình (có sẵn tại [thư viện ảnh] \ [tên người dùng] \ hình ảnh). Các tài nguyên khác nhau phải được giải quyết một cách rõ ràng, không thể có bất kỳ nhân chứng nào trong đó. Trang web có thể cho phép người dùng khác thêm hình ảnh vào thư viện của bạn - đó sẽ là một phần của kiểm soát truy cập giống như bạn có thể có một siêu người dùng có thể giết chết bất kỳ phiên nào của bất kỳ ai.
VinayC

1
Về mặt triết học, bạn có thể gọi các phiên và ảnh là 'tài nguyên', nhưng thực tế tôi sẽ không đối xử với họ như vậy. Phiên luôn luôn bị giới hạn về bản chất đối với người dùng hiện tại (do đó có tên Phiên) và, ít nhất là trong ASP.NET, không có cách nào để truy cập các phiên của người dùng khác. Ngay cả nhà phát triển ứng dụng cũng không có cách trực tiếp liệt kê tất cả các phiên hoạt động hoặc phương tiện để giết từng phiên riêng lẻ. Bạn có thể khởi động lại ứng dụng để hủy tất cả các phiên (InProc), nhưng tôi sẽ không gọi điều khiển truy cập đó. Bỏ qua các URL, câu hỏi vẫn còn: NHẬN hoặc POST?
Daniel Liuzzi

Tài nguyên, do đó địa chỉ của nó (URL) là một phần quan trọng của REST. Vì vậy, nếu bạn chọn URL như tôi đã nói, XÓA trở thành từ chính xác - không phải GET hoặc POST. Ngoài ra, ngay cả khi bạn giới hạn bản thân với ASP.NET, bạn luôn có thể có nhà cung cấp trạng thái tùy chỉnh có thể cung cấp cho bạn cách liệt kê qua các phiên và giết các phiên khác nếu cần. Đối với các phiên in-box ngoài luồng, một số câu đố trong global.asax sẽ cung cấp cho bạn chức năng. Đây thực sự là một câu hỏi về việc liệu chức năng đó có cần thiết hay không. Đối với các nhu cầu không thường xuyên, mọi người có xu hướng khởi động lại trang web để đuổi mọi người ra khỏi trang web.
VinayC

Điều này có ý nghĩa nhất đối với tôi. Cung cấp cho web api một lộ trình phiên và gọi XÓA trên đó. Hãy là ../session hoặc ../session/c Hiện tại. Cảm ơn @VinayC
Simon Hooper

16

Đăng xuất không làm gì cho chính ứng dụng. Nó thay đổi trạng thái của người dùng liên quan đến ứng dụng. Trong trường hợp này, có vẻ như câu hỏi của bạn dựa nhiều hơn vào cách lệnh nên được bắt đầu từ người dùng để bắt đầu hành động này. Vì đây không phải là "hành động phá hoại", chắc chắn phiên bị bỏ hoặc hủy nhưng ứng dụng hoặc dữ liệu của bạn không bị thay đổi, nên không thể cho phép cả hai phương thức bắt đầu quy trình đăng xuất. Bài đăng nên được sử dụng bởi bất kỳ hành động nào do người dùng khởi tạo (ví dụ: người dùng nhấp vào "Đăng xuất"), trong khi get có thể được dành riêng cho đăng xuất do ứng dụng khởi tạo (ví dụ: một ngoại lệ phát hiện sự xâm nhập của người dùng tiềm năng chuyển hướng đến trang đăng nhập với đăng xuất GET ).


Hấp dẫn; Tôi chưa bao giờ nghĩ về nó theo cách này. +1.
strager

Điều này có thể hình dung tùy thuộc vào ứng dụng (một số loại hành vi "xóa tầng"), nhưng bạn đã đúng.
Andres Jaan Tack

@JoelEtherton Cảm ơn bạn Joel, tôi đã đọc những câu trả lời tự hỏi khi nào tôi sẽ đến đúng. :)
Kirill Fuchs

4
Thật khó hiểu vì đăng xuất thay đổi trạng thái. POST là động từ thay đổi trạng thái. GET là để có được dữ liệu phi trạng thái. Thật khó hiểu vì chúng tôi hy vọng các yêu cầu POST có tải trọng. Như đã lưu ý dưới đây, XÓA sẽ chính xác nhất trên một đối tượng phiên.
Michael Cole

1
@MichaelCole: Tôi đồng ý với sự thể hiện khó khăn giữa POST và GET. Tuy nhiên, tôi không đồng ý với việc sử dụng động từ XÓA. XÓA là để xử lý tài nguyên và phiên không phải là tài nguyên theo nghĩa này. Hãy xem xét, nếu bạn có thể XÓA nó, thì bạn cũng có thể PUT nó.
Joel Etherton

16

Xin chào theo quan điểm của tôi, khi bạn đăng nhập, bạn kiểm tra tên người dùng / mật khẩu và nếu chúng phù hợp với bạn, hãy tạo mã thông báo đăng nhập.

TẠO mã thông báo => phương thức POST

Khi bạn đang đăng xuất, bạn phân phối mã thông báo, vì vậy với tôi phương thức hợp lý nhất phải là XÓA

XÓA mã thông báo => phương thức XÓA


4
Góc thú vị.
Drumbeg

1
Tôi sử dụng phương pháp đó trong các ứng dụng Spring Boot REST của mình.
Vui lòng_Dont_Bully_Me_SO_Lords

1
đúng ngữ nghĩa Tôi đồng ý ...
DAG

1

Kịch bản của bộ nhớ đệm trước là một điều thú vị. Nhưng tôi đoán rằng nếu nhiều trang web không bao giờ lo lắng về điều này thì có lẽ bạn cũng không nên.

Hoặc có lẽ liên kết có thể được thực hiện trong javascript?

Chỉnh sửa: Theo tôi hiểu, về mặt kỹ thuật, một GET nên dành cho các yêu cầu chỉ đọc, không thay đổi trạng thái ứng dụng. POST nên dành cho các yêu cầu ghi / chỉnh sửa thay đổi trạng thái. Tuy nhiên, các vấn đề ứng dụng khác có thể thích NHẬN hơn POST cho một số yêu cầu thay đổi trạng thái và tôi không nghĩ có vấn đề gì với việc này.


Cảm ơn. Các DB nhà nước sẽ không được thay đổi, nhưng phiên bang sẽ. Vấn đề duy nhất tôi thấy là vấn đề tôi đã đề cập trong câu hỏi, về việc người dùng bị đuổi. Không phá hoại nhưng khá khó chịu. Tôi thường đi theo câu thần chú "nếu các ông lớn làm điều đó, thì nó cũng sẽ ổn thôi". Tôi chỉ muốn biết ý kiến ​​của người khác về vấn đề này.
Daniel Liuzzi

0

Chà, nếu bạn để ứng dụng web của bạn từ bỏ phiên thông qua tập lệnh đăng xuất, bạn thường không cần. Thông thường có một biến phiên duy nhất cho phiên bạn muốn từ bỏ.


Bạn có thể xây dựng "kịch bản đăng xuất"? Tôi không chắc chắn nếu bạn đề cập đến việc thiết lập hết hạn cookie (điều này không loại bỏ sự cần thiết phải cho phép người dùng đăng xuất thủ công.)
Daniel Liuzzi

Một kịch bản đăng xuất sẽ kết thúc phiên của người dùng (thực tế: trình duyệt) gọi nó. Trong ASP.net, phiên là một đối tượng phía máy chủ có thể bị bỏ qua. PHP có một hệ thống tương tự. Vì trình duyệt đó gọi tập lệnh kết thúc phiên, nên nó đã biết kết thúc nào, loại bỏ sự cần thiết của các biến POST hoặc GET.
Cướp

1
Vâng, tôi có được bạn bây giờ. Tôi đã có sẵn tập lệnh, cụ thể là FormsAuthentication.SignOut (), nhưng câu hỏi của tôi là về cách gọi tập lệnh, như trong GET hoặc POST.
Daniel Liuzzi

Oh bạn có url trong một hình thức? Sẽ không có vấn đề gì nếu bạn không chuyển bất kỳ thông tin nào cho nó. Điều tồi tệ nhất có thể xảy ra là ai đó tự mở kịch bản, tự đăng xuất. Tôi thậm chí sẽ không biến nó thành một trường biểu mẫu nếu không cần thiết, một liên kết đến kịch bản cũng sẽ hoạt động. Nếu bạn gửi thông tin đến tập lệnh, có lẽ tôi sẽ gửi POST, để không hiển thị bất kỳ thông tin nào cho người dùng (trừ khi họ xem nguồn trang) và nếu họ làm mới, họ sẽ nhận được cảnh báo từ trình duyệt của họ (trang đã hết hạn), có thể là mong muốn.
Cướp

0

Gần đây tôi đang thực hiện một dự án tôi sử dụng GET để đăng xuất Dưới đây là mã trong Nodejs Express và nó hoạt động hoàn toàn tốt

bộ định tuyến của bạn

const express = require("express");
router.get("/signout", signout);

control.js của bạn

exports.signout  = (req, res) => {
        res.clearCookie('t'); //clearing cookie, which is 
            //assign to the user during sign in.          
            res.json({message : 'Signout success'});   
        };

-2

Tôi không thấy cách đăng xuất (giảm quyền người dùng) là một hành động mang tính mô tả. Đó là bởi vì hành động "đăng xuất" chỉ nên có sẵn cho người dùng đã đăng nhập nếu không nó sẽ bị lỗi thời.

Một chuỗi được tạo ngẫu nhiên có trong cookie trình duyệt của bạn là tất cả đại diện cho phiên người dùng của bạn. Có rất nhiều cách để phá hủy nó để đăng xuất hiệu quả chỉ là một dịch vụ cho khách truy cập của bạn.


2
wgettrong chế độ nhện với cookie phiên chính xác trên wiki riêng là điều tôi thực sự phải làm một lần. Tất nhiên, một trong những URL được thu thập thông tin đầu tiên là /logout.
Helgi

5
Hãy thử truy cập SuperLogout.com để xem các yêu cầu GET phá hủy đối với /logoutcác trang thực sự như thế nào. Ví dụ: bạn sẽ phải đăng nhập lại vào Gmail, đăng nhập lại trò chuyện, tìm vị trí của bạn trong bất kỳ cuộc hội thoại Hangouts nào bạn đã cuộn, v.v. - và điều này chỉ dành cho Google.com.
Dan Dascalescu
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.