asp.netcore2.0webapi基于JWT自定義策略授權(quán)-創(chuàng)新互聯(lián)

JWT(json web token)是一種基于json的身份驗(yàn)證機(jī)制,流程如下:

成都創(chuàng)新互聯(lián)公司IDC提供業(yè)務(wù):四川電信機(jī)房托管,成都服務(wù)器租用,四川電信機(jī)房托管,重慶服務(wù)器租用等四川省內(nèi)主機(jī)托管與主機(jī)租用業(yè)務(wù);數(shù)據(jù)中心含:雙線機(jī)房,BGP機(jī)房,電信機(jī)房,移動(dòng)機(jī)房,聯(lián)通機(jī)房。

asp.net core 2.0 web api基于JWT自定義策略授權(quán)

通過(guò)登錄,來(lái)獲取Token,再在之后每次請(qǐng)求的Header中追加Authorization為Token的憑據(jù),服務(wù)端驗(yàn)證通過(guò)即可能獲取想要訪問(wèn)的資源。關(guān)于JWT的技術(shù),可參考網(wǎng)絡(luò)上文章,這里不作詳細(xì)說(shuō)明,

這篇博文,主要說(shuō)明在asp.net core 2.0中,基于jwt的web api的權(quán)限設(shè)置,即在asp.net core中怎么用JWT,再次就是不同用戶或角色因?yàn)闄?quán)限問(wèn)題,即使援用Token,也不能訪問(wèn)不該訪問(wèn)的資源。

基本思路是我們自定義一個(gè)策略,來(lái)驗(yàn)證用戶,和驗(yàn)證用戶授權(quán),PermissionRequirement是驗(yàn)證傳輸授權(quán)的參數(shù)。在Startup的ConfigureServices注入驗(yàn)證(Authentication),授權(quán)(Authorization),和JWT(JwtBearer)

自定義策略:

已封閉成AuthorizeRolicy.JWT nuget包,并發(fā)布到nuget上:

https://www.nuget.org/packages/AuthorizePolicy.JWT/

源碼如下:

JwtToken.cs

        /// <summary>
        /// 獲取基于JWT的Token
        /// </summary>
        /// <param name="username"></param>
        /// <returns></returns>
        public static dynamic BuildJwtToken(Claim[] claims, PermissionRequirement permissionRequirement)
        {
            var now = DateTime.UtcNow;
            var jwt = new JwtSecurityToken(
                issuer: permissionRequirement.Issuer,
                audience: permissionRequirement.Audience,
                claims: claims,
                notBefore: now,
                expires: now.Add(permissionRequirement.Expiration),
                signingCredentials: permissionRequirement.SigningCredentials
            );
            var encodedJwt = new JwtSecurityTokenHandler().WriteToken(jwt);
            var response = new
            {
                Status = true,
                access_token = encodedJwt,
                expires_in = permissionRequirement.Expiration.TotalMilliseconds,
                token_type = "Bearer"
            };
            return response;
        }

Permission.cs

    /// <summary>
    /// 用戶或角色或其他憑據(jù)實(shí)體
    /// </summary>
    public class Permission
    {
        /// <summary>
        /// 用戶或角色或其他憑據(jù)名稱
        /// </summary>
        public virtual string Name
        { get; set; }
        /// <summary>
        /// 請(qǐng)求Url
        /// </summary>
        public virtual string Url
        { get; set; }
    }

PermissionRequirement.cs

    /// <summary>
    /// 必要參數(shù)類
    /// </summary>
    public class PermissionRequirement : IAuthorizationRequirement
    {
        /// <summary>
        /// 用戶權(quán)限集合
        /// </summary>
        public List<Permission> Permissions { get; private set; }
        /// <summary>
        /// 無(wú)權(quán)限action
        /// </summary>
        public string DeniedAction { get; set; }
        /// <summary>
        /// 認(rèn)證授權(quán)類型
        /// </summary>
        public string ClaimType { internal get; set; }
        /// <summary>
        /// 請(qǐng)求路徑
        /// </summary>
        public string LoginPath { get; set; } = "/Api/Login";
        /// <summary>
        /// 發(fā)行人
        /// </summary>
        public string Issuer { get; set; }
        /// <summary>
        /// 訂閱人
        /// </summary>
        public string Audience { get; set; }
        /// <summary>
        /// 過(guò)期時(shí)間
        /// </summary>
        public TimeSpan Expiration { get; set; } = TimeSpan.FromMinutes(5000);
        /// <summary>
        /// 簽名驗(yàn)證
        /// </summary>
        public SigningCredentials SigningCredentials { get; set; }
        /// <summary>
        /// 構(gòu)造
        /// </summary>
        /// <param name="deniedAction">無(wú)權(quán)限action</param>
        /// <param name="userPermissions">用戶權(quán)限集合</param>
        /// <summary>
        /// 構(gòu)造
        /// </summary>
        /// <param name="deniedAction">拒約請(qǐng)求的url</param>
        /// <param name="permissions">權(quán)限集合</param>
        /// <param name="claimType">聲明類型</param>
        /// <param name="issuer">發(fā)行人</param>
        /// <param name="audience">訂閱人</param>
        /// <param name="signingCredentials">簽名驗(yàn)證實(shí)體</param>
        public PermissionRequirement(string deniedAction, List<Permission> permissions, string claimType, string issuer, string audience, SigningCredentials signingCredentials)
        {
            ClaimType = claimType;
            DeniedAction = deniedAction;
            Permissions = permissions;
            Issuer = issuer;
            Audience = audience;
            SigningCredentials = signingCredentials;
        }
    }

自定義策略類PermissionHandler.cs

    /// <summary>
    /// 權(quán)限授權(quán)Handler
    /// </summary>
    public class PermissionHandler : AuthorizationHandler<PermissionRequirement>
    {    
        /// <summary>
        /// 驗(yàn)證方案提供對(duì)象
        /// </summary>
        public IAuthenticationSchemeProvider Schemes { get; set; }
        /// <summary>
        /// 自定義策略參數(shù)
        /// </summary>
        public PermissionRequirement Requirement
        { get; set; }
        /// <summary>
        /// 構(gòu)造
        /// </summary>
        /// <param name="schemes"></param>
        public PermissionHandler(IAuthenticationSchemeProvider schemes)
        {
            Schemes = schemes;
        }  
        protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionRequirement requirement)
        {
            ////賦值用戶權(quán)限       
            Requirement = requirement;
            //從AuthorizationHandlerContext轉(zhuǎn)成HttpContext,以便取出表求信息
            var httpContext = (context.Resource as Microsoft.AspNetCore.Mvc.Filters.AuthorizationFilterContext).HttpContext;
            //請(qǐng)求Url
            var questUrl = httpContext.Request.Path.Value.ToLower();  
            //判斷請(qǐng)求是否停止
            var handlers = httpContext.RequestServices.GetRequiredService<IAuthenticationHandlerProvider>();
            foreach (var scheme in await Schemes.GetRequestHandlerSchemesAsync())
            {
                var handler = await handlers.GetHandlerAsync(httpContext, scheme.Name) as IAuthenticationRequestHandler;
                if (handler != null && await handler.HandleRequestAsync())
                {
                    context.Fail();
                    return;
                }
            }
            //判斷請(qǐng)求是否擁有憑據(jù),即有沒(méi)有登錄
            var defaultAuthenticate = await Schemes.GetDefaultAuthenticateSchemeAsync();
            if (defaultAuthenticate != null)
            {
                var result = await httpContext.AuthenticateAsync(defaultAuthenticate.Name);
                //result?.Principal不為空即登錄成功
                if (result?.Principal != null)
                {
                    httpContext.User = result.Principal;
                    //權(quán)限中是否存在請(qǐng)求的url
                    if (Requirement.Permissions.GroupBy(g => g.Url).Where(w => w.Key.ToLower() == questUrl).Count() > 0)
                    {
                        var name = httpContext.User.Claims.SingleOrDefault(s => s.Type == requirement.ClaimType).Value;
                        //驗(yàn)證權(quán)限
                        if (Requirement.Permissions.Where(w => w.Name == name && w.Url.ToLower() == questUrl).Count() <= 0)
                        {
                            //無(wú)權(quán)限跳轉(zhuǎn)到拒絕頁(yè)面
                            httpContext.Response.Redirect(requirement.DeniedAction);
                        }
                    }
                    context.Succeed(requirement);
                    return;
                }
            }
            //判斷沒(méi)有登錄時(shí),是否訪問(wèn)登錄的url,并且是Post請(qǐng)求,并助是form表單提交類型,否則為失敗
            if (!questUrl.Equals(Requirement.LoginPath.ToLower(), StringComparison.Ordinal) && (!httpContext.Request.Method.Equals("POST")
               || !httpContext.Request.HasFormContentType))
            {
                context.Fail();
                return;
            }
            context.Succeed(requirement);     
        }
    }

新建asp.net core 2.0的web api項(xiàng)目,并在項(xiàng)目添加AuthorizePolicy.JWT如圖

asp.net core 2.0 web api基于JWT自定義策略授權(quán)

先設(shè)置配置文件,用戶可以定義密匙和發(fā)生人,訂閱人

 "Audience": {

   "Secret": "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890",

   "Issuer": "gsw",

   "Audience": "everone"

}

在ConfigureServices中注入驗(yàn)證(Authentication),授權(quán)(Authorization),和JWT(JwtBearer)

Startup.cs

        public void ConfigureServices(IServiceCollection services)
        {
            //讀取配置文件
            var audienceConfig = Configuration.GetSection("Audience");
            var symmetricKeyAsBase64 = audienceConfig["Secret"];
            var keyByteArray = Encoding.ASCII.GetBytes(symmetricKeyAsBase64);
            var signingKey = new SymmetricSecurityKey(keyByteArray);
            var tokenValidationParameters = new TokenValidationParameters
            {
                ValidateIssuerSigningKey = true,
                IssuerSigningKey = signingKey,
                ValidateIssuer = true,
                ValidIssuer = audienceConfig["Issuer"],
                ValidateAudience = true,
                ValidAudience = audienceConfig["Audience"],
                ValidateLifetime = true,
                ClockSkew = TimeSpan.Zero
            };
            var signingCredentials = new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256);
            services.AddAuthorization(options =>
            {
                //這個(gè)集合模擬用戶權(quán)限表,可從數(shù)據(jù)庫(kù)中查詢出來(lái)
                var permission = new List<Permission> {
                              new Permission {  Url="/", Name="admin"},
                              new Permission {  Url="/api/values", Name="admin"},
                              new Permission {  Url="/", Name="system"},
                              new Permission {  Url="/api/values1", Name="system"}
                          };
                //如果第三個(gè)參數(shù),是ClaimTypes.Role,上面集合的每個(gè)元素的Name為角色名稱,如果ClaimTypes.Name,即上面集合的每個(gè)元素的Name為用戶名
                var permissionRequirement = new PermissionRequirement("/api/denied", permission, ClaimTypes.Role, audienceConfig["Issuer"], audienceConfig["Audience"], signingCredentials);
                options.AddPolicy("Permission",
                          policy => policy.Requirements.Add(permissionRequirement));
            }).AddAuthentication(options =>
            {
                options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            })
            .AddJwtBearer(o =>
            {
                //不使用https
                o.RequireHttpsMetadata = false;
                o.TokenValidationParameters = tokenValidationParameters;
            });
            //注入授權(quán)Handler
            services.AddSingleton<IAuthorizationHandler, PermissionHandler>();
            services.AddMvc();
        }

在需要授的Controller上添加授權(quán)特性

   [Authorize("Permission")]

PermissionController類有兩個(gè)方法,一個(gè)是登錄,驗(yàn)證用戶名和密碼是否正確,如果正確就發(fā)放Token,如果失敗,驗(yàn)證失敗,別一個(gè)成功登后的無(wú)權(quán)限導(dǎo)航action。

    [Authorize("Permission")]
    public class PermissionController : Controller
    {
        /// <summary>
        /// 自定義策略參數(shù)
        /// </summary>
        PermissionRequirement _requirement;
        public PermissionController(IAuthorizationHandler authorizationHander)
        {
            _requirement = (authorizationHander as PermissionHandler).Requirement;
        }
        [AllowAnonymous]
        [HttpPost("/api/login")]
        public IActionResult Login(string username,string password,string role)
        { 
            var isValidated = username == "gsw" && password == "111111";
            if (!isValidated)
            {
                return new JsonResult(new
                {
                    Status = false,
                    Message = "認(rèn)證失敗"
                });
            }
            else
            { 
                //如果是基于角色的授權(quán)策略,這里要添加用戶;如果是基于角色的授權(quán)策略,這里要添加角色
                var claims =new Claim[]{ new Claim(ClaimTypes.Name, username),new Claim(ClaimTypes.Role, role) };
                //用戶標(biāo)識(shí)
                var identity = new ClaimsIdentity(JwtBearerDefaults.AuthenticationScheme); 
                identity.AddClaims(claims);
                //登錄
                HttpContext.SignInAsync(JwtBearerDefaults.AuthenticationScheme, new ClaimsPrincipal(identity));
                var token = JwtToken.BuildJwtToken(claims, _requirement);
                return new JsonResult(token);
            }
        }
        [AllowAnonymous]
        [HttpGet("/api/denied")]
        public IActionResult Denied()
        {
            return new JsonResult(new
            {
                Status = false,
                Message = "你無(wú)權(quán)限訪問(wèn)"
            });
        }
    }

下面定義一個(gè)控制臺(tái)(.NetFramewrok)程序,用RestSharp來(lái)訪問(wèn)我們定義的web api,其中1為admin角色登錄,2為system角色登錄,3為錯(cuò)誤用戶密碼登錄,4是一個(gè)查詢功能,在startup.cs中,admin角色是具有查詢/api/values的權(quán)限的,所以用admin登錄是能正常訪問(wèn)的,用system登錄,能成功登錄,但沒(méi)有權(quán)限訪問(wèn)/api/values,用戶名密碼錯(cuò)誤,訪問(wèn)/aip/values,直接是沒(méi)有授權(quán)的

  class Program
    {
        /// <summary>
        /// 訪問(wèn)Url
        /// </summary>
        static string _url = "http://localhost:39286";
        static void Main(string[] args)
        {
            dynamic token = null;
            while (true)
            {
                Console.WriteLine("1、登錄【admin】 2、登錄【system】 3、登錄【錯(cuò)誤用戶名密碼】 4、查詢數(shù)據(jù) ");
                var mark = Console.ReadLine();
                var stopwatch = new Stopwatch();
                stopwatch.Start();
                switch (mark)
                {
                    case "1":
                        token = AdminLogin();
                        break;
                    case "2":
                        token = SystemLogin();
                        break;
                    case "3":
                        token = NullLogin();
                        break;
                    case "4":
                        AdminInvock(token);
                        break;
                }
                stopwatch.Stop();
                TimeSpan timespan = stopwatch.Elapsed;
                Console.WriteLine($"間隔時(shí)間:{timespan.TotalSeconds}");
            }
        }
        static dynamic NullLogin()
        {
            var loginClient = new RestClient(_url);
            var loginRequest = new RestRequest("/api/login", Method.POST);
            loginRequest.AddParameter("username", "gswaa");
            loginRequest.AddParameter("password", "111111");
            //或用用戶名密碼查詢對(duì)應(yīng)角色
            loginRequest.AddParameter("role", "system");
            IRestResponse loginResponse = loginClient.Execute(loginRequest);
            var loginContent = loginResponse.Content;
            Console.WriteLine(loginContent);
            return Newtonsoft.Json.JsonConvert.DeserializeObject(loginContent);
        }
        static dynamic SystemLogin()
        {
            var loginClient = new RestClient(_url);
            var loginRequest = new RestRequest("/api/login", Method.POST);
            loginRequest.AddParameter("username", "gsw");
            loginRequest.AddParameter("password", "111111");
            //或用用戶名密碼查詢對(duì)應(yīng)角色
            loginRequest.AddParameter("role", "system");
            IRestResponse loginResponse = loginClient.Execute(loginRequest);
            var loginContent = loginResponse.Content;
            Console.WriteLine(loginContent);
            return Newtonsoft.Json.JsonConvert.DeserializeObject(loginContent);
        }
        static dynamic AdminLogin()
        {
            var loginClient = new RestClient(_url);
            var loginRequest = new RestRequest("/api/login", Method.POST);
            loginRequest.AddParameter("username", "gsw");
            loginRequest.AddParameter("password", "111111");
            //或用用戶名密碼查詢對(duì)應(yīng)角色
            loginRequest.AddParameter("role", "admin");
            IRestResponse loginResponse = loginClient.Execute(loginRequest);
            var loginContent = loginResponse.Content;
            Console.WriteLine(loginContent);
            return Newtonsoft.Json.JsonConvert.DeserializeObject(loginContent);
        }
        static void AdminInvock(dynamic token)
        {
            var client = new RestClient(_url);
            //這里要在獲取的令牌字符串前加Bearer
            string tk = "Bearer " + Convert.ToString(token?.access_token);
            client.AddDefaultHeader("Authorization", tk);
            var request = new RestRequest("/api/values", Method.GET);
            IRestResponse response = client.Execute(request);
            var content = response.Content;
            Console.WriteLine($"狀態(tài):{response.StatusCode}  返回結(jié)果:{content}");
        }
    }

運(yùn)行結(jié)果:

asp.net core 2.0 web api基于JWT自定義策略授權(quán)

源碼:https://github.com/axzxs2001/AuthorizePolicy.JWT

創(chuàng)新互聯(lián)www.cdcxhl.cn,專業(yè)提供香港、美國(guó)云服務(wù)器,動(dòng)態(tài)BGP最優(yōu)骨干路由自動(dòng)選擇,持續(xù)穩(wěn)定高效的網(wǎng)絡(luò)助力業(yè)務(wù)部署。公司持有工信部辦法的idc、isp許可證, 機(jī)房獨(dú)有T級(jí)流量清洗系統(tǒng)配攻擊溯源,準(zhǔn)確進(jìn)行流量調(diào)度,確保服務(wù)器高可用性。佳節(jié)活動(dòng)現(xiàn)已開(kāi)啟,新人活動(dòng)云服務(wù)器買多久送多久。

文章題目:asp.netcore2.0webapi基于JWT自定義策略授權(quán)-創(chuàng)新互聯(lián)
文章地址:http://bm7419.com/article0/gidio.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站營(yíng)銷、企業(yè)網(wǎng)站制作虛擬主機(jī)、搜索引擎優(yōu)化企業(yè)建站、ChatGPT

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

成都網(wǎng)站建設(shè)