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的更多相关文章

  1. ASP.NET Core 开源项目整理

    前言: 对 .NET Core 的热情一直没有下降过,新起的项目几乎都是采用 Core 来做开发. 跨平台是一个方面,另外就是 Core 很轻,性能远超很多开发语言(不坑). 一.ASP.NET Co ...

  2. ASP.NET Core DotNetCore 开源GitServer 实现自己的GitHub

    ASP.NET Core 2.0 开源Git HTTP Server,实现类似 GitHub.GitLab. GitHub:https://github.com/linezero/GitServer ...

  3. ASP.NET Core 开源论坛项目 NETCoreBBS

    ASP.NET Core 轻量化开源论坛项目,ASP.NET Core Light forum NETCoreBBS 采用 ASP.NET Core + EF Core Sqlite + Bootst ...

  4. ASP.NET Core开源地址

    https://github.com/dotnet/corefx 这个是.net core的 开源项目地址 https://github.com/aspnet 这个下面是asp.net core 框架 ...

  5. asp.net core开源项目

    Orchard框架:https://www.xcode.me/code/asp-net-core-cms-orchard https://orchardproject.net/ https://git ...

  6. 【分享】Asp.net Core相关教程及开源项目

    入门 全新的ASP.NET:  https://www.cnblogs.com/Leo_wl/p/5654828.html 在IIS上部署你的ASP.NET Core项目: https://www.c ...

  7. Asp.net Core相关教程及开源项目推荐

    入门 全新的ASP.NET:  https://www.cnblogs.com/Leo_wl/p/5654828.html 在IIS上部署你的ASP.NET Core项目: https://www.c ...

  8. ASP.NET Core 介绍

    原文:Introduction to ASP.NET Core 作者:Daniel Roth.Rick Anderson.Shaun Luttin 翻译:江振宇(Kerry Jiang) 校对:许登洋 ...

  9. ASP.NET Core开发-后台任务利器Hangfire使用

    ASP.NET Core开发系列之后台任务利器Hangfire 使用. Hangfire 是一款强大的.NET开源后台任务利器,无需Windows服务/任务计划程序. 可以使用于ASP.NET 应用也 ...

随机推荐

  1. 关于学习Python的一些心得

    1.关注函数参数的类型,如列表,字符串,int,而不是关注函数的功能 2.导入模块numpy,dir(numpy) 查看所有属性 3.''.join(列表)  将列表拆成字符串

  2. lintcode.177 把排序数组转换为高度最小的二叉搜索树

    把排序数组转换为高度最小的二叉搜索树    描述 笔记 数据 评测 给一个排序数组(从小到大),将其转换为一棵高度最小的排序二叉树. 注意事项 There may exist multiple val ...

  3. Oracle--新建用户以及赋予的权限

    1, 以dba方式登录Oracle 2, 创建用户,Users-->New ... 输入用户名和密码 3, 赋予connect/resource角色 4, 赋予该用户 对视图操作的相关权限 以下 ...

  4. Django中的信号及其用法

    Django中提供了"信号调度",用于在框架执行操作时解耦. 一些动作发生的时候,系统会根据信号定义的函数执行相应的操作 Django中内置的signal Model_signal ...

  5. Maven简介(一)

    在现实的企业中,以低成本.高效率.高质量的完成项目,不仅仅需要技术大牛,企业更加需要管理大牛,管理者只懂技术是远远不够的.当然,管理可以说有很多的方面,例如:对人员的管理,也有对项目的管理等等.如果你 ...

  6. 0 can't find referenced pointcut declarePointExpress

    今天在用SpringAOP 的 @pointCut 的时候报错 Exception in thread "main" org.springframework.beans.facto ...

  7. 开源API集成测试工具 Hitchhiker v0.1.3 - 参数化请求

    Hitchhiker 是一款开源的 Restful Api 集成测试工具,你可以轻松部署到本地,和你的team成员一起管理Api. 详细介绍请看: http://www.cnblogs.com/bro ...

  8. Vue.js 服务端渲染业务入门实践

    作者:威威(沪江前端开发工程师) 本文原创,转载请注明作者及出处. 背景 最近, 产品同学一如往常笑嘻嘻的递来需求文档, 纵使内心万般拒绝, 身体倒是很诚实. 接过需求,好在需求不复杂, 简单构思 后 ...

  9. java中堆栈的功能作用 以及區別(搜集)

    1.用new创建的对象在堆区,函数中的临时变量在栈区,Java中的字符串在字符串常量区. 2.栈:存放进本数据类型的数据和对象的引用,但对象本身不存在栈中,而是存放在堆中.     堆:存放new产生 ...

  10. Linux crontab任务调度

    一.crontab说明 Linux crontab任务调度是在规定的时间频率内去执行相应的任务. 二.crontab文件详情 1.crontab文件在Linux中的/etc/crontab 2.查看c ...