详细讲解使用控制台程序搭建OAuth授权服务器,先上一张OAuth的认证图
很多情况下授权服务器和资源服务器时同一台机器,就有了下面这张图
接着可以使用上一篇文章中的控制台程序,做一些改动
首先需要引入Microsoft.AspNet.Identity.Owin包
PM> Install-Package Microsoft.AspNet.Identity.Owin -Pre
添加一个授权提供类ApplicationOAuthServerProvider继承自Microsoft.Owin.Security.OAuth中的OAuthAuthorizationServerProvider ,这个类实现了IOAuthAuthorizationServerProvider接口
usingMicrosoft.Owin.Security.OAuth;usingSystem;usingSystem.Collections.Generic;usingSystem.Linq;usingSystem.Security.Claims;usingSystem.Text;usingSystem.Threading.Tasks;namespaceConsoleWebApi.OAuthServerProvider
{public classApplicationOAuthServerProvider: OAuthAuthorizationServerProvider
{public override asyncTask ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{//This call is required...//but we're not using client authentication, so validate and move on...
awaitTask.FromResult(context.Validated());
}public override asyncTask GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{//DEMO ONLY: Pretend we are doing some sort of REAL checking here:
if (context.Password != "password")
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
context.Rejected();return;
}//Create or retrieve a ClaimsIdentity to represent the//Authenticated user:
ClaimsIdentity identity =
newClaimsIdentity(context.Options.AuthenticationType);
identity.AddClaim(new Claim("user_name", context.UserName));//Add a Role Claim:
identity.AddClaim(new Claim(ClaimTypes.Role, "Admin"));//Identity info will ultimately be encoded into an Access Token//as a result of this call:
context.Validated(identity);
}
}
}
View Code
这里重写了两个方法:
ValidateClientAuthentication()验证客户信息。
GrantResourceOwnerCredentials()创建ClaimsIdentity并通过OWIN middleware将ClaimsIdentity编码生成Access Token。
Suffice it to say that theClaimsIdentity information is encrypted with a private key (generally, but not always the Machine Key of the machine on which the server is running). Once so encrypted, the access token is then added to the body of the outgoing HTTP response.
大意就时通过机器的Machine Key将ClaimsIdentity编码,如果资源服务器和授权服务器不是同一台机器的话,需要在配置文件中配置相同的Machine Key,这样通过OWIN 中间件就能解码出ClaimsIdentity。
下一步修改Startup ,添加ConfigureAuth()
Startup class. Check to make sure you have added the following usings and code to Startup:
Add a ConfigureAuth() Method to the OWIN Startup Class:usingSystem;//Add the following usings:
usingOwin;usingSystem.Web.Http;usingMinimalOwinWebApiSelfHost.Models;usingMinimalOwinWebApiSelfHost.OAuthServerProvider;usingMicrosoft.Owin.Security.OAuth;usingMicrosoft.Owin;namespaceMinimalOwinWebApiSelfHost
{public classStartup
{//This method is required by Katana:
public voidConfiguration(IAppBuilder app)
{
ConfigureAuth(app);var webApiConfiguration =ConfigureWebApi();
app.UseWebApi(webApiConfiguration);
}private voidConfigureAuth(IAppBuilder app)
{var OAuthOptions = newOAuthAuthorizationServerOptions
{
TokenEndpointPath= new PathString("/Token"),
Provider= newApplicationOAuthServerProvider(),
AccessTokenExpireTimeSpan= TimeSpan.FromDays(14),//Only do this for demo!!
AllowInsecureHttp = true};
app.UseOAuthAuthorizationServer(OAuthOptions);
app.UseOAuthBearerAuthentication(newOAuthBearerAuthenticationOptions());
}privateHttpConfiguration ConfigureWebApi()
{var config = newHttpConfiguration();
config.Routes.MapHttpRoute("DefaultApi","api/{controller}/{id}",new { id =RouteParameter.Optional });returnconfig;
}
}
}
View Code
接下来新建一个客户端控制台程序
添加几个类
ApiClientProvider
usingNewtonsoft.Json;usingSystem;usingSystem.Collections.Generic;usingSystem.Linq;usingSystem.Net.Http;usingSystem.Text;usingSystem.Threading.Tasks;namespaceMinimalOwinWebApiClient
{public classApiClientProvider
{string_hostUri;public string AccessToken { get; private set; }public ApiClientProvider(stringhostUri)
{
_hostUri=hostUri;
}public async Task>GetTokenDictionary(string userName, stringpassword)
{
HttpResponseMessage response;var pairs = new List>{new KeyValuePair( "grant_type", "password"),new KeyValuePair( "username", userName ),new KeyValuePair ( "password", password )
};var content = newFormUrlEncodedContent(pairs);using (var client = newHttpClient())
{var tokenEndpoint = new Uri(new Uri(_hostUri), "Token");
response= awaitclient.PostAsync(tokenEndpoint, content);
}var responseContent = awaitresponse.Content.ReadAsStringAsync();if (!response.IsSuccessStatusCode)
{throw new Exception(string.Format("Error: {0}", responseContent));
}returnGetTokenDictionary(responseContent);
}private DictionaryGetTokenDictionary(stringresponseContent)
{
Dictionary tokenDictionary =JsonConvert.DeserializeObject>(
responseContent);returntokenDictionary;
}
}
}
View Code
Company
public classCompany
{public int Id { get; set; }public string Name { get; set; }
}
View Code
CompanyClient
usingSystem;usingSystem.Collections.Generic;usingSystem.Linq;usingSystem.Net;usingSystem.Net.Http;usingSystem.Net.Http.Headers;usingSystem.Text;usingSystem.Threading.Tasks;namespaceMinimalOwinWebApiClient
{public classCompanyClient
{string_accessToken;
Uri _baseRequestUri;public CompanyClient(Uri baseUri, stringaccessToken)
{
_accessToken=accessToken;
_baseRequestUri= new Uri(baseUri, "api/companies/");
}//Handy helper method to set the access token for each request:
voidSetClientAuthentication(HttpClient client)
{
client.DefaultRequestHeaders.Authorization= new AuthenticationHeaderValue("Bearer", _accessToken);
}public async Task>GetCompaniesAsync()
{
HttpResponseMessage response;using (var client = newHttpClient())
{
SetClientAuthentication(client);
response= awaitclient.GetAsync(_baseRequestUri);
}return await response.Content.ReadAsAsync>();
}public async Task GetCompanyAsync(intid)
{
HttpResponseMessage response;using (var client = newHttpClient())
{
SetClientAuthentication(client);//Combine base address URI and ID to new URI//that looks likehttp://hosturl/api/companies/id
response = awaitclient.GetAsync(newUri(_baseRequestUri, id.ToString()));
}var result = await response.Content.ReadAsAsync();returnresult;
}public async TaskAddCompanyAsync(Company company)
{
HttpResponseMessage response;using (var client = newHttpClient())
{
SetClientAuthentication(client);
response= awaitclient.PostAsJsonAsync(
_baseRequestUri, company);
}returnresponse.StatusCode;
}public async TaskUpdateCompanyAsync(Company company)
{
HttpResponseMessage response;using (var client = newHttpClient())
{
SetClientAuthentication(client);
response= awaitclient.PutAsJsonAsync(
_baseRequestUri, company);
}returnresponse.StatusCode;
}public async Task DeleteCompanyAsync(intid)
{
HttpResponseMessage response;using (var client = newHttpClient())
{
SetClientAuthentication(client);//Combine base address URI and ID to new URI//that looks likehttp://hosturl/api/companies/id
response = awaitclient.DeleteAsync(newUri(_baseRequestUri, id.ToString()));
}returnresponse.StatusCode;
}
}
}
View Code
修改Main
usingSystem;usingSystem.Collections.Generic;usingSystem.Linq;usingSystem.Text;usingSystem.Threading.Tasks;namespaceMinimalOwinWebApiClient
{classProgram
{static void Main(string[] args)
{//Wait for the async stuff to run...
Run().Wait();//Then Write Done...
Console.WriteLine("");
Console.WriteLine("Done! Press the Enter key to Exit...");
Console.ReadLine();return;
}static asyncTask Run()
{//Create an http client provider:
string hostUriString = "http://localhost:8080";var provider = newApiClientProvider(hostUriString);string_accessToken;
Dictionary_tokenDictionary;try{//Pass in the credentials and retrieve a token dictionary:
_tokenDictionary = awaitprovider.GetTokenDictionary("john@example.com", "password");
_accessToken= _tokenDictionary["access_token"];//Write the contents of the dictionary:
foreach (var kvp in_tokenDictionary)
{
Console.WriteLine("{0}: {1}", kvp.Key, kvp.Value);
Console.WriteLine("");
}//Create a company client instance:
var baseUri = newUri(hostUriString);var companyClient = newCompanyClient(baseUri, _accessToken);//Read initial companies:
Console.WriteLine("Read all the companies...");var companies = awaitcompanyClient.GetCompaniesAsync();
WriteCompaniesList(companies);int nextId = (from c in companies select c.Id).Max() + 1;
Console.WriteLine("Add a new company...");var result = awaitcompanyClient.AddCompanyAsync(new Company { Name = string.Format("New Company #{0}", nextId) });
WriteStatusCodeResult(result);
Console.WriteLine("Updated List after Add:");
companies= awaitcompanyClient.GetCompaniesAsync();
WriteCompaniesList(companies);
Console.WriteLine("Update a company...");var updateMe = awaitcompanyClient.GetCompanyAsync(nextId);
updateMe.Name= string.Format("Updated company #{0}", updateMe.Id);
result= awaitcompanyClient.UpdateCompanyAsync(updateMe);
WriteStatusCodeResult(result);
Console.WriteLine("Updated List after Update:");
companies= awaitcompanyClient.GetCompaniesAsync();
WriteCompaniesList(companies);
Console.WriteLine("Delete a company...");
result= await companyClient.DeleteCompanyAsync(nextId - 1);
WriteStatusCodeResult(result);
Console.WriteLine("Updated List after Delete:");
companies= awaitcompanyClient.GetCompaniesAsync();
WriteCompaniesList(companies);
}catch(AggregateException ex)
{//If it's an aggregate exception, an async error occurred:
Console.WriteLine(ex.InnerExceptions[0].Message);
Console.WriteLine("Press the Enter key to Exit...");
Console.ReadLine();return;
}catch(Exception ex)
{//Something else happened:
Console.WriteLine(ex.Message);
Console.WriteLine("Press the Enter key to Exit...");
Console.ReadLine();return;
}
}static void WriteCompaniesList(IEnumerablecompanies)
{foreach (var company incompanies)
{
Console.WriteLine("Id: {0} Name: {1}", company.Id, company.Name);
}
Console.WriteLine("");
}static voidWriteStatusCodeResult(System.Net.HttpStatusCode statusCode)
{if (statusCode ==System.Net.HttpStatusCode.OK)
{
Console.WriteLine("Opreation Succeeded - status code {0}", statusCode);
}else{
Console.WriteLine("Opreation Failed - status code {0}", statusCode);
}
Console.WriteLine("");
}
}
}
View Code
给CompaniesController加上[Authorize]后就有了授权认证了。