ASP.NET Core 鉴权授权

SunnyFan大约 6 分钟约 1894 字

ASP.NETopen in new window Core 鉴权授权

演示源码链接:https://pan.baidu.com/s/1Qimlu7p0l2HFMSyfLsN-HQ?pwd=iccfopen in new window

鉴权

Cookies

  • 参照SunnyFan.Author.Cookies
  • Program.cs 注册服务处理
builder.Services
    .AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) //凭证在哪里
    .AddCookie(options =>
    {
        options.Events.OnRedirectToLogin = async context =>
        {
            var result = JsonConvert.SerializeObject(new { code = 401, msg = "无权限!" });
            context.Response.StatusCode = 200;
            context.Response.ContentType = "application/json; charset=utf-8";
            await context.Response.Body.WriteAsync(Encoding.UTF8.GetBytes(result));
        };
    });

app.UseRouting();
app.UseAuthentication();//鉴权中间件---表明请求过程中,需要鉴权
app.UseAuthorization();//授权中间件
  • AuthorController.cs 登录模拟
{
    /// <summary>
    /// 授权
    /// </summary>
    [ApiController]
    [Route("[controller]/[action]")]
    public class AuthorController : ControllerBase
    {

        /// <summary>
        /// 
        /// </summary>
       
        public AuthorController()
        {

        }

        /// <summary>
        /// 登录
        /// </summary>
        /// <param name="loginDto"></param>
        /// <returns></returns>
        [HttpPost]
        public async Task<IActionResult> LoginAsync([FromBody] LoginDto loginDto)
        {
            //登录逻辑
            var claimIdentity = new ClaimsIdentity("Custom");
            claimIdentity.AddClaim(new Claim(ClaimTypes.Name, loginDto.Account!));
            claimIdentity.AddClaim(new Claim(ClaimTypes.Email, "sunnyfancore@163.com"));
            claimIdentity.AddClaim(new Claim(ClaimTypes.Role, "Admin"));
            claimIdentity.AddClaim(new Claim(ClaimTypes.Country, "Chinese"));
            await base.HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(claimIdentity), new AuthenticationProperties
            {
                ExpiresUtc = DateTime.UtcNow.AddMinutes(30),
            });//登陆默认Scheme,写入Cookie

            return new JsonResult(new { code = 200, msg = "success" });
        }
    }

    /// <summary>
    /// 登录对象
    /// </summary>
    public class LoginDto
    {
        /// <summary>
        /// 账户
        /// </summary>
        public string? Account { get; set; }

        /// <summary>
        /// 密码
        /// </summary>
        public string? Pwd { get; set; }
    }
}
  • UserController.cs 授权测试 Authorize
{
    /// <summary>
    /// 用户
    /// </summary>
    [ApiController]
    [Route("[controller]/[action]")]
    public class UserController : ControllerBase
    {

        /// <summary>
        /// 
        /// </summary>
        public UserController()
        {

        }

        /// <summary>
        /// 获取用户详情
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        [HttpGet("{id}")]
        [Authorize]
        public async Task<IActionResult> GetAsync(int id)
        {
            await Task.CompletedTask;
            return new JsonResult(new
            {
                code = 200,
                msg = "success",
                data = new { id = 1, account = "zhangsan", name = "张三" }
            });
        }
    }
}    
  • 测试效果 图

图

图

JWT

  • 参照SunnyFan.Author.JWT
  • Program.cs 注册服务处理
//注册服务
ConfigurationManager configuration = builder.Configuration;
builder.Services.AddAuthentication(options =>
    {
        options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
    })
    .AddJwtBearer(options =>
    {
        options.TokenValidationParameters = new TokenValidationParameters()
        {
            ValidateIssuer = true, //是否验证Issuer
            ValidIssuer = configuration["Jwt:Issuer"], //发行人Issuer
            ValidateAudience = true, //是否验证Audience
            ValidAudience = configuration["Jwt:Audience"], //订阅人Audience
            ValidateIssuerSigningKey = true, //是否验证SecurityKey
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(configuration["Jwt:SecretKey"])), //SecurityKey
            ValidateLifetime = true, //是否验证失效时间
            ClockSkew = TimeSpan.FromSeconds(30), //过期时间容错值,解决服务器端时间不同步问题(秒)
            RequireExpirationTime = true,
            AudienceValidator = (m, n, z) =>
            {
                //等同于去扩展了下     Audience的校验规则---鉴权--可以让我们在这里写判断逻辑
                return true;
            },
            LifetimeValidator = (notBefore, expires, securityToken, validationParameters) =>
            {
                return true;
            }
        };

        //在校验token的过程中,会有很多环节---可以通过注册时间来监听每个环节
        options.Events = new JwtBearerEvents
        {
            OnAuthenticationFailed = async context =>
            {
                //身份验证失败时触发
                Console.WriteLine("this is OnAuthenticationFailed");
                await context.HttpContext.Response.WriteAsync("{ Success=false,Message='授权失败了'  } ");
                await Task.CompletedTask;
            },
            OnForbidden = async context =>
            {
                //OnForbidden 时 触发  //授权失败后触发
                Console.WriteLine("this is OnForbidden");
                await Task.CompletedTask;
            },
            OnMessageReceived = async context =>
            {
                string authHeader = context.HttpContext.Request.Headers["Authorization"];
                if (!string.IsNullOrEmpty(authHeader) && authHeader.Length > 7)
                {
                    authHeader = authHeader.Substring(7);
                    context.Token = authHeader;
                }
                //收到信息时 触发
                Console.WriteLine("this is OnMessageReceived");
                await Task.CompletedTask;
            },
            OnTokenValidated = async context =>
            {
                //验证之后触发
                Console.WriteLine("this is OnTokenValidated");
                await Task.CompletedTask;
            },
            OnChallenge = async context => //最终输出的时候,都会触发
            {
                Console.WriteLine("this is OnChallenge");
                await Task.CompletedTask;
            }

        };
    });


app.UseRouting();
app.UseAuthentication();//鉴权中间件---表明请求过程中,需要鉴权
app.UseAuthorization();//授权中间件
  • AuthorController.cs 登录模拟
{
    /// <summary>
    /// 授权
    /// </summary>
    [ApiController]
    [Route("[controller]")]
    public class AuthorController : ControllerBase
    {
        /// <summary>
        /// 
        /// </summary>
        private readonly IConfiguration _configuration;

        /// <summary>
        /// 
        /// </summary>
        public AuthorController(IConfiguration configuration)
        {
            _configuration = configuration;
        }

        /// <summary>
        /// 登录
        /// </summary>
        /// <param name="loginDto"></param>
        /// <returns></returns>
        [HttpPost]
        public async Task<IActionResult> LoginAsync([FromBody] LoginDto loginDto)
        {

            ClaimsIdentity claimIdentity = new ClaimsIdentity("Custom");
            claimIdentity.AddClaim(new Claim(ClaimTypes.Name, "SunnyFan"));
            claimIdentity.AddClaim(new Claim(ClaimTypes.Email, "sunnyfancore@163.com"));
            claimIdentity.AddClaim(new Claim(ClaimTypes.Role, "Admin"));
            claimIdentity.AddClaim(new Claim(ClaimTypes.Country, "Chinese"));
            SymmetricSecurityKey secretKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["Jwt:SecretKey"]));

            SigningCredentials creds = new SigningCredentials(secretKey, SecurityAlgorithms.HmacSha256);

            //Nuget引入:System.IdentityModel.Tokens.Jwt
            JwtSecurityToken token = new JwtSecurityToken(
                issuer: _configuration["Jwt:Issuer"],
                audience: _configuration["Jwt:Audience"],
                claims: claimIdentity.Claims,
                expires: DateTime.Now.AddMinutes(5),//5分钟有效期
                signingCredentials: creds);

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

            await Task.CompletedTask;
            return new JsonResult(new { code = 200, msg = "success", data = new { token = returnToken } });
        }
    }

    /// <summary>
    /// 登录对象
    /// </summary>
    public class LoginDto
    {
        /// <summary>
        /// 账户
        /// </summary>
        public string? Account { get; set; }

        /// <summary>
        /// 密码
        /// </summary>
        public string? Pwd { get; set; }
    }
}
  • UserController.cs 授权测试 Authorize
{
    /// <summary>
    /// 用户
    /// </summary>
    [ApiController]
    [Route("[controller]")]
    public class UserController : ControllerBase
    {

        /// <summary>
        /// 
        /// </summary>
        public UserController()
        {

        }

        /// <summary>
        /// 获取用户详情
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        [HttpGet("{id}")]
        [Authorize]
        public async Task<IActionResult> GetAsync(int id)
        {
            await Task.CompletedTask;
            return new JsonResult(new
            {
                code = 200,
                msg = "success",
                data = new { id = 1, account = "zhangsan", name = "张三" }
            });
        }
    }
}
  • 测试效果 图

图

图

图

Custom

  • 参照SunnyFan.Author.Custom
  • 添加自定义处理类
{
    /// <summary>
    /// 自定义Url授权处理类
    /// </summary>
    public class UrlTokenAuthenticationHandler : IAuthenticationHandler
    {
        #region 初始化
        /// <summary>
        /// 
        /// </summary>
        private AuthenticationScheme _authenticationScheme;//"UrltokenScheme"

        /// <summary>
        /// 
        /// </summary>
        private HttpContext _httpContext;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="scheme"></param>
        /// <param name="context"></param>
        /// <returns></returns>
        public Task InitializeAsync(AuthenticationScheme scheme, HttpContext context)
        {
            this._authenticationScheme = scheme;
            this._httpContext = context;
            return Task.CompletedTask;
        }
        #endregion

        /// <summary>
        /// 鉴权的核心方法
        /// </summary>
        /// <returns></returns>
        public Task<AuthenticateResult> AuthenticateAsync()
        {
            string userInfo = this._httpContext.Request.Query["UrlToken"];//信息从哪里读

            if (string.IsNullOrWhiteSpace(userInfo))
            {
                return Task.FromResult<AuthenticateResult>(AuthenticateResult.NoResult());
            }

            var claimIdentity = new ClaimsIdentity("Custom");
            claimIdentity.AddClaim(new Claim(ClaimTypes.Name, "SunnyFan"));
            claimIdentity.AddClaim(new Claim(ClaimTypes.Role, "Admin"));
            claimIdentity.AddClaim(new Claim(ClaimTypes.Email, "sunnyfancore@163.com"));
            claimIdentity.AddClaim(new Claim(ClaimTypes.Country, "China"));
            ClaimsPrincipal claimsPrincipal = new ClaimsPrincipal(claimIdentity);//信息拼装和传递
            return Task.FromResult<AuthenticateResult>(AuthenticateResult.Success(new AuthenticationTicket(claimsPrincipal, null, _authenticationScheme.Name)));//保存到context.User

        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="properties"></param>
        /// <returns></returns>
        public async Task ChallengeAsync(AuthenticationProperties? properties)
        {
            var result = JsonConvert.SerializeObject(new { code = 401, msg = "无权限!" });
            _httpContext.Response.StatusCode = 200;
            _httpContext.Response.ContentType = "application/json; charset=utf-8";
            await _httpContext.Response.Body.WriteAsync(Encoding.UTF8.GetBytes(result));
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="properties"></param>
        /// <returns></returns>
        public async Task ForbidAsync(AuthenticationProperties? properties)
        {
            var result = JsonConvert.SerializeObject(new { code = 401, msg = "无权限!" });
            _httpContext.Response.StatusCode = 200;
            _httpContext.Response.ContentType = "application/json; charset=utf-8";
            await _httpContext.Response.Body.WriteAsync(Encoding.UTF8.GetBytes(result));
        }

    }

    /// <summary>
    /// 提供个固定值
    /// </summary>
    public class UrlTokenAuthenticationDefaults
    {
        /// <summary>
        /// 提供固定名称
        /// </summary>
        public const string AuthenticationScheme = "UrlTokenScheme";
    }

}
  • Program.cs 注册服务处理
builder.Services.AddAuthentication(options =>
{//配置如何鉴权

    options.AddScheme<UrlTokenAuthenticationHandler>(UrlTokenAuthenticationDefaults.AuthenticationScheme, "UrlTokenScheme-Demo");
    //定了个Scheme方案,key(scheme名字)-value(UrlTokenAuthenticationHandler)
    options.DefaultAuthenticateScheme = UrlTokenAuthenticationDefaults.AuthenticationScheme;//不能少.默认用UrlTokenScheme
    options.DefaultChallengeScheme = UrlTokenAuthenticationDefaults.AuthenticationScheme;
    options.DefaultSignInScheme = UrlTokenAuthenticationDefaults.AuthenticationScheme;
    options.DefaultForbidScheme = UrlTokenAuthenticationDefaults.AuthenticationScheme;
    options.DefaultSignOutScheme = UrlTokenAuthenticationDefaults.AuthenticationScheme;
}).AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
{
    options.Events.OnRedirectToLogin = async context =>
    {
        var result = JsonConvert.SerializeObject(new { code = 401, msg = "无权限!" });
        context.Response.StatusCode = 200;
        context.Response.ContentType = "application/json; charset=utf-8";
        await context.Response.Body.WriteAsync(Encoding.UTF8.GetBytes(result));
    };
}); 

app.UseRouting();
app.UseAuthentication();//鉴权中间件---表明请求过程中,需要鉴权
app.UseAuthorization();//授权中间件
  • AuthorController.cs 登录模拟
{
    /// <summary>
    /// 授权
    /// </summary>
    [ApiController]
    [Route("[controller]/[action]")]
    public class AuthorController : ControllerBase
    {

        /// <summary>
        /// 
        /// </summary>
        public AuthorController()
        {

        }

        /// <summary>
        /// 登录
        /// </summary>
        /// <param name="loginDto"></param>
        /// <returns></returns>
        [HttpPost]
        public async Task<IActionResult> LoginAsync([FromBody] LoginDto loginDto)
        {
            var claimIdentity = new ClaimsIdentity("Custom");
            claimIdentity.AddClaim(new Claim(ClaimTypes.Name, loginDto.Account!));
            claimIdentity.AddClaim(new Claim(ClaimTypes.Email, "sunnyfancore@163.com"));
            claimIdentity.AddClaim(new Claim(ClaimTypes.Role, "Admin"));
            claimIdentity.AddClaim(new Claim(ClaimTypes.Country, "Chinese"));
            await base.HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(claimIdentity), new AuthenticationProperties
            {
                ExpiresUtc = DateTime.UtcNow.AddMinutes(30),
            });//登陆默认Scheme,写入Cookie


            return new JsonResult(new { code = 200, msg = "success" });
        }
    }

    /// <summary>
    /// 登录对象
    /// </summary>
    public class LoginDto
    {
        /// <summary>
        /// 账户
        /// </summary>
        public string? Account { get; set; }

        /// <summary>
        /// 密码
        /// </summary>
        public string? Pwd { get; set; }
    }
}
  • UserController.cs 授权测试 Authorize
{
    /// <summary>
    /// 用户
    /// </summary>
    [ApiController]
    [Route("[controller]/[action]")]
    public class UserController : ControllerBase
    {

        /// <summary>
        /// 
        /// </summary>
        public UserController()
        {

        }

        /// <summary>
        /// 获取用户详情
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        [HttpGet("{id}")]
        [Authorize()]
        public async Task<IActionResult> GetAsync(int id)
        {
            var result = await base.HttpContext.AuthenticateAsync(UrlTokenAuthenticationDefaults.AuthenticationScheme);
            if (result?.Principal == null)
            {
                return new JsonResult(new
                {
                    code = 401,
                    msg = "无权限!",
                });
            }
            await Task.CompletedTask;
            return new JsonResult(new
            {
                code = 200,
                msg = "success",
                data = new { id = 1, account = "zhangsan", name = "张三" }
            });
        }
    }
}
  • 测试效果 图

图

图

授权

Role(角色)

//Action上标识对应的角色
[Authorize(Roles = "Admin")]
[Authorize(Roles = "User")]

Policy(策略)

  • 定义policy,完成Role的功能
builder.Services.AddAuthorization(options =>
{
//自定义policy,完成Role的功能
    options.AddPolicy("AdminPolicy",
        policyBuilder => policyBuilder.RequireRole("Admin"));

    options.AddPolicy("UserPolicy",
        policyBuilder => policyBuilder.RequireRole("User"));
});       

[Authorize(Roles = "Admin", Policy = "AdminPolicy")]
[Authorize(Roles = "User", Policy = "UserPolicy")]
  • 追加其他自定义策略
builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("ComplexPolicy",
        policyBuilder => policyBuilder.RequireRole("Admin")
                        .RequireUserName("SunnyFan")
                        .RequireClaim(ClaimTypes.Email));
});

[Authorize(Policy = "ComplexPolicy")]
  • 自定义策略之RequireAssertion(静态条件)
builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("ComplexPolicy",
        policyBuilder => policyBuilder.RequireRole("Admin")
                        .RequireUserName("SunnyFan")
                        .RequireClaim(ClaimTypes.Email)
                        .RequireAssertion(context =>//加个断言---一切都可以要求
                            context.User.HasClaim(c => c.Type == ClaimTypes.Role)
                            && context.User.Claims.First(c => c.Type.Equals(ClaimTypes.Role)).Value == "Admin"
                            && context.User.HasClaim(c => c.Type == ClaimTypes.Name)
                            && context.User.Claims.First(c => c.Type.Equals(ClaimTypes.Name)).Value == "SunnyFan"
                            && context.User.HasClaim(c => c.Type == ClaimTypes.Email)
                            && context.User.Claims.First(c => c.Type.Equals(ClaimTypes.Email)).Value.EndsWith("@163.com")
                        ));
});

[Authorize(Policy = "ComplexPolicy")]
  • 自定义策略之AddRequirements(动态条件)
//单个叠加AddRequirements并关系(条件并)
builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("ComplexDynamicPolicy",
        policyBuilder => policyBuilder.RequireRole("Admin")
                        .RequireUserName("SunnyFan")
                        .RequireClaim(ClaimTypes.Email)
                        .RequireAssertion(context =>//加个断言---一切都可以要求
                            context.User.HasClaim(c => c.Type == ClaimTypes.Role)
                            && context.User.Claims.First(c => c.Type.Equals(ClaimTypes.Role)).Value == "Admin"
                            && context.User.HasClaim(c => c.Type == ClaimTypes.Name)
                            && context.User.Claims.First(c => c.Type.Equals(ClaimTypes.Name)).Value == "SunnyFan"
                            && context.User.HasClaim(c => c.Type == ClaimTypes.Email)
                            && context.User.Claims.First(c => c.Type.Equals(ClaimTypes.Email)).Value.EndsWith("@163.com")
                        )
                        .AddRequirements(new SingleRequirement("mail.qq.com"))
                        );
});

//多个叠加AddRequirements自定义并或关系
builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("ComplexDynamicPolicy",
        policyBuilder => policyBuilder.RequireRole("Admin")
                        .RequireUserName("SunnyFan")
                        .RequireClaim(ClaimTypes.Email)
                        .RequireAssertion(context =>//加个断言---一切都可以要求
                            context.User.HasClaim(c => c.Type == ClaimTypes.Role)
                            && context.User.Claims.First(c => c.Type.Equals(ClaimTypes.Role)).Value == "Admin"
                            && context.User.HasClaim(c => c.Type == ClaimTypes.Name)
                            && context.User.Claims.First(c => c.Type.Equals(ClaimTypes.Name)).Value == "SunnyFan"
                            && context.User.HasClaim(c => c.Type == ClaimTypes.Email)
                            && context.User.Claims.First(c => c.Type.Equals(ClaimTypes.Email)).Value.EndsWith("@163.com")
                        )
                         .AddRequirements(new MultiRequirement()) //控制或并关系
                        );
});
builder.Services.AddSingleton<IAuthorizationHandler, Mail163Handler>();
builder.Services.AddSingleton<IAuthorizationHandler, MailQQHandler>();

//使用标记
[Authorize(Roles = "Admin", Policy = "ComplexDynamicPolicy")]

AuthenticationSchemes(鉴权方案)

当系统启用多套鉴权方案时

[Authorize(AuthenticationSchemes = $"{CookieAuthenticationDefaults.AuthenticationScheme},{UrlTokenAuthenticationDefaults.AuthenticationScheme}")]
public async Task<IActionResult> UrlCookieByCookie()
{
    Console.WriteLine($"主动鉴权之前:base.HttpContext.User?.Claims?.First()?.Value == null?{base.HttpContext.User?.Claims?.First()?.Value == null}");
    var urlToken = await base.HttpContext.AuthenticateAsync(UrlTokenAuthenticationDefaults.AuthenticationScheme);
    Console.WriteLine($"urlToken?.Principal == null ={urlToken?.Principal == null}");
    var cookie = await base.HttpContext.AuthenticateAsync(CookieAuthenticationDefaults.AuthenticationScheme);
    Console.WriteLine($"cookie?.Principal == null ={cookie?.Principal == null}");
    if (urlToken?.Principal == null && cookie?.Principal == null)
    {
        return new JsonResult(new
        {
            Result = true,
            Message = $"认证失败,用户未登录"
        });
    }
    else
    {
        return new JsonResult(new
        {
            Result = true,
            Message = $"认证成功,有用户信息"
        });
    }
}