用 Deny Hosts 保護你的 Linux 伺服器

我在管理 Linux 主機時最常遇到的一個問題就是只要系統一上線,就會開始有人來 try 我的主機,所以我通常把不太對外的 port 用 iptables 設定僅限於特定 IP 可以存取,不過有時後主機必須對外的時候,被 try 的機會就很高了,例如說 SSHD 就是最常被入侵攻擊的服務之一,今天將介紹 DenyHosts 套件,可有效的幫你阻擋非法的入侵行為。

DenyHosts 是一支用 python 寫成的 Script,主要目的就是幫助 Linux 系統管理者保護你的 SSH 伺服器,避免被駭客用字典攻擊或暴力攻擊(就是一直用不同的帳號、密碼嘗試登入就是了)。如果你有固定在看你的 /var/log/auth.log (或 /var/log/secure)安全紀錄檔的話,就可以知道有多少人在攻擊或嘗試登入你的主機了。而 DenyHosts 就是用來分析這個檔案,將非法登入的行為紀錄下來,超過允許的次數後就會將遠端的 IP 寫入到 /etc/hosts.deny 檔案裡,直接將該 IP 給封鎖住,讓駭客無法再連線。

DenyHosts 真的很簡單易用,不像 iptables 要設定許多複雜的 rules,如果你是用 Ubuntu 的話,只要下一行指令就可以完成所有安裝、設定、啟用服務的工作了,如下:

apt-get install denyhosts

如果你想讓 DenyHosts 偵測到攻擊行為時發一封 Email 通知你的話,你也可以設定 /etc/denyhosts.conf 檔案,並找到 ADMIN_EMAIL 的地方設定即可。

設定好之後必須要重新啟動 DenyHosts:

/etc/init.d/denyhosts restart

相關連結

  

此文章由 will 發表於 2008/5/17 下午 06:54:29

永久連結 | 評論 (0) | 此文章的RSSRSS comment feed |

分類: Linux | 系統管理

標籤: , ,

收藏:

在撰寫 LINQ to SQL 時應注意的幾個小地方

這陣子的專案幾乎每個都會用到 LINQ to SQL 技術,但我發現有些人在撰寫程式碼的時候有些不太好的習慣,會對資料庫進行一些多餘的查詢動作或建立多餘的 DataContext,以下是我最近觀察到的幾種狀況與建議的寫法:

1. 一個頁面中只需要共用一個 DataContext

錯誤的程式碼

var q = from p in SQLHelper.GetDataContext().News
        select p;

建議的程式碼

MyDataContext db = SQLHelper.GetDataContext();

var q = from p in db.News
        select p;

說明:所有 LINQ to SQL 語法都共用 db 變數即可,不需要每次執行 LINQ to SQL 都產生一個新的 DataContext 浪費資源。

2. 取出結果的第一筆資料不要重複執行 q.First() 方法

錯誤的程式碼

var q = from p in db.News
        where p.ID.CompareTo(new Guid(Request.QueryString["id"])) == 0
        select p;
if(q.First().Title.Contains(strKeyword) || 
   q.First().Content.Contains(strKeyword)) {
    return true;
}

建議的程式碼

var q = from p in db.News
        where p.ID.CompareTo(new Guid(Request.QueryString["id"])) == 0
        select p;
News n = q.First();
if(n.Title.Contains(strKeyword) || n.Content.Contains(strKeyword)) {
    return true;
}

說明:你每次執行 q.First() 他都會進資料庫做一次資料查詢,你判斷五次就會執行五次,是很沒效率的作法。直接呼叫 q.First() 也有風險,請看第 3 點的說明。

3. 若要取得單筆資料,要判斷是否有從資料庫中取到資料時不要用 q.Count() 方法

錯誤的程式碼

var q = from p in db.News
        where p.ID.CompareTo(new Guid(Request.QueryString["id"])) == 0
        select p;

if (q.Count() == 1)
{
    m = q.First();
}
else
{   
    Response.Redirect("/", true);
}

建議的程式碼

var q = from p in db.News
        where p.ID.CompareTo(new Guid(Request.QueryString["id"])) == 0
        select p;

News n = q.FirstOrDefault(); // 如果用 q.First() 在沒資料時會發生 Exception

if (n == null)
{
    Response.Redirect("/", true);
    return;
}

說明:你每執行一次 q.Count() 方法,程式都會進資料庫執行一遍 SELECT COUNT(*) 的動作,如果你只需要取出一筆資料的話,這個動作其實是多餘的。如果你用的是 q.First() 來取得第一筆資料的話,當資料庫沒資料時是會發生 Exception 的!

4. 不要在 LINQ 語法中轉型(Casting)或執行太多的 .NET 方法

錯誤的程式碼

var q = from p in db.News
        where p.ID.CompareTo(new Guid(Request.QueryString["id"])) == 0
        select p;

建議的程式碼

try {
    Guid id = new Guid(Request.QueryString["id"]);
} catch {
    Response.Redirect("/", true);
    return;
}
var q = from p in db.News
        where p.ID.CompareTo(id) == 0
        select p;

說明:以本範例為例,如果你傳入的 id 不是有效的 Guid 字串,就會發生例外事件。這個例外事件會在 LINQ to SQL 在執行的過程中發生失敗 (Inner Exception),他會給你類似這樣的錯誤訊息:

System.Web.HttpUnhandledException: Exception of type 'System.Web.HttpUnhandledException' was thrown. ---> System.ArgumentNullException: Value cannot be null.
Parameter name: g
   at System.Data.Linq.SqlClient.QueryConverter.VisitInvocation(InvocationExpression invoke)
   at System.Data.Linq.SqlClient.QueryConverter.VisitInner(Expression node)
   at System.Data.Linq.SqlClient.QueryConverter.VisitMethodCall(MethodCallExpression mc)
   at System.Data.Linq.SqlClient.QueryConverter.VisitInner(Expression node)
   at System.Data.Linq.SqlClient.QueryConverter.VisitExpression(Expression exp)
   at System.Data.Linq.SqlClient.QueryConverter.VisitBinary(BinaryExpression b)
   at System.Data.Linq.SqlClient.QueryConverter.VisitInner(Expression node)
   at System.Data.Linq.SqlClient.QueryConverter.VisitExpression(Expression exp)
   at System.Data.Linq.SqlClient.QueryConverter.VisitBinary(BinaryExpression b)
   at System.Data.Linq.SqlClient.QueryConverter.VisitInner(Expression node)
   at System.Data.Linq.SqlClient.QueryConverter.VisitExpression(Expression exp)
   at System.Data.Linq.SqlClient.QueryConverter.VisitWhere(Expression sequence, LambdaExpression predicate)
   at System.Data.Linq.SqlClient.QueryConverter.VisitSequenceOperatorCall(MethodCallExpression mc)
   at System.Data.Linq.SqlClient.QueryConverter.VisitInner(Expression node)
   at System.Data.Linq.SqlClient.QueryConverter.VisitAggregate(Expression sequence, LambdaExpression lambda, SqlNodeType aggType, Type returnType)
   at System.Data.Linq.SqlClient.QueryConverter.VisitSequenceOperatorCall(MethodCallExpression mc)
   at System.Data.Linq.SqlClient.QueryConverter.VisitInner(Expression node)
   at System.Data.Linq.SqlClient.QueryConverter.ConvertOuter(Expression node)
   at System.Data.Linq.SqlClient.SqlProvider.BuildQuery(Expression query, SqlNodeAnnotations annotations)
   at System.Data.Linq.SqlClient.SqlProvider.System.Data.Linq.Provider.IProvider.Execute(Expression query)
   at System.Data.Linq.DataQuery`1.System.Linq.IQueryProvider.Execute[S](Expression expression)
   at System.Linq.Queryable.Count[TSource](IQueryable`1 source)
   at ASP.masterpage_master.Page_Load(Object sender, EventArgs e) in c:\XXX\AAA\BBB\MasterPage.master:line 20
   at System.Web.Util.CalliHelper.EventArgFunctionCaller(IntPtr fp, Object o, Object t, EventArgs e)
   at System.Web.Util.CalliEventHandlerDelegateProxy.Callback(Object sender, EventArgs e)
   at System.Web.UI.Control.OnLoad(EventArgs e)
   at System.Web.UI.Control.LoadRecursive()
   at System.Web.UI.Control.LoadRecursive()
   at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)
   --- End of inner exception stack trace ---
   at System.Web.UI.Page.HandleError(Exception e)
   at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)
   at System.Web.UI.Page.ProcessRequest(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)
   at System.Web.UI.Page.ProcessRequest()
   at System.Web.UI.Page.ProcessRequest(HttpContext context)
   at ASP.productmodel_aspx.ProcessRequest(HttpContext context) in c:\WINDOWS\Microsoft.NET\Framework64\v2.0.50727\Temporary ASP.NET Files\root\52723026\465b3099\App_Web_xglot_f2.3.cs:line 0
   at System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
   at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)

這樣的錯誤訊息將會讓你比較不容易除錯。

5. 使用具型別的語法時發生無法 Compile 的情形

例如說你有個 LINQ 語法如下:

var q = from p in db.News
        select new { p.ID, p.Title, p.Summary };

var myList = q.ToList();

上面這斷的 LINQ 語法選出來的資料在執行 ToList() 方法之後變成了一組「匿名型別(Anonymous Type)集合」,但是匿名型別是無法當成參數傳遞的,也無法序列化(Serialization),也就是說你沒辦法把這些資料存在 ViewState、Session 或 Cache 物件裡!

通常這種情況我們會自訂一個類別 ,用來儲存透過 LINQ to SQL 所 select 出來的欄位,例如說以下程式:

public class MyNews
{
    public Guid ID;
    public string Title;
    public string Summary;
}

而你原本的 LINQ 語法要改成這樣:

var q = from p in db.News
        select new MyNews { p.ID, p.Title, p.Summary };

如果你這樣寫的話,那你就錯了!因為這樣的程式碼在編譯的時候會出現以下錯誤訊息:

Cannot initialize type 'MyNews' with a collection initializer because it does not implement 'System.Collections.IEnumerable'

當你看著這個錯誤訊息,你可能會想趕快在 MyNews 類別上實做 System.Collections.IEnumerable 介面,但是問題根本不在這裡!

你必須「明確指定」該類別的欄位名稱(Field Name)才可以正常編譯,如下程式範例:

var q = from p in db.News
        select new MyNews { ID=p.ID, Title=p.Title, Summary=p.Summary };

有了明確的型別,我們透過 ToList() 方法取得的資料就可以當成參數傳遞了,也可以將取得的資料 Cache 起來,雖然是很小的地方,但第一次遇到的人可能會弄很久才解決!

以上這 5 點是我最近發現的小狀況,如果日後有發現新的狀況我還會補充上來。

  

此文章由 will 發表於 2008/5/16 上午 09:00:26

永久連結 | 評論 (4) | 此文章的RSSRSS comment feed |

分類: ASP.NET | C# | LINQ

標籤: , , , ,

收藏:

Web開發人員的百科全書:Google Doctype

Web 的開發工作千頭萬緒,什麼樣的鬼事都遇的到,什麼 CSS, DOM, JavaScript, ... 在每個 Browser 都有些小地方不太一樣,難怪很多學 Windows AP 或 Windows Form 的開發人員都不太願意轉到 ASP.NET 開發(至少我遇過好幾個不太想轉換的朋友)。

Google 最近推出了個名為 Google Doctype 的網站,目的是想創立一個所有跟 Web 有關的百科全書,所有跟 Web 安全、JavaScript 的 DOM 處理、CSS 的撰寫技巧、網頁效能、跨瀏覽器的議題,或其他所有跟 Web 相關的東西都會在這上面。

我自己上去看了一下覺得有些文章還蠻不錯的,都是非常實用的技巧與觀點,例如說 Web security。其實目前所看到的這些文章都是 Google 的員工(Googler)寫的,不過只要你有 Google 的帳號就可以參與創作,一起貢獻你的想法、發表你的開發技巧、評論現有的文章或修改現有的文章。

雖然目前 Google Doctype 上面的內容還不多,不過我想未來應該會有不少東西吧!

相關連結

  

此文章由 will 發表於 2008/5/15 下午 05:34:10

永久連結 | 評論 (0) | 此文章的RSSRSS comment feed |

分類: CSS | JavaScript | Web

標籤: ,

收藏:

介紹好用工具:USB Disk Ejector

有沒有覺得每次要移除 USB Disk 都要按一下「安全的移除硬體」 圖示 ( image ) 很煩,每次都要用滑鼠 Click 一下,等待一秒的時間,然後再點選一下你的 USB 磁碟機才能安全的移除。

最近找到一個不錯的工具叫做 USB Disk Ejector,可以讓你用一行指令就能快速的移除 USB Disk,最主要是可以讓你用批次檔 ( *.bat ) 或設定捷徑就能夠快速移除 USB Disk,例如說我將下載回來的 USB Disk Ejector 程式 ( USB_Disk_Eject.exe ) 放在 C:\Program Files 目錄下,並且寫一支批次檔擺在「桌面」或「快速啟動」裡,要移除的時候只要點選一下就可以移除了,程式如下:

@echo off 

"C:\Program Files\USB_Disk_Eject.exe" /REMOVELETTER K

批次檔程式中的 /REMOVELETTER K 是代表要移除 K 磁碟機的意思,如果你的 USB 磁碟機預設在 G 槽的話,你只要換成 G 即可。

或者你可以建立一個「捷徑(Shortcut)」的方式也可以達到相同的效果,只要參數設定好就沒問題了:

透過設定捷徑設定 USB Disk Ejector

或者更方便的可以用我之前介紹過的 WinKey 工具設定好參數就可快速移除了,如下圖的設定,當我按下 Ctrl + K 的時候就可以立刻移除 USB Disk 了,超級方便的。

設定 WinKey 快速移除 USB Disk

 相關連結

  

此文章由 will 發表於 2008/5/14 下午 11:50:01

永久連結 | 評論 (0) | 此文章的RSSRSS comment feed |

分類: 介紹好用工具

標籤: ,

收藏:

亂數產生器:Random 與 RNGCryptoServiceProvider

一般眾所周知的亂數的產生應該都會使用 Random 類別,而在大部分的情境中使用 Random 類別其實是足夠用的,例如說你想要透過亂數選取目錄中的照片輸出到網頁中,或是將篩選出來的資料亂數排序後輸出到檔案(例如說抽獎程式)。

假設你要用 Random 類別產生 10 組最大值為 100 的亂數(0 ~ 100),可以用以下寫法:

Random rnd = new Random();
for (int i = 0; i < 10; i++)
{
   int randomNumber = rnd.Next(100);
}

詳細的說明與範例可參照 MSDN 文件:Random 類別。

不過 Random 類別是使用「有限性數學演算法」所計算出來的結果(目前 Random 類別的實作是以 Donald E. Knuth 的減法亂數產生器演算法為主),因此計算出來的「亂數」其實不是真的那麼亂,若要將亂數套用在「密碼產生器」或用來產生「強勢金鑰(Strong Key)」的話,就應該使用 System.Security.Cryptography.RNGCryptoServiceProvider 類別幫你產生「夠強」的亂數。

如果要使用 RNGCryptoServiceProvider 類別產生亂數的話可以參考 MSDN 上的範例,或是用我之前常用的一個靜態類別來產生亂數(介面跟 Random 類別類似),如下:

using System;
using System.Security.Cryptography;

/// <summary>
/// 使用 RNGCryptoServiceProvider 產生由密碼編譯服務供應者 (CSP) 提供的亂數產生器。
/// </summary>
public static class RNG
{
    private static RNGCryptoServiceProvider rngp = new RNGCryptoServiceProvider();
    private static byte[] rb = new byte[4];

    /// <summary>
    /// 產生一個非負數的亂數
    /// </summary>
    public static int Next()
    {
        rngp.GetBytes(rb);
        int value = BitConverter.ToInt32(rb, 0);
        if (value < 0) value = -value;
        return value;
    }
    /// <summary>
    /// 產生一個非負數且最大值 max 以下的亂數
    /// </summary>
    /// <param name="max">最大值</param>
    public static int Next(int max)
    {
        rngp.GetBytes(rb);
        int value = BitConverter.ToInt32(rb, 0);
        value = value % (max + 1);
        if (value < 0) value = -value;
        return value;
    }
    /// <summary>
    /// 產生一個非負數且最小值在 min 以上最大值在 max 以下的亂數
    /// </summary>
    /// <param name="min">最小值</param>
    /// <param name="max">最大值</param>
    public static int Next(int min, int max)
    {
        int value = Next(max - min) + min;
        return value;
    }
}

有個這個「夠強」的亂數產生器,就可以用來產生密碼了,如果你要產生一組固定長度 8 碼的密碼,可以用以下程式碼:

System.Text.StringBuilder sb = new System.Text.StringBuilder();
char[] chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".ToCharArray();
int length = RNG.Next(8, 8);
for (int i = 0; i < length; i++)
{
    sb.Append(chars[RNG.Next(chars.Length - 1)]);
}
string YourPassword = sb.ToString();

這樣的密碼並沒有說就比較安全,只能說在演算法的角度來看是比較亂的密碼。如果你只是想要一串「亂亂的文字」的話,我也看過有人直接用 Guid 來當作亂碼。

string RandomString = Guid.NewGuid().ToString();

下面這個是我實做 RNGCryptoServiceProvider 的密碼產生器:

實做 RNGCryptoServiceProvider 的密碼產生器 產生的密碼:[p3JyDfXuf5]

相關連結

  

此文章由 will 發表於 2008/5/13 下午 10:44:43

永久連結 | 評論 (0) | 此文章的RSSRSS comment feed |

分類: .Net | C#

標籤: , , ,

收藏:

在 ASP.NET 頁面中取得 MasterPage 的具型別物件資料

我們在 MasterPage 中的程式是會比 Page 中的程式還早執行的,所以我們其實可以把一些頁面中共用的變數其中放置在 MasterPage 中,例如:登入者資訊、共用的頁面資料、...等資料。

如果要在頁面(Page)中存取MasterPage中的控制項是可以用 this.Master.FindControl 取得,不過要取得 Master 中的物件就必須注意以下幾點:

1. ASPX 頁面必須宣告 <%@ MasterType %> 明確指出該頁面參考到的 MasterPage 類別在哪裡

<%@ Page Language="C#" MasterPageFile="~/MasterPage.master" %>
<%@ MasterType VirtualPath="~/MasterPage.master" %>

2. 透過 ASP 命名空間取得 MasterPage 類別

ASP.masterpage_master PageMaster = this.Master as ASP.masterpage_master;

備註:這個 ASP 命名空間是 ASP.NET 動態編譯機制內建的命名空間,任何透過參考進來的 MasterPage 或 UserControl 都可以透過這個 ASP 命名空間取得物件,你如果在 Visual Studio 中使用 ASP. 就可以透過 Intellisense 得知該頁面可以存取哪些類別了。

這樣一來你就可以在頁面中透過 PageMaster 物件取得在 Masterpage.master 中所定義的所有物件了,範例如下:

MasterPage

<%@ Master Language="C#" %>
<%@ Import Namespace="System.Linq" %>
<script runat="server">
   1:  
   2:     public MyDataContext db;
   3:     public ProductModel m = new ProductModel();
   4:     protected void Page_Load(object sender, EventArgs e)
   5:     {
   6:         db = new MyDataContext();
   7:         var q = from p in db.ProductModel
   8:                 where p.ID.CompareTo(new Guid(Request.QueryString["id"])) == 0
   9:                 select p;
  10:         m = q.First();
  11:         if (m == null)
  12:         {
  13:             Response.Redirect("/", true);
  14:         }
  15:     }
</script>

ASPX Page

<%@ Page Language="C#" MasterPageFile="~/MasterPageProductModel.master" %>
<%@ MasterType VirtualPath="~/MasterPageProductModel.master" %>
<%@ Import Namespace="System.Linq" %>
<script runat="server">
   1:  
   2:     protected ProductModel m = new ProductModel();
   3:     protected void Page_Load(object sender, EventArgs e)
   4:     {
   5:         ASP.masterpageproductmodel_master ProductModelMaster = 
   6:              this.Master as ASP.masterpageproductmodel_master;
   7:         m = ProductModelMaster.m;
   8:     }
</script>

以上的範例可以讓你同時在 MasterPage 與 ASPX 頁面裡同時使用同一個 m 變數,這樣子你在套版的時候程式碼就會乾淨許多,例如:

<h3><%= m.Title %></h3>
<p>
    <%= m.Summary %>
</p>

這樣是不是簡潔許多呢!

  

此文章由 will 發表於 2008/5/12 下午 11:56:11

永久連結 | 評論 (0) | 此文章的RSSRSS comment feed |

分類: ASP.NET

標籤: , ,

收藏:

介紹好用工具:WinMerge

WinMerge 幾乎是我每天必用的軟體之一,他可以幫我比對我在 Subversion 受控管的檔案差異,也可以幫我比對兩個目錄的檔案差異。最主要來說是用來替換掉 TortoiseSVN 內建的 Diff 工具,因為 TortoiseSVN 內建的 Diff 工具對中文字的顯示有些問題,改用 WinMerge 就會好很多。使用 WinMerge 幾乎不用看手冊就會操作了,而且跟檔案總管也整合的十分的好,比對目錄時只要選取兩個資料夾按右鍵選 WinMerge 就會進行比對了。

檔案比對圖例

File Comparison

目錄比對圖例

Folder Comparison Results

安裝好 WinMerge 後的預設「選項」我是建議修改一下,讓使用的時候比較順暢一些,以下是我習慣的設定:

一般:勾選「自動捲至最初差異」

image

比較:比對空白通常沒什麼意義,且檔案多按了個 Enter 也不需要大驚小怪的,斷行符號也不是挺重要的。

image

編碼頁:我們的程式檔案通常都是 UTF-8 編碼,使用「系統用的編碼頁」在繁體作業系統是 Big5,常會造成無法比對的情況。

image

相關連結

  

此文章由 will 發表於 2008/5/11 下午 11:50:19

永久連結 | 評論 (0) | 此文章的RSSRSS comment feed |

分類: Subversion | 介紹好用工具

標籤: , ,

收藏:

ASP.NET 有時必須同時停用 Theme 與 StyleSheetTheme

ASP.NET 2.0 在 Page 類別新增了個 StyleSheetTheme 屬性,可以指定頁面中要載入所有在該 Theme 目錄中的所有 CSS 檔案 ( App_Themes\ThemeName\*.css ),預設來說你只要有指定 Theme 屬性其 StyleSheetTheme 就會跟 Theme 一樣,也就是說該頁面除了會套用 Theme 所需的 *.skin 檔案之外,還會載入所有放在 Theme 目錄下的所有 *.css 檔案。

我通常的習慣是會將整個網站會用到的 Theme 定義在 web.config 中,才不用再每一頁的第一行加上 Theme="XXXXTheme" 的屬性宣告。不過如果你網站中某一頁不想要設定任何 Theme 的時候,你也可能會直覺得想到在頁面第一行宣告 <%@ Page EnableTheming="false" %> 用以關閉 Theme 的套用,但這是有問題的!

我所遇到的問題是設定了 EnableTheming="false" 的確會將所有 *.skin 的套用關閉,但是該 Theme 目錄下的所有 *.css 檔案還是依然會載入到頁面中,導致我的頁面的 Layout 還是被不想載入的 Style Sheet 設定給破壞。我自己是有研究出兩種解決方法:

第一種: 在 App_Theme 目錄下新增一個 None 目錄,裡面不要放任何檔案,並在頁面加上 <%@ Page Theme="None" %> 的設定即可。

第二種: 在頁面中設定 <%@ Page Theme="" StyleSheetTheme="" %> 就可以同時關閉 Theme 與 StyleSheetTheme 了。

相關連結

 

  

此文章由 will 發表於 2008/5/10 下午 09:42:00

永久連結 | 評論 (0) | 此文章的RSSRSS comment feed |

分類: ASP.NET

標籤: ,

收藏:

使用 Regular Expression 驗證密碼

以前我都沒想過可以用 Regular Expressions 驗證複雜格式的密碼,原來可以用 Regular Expression 中的群組建構式達成這個目的,例如說密碼的條件是這樣:

  1. 至少有一個數字
  2. 至少有一個小寫英文字母
  3. 至少有一個大寫英文字母
  4. 字串長度在 6 ~ 30 個字母之間

那麼你的 Regular Expression 可以長這樣:

^(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{6,30}$

在這段 Regular Expression 的語法中值得注意的就是 (?=.*\d) 這段語法,這段於法又稱做「右合樣 (Positive Lookahead)」,右合樣(或左合樣)語法所佔用的寬度為 0,也就是說這段語法本真不會佔用比對的字元,僅僅只是 Regular Expression 中的一種「判斷式」而已,右合樣 (Positive Lookahead)會判斷右邊緊接著的字元是否符合比對條件,如果符合條件才會繼續比對下去。

所以剛剛這段於法的解釋大致可以這樣說明:

(?=.*\d) 與 (?=.*[a-z]) 與 (?=.*[A-Z]) 寬度都會是零,所以整個字串比對會以 .{6,30} 為主,但比對之前會分別比對 (?=.*\d) 與 (?=.*[a-z]) 與 (?=.*[A-Z]) 這三個判斷式,都判斷成功才會進行 .{6,30} 的比對。所以 (?=.*\d) 的 .*\d 就是說右邊的文字中一定要出現一個數字符號,(?=.*[a-z]) 的 .*[a-z] 就是說右邊的文字中一定要出現一個小寫字母,(?=.*[A-Z]) 的 .*[A-Z] 就是說右邊的文字中一定要出現一個大小字母,最後才會比對 .{6,30} 也就是說比對字串的長度必須在 6 ~ 30 個字的任意字元。

如果你的條件修改成:

  1. 至少有一個數字
  2. 至少有一個大寫或小寫英文字母
  3. 至少有一個特殊符號
  4. 字串長度在 6 ~ 30 個字母之間

那麼你的 Regular Expression 可以長這樣(以 C# 作範例):

Regex regex = new Regex(@"^(?=.*\d)(?=.*[a-zA-Z])(?=.*\W).{6,30}$");

這樣一來你知道如何融會貫通了嗎?^_^

相關連結

  

此文章由 will 發表於 2008/5/9 下午 09:10:00

永久連結 | 評論 (0) | 此文章的RSSRSS comment feed |

分類: .Net | ASP.NET | C#

標籤: ,

收藏:

如何查看目前 IIS6 有多少人在看哪些網頁

微軟有推出一個 Internet Information Services Diagnostic Tools 工具程式集,可以幫助你分析 IIS 6 的各種問題,其中有個很棒的工具叫做 Request Viewer 可以做到類似 Apache 中 mod_status 產生出來的伺服器執行狀態。如下圖示:

Apache 的 server-status 畫面

Apache 的 server-status 畫面

啟動 IIS Diagnostic Tools 的 Request Viewer

啟動 IIS Diagnostic Tools 的 Request Viewer

IIS Diagnostic Tools 的 Request Viewer 的執行畫面

 IIS Diagnostic Tools 的 Request Viewer 的執行畫面

不過我一開始執行 Request Viewer 的時候發生了一個小狀況,就是會出現【OpenTraceLogFile() failed ( Win32 error: -2147024735 - 指定的路徑不正確。 )】這個錯誤訊息,如下圖所示:

一開始執行 Request Viewer 的錯誤訊息:OpenTraceLogFile() failed ( Win32 error: -2147024735 - 指定的路徑不正確。 )

這個錯誤是因為系統的 TEMP 環境變數中預設值是 %USERPROFILE%\Local Settings\Temp,如果你登入的帳號是 Administrator 的話,這個路徑就在 C:\Documents and Settings\Administrator\Local Settings\Temp:

系統的 TEMP 環境變數

不過這卻會造成 Request Viewer 無法正確存取暫存目錄,解決之道就是將 TEMP 環境變數「改短一點」,例如說你可以在 C:\ 建立一個 TEMP 目錄,並修改 TEMP 環境變數到 C:\TEMP,這樣一來 Request Viewer 就會正確執行了!

相關連結

  

此文章由 will 發表於 2008/5/8 下午 10:21:49

永久連結 | 評論 (0) | 此文章的RSSRSS comment feed |

分類: IIS | 系統管理

標籤: , ,

收藏: