ASP.NET Core 2.0 开源Git HTTP Server,实现类似 GitHub、GitLab。

GitHub:https://github.com/linezero/GitServer

设置

  1. "GitSettings": {
  2. "BasePath": "D:\\Git",
  3. "GitPath": "git"
  4. }

需要先安装Git,并确保git 命令可以执行,GitPath 可以是 git 的绝对路径。

目前实现的功能

  • 创建仓库
  • 浏览仓库
  • git客户端push pull
  • 数据库支持 SQLite、MSSQL、MySQL
  • 支持用户管理仓库

更多功能可以查看readme,也欢迎大家贡献支持。

Git交互

LibGit2Sharp 用于操作Git库,实现创建读取仓库信息及删除仓库。

以下是主要代码:

  1. public Repository CreateRepository(string name)
  2. {
  3. string path = Path.Combine(Settings.BasePath, name);
  4. Repository repo = new Repository(Repository.Init(path, true));
  5. return repo;
  6. }
  7.  
  8. public Repository CreateRepository(string name, string remoteUrl)
  9. {
  10. var path = Path.Combine(Settings.BasePath, name);
  11. try
  12. {
  13. using (var repo = new Repository(Repository.Init(path, true)))
  14. {
  15. repo.Config.Set("core.logallrefupdates", true);
  16. repo.Network.Remotes.Add("origin", remoteUrl, "+refs/*:refs/*");
  17. var logMessage = "";
  18. foreach (var remote in repo.Network.Remotes)
  19. {
  20. IEnumerable<string> refSpecs = remote.FetchRefSpecs.Select(x => x.Specification);
  21. Commands.Fetch(repo, remote.Name, refSpecs, null, logMessage);
  22. }
  23. return repo;
  24. }
  25. }
  26. catch
  27. {
  28. try
  29. {
  30. Directory.Delete(path, true);
  31. }
  32. catch { }
  33. return null;
  34. }
  35. }
  36.  
  37. public void DeleteRepository(string name)
  38. {
  39. Exception e = null;
  40. for(int i = ; i < ; i++)
  41. {
  42. try
  43. {
  44. string path = Path.Combine(Settings.BasePath, name);
  45. Directory.Delete(path, true);
  46.  
  47. }
  48. catch(Exception ex) { e = ex; }
  49. }
  50.  
  51. if (e != null)
  52. throw new GitException("Failed to delete repository", e);
  53. }

执行Git命令

git-upload-pack

git-receive-pack

主要代码 GitCommandResult 实现IActionResult

  1. public async Task ExecuteResultAsync(ActionContext context)
  2. {
  3. HttpResponse response = context.HttpContext.Response;
  4. Stream responseStream = GetOutputStream(context.HttpContext);
  5.  
  6. string contentType = $"application/x-{Options.Service}";
  7. if (Options.AdvertiseRefs)
  8. contentType += "-advertisement";
  9.  
  10. response.ContentType = contentType;
  11.  
  12. response.Headers.Add("Expires", "Fri, 01 Jan 1980 00:00:00 GMT");
  13. response.Headers.Add("Pragma", "no-cache");
  14. response.Headers.Add("Cache-Control", "no-cache, max-age=0, must-revalidate");
  15.  
  16. ProcessStartInfo info = new ProcessStartInfo(_gitPath, Options.ToString())
  17. {
  18. UseShellExecute = false,
  19. CreateNoWindow = true,
  20. RedirectStandardInput = true,
  21. RedirectStandardOutput = true,
  22. RedirectStandardError = true
  23. };
  24.  
  25. using (Process process = Process.Start(info))
  26. {
  27. GetInputStream(context.HttpContext).CopyTo(process.StandardInput.BaseStream);
  28.  
  29. if (Options.EndStreamWithNull)
  30. process.StandardInput.Write('\0');
  31. process.StandardInput.Dispose();
  32.  
  33. using (StreamWriter writer = new StreamWriter(responseStream))
  34. {
  35. if (Options.AdvertiseRefs)
  36. {
  37. string service = $"# service={Options.Service}\n";
  38. writer.Write($"{service.Length + 4:x4}{service}0000");
  39. writer.Flush();
  40. }
  41.  
  42. process.StandardOutput.BaseStream.CopyTo(responseStream);
  43. }
  44.  
  45. process.WaitForExit();
  46. }
  47. }

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

  1. public class BasicAuthenticationHandler : AuthenticationHandler<BasicAuthenticationOptions>
  2. {
  3. public BasicAuthenticationHandler(IOptionsMonitor<BasicAuthenticationOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock)
  4. : base(options, logger, encoder, clock)
  5. { }
  6. protected async override Task<AuthenticateResult> HandleAuthenticateAsync()
  7. {
  8. if (!Request.Headers.ContainsKey("Authorization"))
  9. return AuthenticateResult.NoResult();
  10.  
  11. string authHeader = Request.Headers["Authorization"];
  12. if (!authHeader.StartsWith("Basic ", StringComparison.OrdinalIgnoreCase))
  13. return AuthenticateResult.NoResult();
  14.  
  15. string token = authHeader.Substring("Basic ".Length).Trim();
  16. string credentialString = Encoding.UTF8.GetString(Convert.FromBase64String(token));
  17. string[] credentials = credentialString.Split(':');
  18.  
  19. if (credentials.Length != )
  20. return AuthenticateResult.Fail("More than two strings seperated by colons found");
  21.  
  22. ClaimsPrincipal principal = await Options.SignInAsync(credentials[], credentials[]);
  23.  
  24. if (principal != null)
  25. {
  26. AuthenticationTicket ticket = new AuthenticationTicket(principal, new AuthenticationProperties(), BasicAuthenticationDefaults.AuthenticationScheme);
  27. return AuthenticateResult.Success(ticket);
  28. }
  29.  
  30. return AuthenticateResult.Fail("Wrong credentials supplied");
  31. }
  32. protected override Task HandleForbiddenAsync(AuthenticationProperties properties)
  33. {
  34. Response.StatusCode = ;
  35. return base.HandleForbiddenAsync(properties);
  36. }
  37.  
  38. protected override Task HandleChallengeAsync(AuthenticationProperties properties)
  39. {
  40. Response.StatusCode = ;
  41. string headerValue = $"{BasicAuthenticationDefaults.AuthenticationScheme} realm=\"{Options.Realm}\"";
  42. Response.Headers.Append(Microsoft.Net.Http.Headers.HeaderNames.WWWAuthenticate, headerValue);
  43. return base.HandleChallengeAsync(properties);
  44. }
  45. }
  46.  
  47. public class BasicAuthenticationOptions : AuthenticationSchemeOptions, IOptions<BasicAuthenticationOptions>
  48. {
  49. private string _realm;
  50.  
  51. public IServiceCollection ServiceCollection { get; set; }
  52. public BasicAuthenticationOptions Value => this;
  53. public string Realm
  54. {
  55. get { return _realm; }
  56. set
  57. {
  58. _realm = value;
  59. }
  60. }
  61.  
  62. public async Task<ClaimsPrincipal> SignInAsync(string userName, string password)
  63. {
  64. using (var serviceScope = ServiceCollection.BuildServiceProvider().CreateScope())
  65. {
  66. var _user = serviceScope.ServiceProvider.GetService<IRepository<User>>();
  67. var user = _user.List(r => r.Name == userName && r.Password == password).FirstOrDefault();
  68. if (user == null)
  69. return null;
  70. var identity = new ClaimsIdentity(BasicAuthenticationDefaults.AuthenticationScheme, ClaimTypes.Name, ClaimTypes.Role);
  71. identity.AddClaim(new Claim(ClaimTypes.Name, user.Name));
  72. var principal = new ClaimsPrincipal(identity);
  73. return principal;
  74. }
  75. }
  76. }
  77.  
  78. public static class BasicAuthenticationDefaults
  79. {
  80. public const string AuthenticationScheme = "Basic";
  81. }
  82. public static class BasicAuthenticationExtensions
  83. {
  84. public static AuthenticationBuilder AddBasic(this AuthenticationBuilder builder)
  85. => builder.AddBasic(BasicAuthenticationDefaults.AuthenticationScheme, _ => { _.ServiceCollection = builder.Services;_.Realm = "GitServer"; });
  86.  
  87. public static AuthenticationBuilder AddBasic(this AuthenticationBuilder builder, Action<BasicAuthenticationOptions> configureOptions)
  88. => builder.AddBasic(BasicAuthenticationDefaults.AuthenticationScheme, configureOptions);
  89.  
  90. public static AuthenticationBuilder AddBasic(this AuthenticationBuilder builder, string authenticationScheme, Action<BasicAuthenticationOptions> configureOptions)
  91. => builder.AddBasic(authenticationScheme, displayName: null, configureOptions: configureOptions);
  92.  
  93. public static AuthenticationBuilder AddBasic(this AuthenticationBuilder builder, string authenticationScheme, string displayName, Action<BasicAuthenticationOptions> configureOptions)
  94. {
  95. builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<IOptions<BasicAuthenticationOptions>, BasicAuthenticationOptions>());
  96. return builder.AddScheme<BasicAuthenticationOptions, BasicAuthenticationHandler>(authenticationScheme, displayName, configureOptions);
  97. }
  98. }

CookieAuthentication Cookie认证

实现自定义登录,无需identity ,实现注册登录。

主要代码:

启用Cookie

https://github.com/linezero/GitServer/blob/master/GitServer/Startup.cs#L60

  1. services.AddAuthentication(options =>
  2. {
  3. options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
  4. options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;
  5. }).AddCookie(options=> {
  6. options.AccessDeniedPath = "/User/Login";
  7. options.LoginPath = "/User/Login";
  8. })

登录

https://github.com/linezero/GitServer/blob/master/GitServer/Controllers/UserController.cs#L34

  1. var identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme, ClaimTypes.Name, ClaimTypes.Role);
  2. identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, user.Name));
  3. identity.AddClaim(new Claim(ClaimTypes.Name, user.Name));
  4. identity.AddClaim(new Claim(ClaimTypes.Email, user.Email));
  5. var principal = new ClaimsPrincipal(identity);
  6. await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, principal);

官方文档介绍:https://docs.microsoft.com/zh-cn/aspnet/core/security/authentication/cookie?tabs=aspnetcore2x

部署说明

发布后配置数据库及git目录(可以为绝对地址和命令)、git 仓库目录。

  1. {
  2. "ConnectionStrings": {
  3. "ConnectionType": "Sqlite", //Sqlite,MSSQL,MySQL
  4. "DefaultConnection": "Filename=gitserver.db"
  5. },
  6. "GitSettings": {
  7. "BasePath": "D:\\Git",
  8. "GitPath": "git"
  9. }
  10. }

运行后注册账户,登录账户创建仓库,然后根据提示操作,随后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. expect实现scp/ssh-copy-id非交互

    expect工具可以实现自动应答,从而达到非交互的目的. expect具体使用用法比较复杂,中文手册我正在翻译中,以后翻译完了做了整理再补.本文只有几个ssh相关最可能用上的示例. yum -y in ...

  2. 纳税服务系统【异常处理、抽取BaseAction】

    前言 本博文主要讲解在项目中异常是怎么处理的.一般我们都不会直接把后台异常信息返回给用户,用户是看不懂的.让用户看见一大串的错误代码,这是不合理的.因此我们需要对报错进行处理. 我们在开发的时候是使用 ...

  3. 二叉树终极教程--BinarySearchTree

    BinarySearchTreeMap 的 实现 public interface Map<K extends Comparable<K>, V> { void put(K k ...

  4. 循环语句for,while,until,select

    循环 *循环执行 将某代码段重复运行多次 重复运行多少次: 循环次数事先已知 循环次数事先未知 有进入条件和退出条件 *常见的循环语句有for,while,until for循环 for 变量名 n ...

  5. struts1.3整合spring2.5(将spring委托给struts方式)

    前提是配置完struts1.3 导包 spring-2.5.6.jar //spring核心包 spring-webmvc-struts-2.5.5.jar //struts整合spring使用 lo ...

  6. Java多线程Runnable与Callable区别与拓展

    我们先来分别看一下这两个接口 Runnable: // // Source code recreated from a .class file by IntelliJ IDEA // (powered ...

  7. JAVA多线程---volatile关键字

    加锁机制既可以确保可见性又可以保证原子性,而volatile变量只能确保可见性. 当把变量声明为volatile时候 编译器与运行时都会注意到这个变量是共享的,不会将该变量上的操作与其他内存操作一起重 ...

  8. iOS 多人共享开发证书

    当多人开发时,如果已经申请了几个开发者证书和发布者证书,苹果就不允许再创建了,页面添加的地方被灰化了,所以不可能每个人都建一个开发证书,这时候需要共用一个证书了.(其实一般在我们的证书界面中应该只有一 ...

  9. TCP/IP(四)网络层

    前言 前面给大家介绍了计算机网络的基本概述,物理层和数据链路层.这一篇给大家介绍面试中经常会被问到的网络层.在介绍之前我们回顾一下之前学习的知识! CP/IP协议栈:物理层.链路层.网络层.传输层.应 ...

  10. Angular JS的正确打开姿势——简单实用(下)

        前  言 絮叨絮叨 继上篇内容,本篇继续讲一下这款优秀并且实用的前端插件AngularJS. 六. AngularJS中的HTTP 6.1先看看JQuery的Ajax写法 $ajax({ me ...