1. 程式人生 > >OAuth2認證和授權:ClientCredentials認證

OAuth2認證和授權:ClientCredentials認證

openid .net 三方 分享圖片 play 一次 code list 判斷

1:創建授權服務器項目:AuthorizationServer,添加包:IdentityServer4

2:創建資源服務器項目:ResourcesServer,添加包:IdentityServer4.AccesstokenValidation

3:創建請求客戶端項目:ClientCredentials,添加包:IdentityModel

AuthorizationServer代碼示例:

  public class Config
    {
        /// <summary>
        /// 定義用戶可以訪問的資源
        /// </summary>
/// <returns></returns> public static List<ApiResource> GetApiResources() { return new List<ApiResource> { /* 具有單個作用域的簡單API,這樣定義的話,作用域(scope)和Api名稱(ApiName)相同 */ new ApiResource("
api","描述"), //如果需要更多控制,則擴展版本 new ApiResource{ Name="userinfo", //資源名稱,對應客戶端的:ApiName,必須是唯一的 Description="描述", DisplayName="", //顯示的名稱 //ApiSecrets =
//{ // new Secret("secret11".Sha256()) //}, //作用域,對應下面的Cliet的 AllowedScopes Scopes={ new Scope { Name = "apiInfo.read_full", DisplayName = "完全的訪問權限", UserClaims={ "super" } }, new Scope { Name = "apiinfo.read_only", DisplayName = "只讀權限" } }, }, }; } /// <summary> /// 客戶端合法性驗證 /// </summary> /// <returns></returns> public static List<Client> GetClients() { #region 客戶端模式 ClientCredentials var ClientCredentials = new Client { /******************客戶端 請求對應的字段******************* client_id:客戶端的ID,必選 grant_type:授權類型,必選,此處固定值“code” client_secret:客戶端的密碼,必選 scope:申請的權限範圍,可選,如果傳了必須是正確的,否則也不通過 ************************************/ //這個Client集合裏面,ClientId必須是唯一的 ClientId = "780987652", // 客戶端ID,客戶端傳過來的必須是這個,驗證才能通過, AllowedGrantTypes = GrantTypes.ClientCredentials,// 授權類型,指客戶端可以使用的模式 ClientSecrets = { new Secret("secret".Sha256()) }, //客戶端密鑰 //ClientSecrets={new Secret("secret".Sha512()) }, //RequireClientSecret = false, //不驗證secret ,一般是信得過的第三方 ClientName = "客戶端名稱", Description = "描述", //Claims = new List<Claim> { // new Claim("super","super") //}, /* 權限範圍,對應的ApiResouce,這裏是客戶端模式,對應的是用戶資源,所以是ApiResouce 如果是oidc 這對應的是identityResouece,身份資源 所以是取決於AllowedGrantTypes的類型 允許客戶端訪問的API作用域 */ AllowedScopes = { "apiInfo.read_full" } // }; var ClientCredentials1 = new Client { ClientId = "userinfo", AllowedGrantTypes = GrantTypes.ClientCredentials, //客戶端輸入:client_credentials ClientSecrets = { new Secret("secret".Sha256()) }, ClientName = "客戶端名稱", AllowedScopes = { "apiInfo.read_full" } // }; #endregion #region 密碼模式 ResourceOwnerPassword var pwd = new Client { ClientId = "userinfo_pwd", AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,//客戶端輸入:password ClientSecrets = { new Secret("secret".Sha256()) }, ClientName = "客戶端名稱", RefreshTokenUsage = TokenUsage.ReUse, AlwaysIncludeUserClaimsInIdToken = true, AllowOfflineAccess = true, AllowedScopes = { "apiInfo.read_full" } // }; #endregion return new List<Client> { ClientCredentials, //ClientCredentials1, //pwd, }; } /// <summary> /// 密碼模式,需要用的到用戶名和密碼,正式操作是在數據庫中找 /// </summary> /// <returns></returns> public static List<TestUser> GetTestUsers() { return new List<TestUser> { new TestUser { SubjectId="100000", //用戶ID Username="cnblogs", //用戶名 Password="123", //密碼 Claims=new List<Claim>{ new Claim("name","name") } } }; } }

Startup.cs配置:

public void ConfigureServices(IServiceCollection services)
        {
            services.Configure<CookiePolicyOptions>(options =>
            {
                // This lambda determines whether user consent for non-essential cookies is needed for a given request.
                options.CheckConsentNeeded = context => true;
                options.MinimumSameSitePolicy = SameSiteMode.None;
            });


            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

            //註冊ids中間件
            services.AddIdentityServer()
               //設置開發者臨時簽名憑據
               .AddDeveloperSigningCredential()
               
               //in-men 方式把信息添加到內存中
               .AddInMemoryApiResources(Config.GetApiResources())
               .AddInMemoryClients(Config.GetClients())
               .AddTestUsers(Config.GetTestUsers());

        }

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
//使用ids中間件
app.UseIdentityServer();
}

ResourcesServer 資源服務器認證示例:

 public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

            //默認的認證方式是Bearer認證
            services.AddAuthentication("Bearer")
            //配置要驗證的信息
            .AddIdentityServerAuthentication(options =>
            {
                //令牌或者說AccessToken頒發的地址,Token中會包含該地址
                //第一次會去認證服務器獲取配置信息
                options.Authority = "http://localhost:5003"; //必填
                options.ApiName = "userinfo";
                options.ApiSecret = "secret";
                //options.SaveToken = true;
                options.RequireHttpsMetadata = false;//暫時取消Https驗證,
            });

            //services.AddAuthorization(options => {
            //    options.AddPolicy("client", policy => policy.RequireClaim("client_id"));
            //});
        }
 public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
 app.UseAuthentication();

}

資源接口:

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System.Linq;

namespace ResourcesServer.Controllers
{
    [Route("identity")]
    [Authorize]
    public class IdentityController : ControllerBase
    {
        /// <summary>
        /// 獲取當前的信息
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        public ActionResult Get()
        {
            return new JsonResult(User.Claims.Select(
                c => new { c.Type, c.Value }));
        }

        [HttpGet]
        [Route("userInfo")]
        public ActionResult GetUserInfo()
        {
            return new JsonResult(User.Claims.Select(
                c => new { c.Type, c.Value }));
        }
      
    }
}

通過Postman請求授權服務器獲取access_token

參數:

client_id:780987652
client_secret:secret
grant_type:client_credentials

技術分享圖片

然後通過該access_token 請求資源服務器獲取資源

技術分享圖片

nbf:非必須。not before。如果當前時間在nbf裏的時間之前,則Token不被接受;一般都會留一些余地,比如幾分鐘。

exp:#非必須。expire 指定token的生命周期。unix時間戳格式

iss:#非必須。issuer 請求實體,可以是發起請求的用戶的信息,也可是jwt的簽發者。

aud:#非必須。接收該JWT的一方。

詳細信息參考:https://www.cnblogs.com/zjutzz/p/5790180.html

可以把access_token放到jwt.io 看下:

技術分享圖片

可以通過 http://localhost:5003/.well-known/openid-configuration 查看配置信息

ClientCredentials第三方代碼請求方式:

using IdentityModel;
using IdentityModel.Client;
using Newtonsoft.Json.Linq;
using System;
using System.Net.Http;
using System.Text;

namespace ClientCredentials
{
    /// <summary>
    /// 客戶端模式,請求授權服務器獲取token,請求資源服務器獲取資源
    /// 依賴包:IdentityModel
    /// </summary>
    class Program
    {
        static void Main(string[] args)
        {
            string Authority = "http://localhost:5003";
            string ApiResurce = "http://localhost:5002/";
            var tokenCliet = new HttpClient()
            {
                BaseAddress = new Uri(ApiResurce)
            };

            /*
             這樣做的目的是:
             資源服務器會去授權服務器認證,所以在客戶端可以先判斷下授權服務器是否掛了
             */
            DiscoveryCache _cache = new DiscoveryCache(Authority);
            var disco1 =  _cache.GetAsync().Result;
            if (disco1.IsError) throw new Exception(disco1.Error);
            //或者
            var disco = tokenCliet.GetDiscoveryDocumentAsync(Authority).Result;
            if (disco.IsError) throw new Exception(disco.Error);


            var response = tokenCliet.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest
            {
                Address = disco.TokenEndpoint,
                ClientId = "780987652",
                ClientSecret= "secret",
                //GrantType= "client_credentials"
            }).Result;

            if (response.IsError) throw new Exception(response.Error);

            var token = response.AccessToken;

            //把token,Decode
            if (response.AccessToken.Contains("."))
            {
                //Console.WriteLine("\nAccess Token (decoded):");
                Console.ForegroundColor = ConsoleColor.Green;
                Console.WriteLine("\nAccess Token (decoded):");
                Console.ResetColor();

                var parts = response.AccessToken.Split(.);
                var header = parts[0];
                var claims = parts[1];

                Console.WriteLine(JObject.Parse(Encoding.UTF8.GetString(Base64Url.Decode(header))));
                Console.WriteLine(JObject.Parse(Encoding.UTF8.GetString(Base64Url.Decode(claims))));
            }
            //設置請求的Token
            tokenCliet.SetBearerToken(token);
            //請求並返回字符串
            var apiResource1 = tokenCliet.GetStringAsync("identity").Result;
            var userinfo = tokenCliet.GetStringAsync("identity/userinfo").Result;

           var j =  JObject.Parse(userinfo);
            //或者
            var getVal = tokenCliet.GetAsync("api/values").Result;
            if (getVal.IsSuccessStatusCode)
            {
                Console.WriteLine(getVal.Content.ReadAsStringAsync().Result);
            }
            Console.ReadLine();
        }
    }
}

Access Token (decoded)的結果

技術分享圖片

OAuth2認證和授權:ClientCredentials認證