OWIN OAuth 2.0 Authorization Server

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了OWIN OAuth 2.0 Authorization Server相关的知识,希望对你有一定的参考价值。

OWIN OAuth 2.0 Authorization Server

源码在上面的地址中可以下载 

打开客户端页面http://localhost:38500/

技术分享

客户端代码引用了DotNetOpenAuth.OAuth2

技术分享
public class HomeController : Controller
    {
        private WebServerClient _webServerClient;

        public ActionResult Index()
        {
            ViewBag.AccessToken = Request.Form["AccessToken"] ?? "";
            ViewBag.RefreshToken = Request.Form["RefreshToken"] ?? "";
            ViewBag.Action = "";
            ViewBag.ApiResponse = "";

            InitializeWebServerClient();
            var accessToken = Request.Form["AccessToken"];
            if (string.IsNullOrEmpty(accessToken))
            {
                var authorizationState = _webServerClient.ProcessUserAuthorization(Request);
                if (authorizationState != null)
                {
                    ViewBag.AccessToken = authorizationState.AccessToken;
                    ViewBag.RefreshToken = authorizationState.RefreshToken;
                    ViewBag.Action = Request.Path;
                }
            }

            if (!string.IsNullOrEmpty(Request.Form.Get("submit.Authorize")))
            {
                var userAuthorization = _webServerClient.PrepareRequestUserAuthorization(new[] { "bio", "notes" });
                userAuthorization.Send(HttpContext);
                Response.End();
            }
            else if (!string.IsNullOrEmpty(Request.Form.Get("submit.Refresh")))
            {
                var state = new AuthorizationState
                {
                    AccessToken = Request.Form["AccessToken"],
                    RefreshToken = Request.Form["RefreshToken"]
                };
                if (_webServerClient.RefreshAuthorization(state))
                {
                    ViewBag.AccessToken = state.AccessToken;
                    ViewBag.RefreshToken = state.RefreshToken;
                }
            }
            else if (!string.IsNullOrEmpty(Request.Form.Get("submit.CallApi")))
            {
                var resourceServerUri = new Uri(Paths.ResourceServerBaseAddress);
                var client = new HttpClient(_webServerClient.CreateAuthorizingHandler(accessToken));
                var body = client.GetStringAsync(new Uri(resourceServerUri, Paths.MePath)).Result;
                ViewBag.ApiResponse = body;
            }

            return View();
        }

        private void InitializeWebServerClient()
        {
            var authorizationServerUri = new Uri(Paths.AuthorizationServerBaseAddress);
            var authorizationServer = new AuthorizationServerDescription
            {
                AuthorizationEndpoint = new Uri(authorizationServerUri, Paths.AuthorizePath),
                TokenEndpoint = new Uri(authorizationServerUri, Paths.TokenPath)
            };
            _webServerClient = new WebServerClient(authorizationServer, Clients.Client1.Id, Clients.Client1.Secret);
        }
    }
View Code

2.点击Authorize按钮,在客户端添加两个Scope

var userAuthorization = _webServerClient.PrepareRequestUserAuthorization(new[] { "bio", "notes" });

调用如下

public OutgoingWebResponse PrepareRequestUserAuthorization(IEnumerable<string> scopes = null, Uri returnTo = null);

此时会到服务端进行客户验证

private Task ValidateClientRedirectUri(OAuthValidateClientRedirectUriContext context)
        {
            if (context.ClientId == Clients.Client1.Id)
            {
                context.Validated(Clients.Client1.RedirectUrl);
            }
            else if (context.ClientId == Clients.Client2.Id)
            {
                context.Validated(Clients.Client2.RedirectUrl);
            }
            return Task.FromResult(0);
        }

验证后跳转到Authorize,首先判断是否登陆

var authentication = HttpContext.GetOwinContext().Authentication;
            var ticket = authentication.AuthenticateAsync("Application").Result;
            var identity = ticket != null ? ticket.Identity : null;
            if (identity == null)
            {
                authentication.Challenge("Application");
                return new HttpUnauthorizedResult();
            }

没有登陆的话跳转到Login页面

http://localhost:11625/Account/Login?ReturnUrl=%2FOAuth%2FAuthorize%3Fclient_id%3D123456%26redirect_uri%3Dhttp%253A%252F%252Flocalhost%253A38500%252F%26state%3D4PH8wxITy6KAVSpnt4YpAA%26scope%3Dbio%2520notes%26response_type%3Dcode

使用UrlDecode可以看得更明白

http://localhost:11625/Account/Login?ReturnUrl=/OAuth/Authorize?client_id=123456&redirect_uri=http%3A%2F%2Flocalhost%3A38500%2F&state=4PH8wxITy6KAVSpnt4YpAA&scope=bio%20notes&response_type=code

再次解码

http://localhost:11625/Account/Login?ReturnUrl=/OAuth/Authorize?client_id=123456&redirect_uri=http://localhost:38500/&state=4PH8wxITy6KAVSpnt4YpAA&scope=bio notes&response_type=code

技术分享

这里随便输入一个用户名123456,点击Sign In按钮

public ActionResult Login()
        {
            var authentication = HttpContext.GetOwinContext().Authentication;
            if (Request.HttpMethod == "POST")
            {
                var isPersistent = !string.IsNullOrEmpty(Request.Form.Get("isPersistent"));

                if (!string.IsNullOrEmpty(Request.Form.Get("submit.Signin")))
                {
                    authentication.SignIn(
                        new AuthenticationProperties { IsPersistent = isPersistent },
                        new ClaimsIdentity(new[] { new Claim(ClaimsIdentity.DefaultNameClaimType, Request.Form["username"]) }, "Application"));
                }
            }

            return View();
        }

会通过OWIN写入身份,再次到服务端进行验证

技术分享

 

 验证后进行授权确认

http://localhost:11625/OAuth/Authorize?client_id=123456&redirect_uri=http%3A%2F%2Flocalhost%3A38500%2F&state=4PH8wxITy6KAVSpnt4YpAA&scope=bio%20notes&response_type=code

解码后

http://localhost:11625/OAuth/Authorize?client_id=123456&redirect_uri=http://localhost:38500/&state=4PH8wxITy6KAVSpnt4YpAA&scope=bio notes&response_type=code

技术分享

点击Grant按钮,同样会先进行验证,然后写入Scope,注意这里写入的Bearer,在后面的资源服务器验证时使用

技术分享

通过登陆后会生成token

private void CreateAuthenticationCode(AuthenticationTokenCreateContext context)
        {
            context.SetToken(Guid.NewGuid().ToString("n") + Guid.NewGuid().ToString("n"));
            _authenticationCodes[context.Token] = context.SerializeTicket();
        }

返回到客户端,请求token

技术分享

服务端先验证授权

技术分享

private Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
        {
            string clientId;
            string clientSecret;
            if (context.TryGetBasicCredentials(out clientId, out clientSecret) ||
                context.TryGetFormCredentials(out clientId, out clientSecret))
            {
                if (clientId == Clients.Client1.Id && clientSecret == Clients.Client1.Secret)
                {
                    context.Validated();
                }
                else if (clientId == Clients.Client2.Id && clientSecret == Clients.Client2.Secret)
                {
                    context.Validated();
                }
            }
            return Task.FromResult(0);
        }

然后移除前面生成的Code

private void ReceiveAuthenticationCode(AuthenticationTokenReceiveContext context)
        {
            string value;
            if (_authenticationCodes.TryRemove(context.Token, out value))
            {
                context.DeserializeTicket(value);
            }
        }

再生成RefreshToken

技术分享

回到客户端

技术分享

页面

http://localhost:38500/?code=cd086e2e868e4899ac8a15c332036cd4e52f344dee804351a0789eb20bb1ead5&state=m54g6dYyfMUfYNf7F9Qbcw

技术分享

点击Refresh按钮

会重新到服务器先做授权验证ValidateClientAuthentication,验证成功后重新生成token

技术分享

技术分享

点击访问受保护资源API,因为受保护资源同样是使用OWIN验证的

using System;
using System.Threading.Tasks;
using Microsoft.Owin;
using Owin;

[assembly: OwinStartup(typeof(ResourceServer.Startup))]

namespace ResourceServer
{
    public partial class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            ConfigureAuth(app);
        }
    }
}
using Owin;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace ResourceServer
{
    public partial class Startup
    {
        public void ConfigureAuth(IAppBuilder app)
        {
            app.UseOAuthBearerAuthentication(new Microsoft.Owin.Security.OAuth.OAuthBearerAuthenticationOptions());
        }
    }
}
namespace ResourceServer.Controllers
{
    [Authorize]
    public class MeController : ApiController
    {
        public string Get()
        {
            return this.User.Identity.Name;
        }
    }
}

结果如下

技术分享

这里从授权Server到资源Server都是使用OWIN,关键就在这两个项目中有相同的配置

<system.web>
    <compilation debug="true" targetFramework="4.5" />
    <httpRuntime targetFramework="4.5" />
    <machineKey decryptionKey="B7EFF1C5839A624E3F97D0268917EDE82F408D2ECBFAC817" validation="SHA1" validationKey="C2B8DF31AB9624D69428066DFDA1A479542825F3B48865C4E47AF6A026F22D853DEC2B3248DF268599BF89EF78B9E86CA05AC73577E0D5A14C45E0267588850B" />
  </system.web>

 

以上是关于OWIN OAuth 2.0 Authorization Server的主要内容,如果未能解决你的问题,请参考以下文章

Owin OAuth 2.0密码授权流程

OWIN OAuth 2.0 Authorization Server

WebApi使用Token(OAUTH 2.0方式)

“错误:” “Unsupported_grant_type” 使用 OAuth 2.0,Owin。密码授予和授权授予

OAuth 2.0 - 自己网站的帐户管理

OpenId Connect 问题 - 授权代码流 (OAuth 2.0)