출처 : http://aspnet.4guysfromrolla.com/articles/051408-1.aspx
Introduction
Most security systems' passwords are case-sensitive. Case sensitivity nearly doubles the number of possible characters that can appear in the password, which makes it harder for nefarious users trying to break into the system. As a result, if a user logging into the system has Caps Lock turned on, they'll enter letters in the opposite case and not be able to login. Because the textboxes that collect user input typically are masked to prevent an onlooker from seeing a user's password, the user typing in her password may not realize that Caps Lock is on. To help prevent this type of user error, many login screens for desktop-based applications display some sort of warning if the user's Caps Lock is on when they start typing in their password.
Unfortunately, such functionality is rarely seen in web applications. A 4Guys reader recently asked me if there was a way to provide such feedback to a user logging into an ASP.NET website. The short answer is, Yes. It is possible to detect whether a user has Caps Lock enabled through JavaScript. Likewise, it's possible to have this client-side logic execute whenever a user starts typing in a particular textbox. With a little bit of scripting, such an interface could be devised.
After thinking about the implementation a bit, I decided to create a compiled server-side control that would emit the necessary JavaScript. This article introduces this control, WarnWhenCapsLockIsOn, and shows how it works and how to use it in an ASP.NET web page. Read on to learn more!
The WarnWhenCapsLockIsOn control is designed to display a page developer-specified message if a user types in a specified TextBox control with Caps Lock on. The control extends the Label class so it has the same set of properties as the Label control:
Text
, Font
, Width
, CssClass
, and so on. Use these properties to configure the message and the message's appearance. In addition to the Label control's properties, the WarnWhenCapsLockIsOn control also has a TextBoxControlId
property, which you must set to the ID
of a TextBox control. This property is required. (To provide a Caps Lock warning for multiple TextBox controls on the page, add a WarnWhenCapsLockIsOn control for each TextBox.) If the user starts typing in the specified textbox with the Caps Lock on, the WarnWhenCapsLockIsOn control is displayed. The WarnWhenCapsLockIsOn control is hidden again when one of the following conditions apply:
- The user types in the same textbox with Caps Lock off.
- The WarnWhenCapsLockIsOn control has been displayed for
WarningDisplayTime
milliseconds. TheWarningDisplayTime
property defaults to a value of 2500, meaning that the control will be displayed for 2.5 seconds after the last character with Caps Lock on has been typed. You can lengthen or shorten this property value as needed. A value of 0 displays the warning until the user types in a character without Caps Lock on or after there's a postback.
To provide a warning to the user if Caps Lock is on, then, you'll add this control to the page, set its Text
property (and, perhaps, other formatting properties), and indicate the TextBox to "watch" via the control's TextBoxControlId
property. That's all there is to it! We'll discuss how to add the WarnWhenCapsLockIsOn control to your ASP.NET website project and look at an end-to-end example in the "Using the WarnWhenCapsLockIsOn Control in an ASP.NET Web Page" section later on in this article.
Initially Hiding the WarnWhenCapsLockIsOn Control
Whenever the ASP.NET page is loaded in the user's browser, the WarnWhenCapsLockIsOn control needs to be hidden so that it does not appear. The WarnWhenCapsLockIsOn control should only appear when the user starts typing in the specified textbox with Caps Lock on. To initially hide the WarnWhenCapsLockIsOn control, I overrided the control's AddAttributesToRender
method and added code that set the visibility
and display
style attributes to "hidden" and "none", respectively.
public class WarnWhenCapsLockIsOn : System.Web.UI.WebControls.Label |
The effects of these style attribute settings are evinced by the markup sent to the browser. Because the WarnWhenCapsLockIsOn control extends the Label class, it renders as a <span>
element with the value of its Text
property rendered as the <span>
element's inner content, just like the Label control. In the style
attribute you can see that the visibility
and display
settings have been configured to hide the control:
<span id="ID" style="visibility:hidden;display:none;">Value of Text property</span> |
We now need to write JavaScript that sets the visibility
and display
settings to "visible" and "inline" whenever the user types into the corresponding textbox with Caps Lock on.
Executing JavaScript in Response to Typing in a Textbox
It is possible to execute JavaScript in response to a variety of client-side events, such as when the document has loaded, when the user changes the selection of a drop-down list, when an input field receives (or loses) focus, or when the user presses a key. To execute client-side script in response to a user pressing a key, use the onkeypress
event like so:
<input type="text" ... onkeypress="action" /> |
The action can be inline script or a call to a function. In addition to noting that a key has been pressed, JavaScript offers the event
object, which includes information such as what key was pressed and whether or not the Shift key was depressed, among other useful tidbits. In short, we need to add an onkeypress
client-side event handler to the TextBox control referenced by the WarnWhenCapsLockIsOn control's TextBoxControlId
property. The event handler called must determine whether Caps Lock is on or off and show or hide the WarnWhenCapsLockIsOn control in response.
To add client-side event handlers to a server-side Web control use the Attributes
collection like so:
WebControlID.Attributes["attribute"] = "value"; |
The above code adds the markup attribute="value"
to the Web control's rendered output. Usually this sort of logic is applied during the PreRender stage of the page lifecycle. Therefore, to implement this functionality I overrode the OnPreRender
method in the WarnWhenCapsLockIsOn class (which is fired during the PreRender stage) and added the following code:
public class WarnWhenCapsLockIsOn : System.Web.UI.WebControls.Label |
The GetTextBoxControl
method (not shown in this article) returns a reference to the TextBox control specified by the TextBoxControlId
property. This referenced TextBox has its Attributes
collection updated to include an onkeypress
event handler that calls the JavaScript function skm_CheckCapsLock
. The skm_CheckCapsLock
function (which we'll examine shortly), is passed three input parameters:
event
- information about thekeypress
event, including what key was pressed and whether the Shift key was depressed.- The value of the WarnWhenCapsLockIsOn's
ClientID
property - we need to supply the client-sideid
of the WarnWhenCapsLockIsOn control so that it can be displayed or hidden, depending on whether the user has Caps Lock on. WarningDisplayTime
- the number of milliseconds to display the Caps Lock warning; defaults to 2500 milliseconds (2.5 seconds).
The WarnWhenCapsLockIsOn control is one of a few controls in my open-source control library, skmControls2. Another control in the same library is the TextBoxCounter control, which interactively displays how many characters the user has currently typed into a given textbox. (For more information on the TextBoxCounter control, read: Creating a TextBox Word / Character Counter Control.) The TextBoxCounter control requires a bit of JavaScript, and that JavaScript is already defined in a file named skmControls2.js
. This file is compiled as an embedded resource in the assembly and is loaded into the ASP.NET page on which the TextBoxCounter or WarnWhenCapsLockIsOn controls are used via a call to the GetWebResourceUrl
method. For more information on this technique see: Accessing Embedded Resources through a URL using WebResource.axd
.
Because the skmControls2 library already has a JavaScript file, I decided to place the skm_CheckCapsLock
function (and ancillary helper functions) there. It is injected into the ASP.NET page in the OnPreRender
method (see step 1 in the comments in OnPreRender
).
Determining Whether Caps Lock is On
When the keypress
event is raised in client-side script, the event
object contains information as to what key was pressed and whether the Shift key was depressed (along with whether Alt and Ctrl were depressed). It does not, however, indicate whether Caps Lock was on. The good news is that with a bit of code we can determine whether Caps Lock is on. If the user has entered a capital letter and the Shift key is not depressed, then Caps Lock must be on; likewise, if the user enters a lowercase letter and the Shift key is depressed, then Caps Lock is on.
The skm_CheckCapsLock
function determines whether Caps Lock is on and, if so, calls the skm_ShowCapsWarning
function, which displays the WarnWhenCapsLockIsOn control for a specified interval. If Caps Lock is not on then the skm_HideCapsWarning
function is called, which hides the WarnWhenCapsLockIsOn control. The skm_CheckCapsLock
function uses a modified version of a script created by John G. Wang, available online at http://javascript.internet.com/forms/check-cap-locks.html.
function skm_CheckCapsLock( e, warnId, dispTime ) { |
The keen reader will note that this check doesn't really identify when Caps Lock is on or off. Rather, it identifies if Caps Lock is on or off when typing in an alphabetic character. If the user types in the number "9" or presses the left arrow, the first conditional statement will evaluate to false
. In other words, the skm_ShowCapsWarning
or skm_HideCapsWarning
functions are only called when the user enters an alphabetic character.
The skm_ShowCapsWarning
and skm_HideCapsWarning
functions are not shown in this article; you'll have to download the source code to inspect them. They're both fairly straightforward: both reference the WarnWhenCapsLockIsOn control and then set the visibility
and display
properties to show or hide the warning message. The only trickiness deals with the code that displays the warning for at most a specified number of milliseconds after the user types in with Caps Lock on. Specifically, these functions use the JavaScript setTimeout
and clearTimeout
methods. For more information on these functions, see the JavaScript Timing Events tutorials at W3 Schools.
Using the WarnWhenCapsLockIsOn Control in an ASP.NET Web Page
The download available at the end of this article includes the complete source code for the WarnWhenCapsLockIsOn controls, as well as a demo ASP.NET website. To use the skmControls2
controls in an ASP.NET website, copy the DLL to the website's /Bin
directory and then add the following @Register
directive to the tops of the .aspx
pages where you want to use the controls:
<%@ Register Assembly="skmControls2" Namespace="skmControls2" TagPrefix="skm" %> |
(Alternatively, you can add this @Register
directive in the Web.config
file so that you do not need to add it to every ASP.NET page that uses the controls. See Tip/Trick: How to Register User Controls and Custom Controls in Web.config
.)
The demo included in the download has an ASP.NET page that shows using the WarnWhenCapsLockIsOn control in two scenarios: on a stand-alone TextBox and on the Password TextBox in the Login control. Let's look at the stand-alone TextBox example first:
<b>Please enter your name:</b> |
The declarative markup above shows three controls: a TextBox (YourName
); a WarnWhenCapsLockIsOn control; and a Button Web control. The WarnWhenCapsLockIsOn control is configured to display the message "Warning: Caps Lock is on" when the user types into the YourName
textbox with Caps Lock on. It uses the CSS class CapsLockWarning
for its styling, which I've defined on the page; the class specifies a light yellow background, padding, centered text, and a red border. The warning message is displayed for (at most) five seconds.
The following screen shot shows the demo page in action. Note that when Caps Lock is on and an alphabetic character is typed, the warning is displayed. This warning immediately disappears when Caps Lock is turned off and another alphabetic character is typed, or after five seconds, whatever comes first.
This demo also shows how to apply the WarnWhenCapsLockIsOn control to the Password TextBox of a Login control. Start by adding a Login control to the page and then, from the Designer, select the "Convert to Template" from its smart tag. This will convert the Login control into a template, at which point you can add the WarnWhenCapsLockIsOn control in the Login user interface as needed, referencing the Password TextBox (whose ID
property is "Password"). Be sure to put the WarnWhenCapsLockIsOn control within the Login control's template so that it can "see" the Password TextBox.
Happy Programming!
'.NET > ASP.NET' 카테고리의 다른 글
aspnet request processing cycle (0) | 2010.02.02 |
---|---|
[펌] How to call client-side Javascript function after an UpdatePanel asychronous (Ajax) request is over (0) | 2009.09.30 |
[펌] 동적으로 웹 서버 컨트롤 템플릿 만들기 (0) | 2009.09.29 |
닷넷 웹서비스 에서 WebLogic WebService Call 시 Connection 끊기는 문제... (0) | 2008.08.25 |
asp.net TreeView IE 7 에서 스타일 깨질때... (0) | 2008.07.28 |
27 Responses
Joel Chinchilla - April 17, 2009
Hi, I tried to test your example with a Java Web Service, however I need to pass credentials to Web Service. How can I do it?
Neena - May 7, 2009
Can I call this code from java
crow - May 10, 2009
Not that I am aware of.
Sergio - May 14, 2009
Hi!
finally a good example on how to call WS dynamically!
I tryed your code, and got this problem:
- my WS is as follows:
public class Service1 : System.Web.Services.WebService
{
public Service1()
{
InitializeComponent();
}
[WebMethod]
public double Sum(double num1, double num2)
{
if ( guid == “”)
return -1;
return num1 + num2;
}
}
couldn’t be as simpler as this.
When i run your code, it does in fact create an assemby whith the correct Service1 classname and the Sum method.
So everything seems to be ok.
Problem is when I invoke Sum method it sends an exception saying that “Method ‘Service1.Sum’ not found” on line :
return (T)type.InvokeMember(methodName, BindingFlags.InvokeMethod, null, obj, args);
Any clue?
thanks in advance!
shortcuts - May 18, 2009
Off-topic Help - Need a secure web proxy to get around my school firewall. Any suggestions?
Jeremy Foster - May 19, 2009
I have this implemented and working but I have run into a limitation. I can not figure out how to capture multiple returned values in the SOAP response. Do you know of a way to do this?
Sergio - May 19, 2009
Jeremy,
what do you mean with “multiple returned values in the SOAP response”. A method only returns 1 value/object. The value itself can have a collection of values/objects. Is this what you meant?
You may try to transform the returned value to XML. Use this:
public static string SerializeObjectToXML(T obj)
{
try
{
string xmlString = null;
MemoryStream memoryStream = new MemoryStream();
System.Xml.Serialization.XmlSerializer xs = new System.Xml.Serialization.XmlSerializer(typeof(T));
string s = “”;
XmlTextWriter xmlTextWriter = new XmlTextWriter(memoryStream, Encoding.UTF8);
System.Runtime.Serialization.Formatters.Soap.SoapFormatter xsoap = new System.Runtime.Serialization.Formatters.Soap.SoapFormatter();
xsoap.Serialize(memoryStream, obj);
xmlString = UTF8ByteArrayToString(memoryStream.ToArray());
return xmlString;
}
catch(Exception ex)
{
ex.GetType();
return string.Empty;
}
}
public static T UnserializeObjectFromXML(string xml)
{
System.Xml.Serialization.XmlSerializer xs = new System.Xml.Serialization.XmlSerializer(typeof(T));
MemoryStream memoryStream = new MemoryStream(StringToUTF8ByteArray(xml));
XmlTextWriter xmlTextWriter = new XmlTextWriter(memoryStream, Encoding.UTF8);
return (T)xs.Deserialize(memoryStream);
}
The Serialize method uses the SoapFormatter class, and in fact serializes the returned object just like what the Webservice call does.
Izzy - May 22, 2009
Hi, Crows.
i get this webpage/artical by google.
i got one question.
if the input parameter of the method is normal types, like string, int etc, then it is ok.
right now, I got one webservie, and its input parameter of the function is like this
string
string
int
int
int
int
int
int
int
int
there is only one input parameter : TestMessage_input.
it is a type which is similiar with Struct.
I’m confused about how to forming such an input parameter in GUI???
thanks
your reply will be appreciated and helpful.
Izzy - May 22, 2009
oh,some infos are missing.they were deleted automatically.
would you mind telling me your email?
since i really have some confusions about using this.
is that ok?
Shaun - May 22, 2009
Excellent routine! I’m having a problem using this class however. When I call “return results.CompiledAssembly;” I get the following exception:
Execution permission cannot be acquired.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.Security.Policy.PolicyException: Execution permission cannot be acquired.
Source Error:
An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.
Stack Trace:
[PolicyException: Execution permission cannot be acquired.]
System.Security.SecurityManager.ResolvePolicy(Evidence evidence, PermissionSet reqdPset, PermissionSet optPset, PermissionSet denyPset, PermissionSet& denied, Boolean checkExecutionPermission) +10236136
System.Security.SecurityManager.ResolvePolicy(Evidence evidence, PermissionSet reqdPset, PermissionSet optPset, PermissionSet denyPset, PermissionSet& denied, Int32& securitySpecialFlags, Boolean checkExecutionPermission) +97
[FileLoadException: Could not load file or assembly 'm2wivo6v, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. Failed to grant permission to execute. (Exception from HRESULT: 0x80131418)]
System.Reflection.Assembly._nLoad(AssemblyName fileName, String codeBase, Evidence assemblySecurity, Assembly locationHint, StackCrawlMark& stackMark, Boolean throwOnFileNotFound, Boolean forIntrospection) +0
System.Reflection.Assembly.InternalLoad(AssemblyName assemblyRef, Evidence assemblySecurity, StackCrawlMark& stackMark, Boolean forIntrospection) +416
System.Reflection.Assembly.Load(AssemblyName assemblyRef, Evidence assemblySecurity) +33
System.CodeDom.Compiler.CompilerResults.get_CompiledAssembly() +105…
I’m running this code in SharePoint 2007 on Windows Server 2008.
vikram - May 26, 2009
It’s working fine but application memory increases when web service call are made 2500 times.
Can we delete the temporary files created while compiling assembly ?
marmri - May 31, 2009
Hi,
Thank you for this great example.
I have the same problem as Sergio - May 14, 2009
Did you wirte him any suggestion? Please forward it or add it in this article for the whole aucience!
I have another question: how to do, if you have following situation:
MyWebReference.GoodbyeWorldWSService myProxy = new MyWebReference.GoodbyeWorldWSService();
MyWebReference.sayGoodbye request = new MyWebReference.sayGoodbye();
request.message = “ciao”;
myProxy.sayGoodbye(request);
I mean, you have like an object as parameter you first initialize then invoke the service method!
Please help, is urgent. Thanx a lot in advance
Jeff - June 13, 2009
Hi As I walk on the code.. i see that the reason why its throwing an exception on this line (T)type.InvokeMember(methodName, BindingFlags.InvokeMethod, null, obj, args); it is maybe because you are calling a method with complex (custom) type and not a generic type. try to have a breakpoint on that line and see the details of the “type” variable.
shail - June 27, 2009
I am using your example. But its giving me an error on InvokeMember that “The request failed with HTTP status 401: Access Denied” Is there anything special, i will have to do here?
Thanks,
Shail …
RadoColo - June 30, 2009
Beautiful, works like a charm!
usmanbinasif - July 6, 2009
Dear it is awesome code example of this problem domain, and it is very helpful for me.
Thanks for sharing such a wonderful peice of code.
Regards,
Usman Javed
newbieDotNET - July 7, 2009
Will it be possible to invoke or set the timeout property? Any help would be greatly appreciated.
Thanks,
Jon
David Roys - July 8, 2009
What an awesome example. Well explained and a great download! Thank you for your time and effort. Unfortunately it doesn’t quite work for me.
I am also having the problem with authentication that shail posted. I know that when I use my web service by generating the proxy class, I need to use the line:
ws.UseDefaultCredentials = true in order to be able to user the service.
I am getting the following error:
Unable To Load Service: The remote server returned an error: (401) Unauthorized.
when I click the Load Service button and I can only assume it is because the XMLTextReader is not negotiating my credentials. If I use Internet explorer to hit the URL it will give me the WSDL but I believe this is because it handles the challenge response correctly. Do I need to use another method of getting my WSDL - i.e. not using XMLTextReader?
Thanks again for such a great post! I learnt a lot.
Mark - July 8, 2009
Good work, excellent explanation.
functions that expect strings do work, but functions that expect other types (int, byte[], …) do not work.
Any idea how to solve this?
Thanks
Mark
David Roys - July 10, 2009
Hi there, OK, I thought I would let other readers know that I managed to sort out my problems with authentication.
You can read the details on a blog post I made http://www.teachmenav.com/blogs/dave/archive/2009/07/11/using-reflection-to-call-nav-2009-web-services.aspx
Thanks again for the great post.
Crows Programming » I’m Back - July 15, 2009
[...] Hey guys, sorry I’ve been away for a bit. I’ll try and respond to your questions, especially those over on the C# dynamic web service page. [...]
MaTZ - July 22, 2009
Hi,
I have a problem with http authentication.
I managed to download WSDL by modifying BuildAssemblyFromWSDL like this:
System.Net.WebClient client = new System.Net.WebClient();
if (login.Length > 0)
{
client.Credentials = new NetworkCredential(login, password);
}
System.IO.Stream stream = client.OpenRead(webServiceUri.ToString() + “?wsdl”);
XmlTextReader xmlreader = new XmlTextReader(stream);
ServiceDescriptionImporter descriptionImporter = BuildServiceDescriptionImporter(xmlreader);
return CompileAssembly(descriptionImporter);
Now I’m getting exception (error 401) later when I try to invoke web method. Any ideas?
Thanks for the post.
facebook proxy - July 25, 2009
Interesting article. Were did you got all the information from…
proxies - July 25, 2009
You are a very smart person!
Andrew Brown - August 4, 2009
Very useful post. I have been using this technique for over a year.
I am having a problem where the web service request takes longer than 110 seconds to return a response. This causes a timeout error. I have been troubleshooting the timeout issue by changing all configurable timeout settings from the application setting (web.config) and IIS.
[Error Details]
System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. —> System.Net.WebException: The operation has timed out.
I setup a simple test where the web server thread sleeps for 2 mins and 30 seconds.
Please post if anyone has solved this.
Donald Drake - August 5, 2009
““Method ‘Service1.Sum’ not found” ”
I was able to fix this error by using the following instead.
MethodInfo method = type.GetMethod(methodName);
return (T) method.Invoke(instance, args);
I believe the issue is due to the fact that it is using BindingFlags.InvokeMethod.. I was not able to use any other bindingflag with the type.InvokeMember method.
Hope this helps.
Thanks,
Don
Andrew Brown - August 5, 2009
I found a solution to the Timeout issue. I had to set the Timeout property of the proxy instance.
[CODE]
PropertyInfo propInfo = obj.GetType().GetProperty(”Timeout”);
propInfo.SetValue(obj, 300000 /* set this to whatever timeout period you want */, null);
Then invoke the method.