天,我們將學習如何制作一個在懸停時展開和折疊的側邊欄。它看起來很漂亮,實現比看起來簡單得多。我們將逐步完成本教程,并且在此過程中我還將介紹一些值得注意的HTML / CSS技巧。
以下是本文將涉及的提示和技巧列表:
讓我們開始!
可以在此筆中找到此步驟的完整代碼:https://codepen.io/dalisc/pen/rEjRWo
只使用HTML,您的網頁將如下圖所示。一些CSS可以做出什么改變!所以我們需要用一些CSS來設置它,使其看起來像上面的gif。
.sidebar { height: 100%; width: 250px; position: fixed; top: 0; left: 0; background-color: #111; padding-top: 60px;
此代碼為側邊欄提供250px(width:250px;)的寬度,背景顏色為深色(background-color:#111;),并使其在頁面上完全垂直延伸(height:100%;)。在code pen以自定義側邊欄。
.sidebar a { padding: 8px 8px 8px 32px; text-decoration: none; font-size: 25px; color: #818181; display: block; }
在這里,我們使用“text-decoration:none;”刪除了文本的下劃線和藍色,并在側邊欄項目周圍添加了填充和塊顯示的一些空格。增強用戶體驗的部分是當項目顏色從灰色變為白色時,這可以通過以下代碼完成:
.sidebar a:hover { color: #f1f1f1; }
此代碼表示當您將鼠標懸停在類“側欄”中用<a>標記的元素上時,元素的顏色將更改為您設置的任何顏色,在本例中為#f1f1f1。
如果您使用Google的素材圖標,您會發現一個令人沮喪的問題:默認情況下,圖標和相鄰文字沒有正確垂直對齊。
.material-icons, .icon-text { vertical-align: middle; } .material-icons { padding-bottom: 3px; margin-right: 30px; }
你需要做的是在CSS中垂直對齊它們(vertical-align:middle;)。即使這樣,對齊也有點偏離,所以在此之后給你的圖標一個3px垂直增強(padding-bottom:3px;)。
現在我們將添加一些Javascript,因為我們將在側邊欄中引入一些功能。可以在此筆中找到此步驟的完整代碼
兩個非常有用的事件是onmouseover和onmouseout,它們分別檢測您的鼠標是否懸停在特定元素之上或之外。對于我們的側邊欄,我們希望檢測位于側邊欄的任何部分,因此我們需要將這些事件添加到側邊欄的<div>中,如下所示:
<div id=”mySidebar” class=”sidebar” onmouseover=”somethinghappens” onmouseout=”somethinghappens”>
現在,我們可以決定鼠標懸停在側邊欄上或從邊欄懸停的情況。我們需要將“ somethinghappens”替換為我們想要實際發生的事情,但首先,讓我們通過向控制臺發送消息來檢查是否發生了檢測。
現在讓我們編寫兩個Javascript函數來確認檢測到事件:
function testIn() { console.log(“hovering in sidebar”); } function testOut() { console.log(“hovering outside sidebar”); }
更新我們的側邊欄:
<div id=”mySidebar” class=”sidebar” onmouseover=”testIn()” onmouseout=”testOut()”>
現在進行懸停并檢查控制臺以查找我們編寫的消息。它應該可以工作了!我們已經設置了告訴側邊欄是折疊還是展開所需的檢測。
可以在此筆中找到此最終部分的完整代碼:https://codepen.io/dalisc/pen/qzRGxQ
我們將折疊邊欄稱為迷你側邊欄。我們現在想要根據鼠標是否懸停在我的側邊欄上進行兩次查找,因此我們需要在javascript部分中創建一個布爾變量mini。
我們還將創建一個函數來切換側邊欄的擴展。該功能的邏輯如下:如果側邊欄處于迷你模式,將鼠標懸停在側邊欄上會將側邊欄擴展為其完整模式(并將變量mini設置為false)。如果側邊欄處于完全模式,將鼠標懸停在側邊欄上會將其折疊為迷你模式(并將變量mini設置為true)。
因此,我們需要更改onmouseover和onmouseout事件,并相應地引入新函數toggleSidebar()。
更改html:
<div id=”mySidebar” class=”sidebar” onmouseover=”toggleSidebar()” onmouseout=”toggleSidebar()”>
添加到JS(我們現在可以刪除testIn()和testOut()):
var mini = true; function toggleSidebar() { if (mini) { console.log(“opening sidebar”); document.getElementById(“mySidebar”).style.width = “250px”; document.getElementById(“main”).style.marginLeft = “250px”; this.mini = false; } else { console.log(“closing sidebar”); document.getElementById(“mySidebar”).style.width = “100px”; document.getElementById(“main”).style.marginLeft = “100px”; this.mini = true; } }
從功能中可以看出,它基本上都是改變側邊欄黑色塊的寬度。完整模式的寬度為250px,迷你模式的寬度為85px。我們還策略性地定位文本和圖標,以便在側邊欄折疊時完全隱藏文本,僅顯示圖標。
默認情況下,我們希望側邊欄處于迷你模式,因此我們也將側邊欄的寬度(最初未被遮擋時)更改為85px。
.sidebar { height: 100%; width: 85px; position: fixed; z-index: 1; top: 0; left: 0; background-color: #111; transition: 0.5s; padding-top: 60px; }
當前默認外觀:
此時,文本溢出仍有一些問題,所以我將介紹一些CSS提示和技巧!
將“white-space:nowrap;”添加到側欄CSS。
.sidebar { height: 100%; width: 85px; position: fixed; top: 0; left: 0; background-color: #111; padding-top: 60px; white-space: nowrap; }
即使文本大于側邊欄的寬度,這也會阻止文本換行到下一行。但正如你在下面看到的那樣,雖然它現在在一行中,它會溢出,你可以看到溢出...所以我們需要找到隱藏它的方法!
s
要隱藏溢出的文本,只需將“overflow-x:hidden;”和“z-index:1;”添加到側邊欄css即可。這將隱藏任何寬于側邊欄寬度的內容。
.sidebar { height: 100%; width: 85px; position: fixed; z-index: 1; top: 0; left: 0; background-color: #111; overflow-x: hidden; padding-top: 60px; white-space: nowrap; }
現在我們的側邊欄看起來非常好!(我也改變了主要內容,但主要內容未在本教程中介紹,它包含在code pen。)
現在我們已經遇到了我們需要修復的最后一個小故障,以使側邊欄順利移動。目前,隨著側邊欄折疊和展開,沒有動畫添加到它,所以它看起來有點不連貫,像這樣:
所以我們想要的是一個非常平滑的過渡,如第一頁上的gif所示。現在,變化立即發生。為了順利,我們需要減緩變化。首先,我們需要讓側邊欄擴展得更慢,比方說0.5秒。將其添加到側邊欄CSS。
.sidebar { height: 100%; width: 85px; position: fixed; z-index: 1; top: 0; left: 0; background-color: #111; overflow-x: hidden; transition: 0.5s; padding-top: 60px; white-space: nowrap; }
我們還需要將主要部分同時推到左側。
#main { transition: margin-left .5s; padding: 16px; margin-left: 85px; }
你有一個漂亮的側邊欄!
在GitHub存儲庫中找到完整的工作代碼:https://github.com/dalisc/hover-collapsible-sidebar
轉:https://medium.com/@9cv9official/create-a-beautiful-hover-triggered-expandable-sidebar-with-simple-html-css-and-javascript-9f5f80a908d1
一個有限的空間內顯示用于呈現信息的可折疊的內容面板。
如需了解更多有關 accordion 部件的細節,請查看 API 文檔 折疊面板部件(Accordion Widget)。
默認功能
點擊頭部展開/折疊被分為各個邏輯部分的內容,就像標簽頁(tabs)一樣。您可以選擇性地設置當鼠標懸停時是否切換各部分的打開/關閉狀態。
基本的 HTML 標記是一系列的標題(H3 標簽)和內容 div,因此內容不用通過 JavaScript 即可用。
<!doctype html> <html> <head> <meta charset="utf-8"> <title>jQuery UI 折疊面板(Accordion) - 默認功能</title> <link rel="stylesheet" > <script src="http://code.jquery.com/jquery-1.9.1.js"></script> <script src="http://code.jquery.com/ui/1.10.4/jquery-ui.js"></script> <link rel="stylesheet" > <script> $(function() { $( "#accordion" ).accordion(); }); </script> </head> <body> <div id="accordion"> <h3>部分 1</h3> <div> <p> Mauris mauris ante, blandit et, ultrices a, suscipit eget, quam. Integer ut neque. Vivamus nisi metus, molestie vel, gravida in, condimentum sit amet, nunc. Nam a nibh. Donec suscipit eros. Nam mi. Proin viverra leo ut odio. Curabitur malesuada. Vestibulum a velit eu ante scelerisque vulputate. </p> </div> <h3>部分 2</h3> <div> <p> Sed non urna. Donec et ante. Phasellus eu ligula. Vestibulum sit amet purus. Vivamus hendrerit, dolor at aliquet laoreet, mauris turpis porttitor velit, faucibus interdum tellus libero ac justo. Vivamus non quam. In suscipit faucibus urna. </p> </div> <h3>部分 3</h3> <div> <p> Nam enim risus, molestie et, porta ac, aliquam ac, risus. Quisque lobortis. Phasellus pellentesque purus in massa. Aenean in pede. Phasellus ac libero ac tellus pellentesque semper. Sed ac felis. Sed commodo, magna quis lacinia ornare, quam ante aliquam nisi, eu iaculis leo purus venenatis dui. </p> <ul> <li>List item one</li> <li>List item two</li> <li>List item three</li> </ul> </div> <h3>部分 4</h3> <div> <p> Cras dictum. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aenean lacinia mauris vel est. </p> <p> Suspendisse eu nisl. Nullam ut libero. Integer dignissim consequat lectus. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. </p> </div> </div> </body> </html>
折疊內容
默認情況下,折疊面板總是保持一個部分是打開的。為了讓所有部分都是折疊的,可設置 collapsible
選項為 true。點擊當前打開的部分,來折疊它的內容面板。
<!doctype html> <html> <head> <meta charset="utf-8"> <title>jQuery UI 折疊面板(Accordion) - 折疊內容</title> <link rel="stylesheet" > <script src="http://code.jquery.com/jquery-1.9.1.js"></script> <script src="http://code.jquery.com/ui/1.10.4/jquery-ui.js"></script> <link rel="stylesheet" > <script> $(function() { $( "#accordion" ).accordion({ collapsible: true }); }); </script> </head> <body> <div id="accordion"> <h3>部分 1</h3> <div> <p>Mauris mauris ante, blandit et, ultrices a, suscipit eget, quam. Integer ut neque. Vivamus nisi metus, molestie vel, gravida in, condimentum sit amet, nunc. Nam a nibh. Donec suscipit eros. Nam mi. Proin viverra leo ut odio. Curabitur malesuada. Vestibulum a velit eu ante scelerisque vulputate.</p> </div> <h3>部分 2</h3> <div> <p>Sed non urna. Donec et ante. Phasellus eu ligula. Vestibulum sit amet purus. Vivamus hendrerit, dolor at aliquet laoreet, mauris turpis porttitor velit, faucibus interdum tellus libero ac justo. Vivamus non quam. In suscipit faucibus urna. </p> </div> <h3>部分 3</h3> <div> <p>Nam enim risus, molestie et, porta ac, aliquam ac, risus. Quisque lobortis. Phasellus pellentesque purus in massa. Aenean in pede. Phasellus ac libero ac tellus pellentesque semper. Sed ac felis. Sed commodo, magna quis lacinia ornare, quam ante aliquam nisi, eu iaculis leo purus venenatis dui. </p> <ul> <li>List item one</li> <li>List item two</li> <li>List item three</li> </ul> </div> <h3>部分 4</h3> <div> <p>Cras dictum. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aenean lacinia mauris vel est. </p><p>Suspendisse eu nisl. Nullam ut libero. Integer dignissim consequat lectus. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. </p> </div> </div> </body> </html>
查看演示
自定義圖標
通過 icons
選項自定義標題圖標,icons
選項接受標題默認的和激活的(打開的)狀態的 class。使用 UI CSS 框架中的任意 class,或者使用背景圖像創建自定義的 class。
<!doctype html> <html> <head> <meta charset="utf-8"> <title>jQuery UI 折疊面板(Accordion) - 自定義圖標</title> <link rel="stylesheet" > <script src="http://code.jquery.com/jquery-1.9.1.js"></script> <script src="http://code.jquery.com/ui/1.10.4/jquery-ui.js"></script> <link rel="stylesheet" > <script> $(function() { var icons = { header: "ui-icon-circle-arrow-e", activeHeader: "ui-icon-circle-arrow-s" }; $( "#accordion" ).accordion({ icons: icons }); $( "#toggle" ).button().click(function() { if ( $( "#accordion" ).accordion( "option", "icons" ) ) { $( "#accordion" ).accordion( "option", "icons", null ); } else { $( "#accordion" ).accordion( "option", "icons", icons ); } }); }); </script> </head> <body> <div id="accordion"> <h3>部分 1</h3> <div> <p>Mauris mauris ante, blandit et, ultrices a, suscipit eget, quam. Integer ut neque. Vivamus nisi metus, molestie vel, gravida in, condimentum sit amet, nunc. Nam a nibh. Donec suscipit eros. Nam mi. Proin viverra leo ut odio. Curabitur malesuada. Vestibulum a velit eu ante scelerisque vulputate.</p> </div> <h3>部分 2</h3> <div> <p>Sed non urna. Donec et ante. Phasellus eu ligula. Vestibulum sit amet purus. Vivamus hendrerit, dolor at aliquet laoreet, mauris turpis porttitor velit, faucibus interdum tellus libero ac justo. Vivamus non quam. In suscipit faucibus urna. </p> </div> <h3>部分 3</h3> <div> <p>Nam enim risus, molestie et, porta ac, aliquam ac, risus. Quisque lobortis. Phasellus pellentesque purus in massa. Aenean in pede. Phasellus ac libero ac tellus pellentesque semper. Sed ac felis. Sed commodo, magna quis lacinia ornare, quam ante aliquam nisi, eu iaculis leo purus venenatis dui. </p> <ul> <li>List item one</li> <li>List item two</li> <li>List item three</li> </ul> </div> <h3>部分 4</h3> <div> <p>Cras dictum. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aenean lacinia mauris vel est. </p><p>Suspendisse eu nisl. Nullam ut libero. Integer dignissim consequat lectus. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. </p> </div> </div> <button id="toggle">切換圖標</button> </body> </html>
查看演示
填充空間
由于折疊面板是由塊級元素組成的,默認情況下它的寬度會填充可用的水平空間。為了填充由容器分配的垂直空間,設置 heightStyle
選項為 "fill"
,腳本會自動設置折疊面板的尺寸為父容器的高度。
<!doctype html> <html> <head> <meta charset="utf-8"> <title>jQuery UI 折疊面板(Accordion) - 填充空間</title> <link rel="stylesheet" > <script src="http://code.jquery.com/jquery-1.9.1.js"></script> <script src="http://code.jquery.com/ui/1.10.4/jquery-ui.js"></script> <link rel="stylesheet" > <style> #accordion-resizer { padding: 10px; width: 350px; height: 220px; } </style> <script> $(function() { $( "#accordion" ).accordion({ heightStyle: "fill" }); }); $(function() { $( "#accordion-resizer" ).resizable({ minHeight: 140, minWidth: 200, resize: function() { $( "#accordion" ).accordion( "refresh" ); } }); }); </script> </head> <body> <h3>重新調整外部容器:</h3> <div id="accordion-resizer"> <div id="accordion"> <h3>部分 1</h3> <div> <p>Mauris mauris ante, blandit et, ultrices a, suscipit eget, quam. Integer ut neque. Vivamus nisi metus, molestie vel, gravida in, condimentum sit amet, nunc. Nam a nibh. Donec suscipit eros. Nam mi. Proin viverra leo ut odio. Curabitur malesuada. Vestibulum a velit eu ante scelerisque vulputate.</p> </div> <h3>部分 2</h3> <div> <p>Sed non urna. Donec et ante. Phasellus eu ligula. Vestibulum sit amet purus. Vivamus hendrerit, dolor at aliquet laoreet, mauris turpis porttitor velit, faucibus interdum tellus libero ac justo. Vivamus non quam. In suscipit faucibus urna. </p> </div> <h3>部分 3</h3> <div> <p>Nam enim risus, molestie et, porta ac, aliquam ac, risus. Quisque lobortis. Phasellus pellentesque purus in massa. Aenean in pede. Phasellus ac libero ac tellus pellentesque semper. Sed ac felis. Sed commodo, magna quis lacinia ornare, quam ante aliquam nisi, eu iaculis leo purus venenatis dui. </p> <ul> <li>List item one</li> <li>List item two</li> <li>List item three</li> </ul> </div> <h3>部分 4</h3> <div> <p>Cras dictum. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aenean lacinia mauris vel est. </p><p>Suspendisse eu nisl. Nullam ut libero. Integer dignissim consequat lectus. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. </p> </div> </div> </div> </body> </html>
非自動高度
設置 heightStyle: "content"
,讓折疊面板保持它們初始的高度。
<!doctype html> <html> <head> <meta charset="utf-8"> <title>jQuery UI 折疊面板(Accordion) - 非自動高度</title> <link rel="stylesheet" > <script src="http://code.jquery.com/jquery-1.9.1.js"></script> <script src="http://code.jquery.com/ui/1.10.4/jquery-ui.js"></script> <link rel="stylesheet" > <script> $(function() { $( "#accordion" ).accordion({ heightStyle: "content" }); }); </script> </head> <body> <div id="accordion"> <h3>部分 1</h3> <div> <p>Mauris mauris ante, blandit et, ultrices a, susceros. Nam mi. Proin viverra leo ut odio. Curabitur malesuada. Vestibulum a velit eu ante scelerisque vulputate.</p> </div> <h3>部分 2</h3> <div> <p>Sed non urna. Donec et ante. Phasellus eu ligula. Vestibulum sit amet purus. Vivamus hendrerit, dolor at aliquet laoreet, mauris turpis porttitor velit, faucibus interdum tellus libero ac justo. Vivamus non quam. In suscipit faucibus urna. </p> </div> <h3>部分 3</h3> <div> <p>Nam enim risus, molestie et, porta ac, aliquam ac, risus. Quisque lobortis. Phasellus pellentesque purus in massa. Aenean in pede. Phasellus ac libero ac tellus pellentesque semper. Sed ac felis. Sed commodo, magna quis lacinia ornare, quam ante aliquam nisi, eu iaculis leo purus venenatis dui. </p> <ul> <li>List item</li> <li>List item</li> <li>List item</li> <li>List item</li> <li>List item</li> <li>List item</li> <li>List item</li> </ul> </div> </div> </body> </html>
查看演示
當懸停時打開
點擊頭部展開/折疊被分為各個邏輯部分的內容,就像標簽頁(tabs)一樣。您可以選擇性地設置當鼠標懸停時是否切換各部分的打開/關閉狀態。
基本的 HTML 標記是一系列的標題(H3 標簽)和內容 div,因此內容不用通過 JavaScript 即可用。
<!doctype html> <html> <head> <meta charset="utf-8"> <title>jQuery UI 折疊面板(Accordion) - 當懸停時打開</title> <link rel="stylesheet" > <script src="http://code.jquery.com/jquery-1.9.1.js"></script> <script src="http://code.jquery.com/ui/1.10.4/jquery-ui.js"></script> <link rel="stylesheet" > <script> $(function() { $( "#accordion" ).accordion({ event: "click hoverintent" }); }); /* * hoverIntent | Copyright 2011 Brian Cherne * http://cherne.net/brian/resources/jquery.hoverIntent.html * modified by the jQuery UI team */ $.event.special.hoverintent = { setup: function() { $( this ).bind( "mouseover", jQuery.event.special.hoverintent.handler ); }, teardown: function() { $( this ).unbind( "mouseover", jQuery.event.special.hoverintent.handler ); }, handler: function( event ) { var currentX, currentY, timeout, args = arguments, target = $( event.target ), previousX = event.pageX, previousY = event.pageY; function track( event ) { currentX = event.pageX; currentY = event.pageY; }; function clear() { target .unbind( "mousemove", track ) .unbind( "mouseout", clear ); clearTimeout( timeout ); } function handler() { var prop, orig = event; if ( ( Math.abs( previousX - currentX ) + Math.abs( previousY - currentY ) ) < 7 ) { clear(); event = $.Event( "hoverintent" ); for ( prop in orig ) { if ( !( prop in event ) ) { event[ prop ] = orig[ prop ]; } } // 防止訪問原始事件,因為新事件會被異步觸發,舊事件不再可用 (#6028) delete event.originalEvent; target.trigger( event ); } else { previousX = currentX; previousY = currentY; timeout = setTimeout( handler, 100 ); } } timeout = setTimeout( handler, 100 ); target.bind({ mousemove: track, mouseout: clear }); } }; </script> </head> <body> <div id="accordion"> <h3>部分 1</h3> <div> <p> Mauris mauris ante, blandit et, ultrices a, suscipit eget, quam. Integer ut neque. Vivamus nisi metus, molestie vel, gravida in, condimentum sit amet, nunc. Nam a nibh. Donec suscipit eros. Nam mi. Proin viverra leo ut odio. Curabitur malesuada. Vestibulum a velit eu ante scelerisque vulputate. </p> </div> <h3>部分 2</h3> <div> <p> Sed non urna. Donec et ante. Phasellus eu ligula. Vestibulum sit amet purus. Vivamus hendrerit, dolor at aliquet laoreet, mauris turpis porttitor velit, faucibus interdum tellus libero ac justo. Vivamus non quam. In suscipit faucibus urna. </p> </div> <h3>部分 3</h3> <div> <p> Nam enim risus, molestie et, porta ac, aliquam ac, risus. Quisque lobortis. Phasellus pellentesque purus in massa. Aenean in pede. Phasellus ac libero ac tellus pellentesque semper. Sed ac felis. Sed commodo, magna quis lacinia ornare, quam ante aliquam nisi, eu iaculis leo purus venenatis dui. </p> <ul> <li>List item one</li> <li>List item two</li> <li>List item three</li> </ul> </div> <h3>部分 4</h3> <div> <p> Cras dictum. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aenean lacinia mauris vel est. </p> <p> Suspendisse eu nisl. Nullam ut libero. Integer dignissim consequat lectus. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. </p> </div> </div> </body> </html>
查看演示
排序(Sortable)
拖拽標題來給面板重新排序。
<!doctype html> <html> <head> <meta charset="utf-8"> <title>jQuery UI 折疊面板(Accordion) - 排序(Sortable)</title> <link rel="stylesheet" > <script src="http://code.jquery.com/jquery-1.9.1.js"></script> <script src="http://code.jquery.com/ui/1.10.4/jquery-ui.js"></script> <link rel="stylesheet" > <style> /* 當排序時,IE 存在布局問題(查看 #5413) */ .group { zoom: 1 } </style> <script> $(function() { $( "#accordion" ) .accordion({ header: "> div > h3" }) .sortable({ axis: "y", handle: "h3", stop: function( event, ui ) { // 當排序時,IE 不能注冊 blur,所以觸發 focusout 處理程序來移除 .ui-state-focus ui.item.children( "h3" ).triggerHandler( "focusout" ); } }); }); </script> </head> <body> <div id="accordion"> <div> <h3>部分 1</h3> <div> <p>Mauris mauris ante, blandit et, ultrices a, suscipit eget, quam. Integer ut neque. Vivamus nisi metus, molestie vel, gravida in, condimentum sit amet, nunc. Nam a nibh. Donec suscipit eros. Nam mi. Proin viverra leo ut odio. Curabitur malesuada. Vestibulum a velit eu ante scelerisque vulputate.</p> </div> </div> <div> <h3>部分 2</h3> <div> <p>Sed non urna. Donec et ante. Phasellus eu ligula. Vestibulum sit amet purus. Vivamus hendrerit, dolor at aliquet laoreet, mauris turpis porttitor velit, faucibus interdum tellus libero ac justo. Vivamus non quam. In suscipit faucibus urna. </p> </div> </div> <div> <h3>部分 3</h3> <div> <p>Nam enim risus, molestie et, porta ac, aliquam ac, risus. Quisque lobortis. Phasellus pellentesque purus in massa. Aenean in pede. Phasellus ac libero ac tellus pellentesque semper. Sed ac felis. Sed commodo, magna quis lacinia ornare, quam ante aliquam nisi, eu iaculis leo purus venenatis dui. </p> <ul> <li>List item one</li> <li>List item two</li> <li>List item three</li> </ul> </div> </div> <div> <h3>部分 4</h3> <div> <p>Cras dictum. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aenean lacinia mauris vel est. </p><p>Suspendisse eu nisl. Nullam ut libero. Integer dignissim consequat lectus. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. </p> </div> </div> </div> </body> </html>
們經常使用的各類網站和App均會涉及注冊、登錄和修改密碼等功能,登錄系統后,有些功能會提示沒有權限,甚至有些位置我們無法訪問,這些都是系統權限和認證的體現。
我們從本章及后面的章節中,將學習在ASP.NET Core應用程序中使用ASP.NET Core Identity實現安全認證相關功能所需要掌握的知識。
本章主要向讀者介紹如下內容。
ASP.NET Core Identity是一個會員身份系統,早期它的名字是Membership,當然那是一段“古老”的歷史,現在我們來了解全新的Identity。它允許我們創建、讀取、更新和刪除賬戶。支持賬號驗證、身份驗證、授權、恢復密碼和SMS雙因子身份驗證。它還支持微軟、Facebook和Google等第三方登錄提供商。它提供了一個豐富的API,并且這些API還可以進行大量的擴展。我們將在本書的后面實現這些功能。
這里采用的是EF Core,因為要讓我們的系統支持Identity服務,所以需要安裝它的程序包。打開NuGet管理器,安裝Microsoft.AspNetCore.Identity.EntityFrameworkCore即可。
以下是添加和配置ASP.NET Core Identity服務的步驟。
使AppDbContext繼承類IdentityDbContext,然后引入命名空間,代碼如下。
public class AppDbContext:IdentityDbContext
{
//其余代碼
}
配置ASP.NET Core Identity服務。在Startup類的ConfigureServices()方法中,添加以下代碼行。
services.AddIdentity<IdentityUser,IdentityRole>()
.AddEntityFrameworkStores<AppDbContext>();
接下來,將Authentication()中間件添加到請求管道,代碼如下。
public void Configure(IApplicationBuilder app,IWebHostEnvironment env)
{
//如果環境是Development serve Developer Exception Page
if(env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
//否則顯示用戶友好的錯誤頁面
else if(env.IsStaging() || env.IsProduction() || env.IsEnvironment("UAT"))
{
app.UseExceptionHandler("/Error");
app.UseStatusCodePagesWithReExecute("/Error/{0}");
}
//使用純靜態文件支持的中間件,而不使用帶有終端的中間件
app.UseStaticFiles();
//添加驗證中間件
app.UseAuthentication();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name:"default",
pattern:"{controller=Home}/{action=Index}/{id?}");
});
}
在Startup類的Configure()方法中,調用UseAuthentication()方法將Authentication()中間件添加到應用程序的請求處理管道中。我們希望能夠在請求到達MVC中間件之前對用戶進行身份驗證。因此,在請求處理管道的UseRouting()中間件之前添加認證中間件。這很重要,因為我們之前講過中間件的添加順序不能亂。
現在開始添加身份遷移。在Visual Studio中的程序包控制臺窗口執行以下命令以添加新遷移。
Add-Migration AddingIdentity
此遷移包含用于創建ASP.NET Core Identity系統所需的表的代碼。
如果運行,則會出現以下錯誤。
The entity type'IdentityUserLogin'requires a primary key to be defined.
之前因為要封裝Seed()方法,所以重寫OnModelCreating()方法。出現這個錯誤是因為我們在DbContext類中重寫了OnModelCreating()方法,但未調用基本IdentityDbContext類OnModelCreating()方法。
Identity表的鍵映射在IdentityDbContext類的OnModelCreating()方法中。因此,要解決這個錯誤,需要做的是,調用基類OnModelCreating()使用該方法的基礎關鍵字,代碼如下。
public class AppDbContext:IdentityDbContext
{
public AppDbContext(DbContextOptions<AppDbContext> options):base(options)
{
}
public DbSet<Student> Students{get;set;}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Seed();
}
}
執行Update-Database命令以應用遷移記錄并創建所需的身份表,如圖21.1所示。
圖21.1
現在已經創建好了表的信息,接下來我們增加一個注冊功能,讓用戶能夠注冊到系統中。
新用戶注冊視圖應如圖21.2所示。為了能夠注冊為新用戶,需要郵箱地址和密碼兩個字段。
圖21.2
我們將使用RegisterViewModel類作為Register視圖的模型,它負責將視圖中的信息傳遞給控制器。為了驗證信息是否正確,我們使用了幾個ASP.NET Core驗證屬性。在之前的章節中詳細說明過這些屬性和模型驗證。
using System.ComponentModel.DataAnnotations;
namespace MockSchoolManagement.ViewModels
{
public class RegisterViewModel
{
[Required]
[EmailAddress]
[Display(Name = "郵箱地址")]
public string Email{get;set;}
[Required]
[DataType(DataType.Password)]
[Display(Name = "密碼")]
public string Password{get;set;}
[DataType(DataType.Password)]
[Display(Name = "確認密碼")]
[Compare("Password",
ErrorMessage = "密碼與確認密碼不一致,請重新輸入.")]
public string ConfirmPassword{get;set;}
}
}
在這里我們添加了DataType特性,它的主要作用是指定比數據庫內部類型更具體的數據類型。DataType枚舉提供了多種數據類型,比如日期、時間、電話號碼、貨幣和郵箱地址等。但是請注意,DataType特性不提供任何驗證,它主要服務于我們的視圖文件,比如,DataType.EmailAddress可以在視圖中創建mailto:鏈接,DataType.Date則會在支持HTML5的瀏覽器中提供日期選擇器。
賬戶控制器(AccountController)是指所有與賬戶相關的CRUD(增加、讀取、更新和刪除)操作都將在此控制器中。目前我們只有Register()操作方法,可以通過向/account/register發出GET請求來實現此操作方法。
using Microsoft.AspNetCore.Mvc;
namespace MockSchoolManagement.Controllers
{
public class AccountController:Controller
{
[HttpGet]
public IActionResult Register()
{
return View();
}
}
}
將此視圖放在Views/Account文件夾中,此視圖的模型是我們在前面創建的Register ViewModel。
@model RegisterViewModel
@{ViewBag.Title = "用戶注冊";}
<h1>用戶注冊</h1>
<div class="row">
<div class="col-md-12">
<form method="post">
<div asp-validation-summary="All" class="text-danger"> </div>
<div class="form-group">
<label asp-for="Email"> </label>
<input asp-for="Email" class="form-control" />
<span asp-validation-for="Email" class="text-danger"> </span>
</div>
<div class="form-group">
<label asp-for="Password"> </label>
<input asp-for="Password" class="form-control" />
<span asp-validation-for="Password" class="text-danger"> </span>
</div>
<div class="form-group">
<label asp-for="ConfirmPassword"> </label>
<input asp-for="ConfirmPassword" class="form-control" />
<span asp-validation-for="ConfirmPassword" class="text-danger"> </span>
</div>
<button type="submit" class="btn btn-primary">注冊</button>
</form>
</div>
</div>
在布局視圖中添加注冊按鈕,我們需要在_Layout.cshtml文件中找到ID為collapsibleNavbar的導航菜單欄,在下方添加注冊按鈕,導航到對應的視圖,代碼如下。
<div id="collapsibleNavbar" class="collapse navbar-collapse">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" asp-controller="home" asp-action="Index">學生列表</a>
</li>
<li class="nav-item">
<a class="nav-link" asp-controller="home" asp-action="Create">添加學生</a>
</li>
</ul>
<ul class="navbar-nav ml-auto">
<li class="nav-item">
<a class="nav-link" asp-controller="account" asp-action="register"> 注冊 </a>
</li>
</ul>
</div>
運行項目后,單擊注冊按鈕即可看到圖21.2所示的效果圖,接下來我們實現處理HttpPOST請求到/account/register的Register()操作方法。然后通過表單Taghelpers將數據發布到ASP.NET Core Identity中創建賬戶。
在本節我們學習使用ASP.NET Core Identity提供的UserManager服務創建新用戶,然后使用其提供的SignInManager服務來登錄用戶。
UserManager <IdentityUser>類包含管理基礎數據存儲中的用戶所需的方法。比如,此類具有CreateAsync()、DeleteAsync()和UpdateAsync()等方法來創建、刪除和更新用戶,如圖21.3所示。
圖21.3
SignInManager <IdentityUser>類包含用戶登錄所需的方法。比如,SignInManager類具有SignInAsync()、SignOutAsync()等方法來登錄和注銷用戶,如圖21.4所示。
圖21.4
以下是AccountController的完整代碼。
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using MockSchoolManagement.ViewModels;
using System.Threading.Tasks;
namespace MockSchoolManagement.Controllers
{
public class AccountController:Controller
{
private UserManager<IdentityUser> _userManager;
private SignInManager<IdentityUser> _signInManager;
public AccountController(UserManager<IdentityUser> userManager,
SignInManager<IdentityUser> signInManager)
{
this._userManager = userManager;
this._signInManager = signInManager;
}
[HttpGet]
public IActionResult Register()
{
return View();
}
[HttpPost]
public async Task<IActionResult> Register(RegisterViewModel model)
{
if(ModelState.IsValid)
{
//將數據從RegisterViewModel復制到IdentityUser
var user = new IdentityUser
{
UserName = model.Email,
Email = model.Email
};
//將用戶數據存儲在AspNetUsers數據庫表中
var result = await _userManager.CreateAsync(user,model.Password);
//如果成功創建用戶,則使用登錄服務登錄用戶信息
//并重定向到HomeController的索引操作
if(result.Succeeded)
{
await _signInManager.SignInAsync(user,isPersistent:false);
return RedirectToAction("index","home");
}
//如果有任何錯誤,則將它們添加到ModelState對象中
//將由驗證摘要標記助手顯示到視圖中
foreach(var error in result.Errors)
{
ModelState.AddModelError(string.Empty,error.Description);
}
}
return View(model);
}
}
}
此時,如果讀者運行項目并提供有效的郵箱地址和密碼,則它會在SQL Server數據庫的AspNetUsers表中創建賬戶。讀者可以從Visual Studio的SQL Server對象資源管理器中查看此數據,如圖21.5所示。
圖21.5
在剛剛注冊的時候,我們發現有兩個問題。
這是因為ASP.NET Core IdentityOptions類在ASP.NET Core中用于配置密碼復雜性規則。默認情況下,ASP.NET Core身份不允許創建簡單的密碼來保護我們的應用程序免受自動暴力攻擊。
當我們嘗試使用像abc這樣的簡單密碼注冊新賬戶時,會顯示創建失敗,讀者將看到如圖21.6所示的驗證錯誤。
圖21.6
我們在圖21.6中看到中文提示,后面的章節會告訴讀者如何配置。
在ASP.NET Core Identity中,密碼默認設置在PasswordOptions類中。讀者可以在ASP.NET Core GitHub倉庫中找到此類的源代碼。只需在倉庫中搜索PasswordOptions類。
代碼如下。
public class PasswordOptions
{
public int RequiredLength{get;set;} = 6;
public int RequiredUniqueChars{get;set;} = 1;
public bool RequireNonAlphanumeric{get;set;} = true;
public bool RequireLowercase{get;set;} = true;
public bool RequireUppercase{get;set;} = true;
public bool RequireDigit{get;set;} = true;
}
相關參數的說明如表21.1(略)所示。
我們可以通過在Startup類的ConfigureServices()方法中使用IServiceCollection接口的Configure()方法來實現這一點。
services.Configure<IdentityOptions>(options =>
{
options.Password.RequiredLength = 6;
options.Password.RequiredUniqueChars = 3;
options.Password.RequireNonAlphanumeric = false;
options.Password.RequireLowercase = false;
options.Password.RequireUppercase = false;
});
也可以在添加身份服務時執行此操作,代碼如下。
services.AddIdentity<IdentityUser,IdentityRole>(options =>
{
options.Password.RequiredLength = 6;
options.Password.RequiredUniqueChars = 3;
options.Password.RequireNonAlphanumeric = false;
})
.AddEntityFrameworkStores<AppDbContext>();
當然,在這里推薦使用IdentityOptions的形式進行配置,因為它可以作為一個獨立服務,而不是嵌套在AddIdentity()方法中。
IdentityOptions對象中除了Password的配置信息,還有用戶、登錄、策略等配置信息,我們可以根據不同的場景進行靈活的配置。
Identity提供了AddErrorDescriber()方法,可方便我們進行錯誤內容的配置和處理。
ASP.NET Core默認提供的都是英文提示,我們可以將它們修改為中文。現在我們創建一個CustomIdentityErrorDescriber的類文件,路徑為根目錄下創建的CustomerMiddlewares文件夾,然后繼承IdentityErrorDescriber服務,添加以下代碼。
public class CustomIdentityErrorDescriber:IdentityErrorDescriber
{
public override IdentityError DefaultError()
{
return new IdentityError{Code = nameof(DefaultError),Description = $"發生了未知的故障。" };
}
public override IdentityError ConcurrencyFailure()
{
return new IdentityError{Code = nameof(ConcurrencyFailure),Description = "樂觀并發失敗,對象已被修改。" };
}
public override IdentityError PasswordMismatch()
{
return new IdentityError{Code = nameof(PasswordMismatch),Description = "密碼錯誤" };
}
public override IdentityError InvalidToken()
{
return new IdentityError{Code = nameof(InvalidToken),Description = "無效的令牌." };
}
public override IdentityError LoginAlreadyAssociated()
{
return new IdentityError{Code = nameof(LoginAlreadyAssociated),Description = "具有此登錄的用戶已經存在." };
}
public override IdentityError InvalidUserName(string userName)
{
return new IdentityError{Code = nameof(InvalidUserName),Description = $"用戶名'{userName}'無效,只能包含字母或數字." };
}
public override IdentityError InvalidEmail(string email)
{
return new IdentityError{Code = nameof(InvalidEmail),Description = $"郵箱'{email}'無效." };
}
public override IdentityError DuplicateUserName(string userName)
{
return new IdentityError{Code = nameof(DuplicateUserName),Description = $"用戶名'{userName}'已被使用." };
}
public override IdentityError DuplicateEmail(string email)
{
return new IdentityError{Code = nameof(DuplicateEmail),Description = $"郵箱'{email}'已被使用." };
}
public override IdentityError InvalidRoleName(string role)
{
return new IdentityError{Code = nameof(InvalidRoleName),Description = $"角色名'{role}'無效." };
}
public override IdentityError DuplicateRoleName(string role)
{
return new IdentityError{Code = nameof(DuplicateRoleName),Description = $"角色名'{role}'已被使用." };
}
public override IdentityError UserAlreadyHasPassword()
{
return new IdentityError{Code = nameof(UserAlreadyHasPassword),Description = "該用戶已設置了密碼." };
}
public override IdentityError UserLockoutNotEnabled()
{
return new IdentityError{Code = nameof(UserLockoutNotEnabled),Description = "此用戶未啟用鎖定." };
}
public override IdentityError UserAlreadyInRole(string role)
{
return new IdentityError{Code = nameof(UserAlreadyInRole),Description = $"用戶已關聯角色'{role}'." };
}
public override IdentityError UserNotInRole(string role)
{
return new IdentityError{Code = nameof(UserNotInRole),Description = $"用戶未關聯角色'{role}'." };
}
public override IdentityError PasswordTooShort(int length)
{
return new IdentityError{Code = nameof(PasswordTooShort),Description = $"密碼必須至少是{length}字符." };
}
public override IdentityError PasswordRequiresNonAlphanumeric()
{
return new IdentityError
{
Code = nameof(PasswordRequiresNonAlphanumeric),
Description = "密碼必須至少有一個非字母數字字符."
};
}
public override IdentityError PasswordRequiresDigit()
{
return new IdentityError{Code = nameof(PasswordRequiresDigit),Description = $"密碼必須至少有一個數字('0'-'9')." };
}
public override IdentityError PasswordRequiresUniqueChars(int uniqueChars)
{
return new IdentityError{Code = nameof(PasswordRequiresUniqueChars),Description = $"密碼必須使用至少不同的{uniqueChars}字符。" };
}
public override IdentityError PasswordRequiresLower()
{
return new IdentityError{Code = nameof(PasswordRequiresLower),Description = "密碼必須至少有一個小寫字母('a'-'z')." };
}
public override IdentityError PasswordRequiresUpper()
{
return new IdentityError{Code = nameof(PasswordRequiresUpper),Description = "密碼必須至少有一個大寫字母('A'-'Z')." };
}
}
回到Startup類的ConfigureServices()方法中,在AddIdentity()服務中使用AddErrorDescriber()方法覆蓋默認的錯誤提示內容,代碼如下。
services.AddIdentity<IdentityUser,IdentityRole>().AddErrorDescriber<CustomIdentityErrorDescriber>().AddEntityFrameworkStores<AppDbContext>();
配置完成之后,提示變為中文,注冊時密碼長度達到6位即可。
在本節中我們學習如何判斷用戶是否登錄,以及注冊、登錄和注銷等功能是否可實現。
首先來看一看如何在ASP.NET Core中實現注銷功能。如果用戶未登錄,則顯示登錄和注冊按鈕,如圖21.7所示。
圖21.7
如果用戶已登錄,請隱藏登錄和注冊按鈕并顯示注銷按鈕,如圖21.8所示。
圖21.8
我們需要在_Layout.cshtml文件中找到ID為collapsibleNavbar的導航菜單欄,修改代碼如下。
在下方代碼中注入了SignInManager,以便我們檢查用戶是否已登錄,來決定顯示和隱藏的內容。
@using Microsoft.AspNetCore.Identity @inject SignInManager<IdentityUser>
_signInManager
<div class="collapse navbar-collapse" id="collapsibleNavbar">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" asp-controller="home" asp-action="index">學生列表</a>
</li>
<li class="nav-item">
<a class="nav-link" asp-controller="home" asp-action="create">添加學生</a>
</li>
</ul>
<ul class="navbar-nav ml-auto">
@*如果用戶已登錄,則顯示注銷鏈接*@ @if(_signInManager.IsSignedIn(User)) {
<li class="nav-item">
<form method="post" asp-controller="account" asp-action="logout">
<button type="submit" style="width:auto"
class="nav-link btn btn-link py-0">
注銷 @User.Identity.Name
</button>
</form>
</li>
}else{
<li class="nav-item">
<a class="nav-link" asp-controller="account" asp-action="register">
注冊
</a>
</li>
<li class="nav-item">
<a class="nav-link" asp-controller="account" asp-action="login">
登錄
</a>
</li>
}
</ul>
</div>
</IdentityUser>
然后在AccountController中添加以下Logout()方法。
[HttpPost]
public async Task<IActionResult> Logout()
{
await _signInManager.SignOutAsync();
return RedirectToAction("index","home");
}
請注意,我們使用POST請求將用戶注銷,而不使用GET請求,因為該方法可能會被濫用。惡意者可能會誘騙用戶單擊某張圖片,將圖片的src屬性設置為應用程序注銷URL,這樣會造成用戶在不知不覺中退出了賬戶。
在本節中,我們將討論使用ASP.NET Core Identity的API在ASP.NET Core應用程序中實現登錄功能。要在ASP.NET Core應用程序中實現登錄功能,我們需要實現以下功能。
要在系統中登錄用戶,則需要其郵箱、用戶名、密碼以及使其選擇是否需要持久性Cookie或會話Cookie。
public class LoginViewModel
{
[Required]
[EmailAddress]
public string Email{get;set;}
[Required]
[DataType(DataType.Password)]
public string Password{get;set;}
[Display(Name = "記住我")]
public bool RememberMe{get;set;}
}
登錄視圖的代碼如下。
@model LoginViewModel
@{ViewBag.Title = "用戶登錄";}
<h1>用戶登錄</h1>
<div class="row">
<div class="col-md-12">
<form method="post">
<div asp-validation-summary="All" class="text-danger"> </div>
<div class="form-group">
<label asp-for="Email"> </label>
<input asp-for="Email" class="form-control" />
<span asp-validation-for="Email" class="text-danger"> </span>
</div>
<div class="form-group">
<label asp-for="Password"> </label>
<input asp-for="Password" class="form-control" />
<span asp-validation-for="Password" class="text-danger"> </span>
</div>
<div class="form-group">
<div class="checkbox">
<label asp-for="RememberMe">
<input asp-for="RememberMe" />
@Html.DisplayNameFor(m => m.RememberMe)
</label>
</div>
</div>
<button type="submit" class="btn btn-primary">登錄</button>
</form>
</div>
</div>
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using MockSchoolManagement.ViewModels;
using System.Threading.Tasks;
namespace MockSchoolManagement.Controllers
{
public class AccountController:Controller
{
private UserManager<IdentityUser> _userManager;
private SignInManager<IdentityUser> _signInManager;
public AccountController(UserManager<IdentityUser> userManager,
SignInManager<IdentityUser> signInManager)
{
this._userManager = userManager;
this._signInManager = signInManager;
}
[HttpGet]
public IActionResult Login()
{
return View();
}
[HttpPost]
public async Task<IActionResult> Login(LoginViewModel model)
{
if(ModelState.IsValid)
{
var result = await _signInManager.PasswordSignInAsync(
model.Email,model.Password,model.RememberMe,false);
if(result.Succeeded)
{
return RedirectToAction("index","home");
}
ModelState.AddModelError(string.Empty,"登錄失敗,請重試");
}
return View(model);
}
}
}
維基百科解釋:Cookie并不是它的原意“甜餅”的意思,而是一個保存在客戶機中的簡單的文本文件,這個文件與特定的Web文檔關聯在一起,保存了該客戶機訪問這個Web文檔時的信息,當客戶機再次訪問這個Web文檔時這些信息可供該文檔使用。由于“Cookie”具有可以保存在客戶機上的神奇特性,因此它可以幫助我們實現記錄用戶個人信息的功能,而這一切都不必使用復雜的CGI等程序。
簡單來說,我們把Cookie理解為一個大小不超過4kB,便于我們在客戶端保存一些用戶個人信息的功能。
在ASP.NET Core Identity中,用戶成功登錄后,將發出Cookie,并將此Cookie隨每個請求一起發送到服務器,服務器會解析此Cookie信息來了解用戶是否已經通過身份驗證和登錄。此Cookie可以是會話Cookie或持久Cookie。
會話Cookie是指用戶登錄成功后,Cookie會被創建并存儲在瀏覽器會話實例中。會話Cookie不包含過期時間,它會在瀏覽器窗口關閉時被永久刪除。
持久Cookie是指用戶登錄成功后,Cookie會被創建并存儲在瀏覽器中,因為是持久Cookie,所以在關閉瀏覽器窗口后,它不會被刪除。但是,它通常有一個到期時間,會在到期后被刪除。
在LoginViewModel.cs視圖模型中,我們已經添加了一個bool類型的RememberMe屬性。用戶可在登錄時選擇記住我,選中即使用持久性Cookie,而未選中則為會話Cookie。
現在運行項目,我們可以在登錄的時候選擇記住我,登錄成功后如圖21.9所示。
圖21.9
打開開發者工具(按F12鍵),觀察圖21.9框中的內容,可以發現過期時間是很長的。現在關閉瀏覽器,并將其再次打開,用戶也依然是登錄狀態。這便是持久性Cookie的作用,只有在到期時間到了之后才會刪除。
至于會話Cookie驗證,我們在登錄的時候取消選擇記住我,然后看到如圖21.10所示的內容。
圖21.10
這里已經是一個會話了,它不包含過期時間,在關閉瀏覽器后,再次將其打開,系統會自動注銷用戶。
以上就是持久性Cookie與會話Cookie的區別了。
在本章中我們學習了Identity的基本功能,創建一個系統用戶并完成了登錄注冊及狀態檢查。在后面的章節中,內容會逐步深入,可配合源代碼學習。
本章介紹了ASP.NET Core Identity框架的定位及作用,并利用它提供的API完成了用戶的登錄與注銷等基本功能。在后面的章節中我們會使用更多的API將系統趨于完善。
本文摘自《深入淺出 ASP.NET Core》
這本書原本的計劃是描述EF Core中的知識點,帶領讀者完整地做一個管理系統。但是個人覺得這樣寫與市場上的其他圖書沒有什么區別,它就是一本概述知識點的圖書,無非多了一個較為完整的功能系統而已。對于我而言這是有落差的。有一天和朋友吃飯,他建議把ABP中那些有效的、目前市場上流行的設計理念整合進圖書,不用講解得太明白,只是告訴讀者如何用以及這么用的好處即可。
本書分為以下5個部分。
*請認真填寫需求信息,我們會在24小時內與您取得聯系。