• Asp.net core 学习笔记之 authen + autho + oidc + oauth + spa 第一篇 (without Identity)


    上一回研究 authenticate 和 authorization 已经是 2 年前了.

    业务需求一直没有变化所以也没有再去动它了. 但随着最近业务开始有上去了. 荒废的功夫又得拾起来了.

    上一回我没有耐心, 所以没有写完 identity server 4 和 angular. 这回希望能写完它. 不打算修改之前的了. 从头开始吧.

    这 2 年微软的文档做的很好了. 这里就按着文档做一遍 warm up + summary 就好呗.

    这个系列会涉及到的技术是 : 

    razor page,

    web api,

    identity,

    openiddict core (thrid party for oidc + oauth 我不用 identity server 4 了),

    angular

    authentication (authen) 主要讲的就是登入啦, 身份验证, authorization (autho) 就是权限, 你登入不表示你就有权限. 大概是这个区别啦. 

    在 asp.net core 做 authentication 一般上都会用 Identity framework 这个插件, 然后再它之上做一些魔改. 比较少完全自己写.

    这篇主要就是照着官网的教程撸一遍

    0. before Identity 

    https://docs.microsoft.com/en-us/aspnet/core/security/authentication/cookie?view=aspnetcore-5.0

    一些术语介绍 : 

    Schemes 是方案的意思, 比如 Cookies Schemes, JWT Schemes 等等

    asp.net core 本身有很基本的 authentication 概念, 什么 User Claim Principal 之类的, 

    我个人觉得应该先来一个没有 identity 的版本, 这样往上建会比较好理解. 

    dotnet new webapp -o WithoutIdentity

    在 private page 加上 authorize 标签

    这个时候我们去访问这个页面的话就会报错了. 因为我们没有做任何配置

    现在去 Startup.cs 

    services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
        .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme);

    CookieAuthenticationDefaults.AuthenticationScheme 是一个 string, 就是方案的名字. AddAuthentication 表明了默认使用什么方案.

    AddCookie 则是 asp.net core 封装的一个 cookie 方案. 一般上我们都是用 cookie 来实现登入的嘛. 一样要给方案一个名字

    还有一个方案是比较适合 api 的, 叫 JWT JSON Web Token, 那种放在请求 header 里面 的 bearer token.

    Jwt 需要安装 dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer

    service 方面最基本的是这 2 句

    services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
        .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options => Configuration.Bind("JwtSettings", options))
        .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options => Configuration.Bind("CookieSettings", options));

    默认用 JWT, 然后配置了 2 个方案 JWT 和 Cookie

    然后是添加一个 app.UseAuthentication(); 位置要方对哦

     然后我们访问 private page, 这个时候, error 已经不一样了. 它会跳转到 Account/Login page 

    这个是封装好的. 因为我们刚才 .AddCookie 并没有配置任何东西, 全部设置都是默认的. 

    https://github.com/dotnet/aspnetcore/blob/main/src/Security/Authentication/Cookies/src/CookieAuthenticationDefaults.cs

    我们可以通过 options 来换掉这些逻辑

    .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options => {
        options.Cookie.Name = "MyCookieName";
        options.Cookie.SameSite = Microsoft.AspNetCore.Http.SameSiteMode.Strict;
        options.ReturnUrlParameter = "RedirectUrl";
        options.LoginPath = "/MyAccount/Login";
        options.LogoutPath = "/MyAccount/Logout";
        options.AccessDeniedPath = "/MyAccount/AccessDenied";
    });

    这里有一个 point 可以记入一下. 关于 same site, 默认都是 Lax 我们不需要去设置它就对了. 

    相关文章可以看这里

    https://www.cnblogs.com/keatkeat/p/10779711.html

    https://docs.microsoft.com/en-us/aspnet/core/security/authentication/cookie?view=aspnetcore-5.0#cookie-policy-middleware

    https://docs.microsoft.com/en-us/aspnet/core/security/samesite?view=aspnetcore-5.0

    现在做一个 Account/Login 的 page 当被跳转 (challenge) 过来时, query 上面会有一个 ReturnUrl 记入它是从哪里来的, 一般上登入成功后会返回去哪个页面 

    在这个 login page 做一个 login button post

    public async Task OnPostAsync([FromQuery(Name = "ReturnUrl")] string returnUrl)
    {
        var claimsIdentity = new ClaimsIdentity(new List<Claim> { 
            new Claim(ClaimTypes.Name, "UserName")
        }, CookieAuthenticationDefaults.AuthenticationScheme);
    
        await HttpContext.SignInAsync(
            CookieAuthenticationDefaults.AuthenticationScheme, 
            new ClaimsPrincipal(claimsIdentity),
            new AuthenticationProperties()
        );
    }

    HttpContext.SignInAsync 就是 asp.net core 封装好的方法了. 

    参数首先是方案的名字, 然后是 Principal, 里面的 claims 是用来做 authorization 的 (最少都要有一个 Name 的 claims 哦, 而已必须是 unique 来的)

    最后是 authentication properties 比如 remenber me 之类的配置. 

    不需要做任何返回跳转, SignInAsync 会帮我们搞定这些东西哦

    完成后就访问到 private page 了, 上面是装好的 cookie. 

    value 是用 data protection 加密过的.

    logout 是这样的, 有一个 /Account/Logout page. 把 logout 代码写进去. 

    public async Task OnPostAsync()
    {
        await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
    }

    然后在要 logout 的 page (什么 page 都行) 做一个 form post 去 /Account/Logout. 要附上 returnUrl 哦

    <form asp-page="/Account/Logout/Index" asp-route-returnUrl="/" method="post">
        <button type="submit">Click here to Logout</button>
    </form>

    这里有一篇对 LogoutPath 的解释 : https://stackoverflow.com/questions/52709492/what-does-the-cookieauthenticationoptions-logoutpath-property-do-in-asp-net-core

    我本来以为 logout path 的意思是你 logout 完成后它会跳转到这个 page

    其实不是这样的. 它有点像 login page 是用来做 logout 的. 当调用 SignOutAsync 时, 当前的 path 必须是 LogoutPath (我们在 AddCookie 时写进去的) 那么才会触发 redirect.

    参考一下 Identity 的实现, 这个是 logout button 可以放在任何地方. 它会 post to /Account/Logout page 还附上了 return url

     然后 /Account/Logout.cs 是这样的 

    估计 SignInManger.SignOutAsync 就调用了 HttpContext.SignOutAsync,

    React to back-end cahnges 

    这个概念蛮重要的, 之前我也是没有搞明白. 

    首先要知道, cookie 保存了 user info, 通过 data protection 对称加密来保护.  

    再验证的时候, 直接解密成功就算是登入了. 这个流程是不走数据库的. 

    好处是快咯, 坏处就是无法毁掉这个 cookie. 比如说用户换了密码, 它的预想是之前的 cookie 应该就失效了. 但是没有. 

    那怎么办呢. 微软把这个权衡留给我们自己决定。

    identity 对此的方案叫 security stamp, 它的做法是这样的. 首先定个时间, 比如默认是超过 30 分钟就得去数据库检查一次.

    检查什么呢 ? 就是 security stamp 是否和之前一样. 如果一样就表示这段期间用户并没有做出任何会影响到授权得事儿. 如果有那么就应该要 update 这个 security stamp. 那么期限一到检查得时候就会发现了. 

    identity 检查的时候会跑 5 个 select from table, 性能还是蛮伤的呢. 

    我们可以通过 config 去 set 自己认为合适得时间 

    services.Configure<SecurityStampValidatorOptions>(o => {
        o.ValidationInterval = TimeSpan.FromSeconds(60);
    });

    说太多 identity 了, 讲回 without identity 的情况下, 它是怎样 work 起来的

    首先是做 principal 的时候放一个 claim 记入一个 stamp 

    然后在 AddCookie 的时候放入一个 Events 拦截

     继承 CookieAuthenticationEvents 然后 override 掉 ValidatePrincipal, 里头就可以检查然后 reject 之类的 

     或者像 identity 这样配置也是可以的

    具体 validation 是这样的

    判断时间 > 数据库检查 > 不过就 signout > 过就 replace principal, replace principal 不是 identity 的功能, 而是 asp.nect core authentication cover 的哦

    最后这里讲一下 cookie 的 expires

    refer: https://stackoverflow.com/questions/37086645/how-to-set-asp-net-identity-cookies-expires-time

    有几个东西跟 expires 息息相关 

    SlidingExpiration = 这个是说 keep renew cookie, 每次 request 来的时候如果 cookie 是 ok 的 then 就续命.

    Persistent = 如果是 false 的话, 那么就是没有 remember me, cookie expires 是 session. 如果是 true 那么 expires 就 base on cookie option 的 ExpireTimeSpan (默认 14 天)

    在 SignIn 的时候还可以调绝对过期 expiresUtc 它会覆盖掉 option 的 ExpireTimeSpan, 也不会理会 SlidingExpiration 了 (set 绝对的时候要 Persistent true 哦, 因为 false 就是跑 session)

    cookie expires session 就是说当 browser close 它就被清掉. 需要留意的是 chrome, 一但你 set 了 continue where you left off 它就不会清除掉 expires session cookie 了. 即使是你 off pc 也不会...

      

  • 相关阅读:
    Android网络框架OkHttp之get请求(源码初识)
    Android Studio运行报错,Cannot find System Java Compiler. Ensure that you have installed a JDK......
    Android基于xmpp的即时通讯应用
    Android 基于Http的多线程下载的实现
    1.3 Quick Start中 Step 3: Create a topic官网剖析(博主推荐)
    1.3 Quick Start中 Step 2: Start the server官网剖析(博主推荐)
    1.3 Quick Start中 Step 1: Download the code官网剖析(博主推荐)
    1.2 Use Cases中 Commit Log官网剖析(博主推荐)
    1.2 Use Cases中 Event Sourcing官网剖析(博主推荐)
    1.2 Use Cases中 Stream Processing官网剖析(博主推荐)
  • 原文地址:https://www.cnblogs.com/keatkeat/p/14402857.html
Copyright © 2020-2023  润新知