OsharpNS轻量级.net core快速开发框架简明入门教程
教程目录
-
从零开始启动Osharp
1.1. 使用OsharpNS项目模板创建项目
1.2. 配置数据库连接串并启动项目
1.3. OsharpNS.Swagger使用实例(登录和授权)
1.4. Angular6的前端项目启动
-
Osharp代码生成器的使用
2.1 生成器的使用
-
Osharp部分模块使用
3.1 Osharp.Redis使用
-
Osharp深度学习和使用
4.2 多上下文配置(多个数据库的使用)
4.3. 自定义模块的定义(Senparc.Weixin的使用)
4.4. 继续学习中....
OsharpNS官方资源
项目地址:https://github.com/i66soft/osharp-ns20
演示地址:https://www.osharp.org 直接使用QQ登录可以查看效果
文档地址:https://docs.osharp.org 正在完善中....
发布博客:https://www.cnblogs.com/guomingfeng/p/osharpns-publish.html 大神看这个文档应该就能跑起来,从零开始启动Osharp基于此文档完成
VS生成器插件:https://marketplace.visualstudio.com/items?itemName=LiuliuSoft.osharp
官方交流QQ群:85895249
Osharp.Permissions使用
-
系统授权检查流程
核心代码位于
Osharp/Secutiry/FunctionAuthorizationBase
检查过程如下:
检查function是否为null,为null反馈错误,否则继续检查
检查function是否被禁用,被禁用反馈错误,否则继续检查
检查功能是否任何人可用,如果是,直接返回成功,否则继续检查
检查用户是否登陆,未登录反馈错误,否则继续检查
用户已登陆,判断功能是否登陆即可使用,如果是,反馈成功,否则继续检查
获取用户的角色,判断角色是否有角色允许执行功能,如果是,反馈成功,否则继续检查
获取用户能执行的所有功能,判断是否包含功能,如果是,反馈成功;如果否,反馈失败(系统除了给定用户角色,还能根据用户单独给定功能授权,所以检查完角色之后不满足条件,还要检查用户私有的功能是否包含)
/// <summary>
/// 重写以实现权限检查核心验证操作
/// </summary>
/// <param name="function">要验证的功能信息</param>
/// <param name="principal">当前用户在线信息</param>
/// <returns>功能权限验证结果</returns>
protected virtual AuthorizationResult AuthorizeCore(IFunction function, IPrincipal principal)
{
if (function == null)
{
return new AuthorizationResult(AuthorizationStatus.NoFound);
}
if (function.IsLocked)
{
return new AuthorizationResult(AuthorizationStatus.Locked, $"功能“{function.Name}”已被禁用,无法执行");
}
if (function.AccessType == FunctionAccessType.Anonymouse)
{
return AuthorizationResult.OK;
}
//未登录
if (principal == null || !principal.Identity.IsAuthenticated)
{
return new AuthorizationResult(AuthorizationStatus.Unauthorized);
}
//已登录,无角色限制
if (function.AccessType == FunctionAccessType.Logined)
{
return AuthorizationResult.OK;
}
return AuthorizeRoleLimit(function, principal);
} /// <summary>
/// 重写以实现 角色限制 的功能的功能权限检查
/// </summary>
/// <param name="function">要验证的功能信息</param>
/// <param name="principal">用户在线信息</param>
/// <returns>功能权限验证结果</returns>
protected virtual AuthorizationResult AuthorizeRoleLimit(IFunction function, IPrincipal principal)
{
//角色限制
if (!(principal.Identity is ClaimsIdentity identity))
{
return new AuthorizationResult(AuthorizationStatus.Error, "当前用户标识IIdentity格式不正确,仅支持ClaimsIdentity类型的用户标识");
}
//检查角色-功能的权限
string[] userRoleNames = identity.GetRoles().ToArray();
AuthorizationResult result = AuthorizeRoleNames(function, userRoleNames);
if (result.IsOk)
{
return result;
}
result = AuthorizeUserName(function, principal.Identity.GetUserName());
return result;
} /// <summary>
/// 重写以实现指定角色是否有执行指定功能的权限
/// </summary>
/// <param name="function">功能信息</param>
/// <param name="roleNames">角色名称</param>
/// <returns>功能权限检查结果</returns>
protected virtual AuthorizationResult AuthorizeRoleNames(IFunction function, params string[] roleNames)
{
Check.NotNull(roleNames, nameof(roleNames)); if (roleNames.Length == 0)
{
return new AuthorizationResult(AuthorizationStatus.Forbidden);
}
if (function.AccessType != FunctionAccessType.RoleLimit || roleNames.Contains(SuperRoleName))
{
return AuthorizationResult.OK;
}
string[] functionRoleNames = FunctionAuthCache.GetFunctionRoles(function.Id);
if (roleNames.Intersect(functionRoleNames).Any())
{
return AuthorizationResult.OK;
}
return new AuthorizationResult(AuthorizationStatus.Forbidden);
} /// <summary>
/// 重写以实现指定用户是否有执行指定功能的权限
/// </summary>
/// <param name="function">功能信息</param>
/// <param name="userName">用户名</param>
/// <returns>功能权限检查结果</returns>
protected virtual AuthorizationResult AuthorizeUserName(IFunction function, string userName)
{
if (function.AccessType != FunctionAccessType.RoleLimit)
{
return AuthorizationResult.OK;
} Guid[] functionIds = FunctionAuthCache.GetUserFunctions(userName);
if (functionIds.Contains(function.Id))
{
return AuthorizationResult.OK;
}
return new AuthorizationResult(AuthorizationStatus.Forbidden);
} -
Controller里面对是否检查权限的控制
文件路径为
CanDoo.Test.Web.Areas.Admin.Controllers
,注意看文件中的[RoleLimit]
,带了这个就要做检查// -----------------------------------------------------------------------
// <copyright file="AdminApiController.cs" company="OSharp开源团队">
// Copyright (c) 2014-2018 OSharp. All rights reserved.
// </copyright>
// <site>http://www.osharp.org</site>
// <last-editor>郭明锋</last-editor>
// <last-date>2018-06-27 4:50</last-date>
// ----------------------------------------------------------------------- using Microsoft.AspNetCore.Mvc; using OSharp.AspNetCore.Mvc;
using OSharp.Core; namespace CanDoo.Test.Web.Areas.Admin.Controllers
{
[Area("Admin")]
[RoleLimit]
public abstract class AdminApiController : AreaApiController
{ }
} -
不通过数据库强制给定用户角色的方法
3.1 通过Claim实现
在`IdentityController.cs`中,用户登陆后,生成Claim时,将角色给定
private async Task<string> CreateJwtToken(User user)
{
//在线用户缓存
IOnlineUserCache onlineUserCache = HttpContext.RequestServices.GetService<IOnlineUserCache>();
if (onlineUserCache != null)
{
await onlineUserCache.GetOrRefreshAsync(user.UserName);
} //生成Token,这里只包含最基本信息,其他信息从在线用户缓存中获取
Claim[] claims =
{
new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
new Claim(ClaimTypes.Name, user.UserName),
new Claim("ExtendRoles", "学生") //这行是新增的,强制给定学生角色,用户角色表中不存在 用户和学生 角色的关联
};
OsharpOptions options = HttpContext.RequestServices.GetService<IOptions<OsharpOptions>>().Value;
string token = JwtHelper.CreateToken(claims, options);
return token;
}在`CanDoo.Test.Core`中新建类`OnlineUserJwtSecurityTokenHandler.cs`
// -----------------------------------------------------------------------
// <copyright file="OnlineUserJwtSecurityTokenHandler.cs" company="OSharp开源团队">
// Copyright (c) 2014-2018 OSharp. All rights reserved.
// </copyright>
// <site>http://www.osharp.org</site>
// <last-editor>郭明锋</last-editor>
// <last-date>2018-07-09 15:01</last-date>
// ----------------------------------------------------------------------- using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims; using Microsoft.IdentityModel.Tokens; using OSharp.Collections;
using OSharp.Dependency;
using OSharp.Identity;
using OSharp.Secutiry.Claims; namespace CanDoo.Test.Identity
{
/// <summary>
/// 使用在线用户信息和JwtToken生成在线ClaimsIdentity
/// </summary>
public class OnlineUserJwtSecurityTokenHandler : JwtSecurityTokenHandler
{
/// <summary>
/// Creates a <see cref="T:System.Security.Claims.ClaimsIdentity" /> from a <see cref="T:System.IdentityModel.Tokens.Jwt.JwtSecurityToken" />.
/// </summary>
/// <param name="jwtToken">The <see cref="T:System.IdentityModel.Tokens.Jwt.JwtSecurityToken" /> to use as a <see cref="T:System.Security.Claims.Claim" /> source.</param>
/// <param name="issuer">The value to set <see cref="P:System.Security.Claims.Claim.Issuer" /></param>
/// <param name="validationParameters"> Contains parameters for validating the token.</param>
/// <returns>A <see cref="T:System.Security.Claims.ClaimsIdentity" /> containing the <see cref="P:System.IdentityModel.Tokens.Jwt.JwtSecurityToken.Claims" />.</returns>
protected override ClaimsIdentity CreateClaimsIdentity(JwtSecurityToken jwtToken,
string issuer,
TokenValidationParameters validationParameters)
{
ClaimsIdentity identity = base.CreateClaimsIdentity(jwtToken, issuer, validationParameters);
var extendRoles = identity.GetClaimValueFirstOrDefault("ExtendRoles");//从Claim中获取强制给定的角色 if (identity.IsAuthenticated)
{
//由在线缓存获取用户信息赋给IIdentity
IOnlineUserCache onlineUserCache = ServiceLocator.Instance.GetService<IOnlineUserCache>();
OnlineUser user = onlineUserCache.GetOrRefresh(identity.Name);
if (user == null)
{
return null;
} if (!string.IsNullOrEmpty(user.NickName))
{
identity.AddClaim(new Claim(ClaimTypes.GivenName, user.NickName));
}
if (!string.IsNullOrEmpty(user.Email))
{
identity.AddClaim(new Claim(ClaimTypes.Email, user.Email));
}
//这部分将从Claim中获取的角色进行赋值
string roles = "";
if (user.Roles.Length > 0)
roles = user.Roles.ExpandAndToString() + "," + extendRoles;
else
roles = extendRoles; if (roles != "")
identity.AddClaim(new Claim(ClaimTypes.Role, user.Roles.ExpandAndToString()));
} ScopedDictionary dict = ServiceLocator.Instance.GetService<ScopedDictionary>();
dict.Identity = identity;
return identity;
}
}
}对`CanDoo.Test.Core`中`IdentityPack`中的代码进行调整
jwt.SecurityTokenValidators.Clear();
jwt.SecurityTokenValidators.Add(new OnlineUserJwtSecurityTokenHandler());//这里要使用本文中创建的OnlineUserJwtSecurityTokenHandler jwt.Events = new JwtBearerEvents()
{
// 生成SignalR的用户信息
OnMessageReceived = context =>
{
string token = context.Request.Query["access_token"];
string path = context.HttpContext.Request.Path;
if (!string.IsNullOrEmpty(token) && path.Contains("hub"))
{
context.Token = token;
} return Task.CompletedTask;
}
};3.2 通过替换现有OnlineUserProvider缓存方法实现
在`CanDoo.Test.Core`中新建`MyOnlineUserProvider`
// -----------------------------------------------------------------------
// <copyright file="OnlineUserProvider.cs" company="OSharp开源团队">
// Copyright (c) 2014-2018 OSharp. All rights reserved.
// </copyright>
// <site>http://www.osharp.org</site>
// <last-editor>郭明锋</last-editor>
// <last-date>2018-08-17 22:36</last-date>
// ----------------------------------------------------------------------- using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks; using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.DependencyInjection;
using OSharp.Identity; namespace CanDoo.Test.Identity
{
/// <summary>
/// 在线用户信息提供者
/// </summary>
public class MyOnlineUserProvider<TUser, TUserKey, TRole, TRoleKey> : IOnlineUserProvider
where TUser : UserBase<TUserKey>
where TUserKey : IEquatable<TUserKey>
where TRole : RoleBase<TRoleKey>
where TRoleKey : IEquatable<TRoleKey>
{
/// <summary>
/// 创建在线用户信息
/// </summary>
/// <param name="provider">服务提供器</param>
/// <param name="userName">用户名</param>
/// <returns>在线用户信息</returns>
public virtual async Task<OnlineUser> Create(IServiceProvider provider, string userName)
{
UserManager<TUser> userManager = provider.GetService<UserManager<TUser>>();
TUser user = await userManager.FindByNameAsync(userName);
if (user == null)
{
return null;
}
IList<string> roles = await userManager.GetRolesAsync(user); roles.Add("学生");//这样就强制给用户赋值了,当然可以对其他东西也做手脚 RoleManager<TRole> roleManager = provider.GetService<RoleManager<TRole>>();
bool isAdmin = roleManager.Roles.Any(m => roles.Contains(m.Name) && m.IsAdmin);
return new OnlineUser()
{
Id = user.Id.ToString(),
UserName = user.UserName,
NickName = user.NickName,
Email = user.Email,
HeadImg = user.HeadImg,
IsAdmin = isAdmin,
Roles = roles.ToArray()
};
}
}
}对`CanDoo.Test.Core`中的`IdentityPack`修改代码,将原有的`OnlineUserProvider`换为`MyOnlineUserProvider`
/// <summary>
/// 将模块服务添加到依赖注入服务容器中
/// </summary>
/// <param name="services">依赖注入服务容器</param>
/// <returns></returns>
public override IServiceCollection AddServices(IServiceCollection services)
{
services.AddScoped<IIdentityContract, IdentityService>();
base.AddServices(services);
services.Replace(new ServiceDescriptor(typeof(IOnlineUserProvider), typeof(MyOnlineUserProvider<User, int, Role, int>), ServiceLifetime.Scoped));
return services;
}