ASP.NET Core 开源GitServer 实现自己的GitHub
ASP.NET Core 2.0 开源Git HTTP Server,实现类似 GitHub、GitLab。
GitHub:https://github.com/linezero/GitServer
设置
  "GitSettings": {
    "BasePath": "D:\\Git",
    "GitPath": "git"
  }
需要先安装Git,并确保git 命令可以执行,GitPath 可以是 git 的绝对路径。
目前实现的功能
- 创建仓库
 - 浏览仓库
 - git客户端push pull
 - 数据库支持 SQLite、MSSQL、MySQL
 - 支持用户管理仓库
 
更多功能可以查看readme,也欢迎大家贡献支持。
Git交互
LibGit2Sharp 用于操作Git库,实现创建读取仓库信息及删除仓库。
以下是主要代码:
public Repository CreateRepository(string name)
{
string path = Path.Combine(Settings.BasePath, name);
Repository repo = new Repository(Repository.Init(path, true));
return repo;
} public Repository CreateRepository(string name, string remoteUrl)
{
var path = Path.Combine(Settings.BasePath, name);
try
{
using (var repo = new Repository(Repository.Init(path, true)))
{
repo.Config.Set("core.logallrefupdates", true);
repo.Network.Remotes.Add("origin", remoteUrl, "+refs/*:refs/*");
var logMessage = "";
foreach (var remote in repo.Network.Remotes)
{
IEnumerable<string> refSpecs = remote.FetchRefSpecs.Select(x => x.Specification);
Commands.Fetch(repo, remote.Name, refSpecs, null, logMessage);
}
return repo;
}
}
catch
{
try
{
Directory.Delete(path, true);
}
catch { }
return null;
}
} public void DeleteRepository(string name)
{
Exception e = null;
for(int i = ; i < ; i++)
{
try
{
string path = Path.Combine(Settings.BasePath, name);
Directory.Delete(path, true); }
catch(Exception ex) { e = ex; }
} if (e != null)
throw new GitException("Failed to delete repository", e);
}
执行Git命令
git-upload-pack
git-receive-pack
主要代码 GitCommandResult 实现IActionResult
public async Task ExecuteResultAsync(ActionContext context)
{
HttpResponse response = context.HttpContext.Response;
Stream responseStream = GetOutputStream(context.HttpContext); string contentType = $"application/x-{Options.Service}";
if (Options.AdvertiseRefs)
contentType += "-advertisement"; response.ContentType = contentType; response.Headers.Add("Expires", "Fri, 01 Jan 1980 00:00:00 GMT");
response.Headers.Add("Pragma", "no-cache");
response.Headers.Add("Cache-Control", "no-cache, max-age=0, must-revalidate"); ProcessStartInfo info = new ProcessStartInfo(_gitPath, Options.ToString())
{
UseShellExecute = false,
CreateNoWindow = true,
RedirectStandardInput = true,
RedirectStandardOutput = true,
RedirectStandardError = true
}; using (Process process = Process.Start(info))
{
GetInputStream(context.HttpContext).CopyTo(process.StandardInput.BaseStream); if (Options.EndStreamWithNull)
process.StandardInput.Write('\0');
process.StandardInput.Dispose(); using (StreamWriter writer = new StreamWriter(responseStream))
{
if (Options.AdvertiseRefs)
{
string service = $"# service={Options.Service}\n";
writer.Write($"{service.Length + 4:x4}{service}0000");
writer.Flush();
} process.StandardOutput.BaseStream.CopyTo(responseStream);
} process.WaitForExit();
}
}
BasicAuthentication 基本认证实现
git http 默认的认证为Basic 基本认证,所以这里实现Basic 基本认证。
在ASP.NET Core 2.0 中 Authentication 变化很大之前1.0的一些代码是无法使用。
首先实现 AuthenticationHandler,然后实现 AuthenticationSchemeOptions,创建 BasicAuthenticationOptions。
最主要就是这两个类,下面两个类为辅助类,用于配置和中间件注册。
更多可以查看官方文档
身份验证
https://docs.microsoft.com/zh-cn/aspnet/core/security/authentication/
https://docs.microsoft.com/zh-cn/aspnet/core/migration/1x-to-2x/identity-2x
public class BasicAuthenticationHandler : AuthenticationHandler<BasicAuthenticationOptions>
{
public BasicAuthenticationHandler(IOptionsMonitor<BasicAuthenticationOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock)
: base(options, logger, encoder, clock)
{ }
protected async override Task<AuthenticateResult> HandleAuthenticateAsync()
{
if (!Request.Headers.ContainsKey("Authorization"))
return AuthenticateResult.NoResult(); string authHeader = Request.Headers["Authorization"];
if (!authHeader.StartsWith("Basic ", StringComparison.OrdinalIgnoreCase))
return AuthenticateResult.NoResult(); string token = authHeader.Substring("Basic ".Length).Trim();
string credentialString = Encoding.UTF8.GetString(Convert.FromBase64String(token));
string[] credentials = credentialString.Split(':'); if (credentials.Length != )
return AuthenticateResult.Fail("More than two strings seperated by colons found"); ClaimsPrincipal principal = await Options.SignInAsync(credentials[], credentials[]); if (principal != null)
{
AuthenticationTicket ticket = new AuthenticationTicket(principal, new AuthenticationProperties(), BasicAuthenticationDefaults.AuthenticationScheme);
return AuthenticateResult.Success(ticket);
} return AuthenticateResult.Fail("Wrong credentials supplied");
}
protected override Task HandleForbiddenAsync(AuthenticationProperties properties)
{
Response.StatusCode = ;
return base.HandleForbiddenAsync(properties);
} protected override Task HandleChallengeAsync(AuthenticationProperties properties)
{
Response.StatusCode = ;
string headerValue = $"{BasicAuthenticationDefaults.AuthenticationScheme} realm=\"{Options.Realm}\"";
Response.Headers.Append(Microsoft.Net.Http.Headers.HeaderNames.WWWAuthenticate, headerValue);
return base.HandleChallengeAsync(properties);
}
} public class BasicAuthenticationOptions : AuthenticationSchemeOptions, IOptions<BasicAuthenticationOptions>
{
private string _realm; public IServiceCollection ServiceCollection { get; set; }
public BasicAuthenticationOptions Value => this;
public string Realm
{
get { return _realm; }
set
{
_realm = value;
}
} public async Task<ClaimsPrincipal> SignInAsync(string userName, string password)
{
using (var serviceScope = ServiceCollection.BuildServiceProvider().CreateScope())
{
var _user = serviceScope.ServiceProvider.GetService<IRepository<User>>();
var user = _user.List(r => r.Name == userName && r.Password == password).FirstOrDefault();
if (user == null)
return null;
var identity = new ClaimsIdentity(BasicAuthenticationDefaults.AuthenticationScheme, ClaimTypes.Name, ClaimTypes.Role);
identity.AddClaim(new Claim(ClaimTypes.Name, user.Name));
var principal = new ClaimsPrincipal(identity);
return principal;
}
}
} public static class BasicAuthenticationDefaults
{
public const string AuthenticationScheme = "Basic";
}
public static class BasicAuthenticationExtensions
{
public static AuthenticationBuilder AddBasic(this AuthenticationBuilder builder)
=> builder.AddBasic(BasicAuthenticationDefaults.AuthenticationScheme, _ => { _.ServiceCollection = builder.Services;_.Realm = "GitServer"; }); public static AuthenticationBuilder AddBasic(this AuthenticationBuilder builder, Action<BasicAuthenticationOptions> configureOptions)
=> builder.AddBasic(BasicAuthenticationDefaults.AuthenticationScheme, configureOptions); public static AuthenticationBuilder AddBasic(this AuthenticationBuilder builder, string authenticationScheme, Action<BasicAuthenticationOptions> configureOptions)
=> builder.AddBasic(authenticationScheme, displayName: null, configureOptions: configureOptions); public static AuthenticationBuilder AddBasic(this AuthenticationBuilder builder, string authenticationScheme, string displayName, Action<BasicAuthenticationOptions> configureOptions)
{
builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<IOptions<BasicAuthenticationOptions>, BasicAuthenticationOptions>());
return builder.AddScheme<BasicAuthenticationOptions, BasicAuthenticationHandler>(authenticationScheme, displayName, configureOptions);
}
}
CookieAuthentication Cookie认证
实现自定义登录,无需identity ,实现注册登录。
主要代码:
启用Cookie
https://github.com/linezero/GitServer/blob/master/GitServer/Startup.cs#L60
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;
}).AddCookie(options=> {
options.AccessDeniedPath = "/User/Login";
options.LoginPath = "/User/Login";
})
登录
https://github.com/linezero/GitServer/blob/master/GitServer/Controllers/UserController.cs#L34
var identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme, ClaimTypes.Name, ClaimTypes.Role);
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, user.Name));
identity.AddClaim(new Claim(ClaimTypes.Name, user.Name));
identity.AddClaim(new Claim(ClaimTypes.Email, user.Email));
var principal = new ClaimsPrincipal(identity);
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, principal);
官方文档介绍:https://docs.microsoft.com/zh-cn/aspnet/core/security/authentication/cookie?tabs=aspnetcore2x
部署说明
发布后配置数据库及git目录(可以为绝对地址和命令)、git 仓库目录。
{
  "ConnectionStrings": {
    "ConnectionType": "Sqlite", //Sqlite,MSSQL,MySQL
    "DefaultConnection": "Filename=gitserver.db"
  },
  "GitSettings": {
    "BasePath": "D:\\Git",
    "GitPath": "git"
  }
}
运行后注册账户,登录账户创建仓库,然后根据提示操作,随后git push、git pull 都可以。
ASP.NET Core 开源GitServer 实现自己的GitHub的更多相关文章
- ASP.NET Core 开源项目整理
		
前言: 对 .NET Core 的热情一直没有下降过,新起的项目几乎都是采用 Core 来做开发. 跨平台是一个方面,另外就是 Core 很轻,性能远超很多开发语言(不坑). 一.ASP.NET Co ...
 - ASP.NET Core DotNetCore 开源GitServer 实现自己的GitHub
		
ASP.NET Core 2.0 开源Git HTTP Server,实现类似 GitHub.GitLab. GitHub:https://github.com/linezero/GitServer ...
 - ASP.NET Core 开源论坛项目 NETCoreBBS
		
ASP.NET Core 轻量化开源论坛项目,ASP.NET Core Light forum NETCoreBBS 采用 ASP.NET Core + EF Core Sqlite + Bootst ...
 - ASP.NET Core开源地址
		
https://github.com/dotnet/corefx 这个是.net core的 开源项目地址 https://github.com/aspnet 这个下面是asp.net core 框架 ...
 - asp.net core开源项目
		
Orchard框架:https://www.xcode.me/code/asp-net-core-cms-orchard https://orchardproject.net/ https://git ...
 - 【分享】Asp.net Core相关教程及开源项目
		
入门 全新的ASP.NET: https://www.cnblogs.com/Leo_wl/p/5654828.html 在IIS上部署你的ASP.NET Core项目: https://www.c ...
 - Asp.net Core相关教程及开源项目推荐
		
入门 全新的ASP.NET: https://www.cnblogs.com/Leo_wl/p/5654828.html 在IIS上部署你的ASP.NET Core项目: https://www.c ...
 - ASP.NET Core 介绍
		
原文:Introduction to ASP.NET Core 作者:Daniel Roth.Rick Anderson.Shaun Luttin 翻译:江振宇(Kerry Jiang) 校对:许登洋 ...
 - ASP.NET Core开发-后台任务利器Hangfire使用
		
ASP.NET Core开发系列之后台任务利器Hangfire 使用. Hangfire 是一款强大的.NET开源后台任务利器,无需Windows服务/任务计划程序. 可以使用于ASP.NET 应用也 ...
 
随机推荐
- 关于学习Python的一些心得
			
1.关注函数参数的类型,如列表,字符串,int,而不是关注函数的功能 2.导入模块numpy,dir(numpy) 查看所有属性 3.''.join(列表) 将列表拆成字符串
 - lintcode.177 把排序数组转换为高度最小的二叉搜索树
			
把排序数组转换为高度最小的二叉搜索树 描述 笔记 数据 评测 给一个排序数组(从小到大),将其转换为一棵高度最小的排序二叉树. 注意事项 There may exist multiple val ...
 - Oracle--新建用户以及赋予的权限
			
1, 以dba方式登录Oracle 2, 创建用户,Users-->New ... 输入用户名和密码 3, 赋予connect/resource角色 4, 赋予该用户 对视图操作的相关权限 以下 ...
 - Django中的信号及其用法
			
Django中提供了"信号调度",用于在框架执行操作时解耦. 一些动作发生的时候,系统会根据信号定义的函数执行相应的操作 Django中内置的signal Model_signal ...
 - Maven简介(一)
			
在现实的企业中,以低成本.高效率.高质量的完成项目,不仅仅需要技术大牛,企业更加需要管理大牛,管理者只懂技术是远远不够的.当然,管理可以说有很多的方面,例如:对人员的管理,也有对项目的管理等等.如果你 ...
 - 0 can't find referenced pointcut declarePointExpress
			
今天在用SpringAOP 的 @pointCut 的时候报错 Exception in thread "main" org.springframework.beans.facto ...
 - 开源API集成测试工具 Hitchhiker v0.1.3  -  参数化请求
			
Hitchhiker 是一款开源的 Restful Api 集成测试工具,你可以轻松部署到本地,和你的team成员一起管理Api. 详细介绍请看: http://www.cnblogs.com/bro ...
 - Vue.js 服务端渲染业务入门实践
			
作者:威威(沪江前端开发工程师) 本文原创,转载请注明作者及出处. 背景 最近, 产品同学一如往常笑嘻嘻的递来需求文档, 纵使内心万般拒绝, 身体倒是很诚实. 接过需求,好在需求不复杂, 简单构思 后 ...
 - java中堆栈的功能作用 以及區別(搜集)
			
1.用new创建的对象在堆区,函数中的临时变量在栈区,Java中的字符串在字符串常量区. 2.栈:存放进本数据类型的数据和对象的引用,但对象本身不存在栈中,而是存放在堆中. 堆:存放new产生 ...
 - Linux crontab任务调度
			
一.crontab说明 Linux crontab任务调度是在规定的时间频率内去执行相应的任务. 二.crontab文件详情 1.crontab文件在Linux中的/etc/crontab 2.查看c ...