FormsAuthentication.SignOut () không đăng xuất người dùng


143

Đập đầu tôi chống lại điều này một chút quá lâu. Làm cách nào để ngăn người dùng duyệt các trang của trang sau khi họ đã đăng xuất bằng FormsAuthentication.SignOut? Tôi hy vọng điều này sẽ làm điều đó:

FormsAuthentication.SignOut();
Session.Abandon();
FormsAuthentication.RedirectToLoginPage();

Nhưng nó không. Nếu tôi nhập URL trực tiếp, tôi vẫn có thể duyệt đến trang. Tôi đã không sử dụng bảo mật của chính bạn trong một thời gian để tôi quên tại sao điều này không hoạt động.


Mã đó cũng ổn vì ... nhấp vào lại trong trình duyệt không truy cập lại trang trên máy chủ, nó chỉ tải lại phiên bản được lưu trong bộ nhớ cache cục bộ của trang. Tất cả các giải pháp dưới đây dường như bỏ qua thực tế đó và không thực sự làm gì hơn là bạn đang làm ở đây. Nói tóm lại ... không có câu trả lời cho câu hỏi này sẽ giải quyết việc người dùng nhìn vào bộ đệm của họ cho đến nay Tôi không tin có một cách để xóa bộ nhớ cache trong ... js hoặc với hướng dẫn bên máy chủ.
Chiến tranh

Câu trả lời này cung cấp một số cách để kiểm tra, đặc biệt là nếu trang web của bạn đang thất bại trong các bài kiểm tra PEN: stackoverflow.com/questions/31565632/ chủ
Tyler S. Loeper

Câu trả lời:


211

Người dùng vẫn có thể duyệt trang web của bạn vì cookie không bị xóa khi bạn gọi FormsAuthentication.SignOut()và chúng được xác thực theo mỗi yêu cầu mới. Trong tài liệu MS nói rằng cookie sẽ bị xóa nhưng họ không, lỗi? Nó hoàn toàn giống với Session.Abandon(), cookie vẫn còn đó.

Bạn nên thay đổi mã của bạn thành này:

FormsAuthentication.SignOut();
Session.Abandon();

// clear authentication cookie
HttpCookie cookie1 = new HttpCookie(FormsAuthentication.FormsCookieName, "");
cookie1.Expires = DateTime.Now.AddYears(-1);
Response.Cookies.Add(cookie1);

// clear session cookie (not necessary for your current problem but i would recommend you do it anyway)
SessionStateSection sessionStateSection = (SessionStateSection)WebConfigurationManager.GetSection("system.web/sessionState");
HttpCookie cookie2 = new HttpCookie(sessionStateSection.CookieName, "");
cookie2.Expires = DateTime.Now.AddYears(-1);
Response.Cookies.Add(cookie2);

FormsAuthentication.RedirectToLoginPage();

HttpCookielà trong System.Webkhông gian tên. Tài liệu tham khảo MSDN .


18
Điều này làm việc cho tôi. Tuy nhiên, điều đáng chú ý là nếu thuộc tính Miền đã được đặt trên cookie FormsAuthentication khi đăng nhập, thì cũng cần phải đặt khi hết hạn cookie khi bạn đăng xuất
Phil Hale

8
Cũng đừng quên cookie1.HttpOnly = true;
Dmitry Zaets

6
Đây có vẻ là một giải pháp tốt hơn đối với tôi: Feedback.Cookies [FormsAuthentication.FormsCookieName] .Expires = DateTime.Now.AddDays (-1);
Randy H.

7
@RandyH. Ghi đè cookie FormsAuthentication hiện tại bằng cookie trống mới đảm bảo rằng ngay cả khi khách hàng quay lại đồng hồ hệ thống của họ, họ vẫn sẽ không thể truy xuất bất kỳ dữ liệu người dùng nào từ cookie.
Tri Q Tran

9
Ai đó có thể kết hợp tất cả những ý kiến ​​vào câu trả lời?
David

22

Sử dụng hai trong số các bài đăng trên của x64igor và Phil Haselden đã giải quyết điều này:

1. x64igor đã đưa ra ví dụ để thực hiện Đăng xuất:

  • Trước tiên, bạn cần Xóa Cookie xác thực và Cookie phiên bằng cách gửi lại các cookie trống trong phần Phản hồi khi đăng xuất.

    public ActionResult LogOff()
    {
        FormsAuthentication.SignOut();
        Session.Clear();  // This may not be needed -- but can't hurt
        Session.Abandon();
    
        // Clear authentication cookie
        HttpCookie rFormsCookie = new HttpCookie( FormsAuthentication.FormsCookieName, "" );
        rFormsCookie.Expires = DateTime.Now.AddYears( -1 );
        Response.Cookies.Add( rFormsCookie );
    
        // Clear session cookie 
        HttpCookie rSessionCookie = new HttpCookie( "ASP.NET_SessionId", "" );
        rSessionCookie.Expires = DateTime.Now.AddYears( -1 );
        Response.Cookies.Add( rSessionCookie );

2. Phil Haselden đã đưa ra ví dụ ở trên về cách ngăn chặn bộ đệm ẩn sau khi đăng xuất:

  • Bạn cần Vô hiệu hóa Bộ đệm trên Phía Máy khách thông qua Phản hồi .

        // Invalidate the Cache on the Client Side
        Response.Cache.SetCacheability( HttpCacheability.NoCache );
        Response.Cache.SetNoStore();
    
        // Redirect to the Home Page (that should be intercepted and redirected to the Login Page first)
        return RedirectToAction( "Index", "Home" ); 
    }

1
Lãng phí cả ngày tại nơi làm việc để giải quyết vấn đề này. Sau khi đăng nhập, nút đăng xuất bắt đầu gọi hành động sai trong bộ điều khiển (Đăng nhập không đăng xuất). Cảm ơn bạn, điều này đã giải quyết vấn đề. Môi trường phát triển: ASP.NET 4.51 MVC 5.1
Ako

1
Câu trả lời tốt! Gợi ý khiêm tốn: Sử dụng biểu mẫu để xóa cookie phiên x64igor được sử dụng : SessionStateSection sessionStateSection = (SessionStateSection)WebConfigurationManager.GetSection("system.web/sessionState"); HttpCookie sessionCookie = new HttpCookie(sessionStateSection.CookieName, "");. Nói chung, tên cookie phiên là không "ASP.NET_SessionId".
seebcakes

20

Âm thanh với tôi như bạn không có phần ủy quyền web.config của bạn được thiết lập đúng trong. Xem ví dụ bên dưới.

<authentication mode="Forms">
  <forms name="MyCookie" loginUrl="Login.aspx" protection="All" timeout="90" slidingExpiration="true"></forms>
</authentication>
<authorization>
  <deny users="?" />
</authorization>

Đây là một giải pháp đơn giản hơn nhiều, tôi sẽ đánh dấu đây là một câu trả lời. Khi tôi có một phiên bản mã làm việc trên các máy chủ khác nhau, tôi không cần phải thiết lập các thuộc tính bổ sung mà bạn đã thêm ở đây và trên các phiên bản khác tôi đã làm. Vì vậy, sửa đổi mã không nên là giải pháp chính xác, sửa đổi cấu hình là tốt hơn.
Vladimir Bozic

Theo mặc định, trượtExpirst được đặt thành true ( msdn.microsoft.com/l Library / 1d3t3c61 ( v = vs.100 ) .aspx ). Và điều này cuối cùng sẽ dẫn đến việc cookie trở nên không hợp lệ sau x phút như được đặt trong thời gian chờ - và không phải khi người dùng đăng xuất thông qua SignOut (). Vì vậy, điều này sẽ không dẫn đến hành vi mong muốn để đăng xuất người dùng bằng FormsAuthentication. Xin hãy sửa tôi nếu tôi sai.
OlafW

12

Chìa khóa ở đây là bạn nói "Nếu tôi nhập URL trực tiếp ...".

Theo mặc định, dưới dạng xác thực, trình duyệt lưu trữ các trang cho người dùng. Vì vậy, chọn URL trực tiếp từ danh sách thả xuống của hộp địa chỉ trình duyệt hoặc nhập URL, CÓ THỂ lấy trang từ bộ đệm của trình duyệt và không bao giờ quay lại máy chủ để kiểm tra xác thực / ủy quyền. Giải pháp cho vấn đề này là ngăn bộ nhớ đệm phía máy khách trong sự kiện Page_Load của mỗi trang hoặc trong OnLoad () của trang cơ sở của bạn:

Response.Cache.SetCacheability(HttpCacheability.NoCache);

Bạn cũng có thể muốn gọi:

Response.Cache.SetNoStore();

11

Tôi đã đấu tranh với điều này trước đây quá.

Đây là một sự tương tự cho những gì dường như đang diễn ra ... Một khách truy cập mới, Joe, đến trang web và đăng nhập thông qua trang đăng nhập bằng FormsAuthentication. ASP.NET tạo ra một danh tính mới cho Joe và cho anh ta một cái bánh quy. Cookie đó giống như chìa khóa nhà, và miễn là Joe trở lại với chìa khóa đó, anh ta có thể mở khóa. Mỗi khách truy cập được cung cấp một khóa mới và một khóa mới để sử dụng.

Khi FormsAuthentication.SignOut()được gọi, hệ thống sẽ bảo Joe mất chìa khóa. Thông thường, điều này hoạt động, vì Joe không còn chìa khóa, anh ta không thể vào được.

Tuy nhiên, nếu Joe quay lại và không có chìa khóa bị mất, anh ta sẽ quay lại!

Từ những gì tôi có thể nói, không có cách nào để bảo ASP.NET thay đổi khóa trên cánh cửa!

Cách tôi có thể sống với điều này là nhớ tên của Joe trong biến Phiên. Khi anh ấy đăng xuất, tôi từ bỏ Phiên để tôi không còn tên anh ấy nữa. Sau này, để kiểm tra xem anh ấy có được phép vào hay không, tôi chỉ cần so sánh Danh tính của anh ấy. Hãy xem phiên hiện tại có gì và nếu chúng không khớp, anh ấy không phải là khách truy cập hợp lệ.

Nói tóm lại, đối với một trang web, KHÔNG dựa vào User.Identity.IsAuthenticatedmà không kiểm tra các biến Phiên của bạn!


8
+ 1, tôi nghĩ rằng đây được gọi là 'Tấn công lại cookie'. Có một bài viết về những hạn chế của FormsAuthentication.SignOut: support.microsoft.com/kb/900111
Dmitry

3
Đối với bất cứ ai tìm kiếm để theo liên kết ở trên, nó đã chết. Bạn có thể thử sử dụng WaybackMachine để lấy một bản sao của trang này tại đây, nhưng nó NGAY LẬP TỨC cố gắng chuyển hướng người dùng. web.archive.org/web/20171128133421/https:// cướp
killa-byte

7

Sau rất nhiều tìm kiếm cuối cùng điều này đã làm việc cho tôi. Tôi hy vọng nó sẽ giúp.

public ActionResult LogOff()
{
    AuthenticationManager.SignOut();
    HttpContext.User = new GenericPrincipal(new GenericIdentity(string.Empty), null);
    return RedirectToAction("Index", "Home");
}

<li class="page-scroll">@Html.ActionLink("Log off", "LogOff", "Account")</li>

Tôi đã phát triển các ứng dụng web trong nhiều năm qua trong PHP. Vì vậy, tôi chưa quen với MVC ... Tôi thừa nhận tôi yêu nó, NHƯNG ai có thể nghĩ điều gì đó đơn giản như việc đăng xuất một ai đó sẽ khó khăn đến vậy? Tôi đã thử mọi kịch bản khác trên trang này và đây là kịch bản duy nhất hoạt động. Cảm ơn vì đăng!
Anthony Griggs

6

Điều này làm việc cho tôi

public virtual ActionResult LogOff()
    {
        FormsAuthentication.SignOut();
        foreach (var cookie in Request.Cookies.AllKeys)
        {
            Request.Cookies.Remove(cookie);
        }
        foreach (var cookie in Response.Cookies.AllKeys)
        {
            Response.Cookies.Remove(cookie);
        }
        return RedirectToAction(MVC.Home.Index());
    }

3

Mã bạn đã đăng có vẻ như sẽ xóa chính xác mã thông báo xác thực mẫu, vì vậy có thể các thư mục / trang được đề cập không thực sự được bảo vệ.

Bạn đã xác nhận rằng các trang không thể được truy cập trước khi đăng nhập đã xảy ra?

Bạn có thể đăng các cài đặt web.config và mã đăng nhập mà bạn đang sử dụng không?


3

Tôi đã viết một lớp cơ sở cho tất cả các Trang của mình và tôi đã gặp vấn đề tương tự. Tôi đã có mã như sau và nó không hoạt động. Bằng cách truy tìm, điều khiển chuyển từ câu lệnh RedirectToLoginPage () sang dòng tiếp theo mà không được chuyển hướng.

if (_requiresAuthentication)
{
    if (!User.Identity.IsAuthenticated)
        FormsAuthentication.RedirectToLoginPage();

    // check authorization for restricted pages only
    if (_isRestrictedPage) AuthorizePageAndButtons();
}

Tôi phát hiện ra rằng có hai giải pháp. Hoặc để sửa đổi FormsAuthentication.RedirectToLoginPage (); được

if (!User.Identity.IsAuthenticated)
    Response.Redirect(FormsAuthentication.LoginUrl);

HOẶC để sửa đổi web.config bằng cách thêm

<authorization>
  <deny users="?" />
</authorization>

Trong trường hợp thứ hai, trong khi theo dõi, kiểm soát không đến được trang được yêu cầu. Nó đã được chuyển hướng ngay lập tức đến url đăng nhập trước khi chạm vào điểm dừng. Do đó, phương thức SignOut () không phải là vấn đề, phương thức chuyển hướng là một.

Tôi hy vọng điều đó có thể giúp được ai đó

Trân trọng


2
Ngoài ra, bạn có thể gọi Phản
hồi.End

Tôi nghĩ rằng có một chút thông tin sai lệch về phía MS. Bạn phải khóa mọi người nếu bạn muốn họ quay lại trang đăng nhập. Nếu không, khung sẽ vui vẻ cho phép bạn truy cập. Vì vậy, bạn phải nói giải pháp số 2 trong bài viết này.
Josh Robinson

3

Tôi vừa thử một số đề xuất ở đây và trong khi tôi có thể sử dụng nút quay lại của trình duyệt, khi tôi nhấp vào menu chọn mã thông báo [Ủy quyền] cho [ActionResult] đã gửi tôi trở lại màn hình đăng nhập.

Đây là mã đăng xuất của tôi:

        FormsAuthentication.SignOut();
        Response.Cookies.Remove(FormsAuthentication.FormsCookieName);
        Response.Cache.SetExpires(DateTime.Now.AddSeconds(-1));
        HttpCookie cookie = HttpContext.Request.Cookies[FormsAuthentication.FormsCookieName];
        if (cookie != null)
        {
            cookie.Expires = DateTime.Now.AddDays(-1);
            Response.Cookies.Add(cookie);
        }

Mặc dù chức năng quay lại trên trình duyệt đã đưa tôi trở lại và hiển thị menu được bảo mật (tôi vẫn đang làm việc trên đó) Tôi không thể làm bất cứ điều gì được bảo mật trong ứng dụng.

Hi vọng điêu nay co ich


Cảm ơn. Đây là giải pháp hiệu quả với tôi (không cần <deny users="?" />trong web.config)
Alexei

3

Tôi đã thử hầu hết các câu trả lời trong chủ đề này, không có may mắn. Đã kết thúc với điều này:

protected void btnLogout_Click(object sender, EventArgs e)
{
    FormsAuthentication.Initialize();
    var fat = new FormsAuthenticationTicket(1, "", DateTime.Now, DateTime.Now.AddMinutes(-30), false, string.Empty, FormsAuthentication.FormsCookiePath);
    Response.Cookies.Add(new HttpCookie(FormsAuthentication.FormsCookieName, FormsAuthentication.Encrypt(fat)));
    FormsAuthentication.RedirectToLoginPage();
}

Tìm thấy nó ở đây: http://forums.asp.net/t/1306526.aspx/1


3

Câu trả lời này giống hệt về mặt kỹ thuật với Khosro.Pakmanesh. Tôi đang đăng nó để làm rõ câu trả lời của anh ấy khác với các câu trả lời khác trên chủ đề này như thế nào và trong trường hợp sử dụng nó có thể được sử dụng.

Nói chung để xóa một phiên người dùng, làm

HttpContext.Session.Abandon();
FormsAuthentication.SignOut();

sẽ đăng xuất hiệu quả người dùng. Tuy nhiên , nếu trong cùng một Yêu cầu bạn cần kiểm tra Request.isAuthenticated(chẳng hạn như thường xảy ra trong Bộ lọc ủy quyền), thì bạn sẽ thấy rằng

Request.isAuthenticated == true

thậm chí sau khi bạn đã làm HttpContext.Session.Abandon()FormsAuthentication.SignOut().

Điều duy nhất làm việc là làm

AuthenticationManager.SignOut();
HttpContext.User = new GenericPrincipal(new GenericIdentity(string.Empty), null);

Điều đó có hiệu quả thiết lập Request.isAuthenticated = false.


2

Điều này bắt đầu xảy ra với tôi khi tôi đặt xác thực> biểu mẫu> thuộc tính Đường dẫn trong Web.config. Việc gỡ bỏ đã khắc phục sự cố và đơn giản FormsAuthentication.SignOut();lại xóa cookie.


1

Có thể là bạn đang đăng nhập từ một tên miền phụ (sub1.domain.com) và sau đó cố gắng đăng xuất từ ​​một tên miền phụ khác (www.domain.com).


1

Tôi chỉ gặp vấn đề tương tự, nơi SignOut () dường như không thể loại bỏ vé đúng cách. Nhưng chỉ trong một trường hợp cụ thể, trong đó một số logic khác gây ra chuyển hướng. Sau khi tôi xóa chuyển hướng thứ hai này (thay thế bằng thông báo lỗi), vấn đề đã biến mất.

Vấn đề phải là trang được chuyển hướng không đúng lúc, do đó không kích hoạt xác thực.


1

Bây giờ tôi đang gặp vấn đề tương tự và tôi tin rằng vấn đề trong trường hợp của tôi cũng như người đăng ban đầu là do chuyển hướng. Theo mặc định, một Feedback.Redirect gây ra một ngoại lệ ngay lập tức nổi lên cho đến khi nó bị bắt và chuyển hướng được thực thi ngay lập tức, tôi đoán rằng điều này đang ngăn bộ sưu tập cookie đã sửa đổi được truyền cho khách hàng. Nếu bạn sửa đổi mã của mình để sử dụng:

Response.Redirect("url", false);

Điều này ngăn chặn ngoại lệ và dường như cho phép cookie được gửi lại cho khách hàng.


1

Chỉ cần cố gắng gửi một biến phiên khi bạn nhấn đăng nhập. Và trên trang chào mừng, trước tiên hãy kiểm tra xem phiên đó có trống như thế này trong tải trang hay trong Sự kiện ban đầu không:

if(Session["UserID"] == null || Session["UserID"] == "")
{
    Response.Redirect("Login.aspx");
}

1

Đối với tôi, cách tiếp cận sau đây hoạt động. Tôi nghĩ rằng nếu có bất kỳ lỗi nào sau câu lệnh "FormsAuthentication.SignOut ()", SingOut không hoạt động.

public ActionResult SignOut()
    {
        if (Request.IsAuthenticated)
        {
            FormsAuthentication.SignOut();

            return Redirect("~/");
        }
        return View();
     }

0

Bạn đang thử nghiệm / thấy hành vi này bằng IE? Có thể IE đang phục vụ những trang đó từ bộ đệm. Rất khó để IE có thể xóa bộ nhớ cache của nó và vì vậy, trong nhiều trường hợp, ngay cả sau khi bạn đăng xuất, nhập url của một trong những trang "được bảo mật" sẽ hiển thị nội dung được lưu trong bộ nhớ cache từ trước đó.

(Tôi đã thấy hành vi này ngay cả khi bạn đăng nhập như một người dùng khác và IE hiển thị thanh "Chào mừng" ở đầu trang của bạn, với tên người dùng cũ. Ngày nay, thường thì tải lại sẽ cập nhật nó, nhưng nếu nó vẫn tồn tại , nó vẫn có thể là một vấn đề lưu trữ.)


0

Làm session.abandon () và phá hủy cookie hoạt động khá tốt. Tôi đang sử dụng mvc3 và có vẻ như sự cố xảy ra nếu bạn truy cập trang được bảo vệ, đăng xuất và xem qua lịch sử trình duyệt của bạn. Không phải là một vấn đề lớn nhưng vẫn hơi khó chịu.

Mặc dù vậy, cố gắng đi qua các liên kết trên ứng dụng web của tôi hoạt động đúng cách.

Thiết lập nó để không làm bộ nhớ đệm trình duyệt có thể là cách để đi.


0

Đối với MVC, nó hoạt động với tôi:

        public ActionResult LogOff()
        {
            FormsAuthentication.SignOut();
            return Redirect(FormsAuthentication.GetRedirectUrl(User.Identity.Name, true));
        }

0

Tôi muốn thêm một số thông tin để giúp hiểu vấn đề. Xác thực mẫu cho phép lưu trữ dữ liệu người dùng trong cookie hoặc trong chuỗi truy vấn của URL. Phương thức trang web của bạn hỗ trợ có thể được cấu hình trong tệp web.config.

Theo Microsoft :

Phương thức SignOut xóa thông tin vé xác thực mẫu khỏi cookie hoặc URL nếu CookiesSupported là sai .

Đồng thời, họ nói :

Một trong những giá trị của httpCookieMode cho biết ứng dụng có được cấu hình để xác thực mẫu không nấu ăn hay không. Các mặc định là UseDeviceProfile .

Cuối cùng, liên quan đến UseDeviceProfile, họ nói :

Nếu thuộc tính CookieMode được đặt thành UseDeviceProfile, thì CookieMode CookiesSupported sẽ trả về đúng nếu Trình duyệt cho Yêu cầu hiện tại hỗ trợ cả cookie và chuyển hướng bằng cookie ; nếu không, thuộc tính CookiesSupported sẽ trả về false.

Kết hợp tất cả lại với nhau, tùy thuộc vào trình duyệt của người dùng, cấu hình mặc định có thể dẫn đến CookiesSupported là đúng , điều đó có nghĩa là phương thức SignOut không xóa vé khỏi cookie. Điều này có vẻ phản trực giác và tôi không biết tại sao nó hoạt động theo cách này - tôi sẽ mong SignOut thực sự đăng xuất người dùng trong mọi trường hợp.

Một cách để tự SignOut hoạt động là thay đổi chế độ cookie thành "UseCookies" (nghĩa là cookie là bắt buộc) trong tệp web.config:

<authentication mode="Forms">
  <forms loginUrl="~/Account/SignIn" cookieless="UseCookies"/>
</authentication>

Theo thử nghiệm của tôi, việc này làm cho SignOut tự hoạt động với chi phí trang web của bạn hiện yêu cầu cookie hoạt động đúng.


Tôi nghĩ rằng bạn đang đọc sai. Về SignOut (), tôi khá chắc chắn rằng ý nghĩa của chúng là nó sẽ bị xóa khỏi URL nếu CookiesSupported là sai, nếu không thì từ cookie. Tức là họ nên viết "Phương thức SignOut xóa thông tin vé xác thực mẫu khỏi cookie hoặc, nếu CookiesSupported là sai, từ URL."
Oskar Berggren

-1

Xin lưu ý rằng WIF từ chối yêu cầu trình duyệt dọn dẹp cookie nếu thông báo wsignoutcleanup từ STS không khớp với url của tên ứng dụng từ IIS và ý tôi là CASE SENSITIVE . WIF trả lời với kiểm tra OK màu xanh lá cây, nhưng sẽ không gửi lệnh xóa cookie đến trình duyệt.

Vì vậy, bạn cần chú ý đến độ nhạy trường hợp của url của bạn.

Ví dụ: Máy chủ Nhận dạng ThinkTecture lưu các url của RP truy cập trong một cookie, nhưng nó làm cho tất cả chúng đều viết thường. WIF sẽ nhận được thông báo wsignoutcleanup trong trường hợp thấp hơn và sẽ so sánh nó với tên ứng dụng trong IIS. Nếu nó không khớp, nó sẽ xóa không có cookie, nhưng sẽ báo cáo OK cho trình duyệt. Vì vậy, đối với Máy chủ Nhận dạng này, tôi cần phải viết tất cả các url trong web.config và tất cả các tên ứng dụng trong IIS bằng chữ thường, để tránh các vấn đề như vậy.

Cũng đừng quên cho phép cookie của bên thứ ba trong trình duyệt nếu bạn có các ứng dụng bên ngoài tên miền phụ của STS, nếu không trình duyệt sẽ không xóa cookie ngay cả khi WIF nói với anh ta như vậy.


1
WIF? STS? Máy chủ nhận dạng ThinkTecture? Tất cả những điều này là gì, và làm thế nào để chúng liên quan đến câu hỏi này?
Oskar Berggren
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.