乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      設(shè)計(jì)安全的API-JWT與OAuthor2

       頭號碼甲 2020-05-07

      最近新開發(fā)一個需要給App使用的API項(xiàng)目。開發(fā)API肯定會想到JASON Web Token(JWT)和OAuthor2(之前一篇隨筆記錄過OAuthor2)。

      JWT和OAuthor2的比較

        要像比較JWT和OAuthor2,首先要明白一點(diǎn)就是,這是兩個完全不同的東西,沒有可比性。

        JWT是一種認(rèn)證協(xié)議

          官網(wǎng):http://

          JWT提供了一種用于發(fā)布介入靈擺(Access Token),并對發(fā)布的簽名介入令牌進(jìn)行驗(yàn)證的方法。令牌(Token)本身包含了一系列聲明,應(yīng)用程序可以根據(jù)這些聲明限制用戶對資源的訪問。

          在新開發(fā)的API中,我選擇的是使用JWT,稍后會簡單介紹其在.net core中的使用。

        OAuthor2是一種授權(quán)框架

          OAuthor2是一種授權(quán)框架,提供了一套詳細(xì)的授權(quán)機(jī)制(指導(dǎo))。用戶或應(yīng)用可以通過公開的或私有的設(shè)置,授權(quán)第三方應(yīng)用訪問特定資源。

        既然JWT和OAuthor2沒有可比性,為什么還要把這兩個放在一起說呢?實(shí)際中,會有很多人拿JWT和OAuthor2作比較,或者分不清楚。很多情況下,在討論OAuthor2的實(shí)現(xiàn)時,會把JSON Web Token作為一種認(rèn)證機(jī)制使用。這也是為什么他們會經(jīng)常一起出現(xiàn)。

      JSON Web Token(JWT)

        JWT是一種安全標(biāo)準(zhǔn)?;舅悸肪褪怯脩籼峁┯脩裘兔艽a給認(rèn)證服務(wù)器,服務(wù)器驗(yàn)證用戶提交的信息的合法性,如果認(rèn)證成功,會產(chǎn)生并返回一個Token(令牌),用戶可以使用這個token訪問服務(wù)器上受保護(hù)的資源。

      一個token的例子:

      eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjEiLCJuYW1lIjoibGl1dGFvIiwicm9sZSI6InNob3BVc2VycyIsImh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vd3MvMjAwOC8wNi9pZGVudGl0eS9jbGFpbXMvcm9sZSI6InNob3BVc2VycyIsImFjdCI6IjEiLCJuYmYiOjE1NzQyNTAyMTgsImV4cCI6MTU3NTExNDIxOCwiaXNzIjoiWXVZdWUiLCJhdWQiOiJZdVl1ZSJ9.t39iwO-r_YgX5-7XyIV-by2duHfThqTQayI595VtqF

      一個token包含三個部分:

      header.claims.signature

      為了安全的在url中使用,所有部分都base64 URL-safe進(jìn)行編碼處理。

      Header頭部分

        頭部分簡單聲明了類型(JWT)以及產(chǎn)生簽名所使用的的算法。

      {
        "alg" : "AES256",
        "typ" : "JWT"
      }

      Claims聲明

        聲明部分是整個token的核心,表示要發(fā)送的用戶詳細(xì)信息。游學(xué)情況下,我們和有可能要在一個服務(wù)器上實(shí)現(xiàn)認(rèn)證,然后訪問另一臺服務(wù)器上的資源,或者,通過單獨(dú)的接口來生成token,token被保存在應(yīng)用程序客戶端(比如瀏覽器)使用。

        一個簡單的聲明(claim)的例子:

      {
        "sub": "1234567890",
        "name": "John Doe",
        "admin": true
      }

      Signature簽名

        簽名的目的是為了保證上邊兩部分信息不被篡改。如果嘗試使用Bas64對解碼后的token進(jìn)行修改,簽名信息就會失效。一般使用一個私鑰(private key)通過特定算法對Header和Claims進(jìn)行混淆產(chǎn)生簽名信息,所以只有原始的token才能于簽名信息匹配。
              這里有一個重要的實(shí)現(xiàn)細(xì)節(jié)。只有獲取了私鑰的應(yīng)用程序(比如服務(wù)器端應(yīng)用)才能完全認(rèn)證token包含聲明信息的合法性。所以,永遠(yuǎn)不要把私鑰信息放在客戶端(比如瀏覽器)。

      OAuthor2是什么?

        官網(wǎng):http:///2/

        相反,OAuthor2不是一個標(biāo)準(zhǔn)協(xié)議,而是一個安全的授權(quán)框架,它詳細(xì)描述了系統(tǒng)中不同角色、用戶、服務(wù)前端應(yīng)用(比如API),以及客戶端(比如網(wǎng)站或移動APP)之間怎么實(shí)現(xiàn)相互認(rèn)證。

      OAuthor2的基本概念,可以去閱讀之前的一片隨筆。點(diǎn)擊此處

      使用HTTPS保護(hù)用戶密碼

        在進(jìn)一步討論OAuthor2和JWT的實(shí)現(xiàn)之前,有必要說一下,兩種方案都需要SSL安全保護(hù),也就是對要傳輸?shù)臄?shù)據(jù)進(jìn)行加密編碼。安全地傳輸用戶提供的私密信息,在任何一個安全的系統(tǒng)里都是必要的。否則任何人都可以通過侵入私人wifi,在用戶登錄的時候竊取用戶的用戶名和密碼等信息。

      JWT和OAuthor2應(yīng)該如何選擇

        在做選擇之前,參考一下下邊提到的幾點(diǎn)。

        1、時間投入

          OAuthor2是一個安全框架,描述了在各種不同場景下,多個應(yīng)用之間的授權(quán)問題。有海量的資料需要學(xué)習(xí),要完全理解需要花費(fèi)大量時間。甚至對于一些有經(jīng)驗(yàn)的開發(fā)工程師來說,也會需要大概一個月的時間來深入理解OAuth2。 這是個很大的時間投入。相反,JWT是一個相對輕量級的概念??赡芑ㄒ惶鞎r間深入學(xué)習(xí)一下標(biāo)準(zhǔn)規(guī)范,就可以很容易地開始具體實(shí)施。

        2、出現(xiàn)錯誤的風(fēng)險

          OAuth2不像JWT一樣是一個嚴(yán)格的標(biāo)準(zhǔn)協(xié)議,因此在實(shí)施過程中更容易出錯。盡管有很多現(xiàn)有的庫,但是每個庫的成熟度也不盡相同,同樣很容易引入各種錯誤。在常用的庫中也很容易發(fā)現(xiàn)一些安全漏洞。當(dāng)然,如果有相當(dāng)成熟、強(qiáng)大的開發(fā)團(tuán)隊(duì)來持續(xù)OAuth2實(shí)施和維護(hù),可以一定成都上避免這些風(fēng)險。

        3、社交登錄的好處

          在很多情況下,使用用戶在大型社交網(wǎng)站的已有賬戶來認(rèn)證會方便。如果期望你的用戶可以直接使用Facebook或者Gmail之類的賬戶,使用現(xiàn)有的庫會方便得多。

      JWT的使用場景

      無狀態(tài)的分布式API

        JWT的主要優(yōu)勢在于使用無狀態(tài)、可擴(kuò)展的方式處理應(yīng)用中的用戶會話。服務(wù)端可以通過內(nèi)嵌的聲明信息,很容易地獲取用戶的會話信息,而不需要去訪問用戶或會話的數(shù)據(jù)庫。在一個分布式的面向服務(wù)的框架中,這一點(diǎn)非常有用。但是,如果系統(tǒng)中需要使用黑名單實(shí)現(xiàn)長期有效的token刷新機(jī)制,這種無狀態(tài)的優(yōu)勢就不明顯了。

      優(yōu)勢:

        1、快速開發(fā)

        2、不需要cookie

        3、JSON在移動端的廣泛應(yīng)用

        4、不依賴與社交登錄

        5、相對簡單的概念理解

      限制

        1、token有長度限制

        2、token不能撤銷

        3、需要token有失效時間限制(exp)

      OAuthor2使用場景

      外包認(rèn)證服務(wù)器

        上邊已經(jīng)討論過,如果不介意API的使用依賴于外部的第三方認(rèn)證提供者,你可以簡單地把認(rèn)證工作留給認(rèn)證服務(wù)商去做。也就是常見的,去認(rèn)證服務(wù)商(比如facebook)那里注冊你的應(yīng)用,然后設(shè)置需要訪問的用戶信息,比如電子郵箱、姓名等。當(dāng)用戶訪問站點(diǎn)的注冊頁面時,會看到連接到第三方提供商的入口。用戶點(diǎn)擊以后被重定向到對應(yīng)的認(rèn)證服務(wù)商網(wǎng)站,獲得用戶的授權(quán)后就可以訪問到需要的信息,然后重定向回來。

      優(yōu)勢:

        1、快速開發(fā)

        2、實(shí)施代碼量小

        3、維護(hù)工作減少

      大型企業(yè)解決方案

        如果設(shè)計(jì)的API要被不同的App使用,并且每個App使用的方式也不一樣,使用OAuth2是個不錯的選擇。考慮到工作量,可能需要單獨(dú)的團(tuán)隊(duì),針對各種應(yīng)用開發(fā)完善、靈活的安全策略。當(dāng)然需要的工作量也比較大!

      優(yōu)勢

        1、靈活的實(shí)現(xiàn)方式

        2、可以和JWT同時使用

        3、可以針對不同的應(yīng)用擴(kuò)展

      簡單介紹下在.net core的項(xiàng)目中是如何使用JWT的。

      首先,我們的服務(wù)是基于組件化的,當(dāng)然需要先把身份認(rèn)證的服務(wù)注冊進(jìn)來。在Startup類中的ConfigureServices()方法中:

        services.AddSingleton<ITokenHelper, TokenHelper>();
        // configure strongly typed settings objects
        var jwtConfigSection = Configuration.GetSection("Authentication:JwtBearer");
        services.Configure<JWTConfig>(jwtConfigSection);

        // configure jwt authentication
        var jwtConfig = jwtConfigSection.Get<JWTConfig>();


      services.AddAuthentication(x => { x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }).AddCookie(AdminUserAccountConst.AdminUserCookie, options => { options.Cookie.Name = AdminUserAccountConst.AdminUserCookieName; options.Cookie.HttpOnly = true; options.LoginPath = AdminUserAccountConst.AdminUserLoginPath; options.AccessDeniedPath = AdminUserAccountConst.AdminUserLoginPath; }).AddJwtBearer(AdminUserAccountConst.AdminUserJwt, o => { o.TokenValidationParameters = new TokenValidationParameters { NameClaimType = JwtClaimTypes.Name, RoleClaimType = JwtClaimTypes.Role, ValidateLifetime = false, ValidIssuer = Configuration["Authentication:JwtBearer:Issuer"], ValidAudience = Configuration["Authentication:JwtBearer:Audience"], IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(Configuration["Authentication:JwtBearer:SecurityKey"])) }; o.ForwardChallenge = AdminUserAccountConst.AdminUserCookie; });

      下面是上面所需要用到一些自定義類型:

      AdminUserAccountConst

      public class AdminUserAccountConst
      {
          public const string AdminUserCookie = "AdminUserCookies";
      
          public const string AdminUserCookieName = "AdminUserCookieName";
      
          public const string AdminUserLoginPath = "/account/login";
      
          public const string AdminUserJwt = "AdminUserJwt";
      
          public const string AdminUserRole = "adminuser";
      }
      View Code

      JWTConfig

      public class JWTConfig
      {
          public string Issuer { get; set; }
          public string Audience { get; set; }
          public string IssuerSigningKey { get; set; }
          public int AccessTokenExpiresMinutes { get; set; }
      
          public string RefreshTokenAudience { get; set; }
          public int RefreshTokenExpiresMinutes { get; set; }
      }
      View Code

      至于這些類型的字段,可以自行在appsettings.json中去賦值。

      "Authentication": {
        "JwtBearer": {
          "Issuer": "Bingle",
          "Audience": "Bingle",
          "IssuerSigningKey": "Bingle_C421AAEE0D114EAAACVD",
          "AccessTokenExpiresMinutes": "14400",
      
          "RefreshTokenAudience": "RefreshTokenAudience",
          "RefreshTokenExpiresMinutes": "43200" //60*24*30
        }
      },
      View Code

      ITokenHelper與TokenHepler

       public interface ITokenHelper
       {
           ComplexToken CreateToken(User user);
           ComplexToken CreateToken(Claim[] claims);
           (Result result, string userCode) ConfirmRefreshToken(string refreshToken);
       }
      
       public class TokenHelper : ITokenHelper
       {
           private readonly IOptions<JWTConfig> _options;
           public TokenHelper(IOptions<JWTConfig> options)
           {
               _options = options;
           }
      
           public ComplexToken CreateToken(User user)
           {
               Claim[] claims = new Claim[]
               {
                   new Claim(JwtClaimTypes.Id, user.UserCode),
                   new Claim(JwtClaimTypes.Name, user.UserName),
                   new Claim(JwtClaimTypes.Role, user.UserRole.GetExtendDescription()),
                   new Claim(ClaimTypes.Role, user.UserRole.GetExtendDescription()),
                   new Claim(JwtClaimTypes.Actor, user.PartyId)
               };
               return CreateToken(claims);
           }
      
           public ComplexToken CreateToken(Claim[] claims)
           {
               return new ComplexToken
               {
                   AccessToken = CreateToken(claims, TokenType.AccessToken),
                   RefreshToken = CreateToken(new Claim[]{claims.First(x=>x.Type == JwtClaimTypes.Id)}, TokenType.RefreshToken)
               };
           }
      
           /// <summary>
           /// 用于創(chuàng)建AccessToken和RefreshToken。
           /// 這里AccessToken和RefreshToken只是過期時間不同,【實(shí)際項(xiàng)目】中二者的claims內(nèi)容可能會不同。
           /// 因?yàn)镽efreshToken只是用于刷新AccessToken,其內(nèi)容可以簡單一些。
           /// 而AccessToken可能會附加一些其他的Claim。
           /// </summary>
           /// <param name="claims"></param>
           /// <param name="tokenType"></param>
           /// <returns></returns>
           private Token CreateToken(Claim[] claims, TokenType tokenType)
           {
               var now = DateTime.Now;
               var expires = now.Add(TimeSpan.FromMinutes(tokenType.Equals(TokenType.AccessToken) ? _options.Value.AccessTokenExpiresMinutes : _options.Value.RefreshTokenExpiresMinutes));
               var token = new JwtSecurityToken(
                   issuer: _options.Value.Issuer,
                   audience: tokenType.Equals(TokenType.AccessToken) ? _options.Value.Audience : _options.Value.RefreshTokenAudience,
                   claims: claims,
                   notBefore: now,
                   expires: expires,
                   signingCredentials: new SigningCredentials(new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_options.Value.IssuerSigningKey)), SecurityAlgorithms.HmacSha256));
               return new Token { TokenContent = new JwtSecurityTokenHandler().WriteToken(token), Expires = expires };
           }
      
           public (Result result, string userCode) ConfirmRefreshToken(string refreshToken)
           {
               var tokenHandler = new JwtSecurityTokenHandler();
               if (!tokenHandler.CanReadToken(refreshToken))
                   return (Result.FromCode(ResultCode.InvalidToken, "RefreshToken不正確"), null);
               var jwtSecurityToken = tokenHandler.ReadJwtToken(refreshToken);
               if (jwtSecurityToken.Issuer != _options.Value.Issuer || !jwtSecurityToken.Audiences.Contains(_options.Value.RefreshTokenAudience))
                   return (Result.FromCode(ResultCode.InvalidToken, "RefreshToken不正確"), null);
               if (jwtSecurityToken.ValidTo < DateTime.Now)
                   return (Result.FromCode(ResultCode.InvalidToken, "RefreshToken已經(jīng)過期了"), null);
      
               return (Result.Ok(), jwtSecurityToken.Claims.First(x => x.Type == JwtClaimTypes.Id).Value);
           }
      
       }
      View Code

      還要在Configure方法中使用中間件:

      app.UseAuthentication();

      首先,定義一個API的基類,后面的API繼承此基類就可以了

      [Route("[controller]/[action]")]
      [ApiController]
      [Authorize(
          AuthenticationSchemes = AdminUserAccountConst.AdminUserCookie,
          Roles = AdminUserAccountConst.AdminUserRole)]
      public class BasicAdminController : ControllerBase
      {
      }

      現(xiàn)在新建一個用戶登錄和退出的APIController繼承與上面那個基類就可以了。這里簡化 了代碼

      [HttpPost]
      [AllowAnonymous]
      [ProducesResponseType(typeof(Result<TokenResultDto>), 200)]
      public JsonResult Login([FromBody]LoginDto model)
      {
          var user = new User();//這里需要去數(shù)據(jù)庫中進(jìn)行校驗(yàn)
          if (user == null)
              return Json(new {IsSuccess=false,Msg="參數(shù)錯誤"});
          var result = _tokenHelper.CreateToken(new User
          {
              UserCode = user.UserCode,
              UserName = user.UserName,
              Telphone = user.Telphone,
              PartyId = user.ShopCode,
              UserRole = UserRoleEnum.user,
          });
      
          user.RefreshToken = result.RefreshToken.TokenContent;
          return Json(new TokenResultDto
          {
              AccessToken = result.AccessToken.TokenContent,
              Expires = result.AccessToken.Expires,
              RefreshToken = result.RefreshToken.TokenContent,
          });
      }

      這里使用AllowAnonymous標(biāo)簽,是因?yàn)榈卿洸⒉恍枰M(jìn)行身份驗(yàn)證。當(dāng)需要授權(quán)才能訪問的接口,不需要加上這個標(biāo)簽。

        本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報。
        轉(zhuǎn)藏 分享 獻(xiàn)花(0

        0條評論

        發(fā)表

        請遵守用戶 評論公約

        類似文章 更多