• asp.net权限认证:OWIN实现OAuth 2.0 之授权码模式(Authorization Code)


    授权码模式定义

    通过客户端的后台服务器,与“服务提供商”的认证服务器进行认证。

    1、用户访问客户端,后者将前者导向认证服务器。
    2、用户选择是否给予客户端授权。
    3、假设用户给予授权,认证服务器首先生成一个授权码,并返回给用户,认证服务器将用户导向客户端事先指定的"重定向URI"(redirection URI),同时附上一个授权码。
    4、客户端收到授权码,附上早先的"重定向URI",向认证服务器申请令牌。这一步是在客户端的后台的服务器上完成的,对用户不可见。
    5、认证服务器核对了授权码和重定向URI,确认无误后,向客户端发送访问令牌(access token)和更新令牌(refresh token)。
    6、Client拿着access token去访问Resource资源

    授权码模式的工作流程图

    图 1 (网上搜到的授权码工作流程图说明)

    之前看上边的流程图,看了不下10遍,还是搞不懂,这个图真心画的不好理解!

    我们一步步来,AuthorizationServer与ResourceServer还是用之前的项目

    新建项目:AuthorizationCodeGrant

    HomeController.cs也简单

         public ActionResult Index()
            {
                ViewBag.AccessToken = Request.Form["AccessToken"] ?? "";
                ViewBag.RefreshToken = Request.Form["RefreshToken"] ?? "";
                ViewBag.Action = "";
                ViewBag.ResourceResponse = "";
    
                var authorizationServerUri = new Uri("http://localhost:8270/");
                var authorizationServer = new AuthorizationServerDescription
                {
                    AuthorizationEndpoint = new Uri(authorizationServerUri, "OAuth/Authorize"),
                    TokenEndpoint = new Uri(authorizationServerUri, "OAuth/Token")
                };
    
                // 刷新AccessToken
                var client = new WebServerClient(authorizationServer, "123456", "abcdef");
                if (string.IsNullOrEmpty(ViewBag.AccessToken))
                {
                    var authorizationState = client.ProcessUserAuthorization(Request);
                    if (authorizationState != null)
                    {
                        ViewBag.AccessToken = authorizationState.AccessToken;
                        ViewBag.RefreshToken = authorizationState.RefreshToken;
                        ViewBag.Action = Request.Path;
                    }
                }
    
                // 授权申请
                if (!string.IsNullOrEmpty(Request.Form.Get("btnRequestAuthorize")))
                {
                    var grantRequest = client.PrepareRequestUserAuthorization(new[] { "scopes1", "scopes2" });
                    grantRequest.Send(HttpContext);
                    Response.End();
                }
                
                // 申请资源
                if (!string.IsNullOrEmpty(Request.Form.Get("btnRequestResource")))
                {
                    var resourceServerUri = new Uri("http://localhost:8001/");
                    var resourceRequest = new HttpClient(client.CreateAuthorizingHandler(ViewBag.AccessToken));
                    ViewBag.ResourceResponse = resourceRequest.GetStringAsync(new Uri(resourceServerUri, "api/Values")).Result;
                }
    
                return View();
            }
    

    Index.cshtml

    <!DOCTYPE html>
    
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <title>Authorization Code Grant Client</title>
    </head>
    <body>
        <form id="form1" action="@ViewBag.Action" method="POST">
            <div>
                <input id="AccessToken" name="AccessToken" value="@ViewBag.AccessToken" type="hidden" />
                <input id="Authorize" name="btnRequestAuthorize" value="向认证服务器索要授权" type="submit" />
                <input id="Resource" name="btnRequestResource" value="访问资源(Resource)" type="submit" />
            </div>
            <div>@ViewBag.ResourceResponse</div>
        </form>
    </body>
    </html>
    

    运行项目  

    授权过程

    点击“向认证服务索要授权”,根据HomeController.cs文件的设置,页面预计会跳转到"http://localhost:8270/OAuth/Authorize"

    所以我们需要在认证服务中新增处理授权码模式的处理逻辑

    在项目AuthorizationServer中新增OAuthController.cs、Authorize.cshtml

       public class OAuthController : Controller
        {
            public ActionResult Authorize()
            {
                if (Response.StatusCode != 200)
                {
                    return View("AuthorizeError");
                }
    
                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(); //用户登录凭证失效就报401错误,并且跳转至AccountController中的Login中
                }
    
                ViewBag.IdentityName = identity.Name;
                ViewBag.Scopes = (Request.QueryString.Get("scope") ?? "").Split(' ');
    
                if (Request.HttpMethod == "POST")
                {
              // 点击btnGrant就确认授权,返回token等信息 if (!string.IsNullOrEmpty(Request.Form.Get("btnGrant"))) { identity = new ClaimsIdentity(identity.Claims, "Bearer", identity.NameClaimType, identity.RoleClaimType); foreach (var scope in ViewBag.Scopes) { identity.AddClaim(new Claim("urn:oauth:scope", scope)); } authentication.SignIn(identity); } if (!string.IsNullOrEmpty(Request.Form.Get("btnOtherLogin"))) { authentication.SignOut("Application"); authentication.Challenge("Application"); return new HttpUnauthorizedResult(); } } return View(); } }
    <!DOCTYPE html>
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <title>Authorize</title>
    </head>
    <body>
        <h1>认证页面</h1>
        <form method="POST">
            <p>登录用户:@ViewBag.IdentityName</p>
            <p>第三方应用需要你给他开放以下权限</p>
            <ul>
                @foreach (var scope in ViewBag.Scopes)
                {
                    <li>@scope</li>
                }
            </ul>
            <p>
                <input type="submit" name="btnGrant" value="确认授权" />
                <input type="submit" name="btnOtherLogin" value="以不同用户登录" />
            </p>
        </form>
    </body>
    </html>
    
       public class AccountController : Controller
        {
            public ActionResult Login()
            {
                var authentication = HttpContext.GetOwinContext().Authentication;
                if (Request.HttpMethod == "POST")
                {
                    // 默认用户登录成功
             // 生产环境需要单独整合第三方登录信息 var username = Request.Form["username"]; authentication.SignIn( new AuthenticationProperties { IsPersistent = true }, new ClaimsIdentity( new[] { new Claim(ClaimsIdentity.DefaultNameClaimType, username) }, "Application")); } return View(); } public ActionResult Logout() { return View(); } }

    运行项目,成功跳转至认证登录页面

    点击登录,此时url地址为:

    http://localhost:8270/OAuth/Authorize?client_id=123456&redirect_uri=http%3A%2F%2Flocalhost%3A4825%2F&state=IUKeWFTR1HKi4hlzKOOPgw&scope=scopes1%20scopes2&response_type=code

    7.1 client_id为客户端ID,即之前我们在AuthorizationCodeGrant项目设置的clientID

    7.2 redirect_uri、state为之前登录时就确定的值

    7.3 scope为用户确定授权的范围

    7.4 response_type=code,即指定为授权码模式

    确认授权

    此时url有变化:http://localhost:4825/?code=efab38fc30c741a198b20663ec60869a36c6b25ff21f4c9986bcb9c9ae8d20eb&state=tjB9jXhNiHvIr4Ko9VhEkw

    注意:这一步会会默认获取Token

    点击访问资源

    完全能够对上;

    url中的code即认证服务返回的授权码,之后Client请求Token会用这个code来交换

    这个就是授权码模式的特色的地方了

     

     自此,整个授权码模式已经完毕了哦

    asp.net权限认证系列

    1. asp.net权限认证:Forms认证
    2. asp.net权限认证:HTTP基本认证(http basic)
    3. asp.net权限认证:Windows认证
    4. asp.net权限认证:摘要认证(digest authentication)
    5. asp.net权限认证:OWIN实现OAuth 2.0 之客户端模式(Client Credential)
    6. asp.net权限认证:OWIN实现OAuth 2.0 之密码模式(Resource Owner Password Credential)
    7. asp.net权限认证:OWIN实现OAuth 2.0 之授权码模式(Authorization Code)
    8. asp.net权限认证:OWIN实现OAuth 2.0 之简化模式(Implicit)
  • 相关阅读:
    《构建高性能web站点》阅读笔记(三)
    哈希表的C实现(一)
    《大规模web服务开发技术》阅读笔记
    CentOS搭建python开发环境
    Instagram的技术探索(2)
    CentOS5.5编译安装gvim7.3 失败记录
    由于启动用户实例的进程时出错,导致无法生成 SQL Server 的用户实例。该连接将关闭
    wp开发中解决gb2312的编码问题
    WP8 SDK 视图设计器 未将对象设置到对象的实例解决方法
    Oracle误删表的恢复
  • 原文地址:https://www.cnblogs.com/lanxiaoke/p/6360466.html
Copyright © 2020-2023  润新知