VSCode+.Net Core2.0 WebAPI JWT身份验证+Cookie+自定义权限策略

在上一篇中https://blog.csdn.net/liwan09/article/details/80903729讲到了WebAPI的JWT身份验证,在本篇问文章中,讲述一下Cookie+JWT+自定义权限策略的实现。

首先,在Startup.cs文件中添加 using Microsoft.AspNetCore.Authentication.Cookies;引用

一、将ConfigureServices中的代码修改为以下代码

 public void ConfigureServices(IServiceCollection services)
        {
            //get JwtSettings from appsettings.json 
            services.Configure<JwtSettings>(Configuration.GetSection("JwtSettings"));
            // set JwtSettings model
            var jwtSettings = new JwtSettings();
            Configuration.Bind("JwtSettings", jwtSettings);
            #region " add CookieAuthentication "
            //自定义权限策略    
            services.AddAuthorization(options =>
            {
                options.AddPolicy("Permission", policy => policy.Requirements.Add(new PermissionRequirement("OkRole")));
            })//services.AddAuthentication
            .AddAuthentication(options =>
            {
                options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;
            })
            .AddCookie(o =>
            {
                var tokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
                {
                    ValidIssuer = jwtSettings.Issuer,//Issuer
                    ValidAudience = jwtSettings.Audience,//Audience
                    //SecurityKey
                    IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSettings.SecretKey)),
                    ValidateIssuer = true, //Whether or not validate Issuer
                    ValidateAudience = true, //Whether or not validate Audience
                    ValidateLifetime = true, //Whether or not validate Failure time
                    ValidateIssuerSigningKey = true, //Whether or not validate SecurityKey                              
                    ClockSkew = TimeSpan.Zero//Allowed server time offset
                };
                o.Cookie.Name = "auth_app_token";
                o.TicketDataFormat = new CustomJwtDataFormat(SecurityAlgorithms.HmacSha256, tokenValidationParameters);
                o.Events = new CookieAuthenticationEvents
                {
                    OnRedirectToLogin = ctx =>
                      {
                          if (ctx.Request.Path.StartsWithSegments("/api") && ctx.Response.StatusCode == (int)HttpStatusCode.OK)
                          {
                              ctx.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
                          }
                          else
                          {
                              ctx.Response.Redirect(ctx.RedirectUri);
                          }
                          return Task.FromResult(0);
                      }
                };
            });
            #endregion 
            //注入授权Handler
            services.AddSingleton<IAuthorizationHandler, PermissionHandler>();
            services.AddDistributedMemoryCache(); // Adds a default in-memory implementation of IDistributedCache
            services.AddSession();
            services.AddMvc();
        }

代码中需要用到的相关类的代码:

1、必要参数类:

public class PermissionRequirement : IAuthorizationRequirement

{

/// <summary>

/// 构造

/// </summary>

/// <param name="roleid">角色id</param>

public PermissionRequirement(string roleid)

{

RoleID=roleid;

}

/// <summary>

/// 用户角色ID

/// </summary>

public string RoleID { get; set; }

}

2、在cookie中验证JWT口令的相关处理类CustomJwtDataFormat 相关代码

    /// <summary>
    /// validate jwt in cookie
    /// </summary>
    public class CustomJwtDataFormat : ISecureDataFormat<AuthenticationTicket>
    {
        private readonly string algorithm;
        private readonly TokenValidationParameters validationParameters;
        public CustomJwtDataFormat(string algorithm, TokenValidationParameters validationParameters)
        {
            this.algorithm = algorithm;
            this.validationParameters = validationParameters;
        }
        public AuthenticationTicket Unprotect(string protectedText)=> Unprotect(protectedText, null);

        public AuthenticationTicket Unprotect(string protectedText, string purpose)
        {
            var handler = new JwtSecurityTokenHandler();
            ClaimsPrincipal principal = null;
            SecurityToken validToken = null;

            try
            {
                principal = handler.ValidateToken(protectedText, this.validationParameters, out validToken);

                var validJwt = validToken as JwtSecurityToken;

                if (validJwt == null)
                {
                    throw new ArgumentException("Invalid JWT");
                }

                if (!validJwt.Header.Alg.Equals(algorithm, StringComparison.Ordinal))
                {
                    throw new ArgumentException($"Algorithm must be '{algorithm}'");
                }

                // Additional custom validation of JWT claims here (if any)
            }
            catch (SecurityTokenValidationException)
            {
                return null;
            }
            catch (ArgumentException)
            {
                return null;
            }

            // Validation passed. Return a valid AuthenticationTicket:
            return new AuthenticationTicket(principal, new Microsoft.AspNetCore.Authentication.AuthenticationProperties(), "Cookie");
        }

        // This ISecureDataFormat implementation is decode-only
        public string Protect(AuthenticationTicket data)
        {
            throw new NotImplementedException();
        }

        public string Protect(AuthenticationTicket data, string purpose)
        {
            throw new NotImplementedException();
        }
    }

3、验证方案提供对象相关数据处理类PermissionHandler.cs相关代码

public class PermissionHandler : AuthorizationHandler<PermissionRequirement>

{

/// <summary>

/// 验证方案提供对象

/// </summary>

public IAuthenticationSchemeProvider Schemes { get; set; }



/// <summary>

/// 构造

/// </summary>

/// <param name="schemes"></param>

public PermissionHandler(IAuthenticationSchemeProvider schemes)

{

Schemes = schemes;

}



protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionRequirement requirement)

{



//从AuthorizationHandlerContext转成HttpContext,以便取出表求信息

var httpContext = (context.Resource as Microsoft.AspNetCore.Mvc.Filters.AuthorizationFilterContext).HttpContext;

//请求Url

var questUrl = httpContext.Request.Path.Value.ToLower();

// get session user info

var sessionuser=httpContext.Request.Cookies[".AspNetCore.Session"];

//判断请求是否停止

var handlers = httpContext.RequestServices.GetRequiredService<IAuthenticationHandlerProvider>();

foreach (var scheme in await Schemes.GetRequestHandlerSchemesAsync())

{

var handler = await handlers.GetHandlerAsync(httpContext, scheme.Name) as IAuthenticationRequestHandler;

if (handler != null && await handler.HandleRequestAsync())

{

context.Fail();

return;

}

}

//判断请求是否拥有凭据,即有没有登录

var defaultAuthenticate = await Schemes.GetDefaultAuthenticateSchemeAsync();

if (defaultAuthenticate != null)

{

var result = await httpContext.AuthenticateAsync(defaultAuthenticate.Name);

//result?.Principal不为空即登录成功

if (result?.Principal != null)

{



httpContext.User = result.Principal;

//权限中是否存在请求的url

// if (requirement.Permissions.GroupBy(g => g.Url).Where(w => w.Key.ToLower() == questUrl).Count() > 0)

// {

// var name = httpContext.User.Claims.SingleOrDefault(s => s.Type == requirement.ClaimType).Value;

// //验证权限

// if (requirement.Permissions.Where(w => w.Name == name && w.Url.ToLower() == questUrl).Count() <= 0)

// {

// //无权限跳转到拒绝页面

// httpContext.Response.Redirect(requirement.DeniedAction);

// }

// }

//获取session 中的用户信息,判断用户是否有当前路径的操作权限

}

else

{

return;

}

}

//判断没有登录时,是否访问登录的url,并且是Post请求,并且是form表单提交类型,否则为失败

// if (!questUrl.Equals(requirement.LoginPath.ToLower(), StringComparison.Ordinal) && (!httpContext.Request.Method.Equals("POST")

// || !httpContext.Request.HasFormContentType))

// {

// context.Fail();

// return;

// }

context.Succeed(requirement);

}

}

二、将Configure中的代码修改为

public void Configure(IApplicationBuilder app, IHostingEnvironment env)

{

app.UseAuthentication();

app.UseSession();

if (env.IsDevelopment())

{

app.UseDeveloperExceptionPage();

}
app.UseMvc();

}

三、在TokenColler文件中添加生成CookieToken代码

///<summary>

///create cookie Token

///</summary>

///<param name="Loginuser">login user info<param>

///<returns></returns>

[HttpPost]

[Route("CreateCookieToken")]

public async Task CreateCookieToken([FromBody] LoginInfo loginuser)

{

string strResult = "";

List<UserInfo> userlist = UserinfoBLL.Login(loginuser.UserName, loginuser.PassWord);

if (userlist != null)

{

if (userlist.Count == 1)

{

string accesstoken = MakeAccessToken(userlist[0]);

string refreshtoken = MakeRefreshToken(userlist[0]);

//add cookie

CookieOptions accesscookie = new CookieOptions();

//commented out for client demo purpose, please uncomment this in real development environment

//accesscookie.HttpOnly = true;

Response.Cookies.Append("auth_app_token", accesstoken, accesscookie);

CookieOptions refreshcookie = new CookieOptions();

//commented out for client demo purpose, please uncomment this in real development environment

//refreshcookie.HttpOnly = true;

Response.Cookies.Append("auth_app_refresh_token", refreshtoken, refreshcookie);

//add session

var sessionuser=new

{

UserID=userlist[0].UserID,

RoleID=userlist[0].RoleID

};

HttpContext.Session.SetString("user_session",JsonHelperT.ToJson(sessionuser));

strResult = RequestReturn.ReturnInfo("0000", "", "success").ToString();

}

if (userlist.Count > 1)

{

strResult = RequestReturn.ReturnInfo("00002", "The user is not unique, please contact the administrator", "").ToString();

}

if (userlist.Count == 0)

{

strResult = RequestReturn.ReturnInfo("00001", "error Incorrect username or password", "").ToString();

}

}

else

{

strResult = RequestReturn.ReturnInfo("00001", "error Incorrect username or password", "").ToString();

}

Response.ContentType = "application/json";

await Response.WriteAsync(strResult);

}

四、使用Postman 调用webapi,成功后,会发现在返回的Cookie中,会有生成的Token信息

猜你喜欢

转载自blog.csdn.net/liwan09/article/details/81739553