Repost the author and source. Thank you.
I heard about dotnetopenauth one day last year. When I was reading restful web service, it suddenly seemed like a flash. I think the issue of authorization should be clearly considered before building the service, otherwise, it seems impossible to talk about servitization. For example, Google canlendar is a service. Now you use Google canlendar to build another service, and you are lucky to have some users, but how can these users safely send Google account information to you for Google verification? What's more, there are many projects in our company. After each new project is created, copy a public file such as organization and usermanager (mainly on the UI Layer) to the file. If you increase the workload, updating these copies is a severe challenge, let alone, if a customer uses both of our products at the same time, they will find that the same person wants to maintain users with the same number of software products... all of this makes the requirement to turn components such as organization into services very strong, that is, the organization business itself as an application.ProgramYes. After being published in IIS, other projects can use the data service provided by IIS. This eliminates the need to copy the UI and multiple sets of data between multiple products. By now, SSO seems inevitable, but there is a problem that has not been taken into account. When using web services, it will not always be called directly using pages, most of the time, you must provide a service client component. Otherwise, someone will call your data each time and add a bunch of authorization and cookieCode? Therefore, it seems that using simple SSO implementation is difficult to solve problems efficiently and in a decent manner.
So I found that there was something like dotnetopenauth (I really don't know why it was walled, I want to see it, the source code hosting address, it seems that the introduction covers all the features I want!
The document may be the most fascinating and hateful thing. I don't want to write documents, but I want to use other people's things with complete documents.
I learned jqgrid two days ago. Without a decent document, it would be very painful. After one day, only Google's search records are available in history. At that time, the download of dotnetopenauth was the same. Except for an API token, there was no valuable information. Although they provided several examples, for a complicated technology, these are far from enough. At least you have to have a quick start or how to. Unfortunately, there is almost nothing on the official website. I don't know why. I didn't go back to this component later.
Until two days ago, I still felt that if we wanted to consider whether it was web service, SOA practice, or even the hot cloud computing, it would be impossible if the authorization problem could not be solved. Many books use a lot of space to explain how to design and implement a service, but few mention the SOA practice or cloud computing practice. As a result, the so-called web services are launched one by one after another, but it does not look like a "Cloud ". To solve the authorization problem, it is better to start with dotnetopenauth. It has powerful functions, and oauth and openid are mature products. There are many companies using it and they have almost become the de facto standard, find a point that is close to the current job and learn about openidssoprovider.
(This articleArticleI want to know how to write it. Why? I think SOA or cloud computing is a very ethereal thing. After a while, I always think that the concept is still clear, but it will be blurred after a long time, it is a trivial matter to write things that you are not quite clear about. I am a postpaid viewer, but there are few things about service design practices on the Internet, and dotnetopenauth is rare, however, I don't have to read the post, so I have the right to think about it. I hope I can discuss it with people who have a common idea)
After talking so much nonsense, go to the topic...
Dotnetopenauth provides an ssoprovider example, but only the webform project does not have the ssoprovider example of MVC. This article provides the ssoprovider Implementation Method of MVC. By the way, let's talk about my personal experience in using dotnetopenauth.
1. ssoop SSO service provider
1. Create an ssoop project. I used the razor view engine to add dotnetopenauth. dll reference. See the aboveSource code hosting address.
2. Set the configuration information in the web. config file. For details, see the sample program below this article.
3. Create openidcontroller. CS
Public class openidcontroller: controller {internal static openidprovider = new openidprovider (); Public actionresult identifier () {If (user. Identity. isauthenticated & providerendpoint. pendingauthenticationrequest! = NULL) {util. processauthenticationchallenge (providerendpoint. pendingauthenticationrequest); If (providerendpoint. pendingauthenticationrequest. isauthenticated. hasvalue) {providerendpoint. sendresponse () ;}} if (request. accepttypes. contains ("application/xrds + XML") {return New transferresult ("~ /Openid/xrds ");} return view ();} [validateinput (false)] public actionresult provider () {var request = openidprovider. getrequest (); If (request! = NULL) {If (request. isresponseready) {return openidprovider. prepareresponse (request ). asactionresult ();} providerendpoint. pendingrequest = (ihostprocessedrequest) request; var idrequest = request as iauthenticationrequest; return util. processauthenticationchallenge (idrequest);} return view ();} public actionresult askuser () {return view ();} public actionresult xrds () {return view ();}}
Openidcontroller is the entry point for ssorp (SSO consumer) to use op, where provider is the action that provides the login service, which needs to be mentioned later.
4. Create xrds. cshtml View
@ {Layout = NULL; response. contenttype = "application/xrds + XML"; var uri = new uri (request. url, response. applyapppathmodifier ("~ /Openid/provider "). tostring () ;}<? XML version = "1.0" encoding = "UTF-8"?> <Xrds: xrdsxmlns: xrds = "xri: // $ xrds" xmlns: openid = "http://openid.net/xmlns/1.0" xmlns = "xri: // $ XRD * ($ v * 2.0) "> <XRD> <service priority =" 10 "> <type> http://specs.openid.net/auth/2.0/server </type> <type> http://openid.net/extensions/sreg/1.1 </type> <URI> @ URI </uri> </ service> </XRD> </xrds: xrds>
The usage of this view will also be mentioned later.
5. Create askuser. cshtml View
@ {Layout = NULL; response. contenttype = "application/xrds + XML"; var uri1 = new uri (request. url, response. applyapppathmodifier ("~ /Openid/provider "). tostring (); var uri2 = new uri (request. url, response. applyapppathmodifier ("~ /Openid/provider "). tostring () ;}<? XML version = "1.0" encoding = "UTF-8"?> <Xrds: xrdsxmlns: xrds = "xri: // $ xrds" xmlns: openid = "http://openid.net/xmlns/1.0" xmlns = "xri: // $ XRD * ($ v * 2.0) "> <XRD> <service priority =" 10 "> <type> http://specs.openid.net/auth/2.0/signon </type> <type> http://openid.net/extensions/sreg/1.1 </type> <URI> @ uri1 </uri> </ service> <service priority = "20"> <type> http://openid.net/signon/1.0 </type> <type> http://openid.net/extensions/sreg/1.1 </type> <URI> @ uri2 </uri> </service> </XRD> </xrds: xrds>
The role of this action is still vague and can only be guessed. Its usage will be mentioned later.
6. what is the role of setting up the transferresult class? Here I will explain a little: In ASP. someone on the Net webform page may have used server. the transfer method, which is explained in msdn: for the current request, terminate the execution of the current page and start executing the new page using the specified URL path pointing to a new page. In general, it seems like the Redirect method, but in some special cases, the difference is big. What is it? Redirect is used to execute client redirection, while transfer does not need client redirection. It should be in the HTTP 302 status. In the process of using dotnetopenauth, it may be based on security considerations. openid is not allowed to use redirection requests. Otherwise, errors may occur. In MVC, there is a redirecttoaction method that is very useful, but there is no transfertoaction method, or even the transferresult type, so you have to get one by yourself.
Public class transferresult: redirectresult {public transferresult (string URL): Base (URL) {} public override void executeresult (controllercontext context) {var httpcontext = httpcontext. current; httpcontext. rewritepath (URL, false); ihttphandler httphandler = new mvchttphandler (); httphandler. processrequest (httpcontext. current );}}
7. other codes are not pasted one by one due to space limitations. They are all put into the sample program. The structure is as follows:
/Code/readonlyxmlmembershipprovider. CS role: user verification
/Code/util. CS role: used to process login and permission requests. The main method in this class is processauthenticationchallenge. In the official example, it is a void, which is used in MVC, you must use a method with the return value of actionresult.
/Appdata/users. xml: a database that stores user information
8. Create default. aspx in the root directory of the project. This file is the entry for setting up programs using IIS.
<% @ Page Language = "C #" autoeventwireup = "true" %> <SCRIPT runat = "server"> protected void page_load (Object sender, eventargs e) {response. redirect ("~ /Home/Index ") ;}</SCRIPT>
OK, the main structure of ssoop is the above. For the document structure, see (the selected files are newly added, and others are included in the Project template ):
2. the RP of ssorp has little to do with the character, and serves as an SSO consumer.
The document structure is as follows:
The main content of this project is as follows:
1. Comment out all the content in the accountcontroller class and add the following code:
Public class accountcontroller: controller {private const string rolesattribute = "http://samples.dotnetopenauth.net/sso/roles"; Private Static openidrelyingparty relyingparty = new openidrelyingparty (); Public actionresult Logon () {If (array. indexof (request. accepttypes, "application/xrds + XML")> = 0) {return view ("xrds");} uribuilder returntobuilder = new uribuilder (request. URL); returntobuild Er. path = "/account/logon"; returntobuilder. query = NULL; returntobuilder. fragment = NULL; Uri returnto = returntobuilder. uri; returntobuilder. path = "/account/logon"; realm = returntobuilder. uri; var response = relyingparty. getresponse (); If (response = NULL) {If (request. querystring ["returnurl"]! = NULL & user. identity. isauthenticated) {// The user must have been directed here because he has insufficient // permissions to access something. this. viewbag. message = "1";} else {// because this is a sample of a controlled SSO environment, // we don't ask the user which provider to use... we just send // them straight off to the one provider we trust. vaR request = relyingparty. createrequest (Configurationmanager. deleetask[ "ssoprovideropidentifier"], realm, returnto); var fetchrequest = new fetchrequest (); fetchrequest. attributes. addoptional (rolesattrial); Request. addextension (fetchrequest); Request. redirecttoprovider () ;}} else {Switch (response. status) {Case authenticationstatus. canceled: This. viewbag. message = "Login canceled. "; break; Case authenticationstatus. failed: This. Viewbag. message = httputility. htmlencode (response. exception. message); break; Case authenticationstatus. authenticated: ilist <string> roles = NULL; var fetchresponse = response. getextension <fetchresponse> (); If (fetchresponse! = NULL) {If (fetchresponse. attributes. contains (rolesattrise) {roles = fetchresponse. attributes [rolesattrites]. values ;}}if (roles = NULL) {roles = new list <string> (0) ;}// apply the roles to this auth ticket const int timeoutinminutes = 100; // todo: Look up the right value from the web. config File var ticket = new formsauthenticationticket (2, response. claimedidentifier, datetime. now, d Atetime. now. addminutes (timeoutinminutes), false, // non-persistent, since login is automatic and we wanted updated roles string. join (";", roles. toarray (); httpcookie cookie = new httpcookie (formsauthentication. formscookiename, formsauthentication. encrypt (ticket); response. setcookie (cookie); response. redirect (request. querystring ["returnurl"]? Formsauthentication. defaulturl); break; default: break;} return redirecttoaction ("Index", "home");} public saumsauthenticationservice formsservice {Get; set ;} protected override void initialize (requestcontext) {If (formsservice = NULL) {formsservice = new formsauthenticationservice ();} base. initialize (requestcontext);} public actionresult logoff () {formsservice. signout (); Return redirecttoaction ("Index", "home ");}}
In conjunction with ssoop, I will explain my understanding a little:
Because the following configuration is used in Web. config:
<Authentication mode = "forms"> <forms name = "openidwebringssorelyingparty" loginurl = "~ /Account/logon "Protection =" all "Path ="/"timeout =" 900 "/> </authentication> <authorization> <deny users = "? "/> </Authorization>
Because forms mode is used, the request is forwarded to the logon action of the Account No matter any resource is accessed without logon. In logon, the program first verifies whether there is a providerendpoint to the OP identifier. Op uses the xrds action of openidcontroller (both xrds in the op section. cshtml View content) tells the RP that the provider exists and is valid, and then the RP requests authentication from the provider. In turn, the op also needs to confirm whether the RP exists and is valid (using xrds In the RP. cshtml). If no problem exists, the op also needs to verify that the request-authenticated RP is in the whitelist, which must be in the whitelist with returntobuilder. path = "/account/logon"; this value is completely consistent. For example, if there is no "/" number after logon, you must use http in the whitelist: // localhost: 1220/account/logon, but the "/" number cannot be added to the end Otherwise, it will fail. If everything is okay, the no problem page will be directed to the logon page of op. In this example, account/logon is used. The user enters the correct user and password (in this exampleUser: Bob; Password: Test).
After logging on, return redirecttoaction ("identifier", "openid") according to the code in logon. The request is redirected to openid/identifier, and the program prepares the response data first, the data contains the login user information. People familiar with openid know that openid always uses a URL + User Name to represent the user name. This URL is actually anotherFoundWhy is op another? Where else is there? In openid/identifier.Service Discovery"This mechanism is still vague. I personally think it is equivalent to verifying whether it is similar. identifier should belong to the pre-Logon and logon stages, after the login is complete, use the authentication in the address in the user name ?), Next, use providerendpoint. sendresponse (); send the logon result to the client and use the information in return_to to transfer the request to the RP logon. (In this process, RP will use askuser "Discover" service provider in Op .) In logon, based on the status information of iauthenticationresponse, determine whether the logon is successful or fails (the cause of the failure will be included) to determine whether the request is redirected. Since we have sample code, it should not fail, so the home/index will arrive as scheduled.
There are two ssorp examples. One is in pure MVC mode and the other is in MVC + webforms mode.
Dotnetopenauth seems to have very few materials, and my personal research on it is still in the step by step stage. I can only say that an MVC implementation can be made with the official example, however, I am still quite unfamiliar with many specific principles. This example can only be used to solve problems with and without problems. I hope you will not give me any further advice on the mistakes in this article. I hope someone can send more in-depth information.
Sample program