Asp.Net Core混合使用cookie和JwtBearer认证方案

自己有时捣鼓一些小型演示项目,服务端主要是提供Web Api功能。为了便于管理,需要在服务端加一些简单的MVC网页,用管理员身份登录,做一些简单的操作。

因此需要实现一个功能,在一个Asp.Net Core网站里,MVC网页用cookie认证,Web Api用JwtBearer认证。虽然Identity Server 4可以实现多种认证方案,但是我觉得它太重了,想直接在网站内集成2种认证方案。在网上没有找到现成的DEMO,自己折腾了一段时间搞定了,所以记录一下。

创建cookie认证方案的MVC网站

新建Asp.Net Core MVC项目。无身份验证。无https方便调试。

添加登录网页视图模型类LoginViewModel

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

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

给Home控制器增加登录和注销函数,登录的时候要创建用户身份标识。

        [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)
                {
                    //创建用户身份标识
                    var claimsIdentity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme);
                    claimsIdentity.AddClaims(new List<Claim>()
                    {
                        new Claim(ClaimTypes.Sid, model.UserName),
                        new Claim(ClaimTypes.Name, model.UserName),
                        new Claim(ClaimTypes.Role, "admin"),
                    });

                    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");
        }

新建一个登录网页Login.cshtml

@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认证方案,并开启认证中间件。

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?}");
            });
        }

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

        [Authorize]
        public IActionResult About()

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

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

<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分部视图内容

@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类,定义认证的一些属性。

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认证方案。

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

            services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
                .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
                {
                    //认证失败,会自动跳转到这个地址
                    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);
        }

给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],允许未认证者访问。

        [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;
        }

编写客户端使用JwtBearer认证

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

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
            {
                using (WebClient client = new WebClient())
                {
                    //地址
                    string path = $"{webUrl}/api/Value";

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

                    string value = await client.DownloadStringTaskAsync(path);

                    txbMsg.Text = $"获取到数据={value}";
                }
            }
            catch (Exception ex)
            {
                txbMsg.Text = $"获取数据出错={ex.Message}";
            }
        }

如果直接获取数据,能够捕捉到401错误。

先获取令牌。

再获取数据,就没问题了。

DEMO代码参见:

https://github.com/woodsun2018/MixAuth

原文地址:https://www.cnblogs.com/sunnytrudeau/p/9693512.html

时间: 2024-11-01 18:47:33

Asp.Net Core混合使用cookie和JwtBearer认证方案的相关文章

Asp.Net Core 利用Cookie做身份认证

一 注册Cookie认证服务 ConfigureServices services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie(o=> { o.LoginPath = new PathString("/Home/Index"); o.LogoutPath = new PathString("/Account/Login"); } ); 二 配置中间

关于ASP.Net Core Web及API身份认证的解决方案

6月15日,在端午节前的最后一个工作日,想起有段日子没有写过文章了,倒有些荒疏了.今借夏日蒸蒸之气,偷得浮生半日悠闲.闲话就说到这里吧,提前祝大家端午愉快(屈原听了该不高兴了:))!.NetCore自发布以来,颇受关注,现在.Net Core2.0已经正式发布,边迫不及待的将.Net跨平台移植的工作进行到底.想来,也费不了多少事儿.我经常和同事们说,要敢于尝试新鲜事物,不阴损守旧,方能使自己不断进步,站在队伍的前列.下面就关于Asp.Net Core在Web 及API项目上身份认证的问题做下简单

.net core下用HttpClient和asp.net core实现https的双向认证

原文:.net core下用HttpClient和asp.net core实现https的双向认证 关于https双向认证的知识可先行google,这时矸接代码. 为了双向认证,我们首先得准备两个crt证书,一个是client.crt,一个是server.crt,有时为了验证是否同一个根证书的验证,这两个证书可以共有一个根证书root.crt. 首先要生成这些证书,这里采用了自签证书方式: 证书生成工具可在这里下载(windows下生成): https://github.com/axzxs200

ASP.NET Core 使用Cookie验证身份

ASP.NET Core 1.x提供了通过Cookie 中间件将用户主体序列化为一个加密的Cookie,然后在后续请求中验证Cookie并重新创建主体,并将其分配给HttpContext.User属性.如果您要提供自己的登录界面和用户数据库,可以使用作为独立功能的Cookie中间件. ASP.NET Core 2.x的一个主要变化是不再存在Cookie中间件.取而代之的是在Startup.cs文件中的Configure方法中的调用UseAuthentication方法会添加设置HttpConte

ASP.NET Core 认证与授权[4]:JwtBearer认证

在现代Web应用程序中,通常会使用Web, WebApp, NativeApp等多种呈现方式,而后端也由以前的Razor渲染HTML,转变为Stateless的RESTFulAPI,因此,我们需要一种标准的,通用的,无状态的,与语言无关的认证方式,也就是本文要介绍的JwtBearer认证. 目录 Bearer认证 JWT(JSON WEB TOKEN) 头部(Header) 载荷(Payload) 签名(Signature) 示例 模拟Token 注册JwtBearer认证 添加受保护资源 运行

Asp.net Core认证和授权:Cookie认证

这里我只是记录下自己在学习中的点滴和一些不懂的地方 Cookie一般是用户网站授权,当用户访问需要授权(authorization)的页面,程序会判断是否已经授权,并认证 添加认证代码:引入命名空间:Microsoft.AspNetCore.Authentication.Cookies; 添加服务 publicvoidConfigureServices(IServiceCollection services) { services.AddMvc().SetCompatibilityVersion

ASP.NET Core实现 随处可见的基本身份认证

原文:ASP.NET Core实现 随处可见的基本身份认证 概览 在HTTP中,基本认证(Basic access authentication,简称BA认证)是一种用来允许网页浏览器或其他客户端程序在请求资源时提供用户名和口令形式的身份凭证的一种登录验证方式,不要求cookie,session identifier.login page等标记或载体. 优点:基本上所有流行的网页浏览器都支持BA认证. 缺点:明文传输密钥和口令(容易被拦截); 没有对服务器返回的信息提供保护. https://e

[ASP.NET Core 3.1]浏览器嗅探解决部分浏览器丢失Cookie问题

原文:[ASP.NET Core 3.1]浏览器嗅探解决部分浏览器丢失Cookie问题 今天的干货长驱直入,直奔主题 看了前文的同学们应该都知道,搜狗.360等浏览器在单点登录中反复重定向,最终失败报错. 原因在于,非Chrome80+浏览器不识别Cookie上的SameSite=none属性值,导致认证Cookie在后续请求中被抛弃. 截至2020/3/30号,非Chrome浏览器测试包含两种结果: case1:可设置cookie的samesite=none, 浏览器可读取该cookie ca

.NET跨平台之旅:ASP.NET Core从传统ASP.NET的Cookie中读取用户登录信息

小分享:我有几张阿里云优惠券,用券购买或者升级阿里云相应产品最多可以优惠五折!领券地址:https://promotion.aliyun.com/ntms/act/ambassador/sharetouser.html?userCode=ohmepe03 在解决了asp.net core中访问memcached缓存的问题后,我们开始大踏步地向.net core进军--将更多站点向asp.net core迁移,在迁移涉及获取用户登录信息的站点时,我们遇到了一个问题--如何在asp.net core