(Turn) Asp.Net Core cookie mix and JwtBearer certification scheme

Turn: https: //www.cnblogs.com/sunnytrudeau/p/9693512.html

Some small-scale demonstration projects, the server mainly offer Web Api function. For ease of management, you need to add in some simple server-side MVC page, log in as an administrator, to do some simple operations.

Therefore need to implement a function, at a site in Asp.Net Core, MVC pages use cookie authentication, Web Api with JwtBearer certification. Although the Identity Server 4 can achieve a variety of authentication schemes, but I think it is too heavy, I want to integrate two kinds of certification schemes directly on our website. Not found on the Internet ready DEMO, toss some time to get, so the record about.

 

Create a cookie authentication scheme MVC website

New Asp.Net Core MVC project. No authentication. No https convenient debugging.

 

Add login page view model class LoginViewModel

Copy the code
public class LoginViewModel
    {
        public string UserName { get; set; } = "";

        [DataType(DataType.Password)]
        public string Password { get; set; } = "";
    }
Copy the code

Home controller to increase logon and logoff function, when you want to create a user login identity.

Copy the code
        [HttpGet]
        public IActionResult Login(string returnUrl = "")
        {
            ViewData["ReturnUrl"] = returnUrl;
            return View();
        }

        [HttpPost, ActionName("Login")]
        public async Task<IActionResult> LoginPost(LoginViewModel model, string returnUrl = "")
        {
            ViewData["ReturnUrl"] = returnUrl;
            if (ModelState.IsValid)
            {
                bool succee = (model.UserName == "admin") && (model.Password == "123");

                if (succee)
                {
                    //创建用户身份标识
                    claimsIdentity new new ClaimsIdentity = var (CookieAuthenticationDefaults.AuthenticationScheme); 
                    claimsIdentity.AddClaims (new new List <the Claim> () 
                    { 
                        new new the Claim (ClaimTypes.Sid, model.UserName), 
                        new new the Claim (ClaimTypes.Name, model.UserName), 
                        new new the Claim ( ClaimTypes.Role, "ADMIN"), 
                    }); // call log HttpContext.SignInAsync, note that this method is the first parameter, the parameter required in services.AddAuthentication StartUp.cs same,                     // AddAuthentication login is provided, SigninAsync parameter is set by log 
                    await HttpContext.SignInAsync (CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal ( claimsIdentity));
            
return Redirect(returnUrl); } else { ModelState.AddModelError(string.Empty, "帐号或者密码错误。"); return View(model); } } return View(model); } public async Task<IActionResult> Logout() { await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme); return Redirect("/Home/Index"); }
Copy the code

Create a login page Login.cshtml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@model MixAuth.Models.LoginViewModel
 
@{
     ViewData["Title"] = "登录";
}
 
< div  class="row">
     < div  class="col-xs-10 col-sm-8 col-md-6">
         < form  asp-action="Login" asp-route-returnurl="@ViewData["ReturnUrl"]" method="post">
 
             < div  asp-validation-summary="All" class="text-danger"></ div >
             < div  class="form-group">
                 < label  asp-for="UserName"></ label >
                 < input  asp-for="UserName" class="form-control" placeholder="请输入用户名" />
                 < span  asp-validation-for="UserName" class="text-danger"></ span >
             </ div >
             < div  class="form-group">
                 < label  asp-for="Password"></ label >
                 < input  asp-for="Password" class="form-control" placeholder="请输入密码" />
                 < span  asp-validation-for="Password" class="text-danger"></ span >
             </ div >
 
             < button  type="submit" class="btn btn-primary">登录</ button >
 
         </ form >
     </ div >
</ div >
 
@section Scripts {
     @await Html.PartialAsync("_ValidationScriptsPartial")
}

  

然后在Startup.cs增加cookie认证方案,并开启认证中间件。

Copy the code
public void ConfigureServices(IServiceCollection services)
        {
            services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
                .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
                {
                    //认证失败,会自动跳转到这个地址
                    options.LoginPath = "/Home/Login";
                });

            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_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.UseStaticFiles();
            //app.UseCookiePolicy();

            //开启认证中间件
            app.UseAuthentication();

            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}");
            });
        }
Copy the code

给Home控制器的About函数增加认证要求。

        [Authorize]
        public IActionResult About()

把网站跑起来,点击关于,就会跳转到登录页面,登录通过后,会调回关于页面。

给网页再增加显示用户登录状态的功能。修改\Views\Shared\_Layout.cshtml,增加一个分部视图

1
2
3
4
5
6
7
8
< div  class="navbar-collapse collapse">
                 < ul  class="nav navbar-nav">
                     < li >< a  asp-area="" asp-controller="Home" asp-action="Index">Home</ a ></ li >
                     < li >< a  asp-area="" asp-controller="Home" asp-action="About">About</ a ></ li >
                     < li >< a  asp-area="" asp-controller="Home" asp-action="Contact">Contact</ a ></ li >
                 </ ul >
                 @await Html.PartialAsync("_LoginPartial")
             </ div >

  

_LoginPartial.cshtml分部视图内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@if (User.Identity.IsAuthenticated)
{
     < form  asp-controller="Home" asp-action="Logout" method="post" class="navbar-right">
         < ul  class="nav navbar-nav navbar-right">
             < li >
                 < a  href="#">@User.Identity.Name</ a >
             </ li >
             < li >
                 < button  type="submit" class="btn btn-link navbar-btn navbar-link">
                     退出登录
                 </ button >
             </ li >
         </ ul >
     </ form >
}
else
{
     < ul  class="nav navbar-nav navbar-right">
         < li >
             < a  asp-controller="Home" asp-action="Login" asp-route-returnUrl="/Home">
                 登录
             </ a >
         </ li >
     </ ul >
}

  

现在可以点击页面导航栏的按钮的登录和注销了。

 

至此,网页用cookie认证方案搞定。下面要在这个基础上,增加Web Api和JwtBearer认证。

创建JwtBearer认证方案的Web Api控制器

添加一个Web Api控制器,就用默认的value好了。

增加一个JWTTokenOptions类,定义认证的一些属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public  class  JWTTokenOptions
     {
         //谁颁发的
         public  string  Issuer {  get set ; } =  "server" ;
 
         //颁发给谁
         public  string  Audience {  get set ; } =  "client" ;
 
         //令牌密码
         public  string  SecurityKey {  get private  set ; } =  "a secret that needs to be at least 16 characters long" ;
 
         //修改密码,重新创建数字签名
         public  void  SetSecurityKey( string  value)
         {
             SecurityKey = value;
 
             CreateKey();
         }
 
         //对称秘钥
         public  SymmetricSecurityKey Key {  get set ; }
 
         //数字签名
         public  SigningCredentials Credentials {  get set ; }
 
         public  JWTTokenOptions()
         {
             CreateKey();
         }
 
         private  void  CreateKey()
         {
             Key =  new  SymmetricSecurityKey(Encoding.UTF8.GetBytes(SecurityKey));
             Credentials =  new  SigningCredentials(Key, SecurityAlgorithms.HmacSha256);
         }
     }

  

在startup.cs增加JwtBearer认证方案。

Copy the code
public void ConfigureServices(IServiceCollection services)
        {
            JWTTokenOptions jwtTokenOptions = new JWTTokenOptions();

            services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
                .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
                {
                    //认证失败,会自动跳转到这个地址(认证使用[Authorize]属性)
                    options.LoginPath = "/Home/Login";
                })
                .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, jwtBearerOptions =>
                {
                    jwtBearerOptions.TokenValidationParameters = new TokenValidationParameters
                    {
                        ValidateIssuerSigningKey = true,
                        IssuerSigningKey = jwtTokenOptions.Key,

                        ValidateIssuer = true,
                        ValidIssuer = jwtTokenOptions.Issuer,

                        ValidateAudience = true,
                        ValidAudience = jwtTokenOptions.Audience,

                        ValidateLifetime = true,
                        ClockSkew = TimeSpan.FromMinutes(5)
                    };
                });

            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_1);
        }
Copy the code

给value控制器增加认证方案,注意指定方案名称为JwtBearerDefaults.AuthenticationScheme。MVC控制器无需指定方案名称,因为默认就是CookieAuthenticationDefaults.AuthenticationScheme。

    [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
    [Route("api/[controller]")]
    [ApiController]
    public class ValueController : ControllerBase

此时通过浏览器访问Web Api控制器,http://localhost:5000/api/Value,会得到401错误,这是对的,我们也不打算通过浏览器的方式访问Web Api,而是通过PC或者手机客户端。为了让认证客户端,需要增加一个获取Token的函数,暂时放在Home控制器,它的属性设置为[AllowAnonymous],允许未认证者访问。

Copy the code
        [AllowAnonymous]
        [HttpGet]
        public string GetToken(string userName, string password)
        {
            bool success = ((userName == "user") && (password == "111"));
            if (!success)
                return "";

            JWTTokenOptions jwtTokenOptions = new JWTTokenOptions();

            //创建用户身份标识
            var claims = new Claim[] 
            {
                new Claim(ClaimTypes.Sid, userName),
                new Claim(ClaimTypes.Name, userName),
                new Claim(ClaimTypes.Role, "user"),
            };

            //创建令牌
            var token = new JwtSecurityToken(
                issuer: jwtTokenOptions.Issuer,
                audience: jwtTokenOptions.Audience,
                claims: claims,
                notBefore: DateTime.Now,
                expires: DateTime.Now.AddDays(1),
                signingCredentials: jwtTokenOptions.Credentials
                );

            string jwtToken = new JwtSecurityTokenHandler().WriteToken(token);

            return jwtToken;
        }
Copy the code

编写客户端使用JwtBearer认证

编写一个WPF客户端软件去获取Token,访问Web Api。

Copy the code
private async Task GetTokenAsync()
        {
            try
            {
                using (WebClient client = new WebClient())
                {
                    //地址
                    string path = $"{webUrl}/Home/GetToken?userName=user&password=111";

                    token = await client.DownloadStringTaskAsync(path);

                    txbMsg.Text = $"获取到令牌={token}";
                }
            }
            catch (Exception ex)
            {
                txbMsg.Text = $"获取令牌出错={ex.Message}";
            }
        }

        private async Task GetValueAsync()
        {
            try
            { 
                The using (new new = the WebClient the WebClient Client ()) 
                { 
                    // Address 
                    String $ path = "{} WebUrl / API / the Value"; 

                    client.Headers.Add (HttpRequestHeader.Authorization, $ "Bearer token {}"); 

                    String value the await client.DownloadStringTaskAsync = (path); 

                    txbMsg.Text $ = "Get value} {data ="; 
                } 
            } 
            the catch (Exception EX) 
            { 
                txbMsg.Text $ = "Get data error ex.Message {} ="; 
            } 
        }
Copy the code

If direct access to data, able to capture 401 error.

First get a token.

And then get the data, no problem.

DEMO Code See also:

https://github.com/woodsun2018/MixAuth

Guess you like

Origin www.cnblogs.com/wangle1001986/p/12170581.html