标签 身份验证 下的文章

关于ASP.NET MVC中使用Forms验证的问题

表单验证(Forms验证)是一个基于票据(ticket-based)[也称为基于令牌(token-based)]的系统。这意味着当用户登录系统以后,他们得到一个包含基于用户信息的票据(ticket)。这些信息被存放在加密过的cookie里面,这些cookie和响应绑定在一起,因此每一次后续请求都会被自动提交到服务器。

当用户请求匿名用户无法访问的ASP.NET页面时,ASP.NET运行时验证这个表单验证票据是否有效。如果无效,ASP.NET自动将用户转到登录页面。这时就该由你来操作了。你必须创建这个登录页面并且验证由登录页面提交的凭证。如果用户验证成功,你只需要告诉ASP.NET架构验证成功(通过调用FormsAuthentication类的一个方法),运行库会自动设置验证cookie(实际上包含了票据)并将用户转到原先请求的页面。通过这个请求,运行库检测到验证cookie中包含一个有效票据,然后赋给用户对这个页面的访问权限。

下面,就是具体的实现。我们先写一个生成用户票据的方法:

//生成Token并保存到Cookies中
private void CreateToken(Models.User user)
{
    string json = JsonConvert.SerializeObject(user);
    FormsAuthentication.SetAuthCookie(user.UserName.Trim(), true, FormsAuthentication.FormsCookiePath);
    FormsAuthenticationTicket Ticket = new FormsAuthenticationTicket(1, user.UserName, DateTime.Now, DateTime.Now.AddHours(12), false, json);
    Biz126.Cache.SetCookies.AddCookie(FormsAuthentication.FormsCookieName, FormsAuthentication.Encrypt(Ticket));
}

其中Biz126.Cache.SetCookies.AddCookie是我自己封装的保存Cookie方法。
生成Token并保存到Cookie中的方法我们已经写好了,只要在登录成功之后直接调用这个方法就可以了。

下面是验证票据,验证票据,我们放在过滤器中进行验证。
新建AuthenticationAttribute类,继承ActionFilterAttribute,重写OnActionExecuting方法:

        /// <summary>
        /// 检查用户是否有该Action执行的操作权限
        /// </summary>
        /// <param name="actionContext"></param>
        public override void OnActionExecuting(HttpActionContext filterContext)
        {
            Models.ResultModel<object> result = new Models.ResultModel<object>();
            result.code = -401;
            result.message = "请登录后进行操作";
            result.status = false;
            //如果存在身份信息
            if (!HttpContext.Current.User.Identity.IsAuthenticated)
            {
                filterContext.Response = filterContext.Request.CreateResponse(HttpStatusCode.Unauthorized, result, "application/json");
            }
            else
            {                
                
                BLL.IManager manager = new BLL.Manage.Manager();
                result = manager.Detail();    //取当前登录的用户信息,进行其他的操作,比如判断有没有权限等,这里省略
                //。。。省略掉其他的操作
                
                if (!result.status)
                {
                    filterContext.Response = filterContext.Request.CreateResponse(HttpStatusCode.Unauthorized, result, "application/json");
                    //filterContext.Result = redirect;
                }
            }
        }

之后,在要在需要用户登录的Action上面,加上[Authentication]就可以了,也可以新加一个控制器,如BaseWebApiController,在这个控制器上加上[Authentication],其他的控制器都继承BaseWebApiController,在不需要验证的Action上加上[AllowAnonymous]就可以了。
下面是通过票据取得用户信息:

            if (HttpContext.Current.Request.IsAuthenticated)//是否通过身份验证
            {
                HttpCookie authCookie = HttpContext.Current.Request.Cookies[FormsAuthentication.FormsCookieName];//获取cookie
                FormsAuthenticationTicket Ticket = FormsAuthentication.Decrypt(authCookie.Value);//解密
                string json = Ticket.UserData;//反序列化
                Models.Models.User user = new Models.User();
                try
                {
                    user = JsonConvert.DeserializeObject<Models.User>(json);
                    result.data = user;
                    result.message = "success";
                    result.status = true;
                }
                catch
                {

                }

            }

还需要在web.config中增加配置:

  <system.web>
    <authentication mode="Forms">
      <forms loginUrl="~/Users/Login" timeout="2880" />
    </authentication>
  </system.web>

到这里,其本上都已经完成了,但是你会发现,在IE上,会有很多用户登录的时候验证不通过的情况,如果记录日志,会发现HttpContext.Current.Request.IsAuthenticated==false
HttpContext.Current.Request.User为空
而且这种情况只有在IE下才会出现,其他的现代浏览器比如Chrome、Firefox等都很正常。
通过网络搜索,大部分都没给出答案来。其他把配置按下面的方法修改,就可以解决这个问题:

  <system.web>
    <authentication mode="Forms">
      <forms cookieless="UseCookies" loginUrl="~/Users/Login" timeout="2880" />
    </authentication>
    <authorization>
      <allow users="*" />
    </authorization>
  </system.web>

或者是打开服务器的IIS,按下面的步骤修改:
IIS的“身份证验证”
1.打开IIS,选择自己的站点,之后双击IIS中的“身份验证”功能
设置Forms身份验证
2.选中Forms身份验证,点击右侧操作区的“编辑”菜单,如果没有启用请先点击“启用”
Forms身份验证的默认设置
3.这是Forms身份验证的默认设置,我们需要改动一下
修改之后的Forms身份验证设置
4.按这里修改一下,就可以了。

以上。

.Net Core系列教程(五)—— Token Base身份认证

在ASP.Net Core中实现一个Token Base身份认证,使用场景主要就是Web API下,可以调用Web API的不止是浏览器,还有各种各样的客户端,有些客户端没有Cookies,也无法使用Session。这时候就需要Token来救场了,相比Cookies,Token更开放,而安全性也要比Cookies高很多。

下面使用微软JwtSecurityTokenHandler来实现一个基于beare token的身份认证。

1.创建辅助类:

在项目中,新建一个Auth文件夹,在Auth文件夹中添加一个RSAKeyHelper类:

using System.Security.Cryptography;
namespace Biz126.WebAPI.Auth
{
    public class RSAKeyHelper
    {
        public static RSAParameters GenerateKey()
        {
            using (var key = new RSACryptoServiceProvider(2048))
            {
                return key.ExportParameters(true);
            }
        }
    }
}

和TokenAuthOption类:

using Microsoft.IdentityModel.Tokens;
namespace Biz126.WebAPI.Auth
{
    public class TokenAuthOption
    {
        public static string Audience { get; } = "ExampleAudience";
        public static string Issuer { get; } = "ExampleIssuer";
        public static RsaSecurityKey Key { get; } = new RsaSecurityKey(RSAKeyHelper.GenerateKey());
        public static SigningCredentials SigningCredentials { get; } = new SigningCredentials(Key, SecurityAlgorithms.RsaSha256Signature);
        public static TimeSpan ExpiresSpan { get; } = TimeSpan.FromMinutes(20);
    }
}

2.修改Startup.cs文件:

打开Startup.cs文件,在ConfigureServices中添加:

            services.AddAuthorization(auth =>
            {
                auth.AddPolicy("Bearer", new AuthorizationPolicyBuilder()
                    .AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme‌)
                    .RequireAuthenticatedUser().Build());
            });

完整的ConfigureServices方法:

        public void ConfigureServices(IServiceCollection services)
        {
            // Add framework services.
            services.AddApplicationInsightsTelemetry(Configuration);
            services.Configure<Biz126.Models.ConnectionStrings>(Configuration.GetSection("ConnectionStrings"));    //这个是取数据库配置,与本例无关
            services.AddAuthorization(auth =>
            {
                auth.AddPolicy("Bearer", new AuthorizationPolicyBuilder()
                    .AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme‌)
                    .RequireAuthenticatedUser().Build());
            });

            services.AddMvc();
        }

修改Configure方法,在其中增加:

            app.UseExceptionHandler(appBuilder => {
                appBuilder.Use(async (context, next) => {
                    var error = context.Features[typeof(IExceptionHandlerFeature)] as IExceptionHandlerFeature;
                    //when authorization has failed, should retrun a json message to client
                    if (error != null && error.Error is SecurityTokenExpiredException)
                    {
                        context.Response.StatusCode = 401;
                        context.Response.ContentType = "application/json";
                        await context.Response.WriteAsync(JsonConvert.SerializeObject(
                            //new { authenticated = false, tokenExpired = true }
                            new { Status = false, Message = "Token已失效", Code = -401, Result = new { tokenExpired = true } }
                        ));
                    }
                    //when orther error, retrun a error message json to client
                    else if (error != null && error.Error != null)
                    {
                        context.Response.StatusCode = 500;
                        context.Response.ContentType = "application/json";
                        await context.Response.WriteAsync(JsonConvert.SerializeObject(
                            //new { success = false, error = error.Error.Message }
                            new { Status = false, Code = -500, Message = error.Error.Message }
                        ));
                    }
                    //when no error, do next.
                    else await next();
                });
            });
            app.UseExceptionHandler(appBuilder => {
                appBuilder.Use(async (context, next) => {
                    var error = context.Features[typeof(IExceptionHandlerFeature)] as IExceptionHandlerFeature;
                    //when authorization has failed, should retrun a json message to client
                    if (error != null && error.Error is SecurityTokenExpiredException)
                    {
                        context.Response.StatusCode = 401;
                        context.Response.ContentType = "application/json";
                        await context.Response.WriteAsync(JsonConvert.SerializeObject(
                            new { Status = false, Message = "Token已失效", Code = -401, Result = new { tokenExpired = true } }
                        ));
                    }
                    //when orther error, retrun a error message json to client
                    else if (error != null && error.Error != null)
                    {
                        context.Response.StatusCode = 500;
                        context.Response.ContentType = "application/json";
                        await context.Response.WriteAsync(JsonConvert.SerializeObject(
                            new { Status = false, Code = -500, Message = error.Error.Message }
                        ));
                    }
                    //when no error, do next.
                    else await next();
                });
            });
            //应用JwtBearerAuthentication
            app.UseJwtBearerAuthentication(new JwtBearerOptions
            {
                TokenValidationParameters = new TokenValidationParameters
                {
                    IssuerSigningKey = TokenAuthOption.Key,
                    ValidAudience = TokenAuthOption.Audience,
                    ValidIssuer = TokenAuthOption.Issuer,
                    ValidateIssuerSigningKey = true,
                    ValidateLifetime = true,
                    ClockSkew = TimeSpan.FromMinutes(0)
                }
            });

完整的Configure方法:

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline
        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
            loggerFactory.AddConsole(Configuration.GetSection("Logging"));
            loggerFactory.AddDebug();
            app.UseApplicationInsightsRequestTelemetry();
            app.UseApplicationInsightsExceptionTelemetry();
            app.UseExceptionHandler(appBuilder => {
                appBuilder.Use(async (context, next) => {
                    var error = context.Features[typeof(IExceptionHandlerFeature)] as IExceptionHandlerFeature;
                    //when authorization has failed, should retrun a json message to client
                    if (error != null && error.Error is SecurityTokenExpiredException)
                    {
                        context.Response.StatusCode = 401;
                        context.Response.ContentType = "application/json";
                        await context.Response.WriteAsync(JsonConvert.SerializeObject(
                            //new { authenticated = false, tokenExpired = true }
                            new { Status = false, Message = "Token已失效", Code = -401, Result = new { tokenExpired = true } }
                        ));
                    }
                    //when orther error, retrun a error message json to client
                    else if (error != null && error.Error != null)
                    {
                        context.Response.StatusCode = 500;
                        context.Response.ContentType = "application/json";
                        await context.Response.WriteAsync(JsonConvert.SerializeObject(
                            //new { success = false, error = error.Error.Message }
                            new { Status = false, Code = -500, Message = error.Error.Message }
                        ));
                    }
                    //when no error, do next.
                    else await next();
                });
            });
            app.UseExceptionHandler(appBuilder => {
                appBuilder.Use(async (context, next) => {
                    var error = context.Features[typeof(IExceptionHandlerFeature)] as IExceptionHandlerFeature;
                    //when authorization has failed, should retrun a json message to client
                    if (error != null && error.Error is SecurityTokenExpiredException)
                    {
                        context.Response.StatusCode = 401;
                        context.Response.ContentType = "application/json";
                        await context.Response.WriteAsync(JsonConvert.SerializeObject(
                            new { Status = false, Message = "Token已失效", Code = -401, Result = new { tokenExpired = true } }
                        ));
                    }
                    //when orther error, retrun a error message json to client
                    else if (error != null && error.Error != null)
                    {
                        context.Response.StatusCode = 500;
                        context.Response.ContentType = "application/json";
                        await context.Response.WriteAsync(JsonConvert.SerializeObject(
                            new { Status = false, Code = -500, Message = error.Error.Message }
                        ));
                    }
                    //when no error, do next.
                    else await next();
                });
            });
            app.UseJwtBearerAuthentication(new JwtBearerOptions
            {
                TokenValidationParameters = new TokenValidationParameters
                {
                    IssuerSigningKey = TokenAuthOption.Key,
                    ValidAudience = TokenAuthOption.Audience,
                    ValidIssuer = TokenAuthOption.Issuer,
                    ValidateIssuerSigningKey = true,
                    ValidateLifetime = true,
                    ClockSkew = TimeSpan.FromMinutes(0)
                }
            });
            app.UseMvc();
        }

3.登录授权接口

在Controllers中,增加TokenAuthController的Web API控制器,在该控制器下添加如下方法:

        /// <summary>
        /// 生成Token
        /// </summary>
        /// <param name="user"></param>
        /// <param name="serviceid"></param>
        /// <param name="device"></param>
        /// <param name="expires"></param>
        /// <returns></returns>
        private string GenerateToken(Biz126.Customer.Models.Detail user,string serviceid,string device, DateTime expires)
        {
            var handler = new JwtSecurityTokenHandler();
            ClaimsIdentity identity = new ClaimsIdentity(
                new GenericIdentity(user.UserName, "TokenAuth"),
                new[] {
                    new Claim("ID", user._Id.ToString()),
                    new Claim("TrueName",user.TrueName),
                    new Claim("ServiceId",serviceid),
                    new Claim("Device",device)  //绑定硬件号
                }
            );
            var securityToken = handler.CreateToken(new SecurityTokenDescriptor
            {
                Issuer = TokenAuthOption.Issuer,
                Audience = TokenAuthOption.Audience,
                SigningCredentials = TokenAuthOption.SigningCredentials,
                Subject = identity,
                Expires = expires
            });
            return handler.WriteToken(securityToken);
        }

正如注释中所说的,该方法用于生成Token。

还是在该控制器下,继续添加GetAuthorize方法,用于取得授权,代码如下:

        [HttpPost]
        public Biz126.Models.ResultModel<object> GetAuthorize(Biz126.Authorization.Models.UserAuth user)
        {
            var result = new Biz126.Models.ResultModel<object>();
            var log = new Biz126.Customer.Models.Login() { UserName = user.UserName, Password = user.Password };
            var login = _users.Login(log);    //登录

            if (login.Status)
            {
                //用户账号状态正常
                //1.查看该用户有几个有效的该授权
                //2.已经开通了几个
                //3.在剩余范围内
                //4.查看该机器是否已经有过授权
                //5.如果已经有过授权,更新Token,如果没有过授权,增加新授权
                var requestAt = DateTime.Now;
                var expiresIn = requestAt + TokenAuthOption.ExpiresSpan;    //过期时间(正常为授权结束日期)
                var token = GenerateToken(login.Result,user.ServiceId,user.Device, expiresIn);
                result.Result = new { requestAt = requestAt, expiresIn = TokenAuthOption.ExpiresSpan.TotalSeconds, accessToken = token };
                result.Status = true;
                result.Message = "success";
                result.Code = 0;
            }
            else
            {
                result.Status = false;
                result.Message = "用户名或密码错误";
                result.Code = -403;
            }
            return result;
        }

4.验证授权:

在Controllers中新建一个CheckAuthController的Web API控制器,添加下面的方法:

        [Authorize("Bearer")]
        public string Post()
        {
            var claimsIdentity = User.Identity as ClaimsIdentity;
            var id = claimsIdentity.Claims.FirstOrDefault(c => c.Type == "ID").Value;
            var device= claimsIdentity.Claims.FirstOrDefault(c => c.Type == "Device").Value;
            return $"Hello! {HttpContext.User.Identity.Name}, your Device is:{device}";
        }

5.测试:

打开Postman,先访问/api/TokenAuth/GetAuthorize,使用用户名和密码进行登录,接口会返回生成的Token,记下返回的Token。继续使用Postman工具,访问接口/api/CheckAuth,在Headers中,添加:Authorization:Bearer 上一步生成的Token,如下图所示

要注意“Bearer”与后面的Token之间有一个空格,之后提交请求,可以看到验证通过并给返回相应的信息。

上面代码只是一个例子,具体可以灵活的运用到自己的项目中。

以上。

.Net Core系列教程(四)—— 基础身份认证

在ASP.NET 4.5及之前的版本,可以使用FormsAuthenticationTicket来做基础身份认证,现在到了.Net Core中,发现原来的FormsAuthenticationTicket不能用了,其实在.Net Core中,依然可以使用基础身份认证,下面是使用方法。因为这是在具体项目中使用的,会多出一些其他的代码,请自行忽略。

1.在Startup.cs文件中,public void ConfigureServices(IServiceCollection services)方法下添加:

services.AddAuthorization();

完整代码:

        public void ConfigureServices(IServiceCollection services)
        {
            // Add framework services.
            services.AddApplicationInsightsTelemetry(Configuration);
            services.AddOptions();
            services.Configure<Models.ConnectionStrings>(Configuration.GetSection("ConnectionStrings"));
            services.AddAuthorization();    //Form基础验证
            services.AddMvc();
        }

2.在public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)方法下添加:

            app.UseCookieAuthentication(new CookieAuthenticationOptions
            {
                AuthenticationScheme = "Cookie",
                LoginPath = new PathString("/Manage/Login"),
                AccessDeniedPath = new PathString("/Manage/Forbidden"),
                AutomaticAuthenticate = true,
                AutomaticChallenge = true
            });

完整代码:

        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
            loggerFactory.AddConsole(Configuration.GetSection("Logging"));
            loggerFactory.AddDebug();
            //Form基础验证
            app.UseCookieAuthentication(new CookieAuthenticationOptions
            {
                AuthenticationScheme = "Cookie",
                LoginPath = new PathString("/Manage/Login"),
                AccessDeniedPath = new PathString("/Manage/Forbidden"),
                AutomaticAuthenticate = true,
                AutomaticChallenge = true
            });
            app.UseApplicationInsightsRequestTelemetry();
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                app.UseBrowserLink();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
            }
            app.UseApplicationInsightsExceptionTelemetry();
            app.UseStaticFiles();
            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}");
            });
        }

如果发现有报错,添加引用即可。

3.在控制器文件中,登录的方法下,添加:

                var claims = new List<Claim>()
                {
                    new Claim(ClaimTypes.Name,login.username) 
                    //,new Claim(ClaimTypes.Email,"emailaccount@microsoft.com")  
                };
                //var userPrincipal = new ClaimsPrincipal(new ClaimsIdentity(claims, "SuperSecureLogin"));
                var userPrincipal = new ClaimsPrincipal(new ClaimsIdentity(claims, token));
                HttpContext.Authentication.SignInAsync("Cookie", userPrincipal, new AuthenticationProperties
                {
                    ExpiresUtc = DateTime.UtcNow.AddMinutes(20),
                    IsPersistent = false,
                    AllowRefresh = false
                });

在我自己的项目中,完整的Login方法代码:

        [HttpPost]
        [ModelValidationFilter]
        public Models.ResultModel<object> Login(Models.Login login)
        {
            var result = new Models.ResultModel<object>();
            result = _manage.Login(login);            
            if(result.status)
            {//登录成功
                string token = result.data.ToString();  //登录成功后生成的token,用于验证登录有效性
                var claims = new List<Claim>()
                {
                    new Claim(ClaimTypes.Name,login.username) 
                    //,new Claim(ClaimTypes.Email,"emailaccount@microsoft.com")  
                };
                //var userPrincipal = new ClaimsPrincipal(new ClaimsIdentity(claims, "SuperSecureLogin"));
                var userPrincipal = new ClaimsPrincipal(new ClaimsIdentity(claims, token));
                HttpContext.Authentication.SignInAsync("Cookie", userPrincipal, new AuthenticationProperties
                {
                    ExpiresUtc = DateTime.UtcNow.AddMinutes(20),
                    IsPersistent = false,
                    AllowRefresh = false
                });
            }
            return result;
        }

4.在需要进行身份验证的控制器或Action上,添加[Authorize]特性,如:

加在Action上

        [Authorize]
        public IActionResult Dashboard()

或加在Controller上

    [Authorize]
    public class ManageSystemController : Controller

5.取验证信息

     var auth = await HttpContext.Authentication.GetAuthenticateInfoAsync("Cookie");
     string username = auth.Principal.Identity.Name;    //用户名

6.注销登录

    HttpContext.Authentication.SignOutAsync("Cookie");

具体可参考微软官方Demo:How to achieve a basic authorization in ASP.NET Core

分类

最新文章

最近回复

  • 青山: 某种原因,暂停友链,抱歉。
  • 青山: 计划搬迁到腾讯云,正...
  • 老徐: 具体要哪个呢?
  • 老徐: 是不是有点老?
  • 青山: 哇,林志炫
  • 老白: 哇,这改的可以,能不...
  • 老徐: 23333
  • 许建华: 我是为了表情包来的~
  • vultr vps: 感谢分享深入学习
  • 青山: 每一次都是不同的感受

归档

标签云

C# .net core asp.net 情感 SQL mongodb sql server EasyUI 安全 激活 linux 身份验证 https typecho .net sql注入 kms MVC IIS 高并发 IE 坑爹 服务器 mysql Oracle Combobox Datagrid 口语 数据抓取

其它