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 应用也 ...
随机推荐
- webservice06#异常#Handler
1, 异常处理 package com.yangw.soap.service; public class UserException extends Exception { public UserEx ...
- 【编程之外】还记得曾经给'大学导师'写过的报告嘛 --> 前方高能
写在前面 本文不是讲技术的,也没什么代码可看 本文不是讲技术的,也没什么代码可看 本文不是讲技术的,也没什么代码可看 还记得我们曾经给我们大学''导师''写过的报告嘛? 大学他愿意在凌晨6点向你询问近 ...
- WINDOWS XP中用命令行管理用户 net user命令
net user <username> [password or *] [/add] [options] [/domain] net user <username] /delete ...
- HashMap的源码分析(一)
1.hashMap的关键值 DEFAULT_INITIAL_CAPACITY:默认初始容量16,∈(0,1<<30),实际大小为2的整数次幂: DEFAULT_LOAD_FACTOR:默认 ...
- C#最基本的小说爬虫
新手学习C#,自己折腾弄了个简单的小说爬虫,实现了把小说内容爬下来写入txt,还只能爬指定网站. 第一次搞爬虫,涉及到了网络协议,正则表达式,弄得手忙脚乱跑起来效率还差劲,慢慢改吧. 爬的目标:htt ...
- Clojure——学习迷宫生成
背景 初学clojure,想着看一些算法来熟悉clojure语法及相关算法实现. 找到一个各种语言生成迷宫的网站:http://rosettacode.org/wiki/Maze_generation ...
- 【NOIP】OpenJudge - 15-03:雇佣兵
#include<stdio.h>//雇佣兵 int main() { ; scanf("%d%d%d",&M,&N,&X); n=N; m=M ...
- Gate One——用web展示Terminal(安装)
Gate One可以用web来展示Terminal,虽然存在一些小缺陷,基本功能都还可以的,有兴趣的可以折腾一下. 安装环境: 系统:RHEL 6.1 ,系统自带python 2.6.6 下载需要安装 ...
- Linux 独立安装subversion-1.8.18
一.所需软件包 1.apr-1.4.6.tar.gz 下载地址:http://apr.apache.org/ 2.apr-util-1.4.1.tar.gz 下载地址:http://apr.apa ...
- zoj2818 Root of the Problem 简单数学 开方
Root of the Problem Time Limit: 2 Seconds Memory Limit: 65536 KB Given positive integers B and ...