ASP.NET core2.2 用户验证 和授权有很详细和特贴心的介绍, 我感兴趣的主要是这两篇:
cookie 身份验证
基于角色的授权
我的项目有两类用户:
微信公众号用户, 用户名为公众号的 openid
企业微信的用户, 用户名为企业微信的 userid
每类用户中部分人员具有 "Admin" 角色
因为企业微信的用户有可能同时是微信公众号用户, 即一个人两个名, 所以需要多用户验证和授权. 咱用代码说话最简洁, 如下所示:
- public class DemoController : Controller
- {
- /// <summary>
- /// 企业微信用户使用的模块
- /// </summary>
- /// <returns></returns>
- public IActionResult Work()
- {
- return Content(User.Identity.Name +User.IsInRole("Admin"));
- }
- /// <summary>
- /// 企业微信管理员使用的模块
- /// </summary>
- /// <returns></returns>
- public IActionResult WorkAdmin()
- {
- return Content(User.Identity.Name + User.IsInRole("Admin"));
- }
- /// <summary>
- /// 微信公众号用户使用的模块
- /// </summary>
- /// <returns></returns>
- public IActionResult Mp()
- {
- return Content(User.Identity.Name + User.IsInRole("Admin"));
- }
- /// <summary>
- /// 微信公众号管理员使用的模块
- /// </summary>
- /// <returns></returns>
- public IActionResult MpAdmin()
- {
- return Content(User.Identity.Name + User.IsInRole("Admin"));
- }
- }
下面咱一步一步实现.
第一步 改造类 Startup
修改 ConfigureServices 方法, 加入以下代码
- services.AddAuthentication
- (
- "Work" // 就是设置一个缺省的 cookie 验证的名字, 缺省的意思就是需要写的时候可以不写. 另外很多时候用 CookieAuthenticationDefaults.AuthenticationScheme, 这玩意就是字符串常量 "Cookies",
- )
- .AddCookie
- (
- "Work", //cookie 验证的名字,"Work" 可以省略, 因为是缺省名
- option =>
- {
- option.LoginPath = new PathString("/Demo/WorkLogin"); // 设置验证的路径
- option.AccessDeniedPath= new PathString("/Demo/WorkDenied");// 设置无授权访问跳转的路径
- }).AddCookie("Mp", option =>
- {
- option.LoginPath = new PathString("/Demo/MpLogin");
- option.AccessDeniedPath = new PathString("/Demo/MpDenied");
- });
修改 Configure 方法, 加入以下代码
App.UseAuthentication();
第二步 添加验证
- public async Task WorkLogin(string returnUrl)
- {
- var claims = new List<Claim>
- {
- new Claim(ClaimTypes.Name, "UserId"),
- new Claim(ClaimTypes.Role, "Admin") // 如果是管理员
- };
- var claimsIdentity = new ClaimsIdentity(claims, "Work");//","Work"" 可以省略, 因为是缺省名
- var authProperties = new AuthenticationProperties
- {
- AllowRefresh = true,
- //ExpiresUtc = DateTimeOffset.UtcNow.AddMinutes(10),
- // The time at which the authentication ticket expires. A
- // value set here overrides the ExpireTimeSpan option of
- // CookieAuthenticationOptions set with AddCookie.
- IsPersistent = false, // 持久化保存, 到底什么意思我也不太清楚, 哪位兄弟清楚的话, 盼解释
- //IssuedUtc = <DateTimeOffset>,
- // The time at which the authentication ticket was issued.
- RedirectUri = returnUrl ?? "/Demo/Work"
- };
- await HttpContext.SignInAsync("Work", new ClaimsPrincipal(claimsIdentity), authProperties);
- }
- public IActionResult WorkDenied()
- {
- return Forbid();
- }
- public async Task MpLogin(string returnUrl)
- {
- var claims = new List<Claim>
- {
- new Claim(ClaimTypes.Name, "OpenId"),
- new Claim(ClaimTypes.Role, "Admin") // 如果是管理员
- };
- var claimsIdentity = new ClaimsIdentity(claims, "Mp");//","Mp"" 不能省略, 因为不是缺省名
- var authProperties = new AuthenticationProperties
- {
- AllowRefresh = true,
- IsPersistent = false,
- RedirectUri = returnUrl ?? "/Demo/Mp"
- };
- await HttpContext.SignInAsync("Mp", new ClaimsPrincipal(claimsIdentity), authProperties);
- }
- public IActionResult MpDenied()
- {
- return Forbid();
- }
第三步 添加授权
就是在对应的 Action 前面加 [Authorize]
- /// <summary>
- /// 企业微信用户使用的模块
- /// </summary>
- /// <returns></returns>
- [Authorize(
- AuthenticationSchemes ="Work" // 缺省名可以省略
- )]
- public IActionResult Work()
- {
- return Content(User.Identity.Name + User.IsInRole("Admin"));
- }
- /// <summary>
- /// 企业微信管理员使用的模块
- /// </summary>
- /// <returns></returns>
- [Authorize(AuthenticationSchemes ="Work",Roles ="Admin")]
- public IActionResult WorkAdmin()
- {
- return Content(User.Identity.Name + User.IsInRole("Admin"));
- }
- /// <summary>
- /// 微信公众号用户使用的模块
- /// </summary>
- /// <returns></returns>
- [Authorize(AuthenticationSchemes ="Mp")]
- public IActionResult Mp()
- {
- return Content(User.Identity.Name + User.IsInRole("Admin"));
- }
- /// <summary>
- /// 微信公众号管理员使用的模块
- /// </summary>
- /// <returns></returns>
- [Authorize(AuthenticationSchemes ="Mp",Roles ="Admin")]
- public IActionResult MpAdmin()
- {
- return Content(User.Identity.Name + User.IsInRole("Admin"));
- }
Ctrl+F5 运行, 截屏如下:
最后, 讲讲碰到的坑和求助
坑
一开始的验证的代码如下:
- public async Task<IActionResult> Login(string returnUrl)
- {
- var claims = new List<Claim>
- {
- new Claim(ClaimTypes.Name, "UserId"),
- new Claim(ClaimTypes.Role, "Admin") // 如果是管理员
- };
- var claimsIdentity = new ClaimsIdentity(claims, "Work");//","Work"" 可以省略, 因为是缺省名
- var authProperties = new AuthenticationProperties
- {
- //AllowRefresh = true,
- //IsPersistent = false,
- //RedirectUri
- };
- await HttpContext.SignInAsync("Work", new ClaimsPrincipal(claimsIdentity), authProperties);
- return Content("OK");
- }
返回类型为 Task<IActionResult> , 因为懒得写 View, 顺手写了句 return Content("OK");
从网站复制过来代码, AuthenticationProperties 没有设置任何内容
运行起来以后不停的调用 login, 百度了半天, 改了各种代码, 最后把 return Content("OK"); 改成 return RedirectToAction("Index"); 一切 OK!
揣摩原因可能是当 return Content("OK"); 时, 自动调用 AuthenticationProperties 的 RedirectUri, 而 RedirectUri 为空时, 自动调用自己. 也不知道对不对.
这时候重视起 RedirectUri, 本来就要返回到 returnUrl, 是不是给 RedirectUri 赋值 returnUrl 就能自动跳转?
确实, return Content("OK"); 时候自动跳转了, return RedirectToAction("Index"); 无效.
最后把 Task<IActionResult> 改成 Task , 把 return ... 删除, 一切完美!(弱弱问一句, 是不是原来就应该这样写? 我一直在走弯路?)
求助
User 有属性 Identities, 看起来可以有多个 Identity, 如何有?
来源: https://www.cnblogs.com/catzhou/p/10243069.html