ASP.NET Core快速入門(第5章:認證與授權)

 

任務31:介紹

  • 1.Cookie-based認證與授權

  • 2.Cookie-based認證實現

  • 3.Jwt認證與授權介紹

  • 4.Jwt認證與授權實現

  • 5.Jwt認證與授權

  • 6.Role based授權

  • 7.Claims-based授權

任務32:Cookie-based認證介紹

任務34:Cookie-based認證實現

dotnet new mvc --name MvcCookieAuthSample

在Controllers文件夾新增AdminController.cs

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using MvcCookieAuthSample.Models;

namespace MvcCookieAuthSample.Controllers
{
    public class AdminController : Controller
    {
        public IActionResult Index()
        {
            return View();
        }
    }
}

在Views文件夾新增Admin文件夾,在Admin文件夾新增Index.cshtml

@{
    ViewData["Title"] = "Admin";
}
<h2>@ViewData["Title"]</h2>

<p>Admin Page</p>

啓動項目,瀏覽器訪問https://localhost:5001/Admin

實際情況不應該直接讓用戶訪問到Admin頁面,所以應當跳轉到登陸界面

AdminController.cs

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using MvcCookieAuthSample.Models;
// 添加引用
using Microsoft.AspNetCore.Authorization;

namespace MvcCookieAuthSample.Controllers
{
    public class AdminController : Controller
    {
        [Authorize]
        public IActionResult Index()
        {
            return View();
        }
    }
}

startup.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
// 添加引用
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authentication.Cookies;

namespace MvcCookieAuthSample
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        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;
            });

            // Addmvc之前AddAuthentication,AddCookie
            services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
            .AddCookie();
            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
                app.UseHsts();
            }

            app.UseHttpsRedirection();
            app.UseStaticFiles();
            app.UseCookiePolicy();

            // UseMvc之前UseAuthentication,添加Middleware
            app.UseAuthentication();
            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}");
            });
        }
    }
}

再次訪問https://localhost:5001/Admin,跳轉到登陸界面https://localhost:5001/Account/Login?ReturnUrl=%2FAdmin

在Controllers文件夾新增AccountController.cs

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using MvcCookieAuthSample.Models;
// 添加引用
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using System.Security.Claims;

namespace MvcCookieAuthSample.Controllers
{
    [Authorize]
    public class AccountController : Controller
    {
        public IActionResult MakeLogin()
        {
            var claims = new List<Claim>()
            {
                new Claim(ClaimTypes.Name,"Mingson"),
                new Claim(ClaimTypes.Role,"admin")
            };

            var claimIdentity = new ClaimsIdentity(claims,CookieAuthenticationDefaults.AuthenticationScheme);

            HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme,new ClaimsPrincipal(claimIdentity));
            
            return Ok();
        }

        public IActionResult Logout()
        {
            HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
            
            return Ok();
        }
    }
}

啓動項目

登出:localhost:5000/account/logout
訪問admin:localhost:5000/admin,跳轉到account/login
登陸:localhost:5000/account/makelogin
再次訪問admin:localhost:5000/admin,登陸成功訪問admin

任務35:JWT 認證授權介紹

可在官網解密:https://jwt.io

任務36:應用Jwtbearer Authentication

dotnet new webapi --name JwtAuthSample
dotnet watch run

打開postman調用
http://localhost:5000/api/values

ValuesController.cs

// 添加引用
using Microsoft.AspNetCore.Authorization;

    // 添加特性
    [Authorize]
    [Route("api/[controller]")]
    [ApiController]
    public class ValuesController : ControllerBase

新增一個Models文件夾,在文件夾中新增JwtSettings.cs

namespace JwtAuthSample
{
    public class JwtSettings
    {
        // token頒發者
        public string Issure{get;set;}
        // token使用的客戶端
        public string Audience{get;set;}
        // 加密Key
        public string SecretKey="hellokey";
    }
}

appsettings.json

{
  "Logging": {
    "LogLevel": {
      "Default": "Warning"
    }
  },
  "AllowedHosts": "*",
  "JwtSettings":{
    "Audience":"http://localhost:5000",
    "Issuer":"http://localhost:5000",
    "SecretKey":"Hello-key"
  }
}

Startup.cs

// 添加引用
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.Text;

            // 添加在services.AddMvc()之前
            services.Configure<JwtSettings>(Configuration);
            var JwtSettings = new JwtSettings();
            Configuration.Bind("JwtSettings",JwtSettings);
            // 認證MiddleWare配置
            services.AddAuthentication(options=>{
                options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            })
            // Jwt配置
            .AddJwtBearer(o=>{
                o.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters{
                    ValidIssuer = JwtSettings.Issure,
                    ValidAudience = JwtSettings.Audience,
                    IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(JwtSettings.SecretKey))// 對稱加密
                };
            });
            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

            app.UseHttpsRedirection();
            // 添加在app.UseMvc()之前
            app.UseAuthentication();
dotnet watch run

postman調用
http://localhost:5000/api/values
返回401,未授權

任務37:生成 JWT Token

新建文件夾ViewModels,在文件夾中新建LoginViewModel.cs

using System.ComponentModel.DataAnnotations;

namespace JwtAuthSample
{
    public class LoginViewModel
    {
        [Required]
        public string User{get;set;}
        [Required]
        public string Password{get;set;}
    }
}

AuthorizeController.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
// 添加引用
using System.Security.Claims;
using Microsoft.IdentityModel.Tokens;
using Microsoft.Extensions.Options;
using System.Text;
using System.IdentityModel.Tokens.Jwt;

namespace JwtAuthSample.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class AuthorizeController : ControllerBase
    {
        private JwtSettings _jwtSettings;

        public AuthorizeController(IOptions<JwtSettings> _jwtSettingsAccesser)
        {
            _jwtSettings = _jwtSettingsAccesser.Value;
        }

        public IActionResult Token(LoginViewModel viewModel)
        {
            if (ModelState.IsValid)
            {
                if (!(viewModel.User == "mingson" && viewModel.Password == "123456"))
                {
                    return BadRequest();
                }

                var claims = new Claim[]
                {
                    new Claim(ClaimTypes.Name, "mingson"),
                    new Claim(ClaimTypes.Role, "admin")
                };

                var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_jwtSettings.SecretKey));// 對稱加密算法
                var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);

                // VSCode安裝擴展NuGet Package Manager
                // ctrl + shift + p
                // NuGet Package Manager:Add Pcakage
                // Microsoft.AspNetCore.Authentication.JwtBearer
                // 需要FQ才能添加
                // 2.0.0
                // 安裝到csproj
                // 安裝成功後csproj中出現<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="2.0.0" />
                // dotnet restore

                var token = new JwtSecurityToken(
                    _jwtSettings.Issure,
                    _jwtSettings.Audience,
                    claims,
                    DateTime.Now,
                    DateTime.Now.AddMinutes(30),
                    creds);

                return Ok(new {token = new JwtSecurityTokenHandler().WriteToken(token)});
            }

            return BadRequest();
        }
    }
}

Startup.cs

            // 添加在services.AddMvc()之前
            //services.Configure<JwtSettings>(Configuration);// 獲取不到JwtSettings配置
            services.Configure<JwtSettings>(Configuration.GetSection("JwtSettings"));// 獲取appsettings.json中的配置

appsettings.json

{
  "Logging": {
    "LogLevel": {
      "Default": "Warning"
    }
  },
  "AllowedHosts": "*",
  "JwtSettings":{
    "Audience":"http://localhost:5000",
    "Issuer":"http://localhost:5000",
    "SecretKey長度必須大於128bit=16字符":"",
    "SecretKey":"Hello-key.jessetalk"
  }
}
dotnet watch run

postman調用
http://localhost:5000/Authorize/Token
返回Token

加上token調用
http://localhost:5000/api/values

token可在官網解密:https://jwt.io

輸入正確的SecretKey:Hello-key.jessetalk

任務38:JWT 設計解析及定製

新建文件MyTokenValidator.cs

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
// 添加引用
using Microsoft.AspNetCore.Authentication.JwtBearer;
using System.Security.Claims;
using Microsoft.IdentityModel.Tokens;

namespace JwtAuthSample
{
    public class MyTokenValidator : ISecurityTokenValidator
    {
        bool ISecurityTokenValidator.CanValidateToken => true;

        int ISecurityTokenValidator.MaximumTokenSizeInBytes { get;set; }

        bool ISecurityTokenValidator.CanReadToken(string securityToken)
        {
            return true;
        }

        ClaimsPrincipal ISecurityTokenValidator.ValidateToken(string securityToken, TokenValidationParameters validationParameters, out SecurityToken validatedToken)
        {
            validatedToken = null;
            var identity = new ClaimsIdentity(JwtBearerDefaults.AuthenticationScheme);

            if (securityToken == "abcdefg")
            {
                identity.AddClaim(new Claim("name", "mingson"));
                identity.AddClaim(new Claim("SuperAdminOnly", "true"));
                identity.AddClaim(new Claim(ClaimsIdentity.DefaultNameClaimType, "user"));
            }

            var principal = new ClaimsPrincipal(identity);

            return principal;
        }
    }
}

Startup.cs

            // 認證MiddleWare配置
            services.AddAuthentication(options=>{
                options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            })
            // Jwt配置
            .AddJwtBearer(o=>{
                // o.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters{
                //     ValidIssuer = JwtSettings.Issure,
                //     ValidAudience = JwtSettings.Audience,
                //     IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(JwtSettings.SecretKey))// 對稱加密
                // };

                // 修改token來源
                o.SecurityTokenValidators.Clear();// 一個包含驗證的數組,先清除
                o.SecurityTokenValidators.Add(new MyTokenValidator());

                // 修改token驗證方式
                o.Events = new JwtBearerEvents(){
                  OnMessageReceived = context => {
                      var token = context.Request.Headers["mytoken"];
                      context.Token = token.FirstOrDefault();
                      return Task.CompletedTask;
                  }
                };
            });
            
            services.AddAuthorization(Options=>{
                Options.AddPolicy("SuperAdminOnly", policy => policy.RequireClaim("SuperAdminOnly"));
            });

AuthorizeController.cs

                // var claims = new Claim[]
                // {
                //     new Claim(ClaimTypes.Name, "mingson"),
                //     new Claim(ClaimTypes.Role, "admin")
                // };
                var claims = new Claim[]
                {
                    new Claim(ClaimTypes.Name, "mingson"),
                    new Claim(ClaimTypes.Role, "user"),
                    new Claim("SuperAdminOnly", "true")
                };

ValuesController.cs

// [Authorize]// 添加標籤
    [Authorize(Policy="SuperAdminOnly")]
dotnet run

輸入一個錯誤的mytoken,返回403 Forbidden,禁止訪問

輸入一個正確的mytoken,返回200 OK

任務39:Role以及Claims授權

Role授權

AuthorizeController.cs

                var claims = new Claim[]
                {
                    new Claim(ClaimTypes.Name, "mingson"),
                    new Claim(ClaimTypes.Role, "admin")
                };

ValuesController.cs

    [Authorize(Roles="user")]
dotnet run

帶着token訪問,返回403 Forbidden,禁止訪問

AuthorizeController.cs修改爲user,可訪問

                var claims = new Claim[]
                {
                    new Claim(ClaimTypes.Name, "mingson"),
                    new Claim(ClaimTypes.Role, "user")
                };

Claims授權

Startup.cs

            // 認證MiddleWare配置
            services.AddAuthentication(options=>{
                options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            })
            // Jwt配置
            .AddJwtBearer(o=>{
                o.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters{
                    ValidIssuer = JwtSettings.Issure,
                    ValidAudience = JwtSettings.Audience,
                    IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(JwtSettings.SecretKey))// 對稱加密
                };
            });

            services.AddAuthorization(Options=>{
                Options.AddPolicy("SuperAdminOnly", policy => policy.RequireClaim("SuperAdminOnly"));
            });

ValuesController.cs

    [Authorize(Policy="SuperAdminOnly")]

AuthorizeController.cs

                var claims = new Claim[]
                {
                    new Claim(ClaimTypes.Name, "mingson"),
                    new Claim(ClaimTypes.Role, "user"),
                    new Claim("SuperAdminOnly", "true")
                };
dotnet run

帶着token訪問,返回200 Ok

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章