本文章参考了其他朋友的博客,但是我确实不知道参考的谁了,所以自己记录一下,防止忘记。
首先创建了一个ApiAuthorizedByJWT类库·,具体内容如下(四部分分别是 生成的jwt类,角色权限对应关系,jwt验证方法,许可要求类):
1 using Microsoft.AspNetCore.Mvc; 2 using System; 3 using System.IdentityModel.Tokens.Jwt; 4 using System.Security.Claims; 5 6 namespace ApiAuthorizedByJWT 7 { 8 public class JWTAuthorizeCLASS 9 { 10 public bool Status; 11 public string access_token; 12 public double expires_in; 13 public string token_type; 14 public static dynamic BuildOwnToken(Claim[] claims, PermissionRequirement permissionRequirement) 15 { 16 var now = DateTime.UtcNow; 17 var jwt = new JwtSecurityToken( 18 issuer: permissionRequirement.Issuer, 19 audience: permissionRequirement.Audience, 20 claims: claims, 21 notBefore: now, 22 expires: now.Add(permissionRequirement.Expiration), 23 signingCredentials: permissionRequirement.SigningCredentials 24 ); 25 var encodedJwt = new JwtSecurityTokenHandler().WriteToken(jwt); 26 JWTAuthorizeCLASS rtres = new JWTAuthorizeCLASS 27 { 28 Status = true, 29 access_token = encodedJwt, 30 expires_in = permissionRequirement.Expiration.TotalMilliseconds, 31 token_type = "Bearer" 32 }; 33 34 35 return rtres; 36 } 37 } 38 }
using System; using System.Collections.Generic; using System.Text; namespace ApiAuthorizedByJWT { /// <summary> /// 角色与权限url对应的类 /// </summary> public class Permission { /// <summary> /// 角色名称 /// </summary> public virtual string RoleName { get; set; } /// <summary> /// 链接地址 /// </summary> public virtual string Url { get; set; } } }
using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authorization; using Microsoft.Extensions.DependencyInjection; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; /// <summary> /// 这里必须注意:此处引用了nuget包:Microsoft.AspNetCore.Mvc.Abstractions;否则这个Microsoft.AspNetCore.Mvc不存在 以及Microsoft.AspNetCore.All /// </summary> namespace ApiAuthorizedByJWT { public class PermissionHandler : AuthorizationHandler<PermissionRequirement> { /// <summary> /// 验证方案提供对象 /// </summary> public IAuthenticationSchemeProvider Schemes { get; set; } /// <summary> /// 自定义策略参数 /// </summary> public PermissionRequirement Requirement { get; set; } /// <summary> /// 构造 /// </summary> /// <param name="schemes"></param> public PermissionHandler(IAuthenticationSchemeProvider schemes) { Schemes = schemes; } protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionRequirement requirement) { ////赋值用户权限 Requirement = requirement; //从AuthorizationHandlerContext转成HttpContext,以便取出表求信息 var httpContext = (context.Resource as Microsoft.AspNetCore.Mvc.Filters.AuthorizationFilterContext).HttpContext; //请求Url var questUrl = httpContext.Request.Path.Value.ToLower(); //判断请求是否停止 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.RoleName == name && w.Url.ToLower() == questUrl).Count() <= 0) { context.Fail(); return; } context.Succeed(requirement); } return; //var userroles = requirement.Permissions; } } if (!questUrl.Equals(Requirement.LoginPath.ToLower(), StringComparison.Ordinal) && (!httpContext.Request.Method.Equals("POST") || !httpContext.Request.HasFormContentType)) { context.Fail(); return; } context.Succeed(requirement); } } }
using Microsoft.AspNetCore.Authorization; using Microsoft.IdentityModel.Tokens; using System; using System.Collections.Generic; using System.Text; namespace ApiAuthorizedByJWT { /// <summary> /// 许可要求 /// </summary> public class PermissionRequirement:IAuthorizationRequirement { /// <summary> /// 用户权限集合 /// </summary> public List<Permission> Permissions { get; private set; } /// <summary> /// 无权限action /// </summary> public string DeniedAction { get; set; } = "/Api/ReLogin"; /// <summary> /// 认证授权类型 /// </summary> public string ClaimType { internal get; set; } /// <summary> /// 请求路径 /// </summary> public string LoginPath { get; set; } = "/Api/Login"; /// <summary> /// 发行人 /// </summary> public string Issuer { get; set; } /// <summary> /// 订阅人 /// </summary> public string Audience { get; set; } /// <summary> /// 过期时间 /// </summary> public TimeSpan Expiration { get; set; } = TimeSpan.FromMinutes(60); /// <summary> /// 签名验证 /// </summary> public SigningCredentials SigningCredentials { get; set; } /// <summary> /// 构造 /// </summary> /// <param name="deniedAction">拒约请求的url</param> /// <param name="permissions">权限集合</param> /// <param name="claimType">声明类型</param> /// <param name="issuer">发行人</param> /// <param name="audience">订阅人</param> /// <param name="signingCredentials">签名验证实体</param> public PermissionRequirement(string deniedAction, List<Permission> permissions, string claimType, string issuer, string audience, SigningCredentials signingCredentials) { ClaimType = claimType; DeniedAction = deniedAction; Permissions = permissions; Issuer = issuer; Audience = audience; SigningCredentials = signingCredentials; } } }
那么在真正的使用中,要在core中添加上对应的一些数据信息。首先常见一个关于登录用户的类
using ApiAuthorizedByJWT; using Dapper; using Microsoft.Extensions.Options; using Microsoft.IdentityModel.Tokens; using new_m1_1_0.Models.interfaces; using System; using System.Collections.Generic; using System.Data; using System.Linq; using System.Security.Claims; using System.Text; using System.Threading.Tasks; namespace new_m1_1_0.Models.models { public class LoginUser:ILoginUser { public string Username; public string UserID; public string Accid; public string Password; public string Acctoken; public List<SourceWithRole> LRC; public JWTAuthorizeCLASS apitoken; private bool isInit = false; public LoginUser(string ID,string Key,Func<string,string,string,bool> CheckMethed, string connectstring) { //--------------------校验方法-------------------------------- //pass if (CheckMethed(ID, Key, connectstring)) { Password = Key; IDbConnection conn = new MySql.Data.MySqlClient.MySqlConnection(connectstring); string sql1 = "select a.user_name,a.role_key,b.name,b.remark,c.url,c.name as func_name,c.sourse,d.resource_key,e.source_name from m1_user_roles a,m1_role b,m1_resource c,m1_permission d,m1_sourse e where a.user_name = @ID and b.role_key = a.role_key and d.role_key = a.role_key and c.resource_key = d.resource_key and e.source_key = c.sourse and e.role_key = a.role_key"; List<LoginUserInfo> loginUserInfo = conn.Query<LoginUserInfo>(sql1, new { id = ID }).ToList(); string sql2 = "select a.user_name,a.accid,a.acctoken,b.nick_name from m1_user_im a,m1_user b where a.user_name = @id and b.user_name = a.user_name"; List < LoginUserNeteaseInfo> loginUserNeteaseInfo = conn.Query<LoginUserNeteaseInfo>(sql2, new { id = ID }).ToList(); LRC = new List<SourceWithRole>(); List<string> sourseL = new List<string>(); for (int i = 0; i < loginUserInfo.Count; i++) { if (!sourseL.Contains(loginUserInfo[i].sourse)) { sourseL.Add(loginUserInfo[i].sourse); SourceWithRole sourceWithRole = new SourceWithRole(); sourceWithRole.L = new List<RoleWithCode>(); var rwc = new RoleWithCode(); rwc.Code = loginUserInfo[i].resource_key; rwc.RoleName = loginUserInfo[i].name; rwc.RoleKey = loginUserInfo[i].role_key; rwc.Url = loginUserInfo[i].url; sourceWithRole.L.Add(rwc); sourceWithRole.SourceCode = loginUserInfo[i].sourse; sourceWithRole.SourceName = loginUserInfo[i].source_name; LRC.Add(sourceWithRole); } else { for (int j = 0; j < LRC.Count; j++) { if (LRC[j].SourceCode == loginUserInfo[i].sourse) { var rwc = new RoleWithCode(); rwc.Code = loginUserInfo[i].resource_key; rwc.RoleName = loginUserInfo[i].name; rwc.RoleKey = loginUserInfo[i].role_key; rwc.Url = loginUserInfo[i].url; LRC[j].L.Add(rwc); } else { continue; } } } } try { Username = loginUserNeteaseInfo[0].nick_name; Accid = loginUserNeteaseInfo[0].accid; Acctoken = loginUserNeteaseInfo[0].acctoken; UserID = loginUserNeteaseInfo[0].user_name; this.apitoken = GetLoginUserToken(); } catch (Exception ex) { Username = ""; Accid = ""; Acctoken = ""; UserID = ""; LRC = null; } } else { Username = ""; UserID = ""; Password = ""; Accid = ""; Acctoken = ""; LRC = null; } } public JWTAuthorizeCLASS GetLoginUserToken() { if (LRC == null || LRC.Count < 1) { return null; } var symmetricKeyAsBase64 = "TTT11111TTT111TTTTT11TTTTTT11111111111";//audienceConfig["Secret"]; var keyByteArray = Encoding.ASCII.GetBytes(symmetricKeyAsBase64); var signingKey = new SymmetricSecurityKey(keyByteArray); var signingCredentials = new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256); int apicount = 0; List<string> roles = new List<string>(); var permission = new List<Permission>(); for (int i = 0; i < LRC.Count; i++) { for (int j = 0; j < LRC[i].L.Count; j++) { if (roles.Contains(LRC[i].L[j].RoleKey)) { } else { roles.Add(LRC[i].L[j].RoleKey); } permission.Add(new Permission { Url = LRC[i].L[j].Url,RoleName= LRC[i].L[j].RoleKey}); } } List<System.Security.Claims.Claim> claimArray = new List<Claim>(); for (int i = 0; i < roles.Count; i++) { claimArray.Add(new Claim(ClaimTypes.Role, roles[i])); } claimArray.Add(new Claim(ClaimTypes.Name, UserID)); return JWTAuthorizeCLASS.BuildOwnToken(claimArray.ToArray(), new PermissionRequirement("/api/login", permission, ClaimTypes.Role, "lsnct", "everyone", signingCredentials)); } } public class RoleWithCode { /// <summary> /// 角色名称 /// </summary> public string RoleName; /// <summary> /// 权限code /// </summary> public string Code; /// <summary> /// 链接 /// </summary> public string Url; /// <summary> /// 角色关键字 /// </summary> public string RoleKey; } public class SourceWithRole { /// <summary> /// 资源模块代码 /// </summary> public string SourceCode; /// <summary> /// 资源模块名称 /// </summary> public string SourceName; /// <summary> /// 所属角色与api列表 /// </summary> public List<RoleWithCode> L; } }
然后在控制器中处理
[HttpPost] public JsonResult Post() { var username = HttpContext.Request.Form["username"].ToString().Trim();//获取 var pwd = HttpContext.Request.Form["pwd"].ToString().Trim(); LoginUser LU = new LoginUser(username,pwd,CheckLogin.checkmethed1, appSettings.dbcc_sys); LU.Password = ""; return new JsonResult(LU); }
生成了token后,后面会拿这个token访问接口。
在对应的工程的startup.cs中处理
var symmetricKeyAsBase64 = "TTT11111TTT111TTTTT11TTTTTT11111111111";//audienceConfig["Secret"];
var keyByteArray = Encoding.ASCII.GetBytes(symmetricKeyAsBase64); var signingKey = new SymmetricSecurityKey(keyByteArray); var tokenValidationParameters = new TokenValidationParameters { ValidateIssuerSigningKey = true, IssuerSigningKey = signingKey, ValidateIssuer = true, ValidIssuer = "lsnct", //audienceConfig["Issuer"], ValidateAudience = true, ValidAudience = "everyone", //audienceConfig["Audience"], ValidateLifetime = true, ClockSkew = TimeSpan.Zero }; var signingCredentials = new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256); services.AddAuthorization(options => { //var permission = GetAllPermission(); List<Permission> permission = null; if (permission == null) { //这个集合模拟用户权限表,可从数据库中查询出来 permission = new List<Permission> { new Permission { Url="/api/getValues1", RoleName="system"}, new Permission { Url="/api/values/getValues1", RoleName="admin"}, new Permission{ Url="/api/getValues3", RoleName="admin"}, new Permission{ Url="/api/Products/GetMomentDetails", RoleName="system"} }; } //如果第三个参数,是ClaimTypes.Role,上面集合的每个元素的Name为角色名称,如果ClaimTypes.Name,即上面集合的每个元素的Name为用户名 //var permissionRequirement = new PermissionRequirement("/api/denied", permission, ClaimTypes.Role, audienceConfig["Issuer"], audienceConfig["Audience"], signingCredentials); var permissionRequirement = new PermissionRequirement("/Api/Login", permission, ClaimTypes.Role, "lsnct", "everyone", signingCredentials); options.AddPolicy("Permission", policy => policy.Requirements.Add(permissionRequirement)); }).AddAuthentication(options => { options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }) .AddJwtBearer(o => { //不使用https o.RequireHttpsMetadata = false; o.TokenValidationParameters = tokenValidationParameters; }); //注入授权Handler services.AddSingleton<IAuthorizationHandler, PermissionHandler>(); services.AddMvc() .AddJsonOptions(options=> { options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;//忽略循环引用 options.SerializerSettings.ContractResolver = new DefaultContractResolver();//不使用驼峰样式key options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss";//时间格式化 });
最后在对应接口加上[Authorize("Permission")]标记即可。