asp.net core webapi验证

转载:https://www.cnblogs.com/ibeisha/p/jwt-webapi.html

一、Asp.Net Core Web项目的登录认证
在MVC Web项目中,做用户登录授权,是必不可少的工作,不知道大家平时是怎么做的,我想,大多朋友还是使用微软提供的一套认证机制,可以省去很多功夫。从WebForm时代的Form身份认证,无非是通过客户端Cookie中存储认证票据,在请求受保护的资源时,通过Cookie中携带的身份票据,再有Asp.net的认证模块,完整对请求者的身份认证。这一过程,是很清晰简单的了。在MVC中,大多是通过中间件(MiddleWare)来完整认证授权过程。在ASP.NETMVC中,我们了解到基于声明的授权认证(Claim),这种认证方式,好处在于,我们想在用户授权时,存储多个属性信息,只需要添加多个声明即可,我们在微软的认证中间件中,看到的都是定义好的常量,当然,我们可以定义自己的ClaimTypes。然我们看看微软在.NetCore中定义的一些声明吧:
在这里插入图片描述
在这里插入图片描述
那么我们在Asp.Net Core项目中的认证,也是比较简单的。也是通过HttpContext的扩展方法SignInAsync,来传入声明的身份信息。要使用的微软的认证组件,我们在.Net Core Web项目中,做如下改动:

首先,在Start.cs类中,添加服务,具体代码如下:

public void ConfigureServices(IServiceCollection services)
{
    services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
    .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, o =>
    {
        o.Cookie.Name = "_AdminTicketCookie";
        o.LoginPath = new PathString("/Account/Login");
        o.LogoutPath = new PathString("/Account/Login");
        o.AccessDeniedPath = new PathString("/Error/Forbidden");
    });
    services.AddTransient<TiKu.Application.Interfaces.IAdminService, TiKu.Application.AdminService>();
    services.AddMvc();
}
    其次,添加认证中间件 
/// <summary>
/// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
/// </summary>
/// <param name="app"></param>
/// <param name="env"></param>
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
 
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
    }
    app.UseStaticFiles();
    app.UseAuthentication();//添加认证中间件
    app.UseMvc(routes =>
    {
        routes.MapRoute(
            name: "default",
            template: "{controller=Home}/{action=Index}/{id?}");
    });
     
}
最后,在用户登录的地方,登录成功后,调用HttpContext的SignIn方法,将授权信息写入Cookie,示例代码如下:
/// <summary>
/// <![CDATA[登陆]]>
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(Models.LoginViewModel model)
{
    try
    {
        //模型验证通过后
        if (ModelState.IsValid)
        {
            model.password = TiKu.Common.Security.MD5.Md5(model.password);//MD5加密
            TiKu.Domain.Entity.tb_Admin admin = await _AdminService.CheckAccountAndPassword(account: model.account, password: model.password);
            //验证用户名密码
            if (admin != null)
            {
                var identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme);//一定要声明AuthenticationScheme
                identity.AddClaim(new Claim(ClaimTypes.Name, admin.Account));
                identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, admin.Id.ToString()));
 
                await HttpContext.SignInAsync(identity.AuthenticationType,
                                              new ClaimsPrincipal(identity),
                                              new AuthenticationProperties
                                              {
                                                  IsPersistent = model.isPersistent,
                                                  RedirectUri = "/Home/Index",
                                                  ExpiresUtc = new System.DateTimeOffset(dateTime: DateTime.Now.AddHours(6)),
                                              });
                //更新登陆时间
                await _AdminService.UpdateLastLoginTime(id: admin.Id);
            }
            else
            {
                await HttpContext.ChallengeAsync(CookieAuthenticationDefaults.AuthenticationScheme);
                ModelState.AddModelError("", "用户名或密码错误!");
            }
        }
    }
    catch (Exception ex)
    {
        ModelState.AddModelError("", "用户名或密码错误!");
        _Logger.Error("用户登录时发生错误!", ex);
    }
    return View(model);
}
  这样就完成了Asp.net core web项目的登录认证工作。

二、Asp.Net Core WebApi基于JWT的认证授权
   关于JWT的工作原理,大家可以自行了解(https://jwt.io/)。JWT实现了服务端无状态,在分布式服务,会话一致性,单点登录等方面,凸显优势,不占用服务端资源。使用JWT需要注意的是,令牌过期后刷新,以及更改密码后令牌未过期的处理问题。

这里,我以JWT作为.net core webapi项目的认证方式。

首先,我再Api项目中新建了一个名为OAuthController的控制器,定义一个Action名为Token的方法,用来让客户端获取令牌之用,具体代码如下:

/// <summary>
/// <![CDATA[获取访问令牌]]>
/// </summary>
/// <param name="user"></param>
/// <param name="password"></param>
/// <returns></returns>
[HttpPost]
public async Task<TiKu.Domain.ValueObject.RestfulData<TiKu.Domain.ValueObject.AccessTokenObj>> Token(string user, string password)
{
    var result = new TiKu.Domain.ValueObject.RestfulData<TiKu.Domain.ValueObject.AccessTokenObj>();
    try
    {
        if (string.IsNullOrEmpty(user)) throw new ArgumentNullException("user", "用户名不能为空!");
        if (string.IsNullOrEmpty(password)) throw new ArgumentNullException("password", "密码不能为空!");
 
        //验证用户名和密码
        var userInfo = await _UserService.CheckUserAndPassword(mobile: user, password: password);
        var claims = new Claim[]
        {
            new Claim(ClaimTypes.Name,user),
            new Claim(ClaimTypes.NameIdentifier,userInfo.Id.ToString()),
        };
 
        var key = new SymmetricSecurityKey(System.Text.Encoding.UTF8.GetBytes(Configuration["JwtSecurityKey"]));
        var expires = DateTime.Now.AddDays(28);//
        var token = new JwtSecurityToken(
                    issuer: Configuration["issuer"],
                    audience: Configuration["audience"],
                    claims: claims,
                    notBefore: DateTime.Now,
                    expires: expires,
                    signingCredentials: new SigningCredentials(key, SecurityAlgorithms.HmacSha256));
 
        //生成Token
        string jwtToken = new JwtSecurityTokenHandler().WriteToken(token);
        result.code = 1;
        result.data = new Domain.ValueObject.AccessTokenObj() { AccessToken = jwtToken, Expires = TiKu.Common.Utility.Util.ToUnixTime(expires) };
        result.message = "授权成功!";
        return result;
    }
    catch (Exception ex)
    {
        result.message = ex.Message;
        result.code = 0;
        logger.Error("获取访问令牌时发生错误!", ex);
        return result;
    }
}
  这里,我定义了一个统一返回数据格式的模型-RestfulData,其中有不返回数据data的RestfulData和带data数据的RestfulData<T>,以及返回集合类型的RestfulArray<T>,具体代码如下: 
/// <summary>
  ///
  /// </summary>
  public class RestfulData
  {
      /// <summary>
      /// <![CDATA[错误码]]>
      /// </summary>
      public int code { get; set; }
 
      /// <summary>
      ///<![CDATA[消息]]>
      /// </summary>
      public string message { get; set; }
 
      /// <summary>
      /// <![CDATA[相关的链接帮助地址]]>
      /// </summary>
      public string url { get; set; }
 
  }
 
  /// <summary>
  ///
  /// </summary>
  /// <typeparam name="T"></typeparam>
  public class RestfulData<T> : RestfulData
  {
      /// <summary>
      /// <![CDATA[数据]]>
      /// </summary>
      public virtual T data { get; set; }
  }
 
  /// <summary>
  /// <![CDATA[返回数组]]>
  /// </summary>
  /// <typeparam name="T"></typeparam>
  public class RestfulArray<T> : ResultData<IEnumerable<T>>
  {
 
  }
  

配置JWT认证服务,在Start.cs启动类中,配置如下: 
		/// <summary>
        ///
        /// </summary>
        /// <param name="services"></param>
        public IServiceProvider ConfigureServices(IServiceCollection services)
        {
            services.AddSingleton<IConfiguration>(Configuration);
            services.AddMemoryCache();//添加基于内存的缓存支持
            services.AddAutofac();
 
            //配置授权
            services.AddAuthentication(options =>
            {
                options.DefaultAuthenticateScheme = "JwtBearer";
                options.DefaultChallengeScheme = "JwtBearer";
 
            }).AddJwtBearer("JwtBearer",
            (jwtBearerOptions) =>
            {
                jwtBearerOptions.TokenValidationParameters = new TokenValidationParameters
                {
                    ValidateIssuerSigningKey = true,
                    IssuerSigningKey = new SymmetricSecurityKey(System.Text.Encoding.UTF8.GetBytes(Configuration["JwtSecurityKey"])),//秘钥
                    ValidateIssuer = true,
                    ValidIssuer = Configuration["issuer"],
                    ValidateAudience = true,
                    ValidAudience = Configuration["audience"],
                    ValidateLifetime = true,
                    ClockSkew = TimeSpan.FromMinutes(5)
                };
            });
 
            services.AddMvc();
 
            //IOC Autofac
            var builder = new ContainerBuilder();
            builder.Populate(services);
 
            //注册应用服务
            var assemblyApplicationService = System.Reflection.Assembly.Load("TiKu.Application");
            builder.RegisterAssemblyTypes(assemblyApplicationService).AsImplementedInterfaces();
 
            var container = builder.Build();
            Container = container;
 
            return new AutofacServiceProvider(container);
        }
  上面使用了IOC容器Autofac。

其次,配置认证中间件:
/// <summary>
///
/// </summary>
/// <param name="app"></param>
/// <param name="env"></param>
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    app.UseAuthentication();//配置授权
    //处理异常
    app.UseStatusCodePages(new StatusCodePagesOptions()
    {
        HandleAsync = (context) =>
        {
            if (context.HttpContext.Response.StatusCode == 401)
            {
                using (System.IO.StreamWriter sw = new System.IO.StreamWriter(context.HttpContext.Response.Body))
                {
                    sw.Write(Newtonsoft.Json.JsonConvert.SerializeObject(new
                    {
                        status = 401,
                        message = "access denied!",
                    }));
                }
            }
            return System.Threading.Tasks.Task.Delay(0);
        }
    });
    app.UseMvc(routes =>
    {
        routes.MapRoute(name: "default", template: "api/{controller=Home}/{action=Index}/{id?}");
        routes.MapRoute(name: "mvc", template: "{controller=Home}/{action=Index}/{id?}");
    });
}  
为了测试,我们给ValuesController控制器添加Authorize特性。 
/// <summary>
 ///
 /// </summary>
 [Authorize]
 [Route("api/[controller]")]
 public class ValuesController : Controller
 {
 
     // GET api/values
     [HttpGet]
     public IEnumerable<string> Get()
     {
         return new string[] { "value1", "value2" };
     }
} 
最后,让我们测试下API的授权,这里,我以Ajax模拟API的调用: 
script type="text/javascript">
    //获取令牌
    $.post("/oauth/token", $.param({ user: "lichaoqiang", password: "fdsfds" })).done(function (data) {
 
        if (data.code === 1) {
            localStorage.setItem("token", data.data.accessToken);
        }
    });
 
    //设置HTTP头
    $.ajaxSetup({
        beforeSend: function (xhr) {
            if (localStorage.getItem("token") !== null) {
                xhr.setRequestHeader('Authorization', 'Bearer ' + localStorage.getItem("token"));
            }
        }
    });
 
    $.getJSON("/api/values", function (data) { console.log(data); });//获取受保护的资源
</script>
  看下效果,直接访问/api/values,会出现如下图:

在这里插入图片描述
当客户请求受保护的资源时,通过HTTP header携带上token。这里需要注意的是,请求头必须是Authorization,值是Bearer空格加上token。这样访问资源时,通过HTTP header携带令牌信息,服务端,通过认证中间件,完成授权认证过程。在上面的示例中,通过向全局Ajax注册事件,将token写入请求Header。
在这里插入图片描述
至此,就完成了JWT认证授权的过程,.Net Core WebAPI配置起来也很简单。
方法一我没试,方法二试了。可行。
在这里插入图片描述

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值