WEB Service for WEB Service & WCF & WebApi authentication, WEBService
In this era of WEB APIs, the WEB Service technology seems outdated. outdated technologies do not mean that they are useless, and they can still be used in some places, the reason why I will explain WEB Service is that I was asked related questions during my recent interview. I will review and summarize it here to show the directions for new users. You can ignore it, of course, please kindly advise on the shortcomings. Thank you!
There are many related articles on the Internet for WEB Service authentication, which are summarized as follows:One or more authentication parameters are added based on custom SoapHeader authentication, Form authentication, integrated Windows authentication, and service methods.Below, we will not talk nonsense. Let's share the implementation code directly. There are some important points in the middle. I will have instructions.
1. Custom SoapHeader-based verification
Definition service: (Note that UserValidationSoapHeader must have no parameter constructor; otherwise, serialization fails)
// UserValidationSoapHeader: public class UserValidationSoapHeader: SoapHeader {public string UserName {get; set;} public string Password {get; set;} public UserValidationSoapHeader () {} public bool IsValid () {if (string. isNullOrEmpty (UserName) | string. isNullOrEmpty (Password) {throw new Exception ("the user name and Password cannot be blank! ");} If (! String. Equals (UserName, "admin") |! String. Equals (Password, "123456") {throw new Exception ("incorrect user name or Password! ");} Return true;} // SoapHeaderWebService: [WebService (Namespace =" http://www.zuowenjun.cn ")] [WebServiceBinding (ConformsTo = WsiProfiles. basicProfile1_1)] [System. componentModel. toolboxItem (false)] // to allow ASP. net ajax calls this Web service from the script. Please cancel the comments to the downstream. [System. web. script. services. scriptService] public class SoapHeaderWebService: System. web. services. webService {public UserValidationSoapHeader userValidation; [WebMethod (Description = "greetings")] [SoapHeader ("userValidation")] public string HelloTo (string name) {if (userValidation. isValid ()&&! String. IsNullOrEmpty (name) {return "Hello" + name;} return "Hello World! ";}}
The client first references the service through the service address. There are two types of service references: Service reference and WEB Service reference (the service references will not be repeated later)
Service reference: (here the WEB Service proxy class is generated according to the WCF Mode)
WEB service reference:
(If you right-click the project, you can directly click "add WEB reference" to display it)
The client uses the following:
private void CallWebServiceFromWebReference() { try { var svc = new RefWebSoapHeaderWebService.SoapHeaderWebService(); svc.UserValidationSoapHeaderValue = new RefWebSoapHeaderWebService.UserValidationSoapHeader() { UserName = "admin", Password = "123456" }; string result = svc.HelloTo(TextBox1.Text); Label1.Text = result; } catch (Exception ex) { Label1.Text = ex.Message; } } private void CallWebServiceFromServiceReference() { try { var svc = new RefSoapHeaderWebService.SoapHeaderWebServiceSoapClient(); var header = new RefSoapHeaderWebService.UserValidationSoapHeader(); header.UserName = "admin"; header.Password = "123456"; string result = svc.HelloTo(header, TextBox1.Text); Label1.Text = result; } catch (Exception ex) { Label1.Text = ex.Message; } } protected void Button1_Click(object sender, EventArgs e) { if (RadioButtonList1.SelectedValue == "1") { CallWebServiceFromServiceReference(); } else { CallWebServiceFromWebReference(); } }
The above Code makes different instances for the two WEB service references. The key is the method call. A soapHeader parameter is automatically added to the service method parameters generated under the service reference, WEB Service references do not. I feel that it is convenient to use WEB service references based on this verification, because you only need to assign values to the soapHeader instance to call different service methods multiple times.
2. Form Verification
Define service:
// WEB service dedicated for Logon: [WebService (Namespace =" http://www.zuowenjun.cn ")] [WebServiceBinding (ConformsTo = WsiProfiles. basicProfile1_1)] [System. componentModel. toolboxItem (false)] // to allow ASP. net ajax calls this Web service from the script. Please cancel the comments to the downstream. [System. web. script. services. scriptService] public class FormAuthWebService: System. web. services. webService {[WebMethod] public bool Login (string username, string password) {return UserBusiness. login (username, password) ;}// normal WEB Service: [WebService (Namespace =" http://www.zuowenjun.cn ")] [WebServiceBinding (ConformsTo = WsiProfiles. basicProfile1_1)] [System. componentModel. toolboxItem (false)] // to allow ASP. net ajax calls this Web service from the script. Please cancel the comments to the downstream. [System. Web. Script. Services. ScriptService] public class FormAuthHelloWebService: System. Web. Services. WebService {[WebMethod] public string HelloTo (string name) {if (! String. isNullOrEmpty (name) {return "Hello" + name;} return "Hello World";} [WebMethod] public void Logout () {UserBusiness. logout () ;}// business auxiliary class: public static class UserBusiness {public static bool Login (string userName, string password) {if (string. isNullOrEmpty (userName) | string. isNullOrEmpty (password) {throw new Exception ("the user name and password cannot be blank! ");} If (! String. Equals (userName, "admin") |! String. Equals (password, "123456") {throw new Exception ("incorrect user name or password! ");} SaveLoginStauts (userName); return true;} public static bool IsAuthenticated () {return HttpContext. current. request. isAuthenticated;} public static void Logout () {System. web. security. formsAuthentication. signOut ();} private static void SaveLoginStauts (string userName) {System. web. security. formsAuthentication. setAuthCookie (userName, false );}}
From the code above, we can see that the logon status is saved Based on ASP. net form verification. Other codes are similar to normal ones.
Because ASP. net form authentication is adopted, the following configuration must be added to web. config:
// In system. add the following configuration to the web node because I put all the WEB Services under the Services Directory, therefore, only the directory is restricted. <authentication mode = "Forms"> <forms name = "auth. webservice "loginUrl = "~ /Services/FormAuthWebService. asmx/Login "> </forms> </authentication> <webServices> <protocols> <add name =" HttpSoap "/> <add name =" HttpPost "/> <add name = "HttpGet"/> <add name = "Documentation"/> </protocols> </webServices> </system. web> <location path = "Services"> <system. web> <authorization> <deny users = "? "/> </Authorization> </system. web> </location>
Note the following two points:
1. The above loginUrl = "~ /Services/FormAuthWebService. asmx/Login ". Here, I directly switch to Login without logon because if Login is not added, you will not be able to perform logon debugging and other service method debugging on the WEB browser.
2. because FORM authentication is adopted and anonymous access is prohibited, services cannot be referenced normally because service reference is also an HTTP request. By default, services are not logged on, if you reference a restricted service address, it will automatically jump to the logon service address. To address this problem, I have adopted two methods: First, do not prohibit anonymous access, after the service is successfully referenced, add the configuration. The second method is: do not directly reference the service and use the code to dynamically request the service method for relevant calls. The client uses the following code:
// First type: Service reference: protected void Button2_Click (object sender, EventArgs e) {try {CookieContainer cookieContainer = new CookieContainer (); var svc = new RefFormAuthLoginWebService. formAuthWebService (); svc. cookieContainer = cookieContainer; // shared cookie container if (svc. login ("admin", "123456") {var svc2 = new RefFormAuthWebService. formAuthHelloWebService (); svc2.CookieContainer = cookieContainer; // Label2.Text = svc2.HelloTo (TextBox2.Text);} catch (Exception ex) {Label2.Text = ex. message; }}// type 2: Dynamic Request call: protected void Button2_Click (object sender, EventArgs e) {try {CookieContainer cookieContainer = new CookieContainer (); var result = CallWebServiceFromHttpWebRequest ("FormAuthWebService. asmx/Login "," username = admin & password = 123456 ", cookieContainer); Label2.Text = result. selectSingleNode ("// ns: boolean", GetXmlNamespaceManager (result. nameTable )). innerText; var result2 = CallWebServiceFromHttpWebRequest ("FormAuthHelloWebService. asmx/HelloTo "," name = "+ HttpUtility. urlEncode (TextBox2.Text, Encoding. UTF8), cookieContainer); Label2.Text = result2.SelectSingleNode ("// ns: string", GetXmlNamespaceManager (result2.NameTable )). innerText; catch (Exception ex) {Label2.Text = ex. message ;}
The auxiliary method is defined as follows:
Private XmlDocument CallWebServiceFromHttpWebRequest (string serviceUrl, string serviceParams, CookieContainer cookieContainer) {HttpWebRequest request = (HttpWebRequest) WebRequest. Create (" http://localhost:8768/Services/ "+ ServiceUrl); request. method = "POST"; request. contentType = "application/x-www-form-urlencoded"; request. credentials = CredentialCache. defaultCredentials; byte [] data = Encoding. UTF8.GetBytes (serviceParams); // request parameter. contentLength = data. length; request. cookieContainer = cookieContainer; // share cookie container Stream writer = request. getRequestStream (); writer. write (data, 0, data. length); writer. close (); WebResponse response = request. getResponse (); StreamReader sr = new StreamReader (response. getResponseStream (), Encoding. UTF8); String retXml = sr. readToEnd (); sr. close (); XmlDocument doc = new XmlDocument (); doc. loadXml (retXml); return doc;} private XmlNamespaceManager GetXmlNamespaceManager (XmlNameTable table) {XmlNamespaceManager nsMgr = new XmlNamespaceManager (table); nsMgr. addNamespace ("ns "," http://www.zuowenjun.cn "); Return nsMgr ;}
Here, whether using service reference or dynamic call service, you must ensure that the same COOKIE container is referenced. When dynamically calling the service, you must note that the final returned result is XML, the specified value must be parsed. For more information, see C # operating xml SelectNodes. SelectSingleNode always returns NULL and xPath. after reading it, you will know that it is an XML namespace problem. You can also remove the namespace, which makes parsing much easier. The following code:
// Here is the obtained XML. Call the method to remove the namespace: String retXml = RemoveNamespace (sr. readToEnd (); private string RemoveNamespace (string xml) {var reg = new System. text. regularExpressions. regex ("xmlns = \". * \ "", System. text. regularExpressions. regexOptions. ignoreCase); return reg. replace (xml, "");} protected void Button2_Click (object sender, EventArgs e ){... var result = CallWebServiceFromHttpWebRequest ("FormAuthWebService. asmx/Login "," username = admin & password = 123456 ", cookieContainer); Label2.Text = result. selectSingleNode ("// boolean "). innerText; var result2 = CallWebServiceFromHttpWebRequest ("FormAuthHelloWebService. asmx/HelloTo "," name = "+ HttpUtility. urlEncode (TextBox2.Text, Encoding. UTF8), cookieContainer); Label2.Text = result2.SelectSingleNode ("// string "). innerText ;...}
3. Integrated Windows authentication. This is very simple. First, set IIS to integrated WINDOWS authentication without special code.
The client uses the following:
Test. webReference. service1 service = new Test. webReference. service1 (); // generate a web service instance service. credentials = new NetworkCredential ("user", "123456"); // the user is the user name and must have certain permissions for lblTest. text = service. helloTo ("zuowenjun"); // call the web service Method
WEB page sample code:
<Form id = "form1" runat = "server"> <fieldset> <legend> call the service after Authentication Based on the custom SoapHeader </legend> <div> <asp: radioButtonList ID = "RadioButtonList1" runat = "server" RepeatDirection = "Horizontal"> <asp: ListItem Value = "1"> From Service Reference </asp: ListItem> <asp: listItem Value = "2"> From Web Reference </asp: ListItem> </asp: RadioButtonList> <asp: textBox ID = "TextBox1" runat = "server"> </asp: TextBox> <asp: button ID = "Button1" runat = "server" Text = "Button" onclick = "button#click"/> <br/> <asp: label ID = "Label1" runat = "server" Text = "Label"> </asp: label> </div> </fieldset> <p> </p> <fieldset> <legend> customized call the service after Form authentication </legend> <div> <asp: textBox ID = "TextBox2" runat = "server"> </asp: TextBox> <asp: button ID = "Button2" runat = "server" onclick = "Button2_Click" Text = "Button"/> <br/> <asp: label ID = "Label2" runat = "server" Text = "Label"> </asp: label> </div> </fieldset> <p> </p> <fieldset> <legend> customized call the service after Windows verification </legend> <div> <asp: textBox ID = "TextBox3" runat = "server"> </asp: TextBox> <asp: button ID = "Button3" runat = "server" Text = "Button" onclick = "Button3_Click"/> <br/> <asp: label ID = "Label3" runat = "server" Text = "Label"> </asp: Label> </div> </fieldset> </form>
The final result is as follows:
Add a knowledge point:
When the above Code verifies user logon, if the verification fails, I directly throw an Exception, and the WEB service will be encapsulated as a SoapException type before returning to the client, in this way, you cannot directly view the error content on the client. Therefore, you can use the method in this article "encapsulate SoapException to handle Webservice exceptions" to add the method for formatting SoapException and the method for parsing SoapException.
Due to my work time, I am in a hurry and have some shortcomings. Please point out, thank you !~ V ~