ASP.NET Core DotNetCore 開源GitServer 實現本身的GitHub

ASP.NET Core 2.0 開源Git HTTP Server,實現相似 GitHub、GitLab。javascript

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

設置java

"GitSettings": { "BasePath": "D:\\Git", "GitPath": "git" } 

須要先安裝Git,並確保git 命令能夠執行,GitPath 能夠是 git 的絕對路徑。git

目前實現的功能github

  • 建立倉庫
  • 瀏覽倉庫
  • git客戶端push pull
  • 數據庫支持 SQLite、MSSQL、MySQL
  • 支持用戶管理倉庫

更多功能能夠查看readme,也歡迎你們貢獻支持。數據庫

Git交互

LibGit2Sharp 用於操做Git庫,實現建立讀取倉庫信息及刪除倉庫。微信

如下是主要代碼:cookie

複製代碼
        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 = 0; i < 3; 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 app

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

複製代碼
 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 != 2)
20                 return AuthenticateResult.Fail("More than two strings seperated by colons found");
21 
22             ClaimsPrincipal principal = await Options.SignInAsync(credentials[0], credentials[1]);
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 = 403;
35             return base.HandleForbiddenAsync(properties);
36         }
37 
38         protected override Task HandleChallengeAsync(AuthenticationProperties properties)
39         {
40             Response.StatusCode = 401;
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

複製代碼
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
好文要頂  關注我  收藏該文   
9
0
 
 
 
« 上一篇:  .NET及.NET Core系統架構
» 下一篇:  Orchard Core一分鐘搭建ASP.NET Core CMS
相關文章
相關標籤/搜索