├── .gitignore ├── Czar.Gateway.sln ├── Czar.Rpc.Client ├── Czar.Rpc.Client.csproj ├── CzarConfig.json └── Program.cs ├── Czar.Rpc.Server ├── Czar.Rpc.Server.csproj ├── CzarConfig.json ├── HelloRpcServer.cs └── Program.cs ├── LICENSE ├── README.md ├── doc ├── czar.gateway.20190109.sqlserver2008r2.bak └── czar.gateway.sqlserver2008r2.bak ├── sample ├── Czar.Rpc.Common │ ├── Czar.Rpc.Common.csproj │ ├── DemoModel.cs │ └── IHelloRpc.cs ├── Czar.Sample.SqlServer │ ├── Czar.Sample.SqlServer.csproj │ ├── CzarConfig.json │ ├── Program.cs │ ├── Startup.cs │ ├── appsettings.Development.json │ └── appsettings.json ├── Czar.Sample.TestApi │ ├── Controllers │ │ └── ValuesController.cs │ ├── Czar.Sample.TestApi.csproj │ ├── Program.cs │ ├── Startup.cs │ ├── appsettings.Development.json │ └── appsettings.json └── Czar.Sample.TestIds4 │ ├── Czar.Sample.TestIds4.csproj │ ├── Program.cs │ ├── Startup.cs │ ├── appsettings.Development.json │ ├── appsettings.json │ ├── tempkey.rsa │ └── wwwroot │ ├── css │ ├── site.css │ └── site.min.css │ ├── favicon.ico │ ├── images │ ├── banner1.svg │ ├── banner2.svg │ └── banner3.svg │ ├── js │ ├── site.js │ └── site.min.js │ └── lib │ ├── bootstrap │ ├── .bower.json │ ├── LICENSE │ └── dist │ │ ├── css │ │ ├── bootstrap-theme.css │ │ ├── bootstrap-theme.css.map │ │ ├── bootstrap-theme.min.css │ │ ├── bootstrap-theme.min.css.map │ │ ├── bootstrap.css │ │ ├── bootstrap.css.map │ │ ├── bootstrap.min.css │ │ └── bootstrap.min.css.map │ │ ├── fonts │ │ ├── glyphicons-halflings-regular.eot │ │ ├── glyphicons-halflings-regular.svg │ │ ├── glyphicons-halflings-regular.ttf │ │ ├── glyphicons-halflings-regular.woff │ │ └── glyphicons-halflings-regular.woff2 │ │ └── js │ │ ├── bootstrap.js │ │ ├── bootstrap.min.js │ │ └── npm.js │ ├── jquery-validation-unobtrusive │ ├── .bower.json │ ├── LICENSE.txt │ ├── jquery.validate.unobtrusive.js │ └── jquery.validate.unobtrusive.min.js │ ├── jquery-validation │ ├── .bower.json │ ├── LICENSE.md │ └── dist │ │ ├── additional-methods.js │ │ ├── additional-methods.min.js │ │ ├── jquery.validate.js │ │ └── jquery.validate.min.js │ └── jquery │ ├── .bower.json │ ├── LICENSE.txt │ └── dist │ ├── jquery.js │ ├── jquery.min.js │ └── jquery.min.map └── src └── Czar.Gateway ├── Authentication ├── CzarAuthenticationProcessor.cs ├── IClientAuthenticationRepository.cs ├── ICzarAuthenticationProcessor.cs └── Middleware │ ├── CzarAuthenticationMiddleware.cs │ └── CzarAuthenticationMiddlewareExtensions.cs ├── Cache ├── CzarCacheController.cs ├── CzarMemoryCache.cs └── RedisInternalConfigurationRepository.cs ├── Configuration ├── CzarCacheRegion.cs ├── CzarOcelotConfiguration.cs ├── DbConfigurationPoller.cs ├── ErrorResult.cs ├── Model │ ├── BaseResult.cs │ └── ClientRoleModel.cs └── RedisStoreMode.cs ├── Czar.Gateway.csproj ├── Errors ├── IdentityServer4Error.cs ├── InternalServerError.cs └── RateLimitOptionsError.cs ├── Extensions └── Ext.cs ├── Helper └── CzarOcelotHelper.cs ├── Middleware ├── CzarOcelotMiddlewareExtensions.cs ├── OcelotPipelineExtensions.cs └── ServiceCollectionExtensions.cs ├── Model ├── CzarAuthGroup.cs ├── CzarClientGroup.cs ├── CzarClientLimitGroup.cs ├── CzarClientReRouteWhiteList.cs ├── CzarClients.cs ├── CzarConfigReRoutes.cs ├── CzarGlobalConfiguration.cs ├── CzarLimitGroup.cs ├── CzarLimitGroupRule.cs ├── CzarLimitRule.cs ├── CzarReRoute.cs ├── CzarReRouteGroupAuth.cs ├── CzarReRouteLimitRule.cs ├── CzarReRouteRpcConfig.cs └── CzarReRoutesItem.cs ├── RateLimit ├── CzarClientRateLimitCounter.cs ├── CzarClientRateLimitOptions.cs ├── CzarClientRateLimitProcessor.cs ├── IClientRateLimitProcessor.cs ├── IClientRateLimitRepository.cs ├── Middleware │ ├── CzarClientRateLimitMiddleware.cs │ └── CzarClientRateLimitMiddlewareExtensions.cs └── RateLimitRuleModel.cs ├── Requester └── Middleware │ ├── CzarHttpRequesterMiddleware.cs │ └── CzarHttpRequesterMiddlewareExtensions.cs ├── Responder ├── CzarErrorsToHttpStatusCodeMapper.cs ├── CzarHttpContextResponder.cs └── Middleware │ ├── CzarResponderMiddleware.cs │ └── CzarResponderMiddlewareExtensions.cs ├── Rpc ├── CzarRpcProcessor.cs ├── ICzarRpcProcessor.cs ├── IRpcRepository.cs ├── Middleware │ ├── CzarRpcMiddleware.cs │ └── CzarRpcMiddlewareExtensions.cs └── RpcHttpContent.cs └── Stores ├── MySql ├── MySqlClientRateLimitRepository.cs ├── MySqlFileConfigurationRepository.cs ├── MySqlRpcRepository.cs └── MysqlClientAuthenticationRepository.cs └── SqlServer ├── SqlServerClientAuthenticationRepository.cs ├── SqlServerClientRateLimitRepository.cs ├── SqlServerFileConfigurationRepository.cs └── SqlServerRpcRepository.cs /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015/2017 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # Visual Studio 2017 auto generated files 33 | Generated\ Files/ 34 | 35 | # MSTest test Results 36 | [Tt]est[Rr]esult*/ 37 | [Bb]uild[Ll]og.* 38 | 39 | # NUNIT 40 | *.VisualState.xml 41 | TestResult.xml 42 | 43 | # Build Results of an ATL Project 44 | [Dd]ebugPS/ 45 | [Rr]eleasePS/ 46 | dlldata.c 47 | 48 | # Benchmark Results 49 | BenchmarkDotNet.Artifacts/ 50 | 51 | # .NET Core 52 | project.lock.json 53 | project.fragment.lock.json 54 | artifacts/ 55 | **/Properties/launchSettings.json 56 | 57 | # StyleCop 58 | StyleCopReport.xml 59 | 60 | # Files built by Visual Studio 61 | *_i.c 62 | *_p.c 63 | *_i.h 64 | *.ilk 65 | *.meta 66 | *.obj 67 | *.iobj 68 | *.pch 69 | *.pdb 70 | *.ipdb 71 | *.pgc 72 | *.pgd 73 | *.rsp 74 | *.sbr 75 | *.tlb 76 | *.tli 77 | *.tlh 78 | *.tmp 79 | *.tmp_proj 80 | *.log 81 | *.vspscc 82 | *.vssscc 83 | .builds 84 | *.pidb 85 | *.svclog 86 | *.scc 87 | 88 | # Chutzpah Test files 89 | _Chutzpah* 90 | 91 | # Visual C++ cache files 92 | ipch/ 93 | *.aps 94 | *.ncb 95 | *.opendb 96 | *.opensdf 97 | *.sdf 98 | *.cachefile 99 | *.VC.db 100 | *.VC.VC.opendb 101 | 102 | # Visual Studio profiler 103 | *.psess 104 | *.vsp 105 | *.vspx 106 | *.sap 107 | 108 | # Visual Studio Trace Files 109 | *.e2e 110 | 111 | # TFS 2012 Local Workspace 112 | $tf/ 113 | 114 | # Guidance Automation Toolkit 115 | *.gpState 116 | 117 | # ReSharper is a .NET coding add-in 118 | _ReSharper*/ 119 | *.[Rr]e[Ss]harper 120 | *.DotSettings.user 121 | 122 | # JustCode is a .NET coding add-in 123 | .JustCode 124 | 125 | # TeamCity is a build add-in 126 | _TeamCity* 127 | 128 | # DotCover is a Code Coverage Tool 129 | *.dotCover 130 | 131 | # AxoCover is a Code Coverage Tool 132 | .axoCover/* 133 | !.axoCover/settings.json 134 | 135 | # Visual Studio code coverage results 136 | *.coverage 137 | *.coveragexml 138 | 139 | # NCrunch 140 | _NCrunch_* 141 | .*crunch*.local.xml 142 | nCrunchTemp_* 143 | 144 | # MightyMoose 145 | *.mm.* 146 | AutoTest.Net/ 147 | 148 | # Web workbench (sass) 149 | .sass-cache/ 150 | 151 | # Installshield output folder 152 | [Ee]xpress/ 153 | 154 | # DocProject is a documentation generator add-in 155 | DocProject/buildhelp/ 156 | DocProject/Help/*.HxT 157 | DocProject/Help/*.HxC 158 | DocProject/Help/*.hhc 159 | DocProject/Help/*.hhk 160 | DocProject/Help/*.hhp 161 | DocProject/Help/Html2 162 | DocProject/Help/html 163 | 164 | # Click-Once directory 165 | publish/ 166 | 167 | # Publish Web Output 168 | *.[Pp]ublish.xml 169 | *.azurePubxml 170 | # Note: Comment the next line if you want to checkin your web deploy settings, 171 | # but database connection strings (with potential passwords) will be unencrypted 172 | *.pubxml 173 | *.publishproj 174 | 175 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 176 | # checkin your Azure Web App publish settings, but sensitive information contained 177 | # in these scripts will be unencrypted 178 | PublishScripts/ 179 | 180 | # NuGet Packages 181 | *.nupkg 182 | # The packages folder can be ignored because of Package Restore 183 | **/[Pp]ackages/* 184 | # except build/, which is used as an MSBuild target. 185 | !**/[Pp]ackages/build/ 186 | # Uncomment if necessary however generally it will be regenerated when needed 187 | #!**/[Pp]ackages/repositories.config 188 | # NuGet v3's project.json files produces more ignorable files 189 | *.nuget.props 190 | *.nuget.targets 191 | 192 | # Microsoft Azure Build Output 193 | csx/ 194 | *.build.csdef 195 | 196 | # Microsoft Azure Emulator 197 | ecf/ 198 | rcf/ 199 | 200 | # Windows Store app package directories and files 201 | AppPackages/ 202 | BundleArtifacts/ 203 | Package.StoreAssociation.xml 204 | _pkginfo.txt 205 | *.appx 206 | 207 | # Visual Studio cache files 208 | # files ending in .cache can be ignored 209 | *.[Cc]ache 210 | # but keep track of directories ending in .cache 211 | !*.[Cc]ache/ 212 | 213 | # Others 214 | ClientBin/ 215 | ~$* 216 | *~ 217 | *.dbmdl 218 | *.dbproj.schemaview 219 | *.jfm 220 | *.pfx 221 | *.publishsettings 222 | orleans.codegen.cs 223 | 224 | # Including strong name files can present a security risk 225 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 226 | #*.snk 227 | 228 | # Since there are multiple workflows, uncomment next line to ignore bower_components 229 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 230 | #bower_components/ 231 | 232 | # RIA/Silverlight projects 233 | Generated_Code/ 234 | 235 | # Backup & report files from converting an old project file 236 | # to a newer Visual Studio version. Backup files are not needed, 237 | # because we have git ;-) 238 | _UpgradeReport_Files/ 239 | Backup*/ 240 | UpgradeLog*.XML 241 | UpgradeLog*.htm 242 | ServiceFabricBackup/ 243 | *.rptproj.bak 244 | 245 | # SQL Server files 246 | *.mdf 247 | *.ldf 248 | *.ndf 249 | 250 | # Business Intelligence projects 251 | *.rdl.data 252 | *.bim.layout 253 | *.bim_*.settings 254 | *.rptproj.rsuser 255 | 256 | # Microsoft Fakes 257 | FakesAssemblies/ 258 | 259 | # GhostDoc plugin setting file 260 | *.GhostDoc.xml 261 | 262 | # Node.js Tools for Visual Studio 263 | .ntvs_analysis.dat 264 | node_modules/ 265 | 266 | # Visual Studio 6 build log 267 | *.plg 268 | 269 | # Visual Studio 6 workspace options file 270 | *.opt 271 | 272 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 273 | *.vbw 274 | 275 | # Visual Studio LightSwitch build output 276 | **/*.HTMLClient/GeneratedArtifacts 277 | **/*.DesktopClient/GeneratedArtifacts 278 | **/*.DesktopClient/ModelManifest.xml 279 | **/*.Server/GeneratedArtifacts 280 | **/*.Server/ModelManifest.xml 281 | _Pvt_Extensions 282 | 283 | # Paket dependency manager 284 | .paket/paket.exe 285 | paket-files/ 286 | 287 | # FAKE - F# Make 288 | .fake/ 289 | 290 | # JetBrains Rider 291 | .idea/ 292 | *.sln.iml 293 | 294 | # CodeRush 295 | .cr/ 296 | 297 | # Python Tools for Visual Studio (PTVS) 298 | __pycache__/ 299 | *.pyc 300 | 301 | # Cake - Uncomment if you are using it 302 | # tools/** 303 | # !tools/packages.config 304 | 305 | # Tabs Studio 306 | *.tss 307 | 308 | # Telerik's JustMock configuration file 309 | *.jmconfig 310 | 311 | # BizTalk build output 312 | *.btp.cs 313 | *.btm.cs 314 | *.odx.cs 315 | *.xsd.cs 316 | 317 | # OpenCover UI analysis results 318 | OpenCover/ 319 | 320 | # Azure Stream Analytics local run output 321 | ASALocalRun/ 322 | 323 | # MSBuild Binary and Structured Log 324 | *.binlog 325 | 326 | # NVidia Nsight GPU debugger configuration file 327 | *.nvuser 328 | 329 | # MFractors (Xamarin productivity tool) working folder 330 | .mfractor/ 331 | -------------------------------------------------------------------------------- /Czar.Gateway.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27703.2026 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{BF264141-E863-40AE-A5A4-9EFE4FDC2C3E}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Czar.Gateway", "src\Czar.Gateway\Czar.Gateway.csproj", "{4C6BA65B-B5BA-42F5-B36B-31AF140DA62B}" 9 | EndProject 10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "sample", "sample", "{4B9D0EA8-0F29-4AC0-9531-76140EC49E38}" 11 | EndProject 12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Czar.Sample.SqlServer", "sample\Czar.Sample.SqlServer\Czar.Sample.SqlServer.csproj", "{FC873F3E-4AE0-430B-B421-21EC1F330DB0}" 13 | EndProject 14 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Czar.Sample.TestIds4", "sample\Czar.Sample.TestIds4\Czar.Sample.TestIds4.csproj", "{4C0ED39A-DB9A-4604-9DA0-944AC3EB02A3}" 15 | EndProject 16 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Czar.Sample.TestApi", "sample\Czar.Sample.TestApi\Czar.Sample.TestApi.csproj", "{45DDBE95-E1BE-4AD3-B2BE-C95EF181F0B8}" 17 | EndProject 18 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "doc", "doc", "{824C8BC2-AA86-4876-94F7-D8DDA3842EF2}" 19 | EndProject 20 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Czar.Rpc.Common", "sample\Czar.Rpc.Common\Czar.Rpc.Common.csproj", "{26A5CECA-B06D-4B9E-B202-08E7152AE966}" 21 | EndProject 22 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Czar.Rpc.Server", "Czar.Rpc.Server\Czar.Rpc.Server.csproj", "{269A2DF7-83EC-451F-AD42-6C7C9D862438}" 23 | EndProject 24 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Czar.Rpc.Client", "Czar.Rpc.Client\Czar.Rpc.Client.csproj", "{ACAFEF06-B0D1-45E3-8CCA-F4F214E5F591}" 25 | EndProject 26 | Global 27 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 28 | Debug|Any CPU = Debug|Any CPU 29 | Release|Any CPU = Release|Any CPU 30 | EndGlobalSection 31 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 32 | {4C6BA65B-B5BA-42F5-B36B-31AF140DA62B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 33 | {4C6BA65B-B5BA-42F5-B36B-31AF140DA62B}.Debug|Any CPU.Build.0 = Debug|Any CPU 34 | {4C6BA65B-B5BA-42F5-B36B-31AF140DA62B}.Release|Any CPU.ActiveCfg = Release|Any CPU 35 | {4C6BA65B-B5BA-42F5-B36B-31AF140DA62B}.Release|Any CPU.Build.0 = Release|Any CPU 36 | {FC873F3E-4AE0-430B-B421-21EC1F330DB0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 37 | {FC873F3E-4AE0-430B-B421-21EC1F330DB0}.Debug|Any CPU.Build.0 = Debug|Any CPU 38 | {FC873F3E-4AE0-430B-B421-21EC1F330DB0}.Release|Any CPU.ActiveCfg = Release|Any CPU 39 | {FC873F3E-4AE0-430B-B421-21EC1F330DB0}.Release|Any CPU.Build.0 = Release|Any CPU 40 | {4C0ED39A-DB9A-4604-9DA0-944AC3EB02A3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 41 | {4C0ED39A-DB9A-4604-9DA0-944AC3EB02A3}.Debug|Any CPU.Build.0 = Debug|Any CPU 42 | {4C0ED39A-DB9A-4604-9DA0-944AC3EB02A3}.Release|Any CPU.ActiveCfg = Release|Any CPU 43 | {4C0ED39A-DB9A-4604-9DA0-944AC3EB02A3}.Release|Any CPU.Build.0 = Release|Any CPU 44 | {45DDBE95-E1BE-4AD3-B2BE-C95EF181F0B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 45 | {45DDBE95-E1BE-4AD3-B2BE-C95EF181F0B8}.Debug|Any CPU.Build.0 = Debug|Any CPU 46 | {45DDBE95-E1BE-4AD3-B2BE-C95EF181F0B8}.Release|Any CPU.ActiveCfg = Release|Any CPU 47 | {45DDBE95-E1BE-4AD3-B2BE-C95EF181F0B8}.Release|Any CPU.Build.0 = Release|Any CPU 48 | {26A5CECA-B06D-4B9E-B202-08E7152AE966}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 49 | {26A5CECA-B06D-4B9E-B202-08E7152AE966}.Debug|Any CPU.Build.0 = Debug|Any CPU 50 | {26A5CECA-B06D-4B9E-B202-08E7152AE966}.Release|Any CPU.ActiveCfg = Release|Any CPU 51 | {26A5CECA-B06D-4B9E-B202-08E7152AE966}.Release|Any CPU.Build.0 = Release|Any CPU 52 | {269A2DF7-83EC-451F-AD42-6C7C9D862438}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 53 | {269A2DF7-83EC-451F-AD42-6C7C9D862438}.Debug|Any CPU.Build.0 = Debug|Any CPU 54 | {269A2DF7-83EC-451F-AD42-6C7C9D862438}.Release|Any CPU.ActiveCfg = Release|Any CPU 55 | {269A2DF7-83EC-451F-AD42-6C7C9D862438}.Release|Any CPU.Build.0 = Release|Any CPU 56 | {ACAFEF06-B0D1-45E3-8CCA-F4F214E5F591}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 57 | {ACAFEF06-B0D1-45E3-8CCA-F4F214E5F591}.Debug|Any CPU.Build.0 = Debug|Any CPU 58 | {ACAFEF06-B0D1-45E3-8CCA-F4F214E5F591}.Release|Any CPU.ActiveCfg = Release|Any CPU 59 | {ACAFEF06-B0D1-45E3-8CCA-F4F214E5F591}.Release|Any CPU.Build.0 = Release|Any CPU 60 | EndGlobalSection 61 | GlobalSection(SolutionProperties) = preSolution 62 | HideSolutionNode = FALSE 63 | EndGlobalSection 64 | GlobalSection(NestedProjects) = preSolution 65 | {4C6BA65B-B5BA-42F5-B36B-31AF140DA62B} = {BF264141-E863-40AE-A5A4-9EFE4FDC2C3E} 66 | {FC873F3E-4AE0-430B-B421-21EC1F330DB0} = {4B9D0EA8-0F29-4AC0-9531-76140EC49E38} 67 | {4C0ED39A-DB9A-4604-9DA0-944AC3EB02A3} = {4B9D0EA8-0F29-4AC0-9531-76140EC49E38} 68 | {45DDBE95-E1BE-4AD3-B2BE-C95EF181F0B8} = {4B9D0EA8-0F29-4AC0-9531-76140EC49E38} 69 | {26A5CECA-B06D-4B9E-B202-08E7152AE966} = {4B9D0EA8-0F29-4AC0-9531-76140EC49E38} 70 | {269A2DF7-83EC-451F-AD42-6C7C9D862438} = {4B9D0EA8-0F29-4AC0-9531-76140EC49E38} 71 | {ACAFEF06-B0D1-45E3-8CCA-F4F214E5F591} = {4B9D0EA8-0F29-4AC0-9531-76140EC49E38} 72 | EndGlobalSection 73 | GlobalSection(ExtensibilityGlobals) = postSolution 74 | SolutionGuid = {04B8C314-25DD-44F9-BDBF-B22B921C26E9} 75 | EndGlobalSection 76 | EndGlobal 77 | -------------------------------------------------------------------------------- /Czar.Rpc.Client/Czar.Rpc.Client.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | netcoreapp2.2 6 | 7 | 8 | 9 | 7.1 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | Always 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /Czar.Rpc.Client/CzarConfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "CzarHost": { 3 | "ProxyEndPoint": true, 4 | "IsSsl": "false", 5 | "PfxPath": "cert/datasync.pfx", 6 | "PfxPassword": "123456", 7 | "ClientConfig": { 8 | "Demo.Rpc.Hello": { 9 | "Host": "127.0.0.1", 10 | "Port": 7711, 11 | "Timeout": 10 12 | } 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /Czar.Rpc.Client/Program.cs: -------------------------------------------------------------------------------- 1 | using AspectCore.Extensions.DependencyInjection; 2 | using Czar.Rpc.Codec; 3 | using Czar.Rpc.Common; 4 | using Czar.Rpc.Diagnostics; 5 | using Czar.Rpc.DotNetty.Extensions; 6 | using Czar.Rpc.Exceptions; 7 | using Czar.Rpc.Extensions; 8 | using Czar.Rpc.Message; 9 | using DotNetty.Common.Internal.Logging; 10 | using Microsoft.Extensions.Configuration; 11 | using Microsoft.Extensions.DependencyInjection; 12 | using Microsoft.Extensions.Logging; 13 | using Microsoft.Extensions.Logging.Console; 14 | using System; 15 | using System.Collections.Generic; 16 | using System.Data.SqlClient; 17 | using System.Diagnostics; 18 | using System.Net; 19 | using System.Threading; 20 | using System.Threading.Tasks; 21 | 22 | namespace Czar.Rpc.Client 23 | { 24 | class Program 25 | { 26 | public static IServiceProvider service; 27 | public static IConfiguration config; 28 | static async Task Main(string[] args) 29 | { 30 | List list_error = new List(); 31 | try 32 | { 33 | var builder = new ConfigurationBuilder(); 34 | config = builder.AddJsonFile("CzarConfig.json").Build(); 35 | 36 | service = new ServiceCollection() 37 | .AddSingleton(config) 38 | .AddLogging(j => j.AddConsole()) 39 | .AddLibuvTcpClient(config) 40 | .AddProxy() 41 | .BuildDynamicProxyServiceProvider(); 42 | 43 | var rpc = service.GetRequiredService(); 44 | rpc.CzarEndPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 7711); 45 | var result = string.Empty; 46 | 47 | string t = "基本调用"; 48 | result = rpc.Hello(18, t); 49 | Console.WriteLine(result); 50 | 51 | result = "无返回结果"; 52 | rpc.HelloHolder(1, out result); 53 | Console.WriteLine(result); 54 | result = await rpc.HelloTask(2, "异步任务"); 55 | Console.WriteLine(result); 56 | result = "单向"; 57 | await rpc.HelloOneway(3, "单向调用"); 58 | Console.WriteLine(result); 59 | result = await rpc.HelloValueTask(4, "ValueTask任务"); 60 | Console.WriteLine(result); 61 | 62 | var modelResult = rpc.HelloModel(5, "返回实体", DateTime.Now); 63 | Console.WriteLine($"{modelResult.T1} {modelResult.T2} {modelResult.T3.ToLongDateString()}"); 64 | 65 | 66 | var modelResult1 = await rpc.HelloModelAsync(6, "返回Task实体", DateTime.Now); 67 | Console.WriteLine($"{modelResult1.T1} {modelResult1.T2} {modelResult1.T3.ToLongDateString()}"); 68 | 69 | var mm = new DemoModel() 70 | { 71 | T1 = 7, 72 | T2 = "传实体返回实体", 73 | T3 = DateTime.Now, 74 | Child = new ChildModel() 75 | { 76 | C1 = "子类1" 77 | } 78 | }; 79 | var model2 = rpc.HelloSendModel(mm); 80 | Console.WriteLine($"{model2.T1} {model2.T2} {model2.T3.ToLongDateString()} {model2.Child.C1}"); 81 | 82 | var list = new List(); 83 | var mm1 = new DemoModel() 84 | { 85 | T1 = 8, 86 | T2 = "传List返回List", 87 | T3 = DateTime.Now, 88 | Child = new ChildModel() 89 | { 90 | C1 = "子类2" 91 | } 92 | }; 93 | var mm3 = new DemoModel() 94 | { 95 | T1 = 9, 96 | T2 = "传List返回List", 97 | T3 = DateTime.Now, 98 | Child = new ChildModel() 99 | { 100 | C1 = "子类3" 101 | } 102 | }; 103 | list.Add(mm1); 104 | list.Add(mm3); 105 | var list3 = rpc.HelloSendModelList(list); 106 | Console.WriteLine($"{list3[0].T1} {list3[0].T2} {list3[0].T3.ToLongDateString()} {list3[0].Child?.C1}"); 107 | 108 | 109 | var mm4 = new DemoModel() 110 | { 111 | T1 = 9, 112 | T2 = "HelloSendModelParm", 113 | T3 = DateTime.Now, 114 | Child = new ChildModel() 115 | { 116 | C1 = "子类4" 117 | } 118 | }; 119 | var dd = rpc.HelloSendModelParm("HelloSendModelParm", mm4); 120 | Console.WriteLine($"{dd.T1} {dd.T2} {dd.T3.ToLongDateString()} {dd.Child.C1}"); 121 | 122 | //异常调用 123 | await rpc.TestBusinessExceptionInterceptor(); 124 | } 125 | catch (BusinessException e) 126 | { 127 | Console.WriteLine($"CzarCode:{e.CzarCode} CzarMessage:{e.CzarMessage}"); 128 | } 129 | catch (Exception ex) 130 | { 131 | Console.WriteLine(ex); 132 | } 133 | Console.ReadLine(); 134 | 135 | } 136 | 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /Czar.Rpc.Server/Czar.Rpc.Server.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | netcoreapp2.2 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | Always 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /Czar.Rpc.Server/CzarConfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "Environment": "Development", 3 | "CzarHost": { 4 | "Port": 7711, 5 | "QuietPeriodSeconds": 2, 6 | "ShutdownTimeoutSeconds": 2, 7 | "IsSsl": "false", 8 | "PfxPath": "cert/datasync.pfx", 9 | "PfxPassword": "123456" 10 | } 11 | } -------------------------------------------------------------------------------- /Czar.Rpc.Server/HelloRpcServer.cs: -------------------------------------------------------------------------------- 1 | using Czar.Rpc.Exceptions; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Threading.Tasks; 5 | using System.Linq; 6 | using System.Net; 7 | using Czar.Rpc.Common; 8 | 9 | namespace Demo.Rpc.Server 10 | { 11 | public class HelloRpcServer: IHelloRpc 12 | { 13 | public EndPoint CzarEndPoint { get; set; } 14 | 15 | public string Hello(int no, string name) 16 | { 17 | string result = $"{no}: Hi, {name}"; 18 | Console.WriteLine(result); 19 | return result + " callback"; 20 | } 21 | 22 | public void HelloHolder(int no, out string name) 23 | { 24 | name = no.ToString() + " out"; 25 | } 26 | 27 | public async Task HelloOneway(int no, string name) 28 | { 29 | await Task.Delay(10000); 30 | Console.WriteLine($"From oneway - {no}: Hi, {name}"); 31 | } 32 | 33 | public Task HelloTask(int no, string name) 34 | { 35 | return Task.FromResult(Hello(no, name)); 36 | } 37 | 38 | public ValueTask HelloValueTask(int no, string name) 39 | { 40 | return new ValueTask(Hello(no, name)); 41 | } 42 | 43 | public Task TestBusinessExceptionInterceptor() 44 | { 45 | throw new BusinessException() 46 | { 47 | CzarCode = "1", 48 | CzarMessage = "test" 49 | }; 50 | } 51 | 52 | public DemoModel HelloModel(int D1, string D2, DateTime D3) 53 | { 54 | return new DemoModel() 55 | { 56 | T1 = D1 + 1, 57 | T2 = D2 + "2", 58 | T3 = D3.AddDays(1) 59 | }; 60 | } 61 | 62 | public async Task HelloModelAsync(int D1, string D2, DateTime D3) 63 | { 64 | return await Task.FromResult( 65 | new DemoModel() 66 | { 67 | T1 = D1 + 1, 68 | T2 = D2 + "77777", 69 | T3 = D3.AddDays(1) 70 | } 71 | ); 72 | } 73 | 74 | public DemoModel HelloSendModel(DemoModel model) 75 | { 76 | model.T1 = model.T1 + 10; 77 | model.T2 = model.T2 + "11"; 78 | model.T3 = model.T3.AddDays(12); 79 | return model; 80 | } 81 | 82 | public DemoModel HelloSendModelParm(string name, DemoModel model) 83 | { 84 | model.T1 = model.T1 + 10; 85 | model.T2 = model.T2 + "11"; 86 | model.T3 = model.T3.AddDays(12); 87 | if (model.Child != null) 88 | { 89 | model.Child.C1 = name+"说:"+ model.Child.C1; 90 | } 91 | return model; 92 | } 93 | 94 | public List HelloSendModelList(List model) 95 | { 96 | return model.Select(t => new DemoModel() { T1=t.T1+10,T2=t.T2+"13",T3=t.T3.AddYears(1),Child=t.Child }).ToList(); 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /Czar.Rpc.Server/Program.cs: -------------------------------------------------------------------------------- 1 | using Czar.Rpc.Codec; 2 | using Microsoft.Extensions.Hosting; 3 | using Microsoft.Extensions.Logging; 4 | using Microsoft.Extensions.Configuration; 5 | using Czar.Rpc.Extensions; 6 | using Czar.Rpc.DotNetty.Extensions; 7 | namespace Czar.Rpc.Server 8 | { 9 | class Program 10 | { 11 | static void Main(string[] args) 12 | { 13 | var host = new HostBuilder() 14 | .ConfigureHostConfiguration(i => i.AddJsonFile("CzarConfig.json")) 15 | .ConfigureLogging((hostContext, configLogging) => 16 | { 17 | configLogging.AddConsole(); 18 | }) 19 | .UseCodec() 20 | .UseLibuvTcpHost() 21 | .UseProxy() 22 | .UseConsoleLifetime() 23 | .Build(); 24 | 25 | host.RunAsync().Wait(); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 jinyancao 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Czar.gateway 2 | 3 | #### 项目介绍 4 | Czar网关项目,负责网关相关功能扩展及应用,目前支持mysql、sqlserver两种存储方式,已经实现动态路由、认证、授权、限流、缓存等特性,下一步将会增加日志和监控等功能。 5 | 6 | #### 博客同步更新地址 7 | 8 | 9 | 10 | #### 使用方式 11 | 12 | ```c# 13 | public void ConfigureServices(IServiceCollection services) 14 | { 15 | var authenticationProviderKey = "TestKey"; 16 | Action gatewayoptions = o => 17 | { 18 | o.Authority = "http://localhost:7777"; 19 | o.ApiName = "gateway"; 20 | o.RequireHttpsMetadata = false; 21 | }; 22 | 23 | services.AddAuthentication() 24 | .AddIdentityServerAuthentication(authenticationProviderKey, gatewayoptions); 25 | 26 | Action options = o => 27 | { 28 | o.Authority = "http://localhost:7777"; //IdentityServer地址 29 | o.RequireHttpsMetadata = false; 30 | o.ApiName = "gateway_admin"; //网关管理的名称,对应的为客户端授权的scope 31 | }; 32 | services.AddOcelot().AddCzarOcelot(option => 33 | { 34 | option.DbConnectionStrings = Configuration["CzarConfig:DbConnectionStrings"]; 35 | option.RedisConnectionStrings = new List() { Configuration["CzarConfig:RedisConnectionStrings"] 36 | }; 37 | //option.EnableTimer = true;//启用定时任务 38 | //option.TimerDelay = 10 * 000;//周期10秒 39 | option.ClientAuthorization = true; 40 | option.ClientRateLimit = true; 41 | }) 42 | //.UseMySql() //使用mysql 43 | .AddAdministration("/CzarOcelot", options); 44 | } 45 | 46 | public void Configure(IApplicationBuilder app, IHostingEnvironment env) 47 | { 48 | if (env.IsDevelopment()) 49 | { 50 | app.UseDeveloperExceptionPage(); 51 | } 52 | else 53 | { 54 | app.UseExceptionHandler("/Error"); 55 | } 56 | app.UseCzarOcelot().Wait(); 57 | } 58 | ``` 59 | 60 | 61 | 62 | #### 版本更新记录 63 | 64 | **0.2.0版本更新记录** 65 | 初始化项目内容,并统一风格。 66 | 67 | **0.2.1版本更新记录** 68 | 69 | 修复缓存信息失效后,未从数据库提出最新的配置信息bug。 70 | 71 | **0.3.0版本更新记录** 72 | 73 | 增加异常信息统一json格式输出 74 | 75 | ```json 76 | {"errcode":500,"errmsg":"请求服务不可用"} 77 | ``` 78 | 79 | **0.3.1版本更新记录** 80 | 81 | 兼容Ids4自定义的错误输出,并保持风格统一 -------------------------------------------------------------------------------- /doc/czar.gateway.20190109.sqlserver2008r2.bak: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinyancao/czar.gateway/aca749cf57a701b5124949a40bc74b73f6484486/doc/czar.gateway.20190109.sqlserver2008r2.bak -------------------------------------------------------------------------------- /doc/czar.gateway.sqlserver2008r2.bak: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinyancao/czar.gateway/aca749cf57a701b5124949a40bc74b73f6484486/doc/czar.gateway.sqlserver2008r2.bak -------------------------------------------------------------------------------- /sample/Czar.Rpc.Common/Czar.Rpc.Common.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.2 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /sample/Czar.Rpc.Common/DemoModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Czar.Rpc.Common 6 | { 7 | public class DemoModel 8 | { 9 | /// 10 | /// 测试1 11 | /// 12 | public int T1 { get; set; } 13 | 14 | /// 15 | /// 测试2 16 | /// 17 | public string T2 { get; set; } 18 | 19 | /// 20 | /// 测试3 21 | /// 22 | public DateTime T3 { get; set; } 23 | 24 | public ChildModel Child { get; set; } 25 | } 26 | 27 | public class ChildModel 28 | { 29 | public string C1 { get; set; } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /sample/Czar.Rpc.Common/IHelloRpc.cs: -------------------------------------------------------------------------------- 1 | using Czar.Rpc.Attributes; 2 | using Czar.Rpc.Exceptions; 3 | using Czar.Rpc.Metadata; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Threading.Tasks; 7 | 8 | namespace Czar.Rpc.Common 9 | { 10 | /// 11 | /// 测试Rpc实体 12 | /// 13 | [BusinessExceptionInterceptor] 14 | [CzarRpc("Demo.Rpc.Hello")] 15 | public interface IHelloRpc: IRpcBaseService 16 | { 17 | string Hello(int no, string name); 18 | 19 | void HelloHolder(int no, out string name); 20 | 21 | Task HelloTask(int no, string name); 22 | 23 | ValueTask HelloValueTask(int no, string name); 24 | 25 | [CzarOneway] 26 | Task HelloOneway(int no, string name); 27 | 28 | Task TestBusinessExceptionInterceptor(); 29 | 30 | DemoModel HelloModel(int D1, string D2, DateTime D3); 31 | 32 | Task HelloModelAsync(int D1, string D2, DateTime D3); 33 | 34 | DemoModel HelloSendModel(DemoModel model); 35 | 36 | DemoModel HelloSendModelParm(string name,DemoModel model); 37 | 38 | List HelloSendModelList(List model); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /sample/Czar.Sample.SqlServer/Czar.Sample.SqlServer.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.2 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | Always 24 | 25 | 26 | Always 27 | 28 | 29 | Always 30 | 31 | 32 | 33 | 34 | 35 | Always 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /sample/Czar.Sample.SqlServer/CzarConfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "Environment": "Development", 3 | "CzarHost": { 4 | "ProxyEndPoint": true, 5 | "IsSsl": "false", 6 | "PfxPath": "cert/datasync.pfx", 7 | "PfxPassword": "bl123456", 8 | "ClientConfig": { 9 | "Demo.Rpc.Hello": { 10 | "Host": "127.0.0.1", 11 | "Port": 1111, 12 | "Timeout": 20 13 | } 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /sample/Czar.Sample.SqlServer/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore; 7 | using Microsoft.AspNetCore.Hosting; 8 | using Microsoft.Extensions.Configuration; 9 | using Microsoft.Extensions.DependencyInjection; 10 | using Microsoft.Extensions.Logging; 11 | 12 | namespace Czar.Sample.SqlServer 13 | { 14 | public class Program 15 | { 16 | public static void Main(string[] args) 17 | { 18 | CreateWebHostBuilder(args).Build().Run(); 19 | } 20 | 21 | public static IWebHostBuilder CreateWebHostBuilder(string[] args) => 22 | WebHost.CreateDefaultBuilder(args) 23 | .ConfigureAppConfiguration(option=>option.AddJsonFile("CzarConfig.json")) 24 | .UseStartup() 25 | ; 26 | 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /sample/Czar.Sample.SqlServer/Startup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using AspectCore.Extensions.Reflection; 6 | using Czar.Gateway.Middleware; 7 | using Czar.Rpc.Clients; 8 | using Czar.Rpc.Codec; 9 | using Czar.Rpc.Configurations; 10 | using Czar.Rpc.DotNetty.Extensions; 11 | using Czar.Rpc.DotNetty.Tcp; 12 | using Czar.Rpc.Extensions; 13 | using DotNetty.Buffers; 14 | using IdentityServer4.AccessTokenValidation; 15 | using Microsoft.AspNetCore.Builder; 16 | using Microsoft.AspNetCore.Hosting; 17 | using Microsoft.AspNetCore.Mvc; 18 | using Microsoft.Extensions.Configuration; 19 | using Microsoft.Extensions.DependencyInjection; 20 | using Microsoft.Extensions.DependencyInjection.Extensions; 21 | using Microsoft.Extensions.Logging; 22 | using Microsoft.Extensions.Options; 23 | using Ocelot.Administration; 24 | using Ocelot.DependencyInjection; 25 | 26 | namespace Czar.Sample.SqlServer 27 | { 28 | public class Startup 29 | { 30 | public Startup(IConfiguration configuration) 31 | { 32 | Configuration = configuration; 33 | } 34 | 35 | public IConfiguration Configuration { get; } 36 | 37 | // This method gets called by the runtime. Use this method to add services to the container. 38 | public void ConfigureServices(IServiceCollection services) 39 | { 40 | var authenticationProviderKey = "TestKey"; 41 | Action gatewayoptions = o => 42 | { 43 | o.Authority = "http://localhost:6611"; 44 | o.ApiName = "gateway"; 45 | o.RequireHttpsMetadata = false; 46 | }; 47 | 48 | services.AddAuthentication() 49 | .AddIdentityServerAuthentication(authenticationProviderKey, gatewayoptions); 50 | 51 | Action options = o => 52 | { 53 | o.Authority = "http://localhost:6611"; //IdentityServer地址 54 | o.RequireHttpsMetadata = false; 55 | o.ApiName = "gateway_admin"; //网关管理的名称,对应的为客户端授权的scope 56 | }; 57 | services.AddCzarOcelot(option => 58 | { 59 | option.RedisOcelotKeyPrefix = "CzarGateway1"; 60 | option.DbConnectionStrings = "Server=.;Database=Ctr_AuthPlatform;User ID=sa;Password=bl123456;"; 61 | option.RedisConnectionString = "192.168.1.111:6379,password=bl123456,defaultDatabase=0,poolsize=50,ssl=false,writeBuffer=10240,connectTimeout=1000,connectRetry=1;" 62 | ; 63 | option.ClientAuthorization = true; 64 | option.ClientRateLimit = true; 65 | }) 66 | //.UseMySql() 67 | .AddAdministration("/CzarOcelot", options); 68 | #region 注入Rpc相关的服务 69 | services.AddLibuvTcpClient(Configuration); 70 | #endregion 71 | } 72 | 73 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 74 | public void Configure(IApplicationBuilder app, IHostingEnvironment env) 75 | { 76 | if (env.IsDevelopment()) 77 | { 78 | app.UseDeveloperExceptionPage(); 79 | } 80 | app.UseCzarOcelot().Wait(); 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /sample/Czar.Sample.SqlServer/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Debug", 5 | "System": "Information", 6 | "Microsoft": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /sample/Czar.Sample.SqlServer/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Warning" 5 | } 6 | }, 7 | "AllowedHosts": "*" 8 | } 9 | -------------------------------------------------------------------------------- /sample/Czar.Sample.TestApi/Controllers/ValuesController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using CSRedis; 6 | using Microsoft.AspNetCore.Mvc; 7 | 8 | namespace Czar.Sample.TestApi.Controllers 9 | { 10 | public struct CzarClientRateLimitCounter 11 | { 12 | public CzarClientRateLimitCounter(DateTime timestamp, long totalRequests) 13 | { 14 | Timestamp = timestamp; 15 | TotalRequests = totalRequests; 16 | } 17 | 18 | /// 19 | /// 最后请求时间 20 | /// 21 | public DateTime Timestamp { get; private set; } 22 | 23 | /// 24 | /// 请求总数 25 | /// 26 | public long TotalRequests { get; private set; } 27 | } 28 | 29 | [Route("api/[controller]")] 30 | [ApiController] 31 | public class ValuesController : ControllerBase 32 | { 33 | public ValuesController() 34 | { 35 | } 36 | // GET api/values 37 | [HttpGet] 38 | public ActionResult> Get() 39 | { 40 | return new string[] { "value11", "value2" }; 41 | } 42 | 43 | // GET api/values/5 44 | [HttpGet("{id}")] 45 | public ActionResult Get(int id) 46 | { 47 | // throw new Exception("测试异常"); 48 | //RedisHelper.Set("2132", id); 49 | //RedisHelper.Publish("CzarGateway1-eff-dsf", $"測試{id}"); 50 | return $"return {id}"; 51 | } 52 | 53 | // POST api/values 54 | [HttpPost] 55 | public void Post([FromBody] string value) 56 | { 57 | } 58 | 59 | // PUT api/values/5 60 | [HttpPut("{id}")] 61 | public void Put(int id, [FromBody] string value) 62 | { 63 | } 64 | 65 | // DELETE api/values/5 66 | [HttpDelete("{id}")] 67 | public void Delete(int id) 68 | { 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /sample/Czar.Sample.TestApi/Czar.Sample.TestApi.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /sample/Czar.Sample.TestApi/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore; 7 | using Microsoft.AspNetCore.Hosting; 8 | using Microsoft.Extensions.Configuration; 9 | using Microsoft.Extensions.Logging; 10 | 11 | namespace Czar.Sample.TestApi 12 | { 13 | public class Program 14 | { 15 | public static void Main(string[] args) 16 | { 17 | CreateWebHostBuilder(args).Build().Run(); 18 | } 19 | 20 | public static IWebHostBuilder CreateWebHostBuilder(string[] args) => 21 | WebHost.CreateDefaultBuilder(args) 22 | .UseStartup(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /sample/Czar.Sample.TestApi/Startup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Builder; 6 | using Microsoft.AspNetCore.Hosting; 7 | using Microsoft.AspNetCore.Mvc; 8 | using Microsoft.Extensions.Configuration; 9 | using Microsoft.Extensions.DependencyInjection; 10 | using Microsoft.Extensions.Logging; 11 | using Microsoft.Extensions.Options; 12 | 13 | namespace Czar.Sample.TestApi 14 | { 15 | public class Startup 16 | { 17 | public Startup(IConfiguration configuration) 18 | { 19 | Configuration = configuration; 20 | } 21 | 22 | public IConfiguration Configuration { get; } 23 | 24 | // This method gets called by the runtime. Use this method to add services to the container. 25 | public void ConfigureServices(IServiceCollection services) 26 | { 27 | services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); 28 | var csredis = new CSRedis.CSRedisClient("192.168.1.111:6379,password=bl123456,defaultDatabase=0,poolsize=500,ssl=false"); 29 | 30 | RedisHelper.Initialization(csredis); 31 | } 32 | 33 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 34 | public void Configure(IApplicationBuilder app, IHostingEnvironment env) 35 | { 36 | //Console.WriteLine(env.EnvironmentName); 37 | if (env.IsDevelopment()) 38 | { 39 | app.UseDeveloperExceptionPage(); 40 | } 41 | 42 | app.UseMvc(); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /sample/Czar.Sample.TestApi/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Warning", 5 | "System": "Warning", 6 | "Microsoft": "Warning" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /sample/Czar.Sample.TestApi/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Warning" 5 | } 6 | }, 7 | "AllowedHosts": "*" 8 | } 9 | -------------------------------------------------------------------------------- /sample/Czar.Sample.TestIds4/Czar.Sample.TestIds4.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /sample/Czar.Sample.TestIds4/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore; 7 | using Microsoft.AspNetCore.Hosting; 8 | using Microsoft.Extensions.Configuration; 9 | using Microsoft.Extensions.Logging; 10 | 11 | namespace Czar.Sample.TestIds4 12 | { 13 | public class Program 14 | { 15 | public static void Main(string[] args) 16 | { 17 | CreateWebHostBuilder(args).Build().Run(); 18 | } 19 | 20 | public static IWebHostBuilder CreateWebHostBuilder(string[] args) => 21 | WebHost.CreateDefaultBuilder(args) 22 | .UseStartup(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /sample/Czar.Sample.TestIds4/Startup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using IdentityServer4.Models; 6 | using Microsoft.AspNetCore.Builder; 7 | using Microsoft.AspNetCore.Hosting; 8 | using Microsoft.AspNetCore.Http; 9 | using Microsoft.AspNetCore.Mvc; 10 | using Microsoft.Extensions.Configuration; 11 | using Microsoft.Extensions.DependencyInjection; 12 | 13 | namespace Czar.Sample.TestIds4 14 | { 15 | public class Startup 16 | { 17 | public Startup(IConfiguration configuration) 18 | { 19 | Configuration = configuration; 20 | } 21 | 22 | public IConfiguration Configuration { get; } 23 | 24 | // This method gets called by the runtime. Use this method to add services to the container. 25 | public void ConfigureServices(IServiceCollection services) 26 | { 27 | services.Configure(options => 28 | { 29 | // This lambda determines whether user consent for non-essential cookies is needed for a given request. 30 | options.CheckConsentNeeded = context => true; 31 | options.MinimumSameSitePolicy = SameSiteMode.None; 32 | }); 33 | services.AddIdentityServer() 34 | .AddDeveloperSigningCredential() 35 | .AddInMemoryApiResources(Config.GetApiResources()) 36 | .AddInMemoryClients(Config.GetClients()); 37 | 38 | services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); 39 | } 40 | 41 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 42 | public void Configure(IApplicationBuilder app, IHostingEnvironment env) 43 | { 44 | if (env.IsDevelopment()) 45 | { 46 | app.UseDeveloperExceptionPage(); 47 | } 48 | else 49 | { 50 | app.UseExceptionHandler("/Error"); 51 | } 52 | 53 | app.UseStaticFiles(); 54 | app.UseCookiePolicy(); 55 | app.UseIdentityServer(); 56 | app.UseMvc(); 57 | } 58 | } 59 | 60 | public class Config 61 | { 62 | // scopes define the API resources in your system 63 | public static IEnumerable GetApiResources() 64 | { 65 | return new List 66 | { 67 | new ApiResource("gateway", "My Test API"), 68 | new ApiResource("gateway_admin", "My admin API") 69 | }; 70 | } 71 | 72 | // clients want to access resources (aka scopes) 73 | public static IEnumerable GetClients() 74 | { 75 | // client credentials client 76 | return new List 77 | { 78 | new Client 79 | { 80 | ClientId = "client1", 81 | AllowedGrantTypes = GrantTypes.ClientCredentials, 82 | 83 | ClientSecrets = 84 | { 85 | new Secret("secret1".Sha256()) 86 | }, 87 | AllowedScopes = { "gateway" } 88 | }, 89 | new Client 90 | { 91 | ClientId = "client2", 92 | AllowedGrantTypes = GrantTypes.ClientCredentials, 93 | 94 | ClientSecrets = 95 | { 96 | new Secret("secret2".Sha256()) 97 | }, 98 | AllowedScopes = { "gateway","gateway_admin" } 99 | } 100 | }; 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /sample/Czar.Sample.TestIds4/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Debug", 5 | "System": "Information", 6 | "Microsoft": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /sample/Czar.Sample.TestIds4/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Warning" 5 | } 6 | }, 7 | "AllowedHosts": "*" 8 | } 9 | -------------------------------------------------------------------------------- /sample/Czar.Sample.TestIds4/tempkey.rsa: -------------------------------------------------------------------------------- 1 | {"KeyId":"502e5e5c2621a429aa0d890088578461","Parameters":{"D":"V1PPOVf20fjW9hcfCiGky09k+pnwxLUnochYyVxv5FSLb+NjwP1SeNdbhKannl3RbzIHStC+CA92XzTxm2sT7dxz+KowyvB9CMhEC2ivPaqDRN1rWr3lf6Nxz4kAvKRwM20ZUg1pORzhqMTa0q39wAwIm6Gl5b3BqaGGhszGEhLgmeUJKmgzM6RcbFDxv2RggDHnfDwDluimX94Uuj89N2dw1304Wk1Jdh7/xqIoJwddhq+W/CjvdpHfYECIDT1ESOW+7jdFSO2ReuWyS9L4HibmjayCwkej4LLx9MjgdRnS34/v3oHxNqgCFsf2zecggxfBTln+2SCsNEpE95v/NQ==","DP":"Zo+coaNw7yZyxp/QOtI8oMM+arl8LbH4IL53BywoD5rxbOdt8v4PsGkqaoWwAjQskOFM1RP0CY5byRngphWnVbDmFdzr0NHVaE+M86SvHybncI7Uq7BoZTPG39eCwEt6dyIdcFtn86P0XFw900EWPDBvoQBAVre3fuI9Be3Pmhs=","DQ":"EYiJjZzGKxY1ghYXEhfFBIH7GZIhAx6HQHFWqDNP3tRXsEZgnBOsMjsbDQNJTpb3hOv/go/Ch3NLn6gK8ZF5g2lUCxob9HAd9UvjVE+oyo1fPM2aPQwld64x9GhmmU9bk2UNGqnJN41FBklX2/LyHriICm+H9iG9wAxzZEPpX1k=","Exponent":"AQAB","InverseQ":"Rc8V+D/BA5x52H9YT8wliWFAMdxkYIQFV8u4iEqu7ADdqa8UMJfk6IgXaEympcjuzL1dZUVZwcmuN2W9UUdpIzJ7lUJlbFSe3dIQNfG6Np1g1MNSCjYAUL1MKv+35OIAGbFVaF5sTkDv52GVplwaSVc/m9XDAyKJd0PI3qKZPiM=","Modulus":"t17UoLlwAEJ6hCe1RYDOq98HhXuHWbUM/eiDcldMaP4BREYkgMiwGnWCGIJ7v1AC3NlYy884VNLI3KJxmpwM5tBmqxmYQEAbxExi0WBS/jwIGUS+p3NiATSMFfjmKxJF9sZiqns1QuxDd/6mx8v38hqICh+oIfRU3yjn5XOquFn7ZkLqOqL/9jzmzSUD1JHy7B4SJFAK+Cbf9eZYzG9wo7TcZ5OyqNxcb3mMN2DmxQxQYO0qyHfRJlpGttbpEPCrXKN+nGrMNe5U12dCAOlxbfM0JfjwoGz6ZpRBqQixnDXyiYUoZP4CeSCdb2Ol0bs1pvNCl/StteCaix9D2/M+7Q==","P":"wPyikO6EoZyQ5Rwmcjm5cgmQXW9a0gLKsbtrxfT0UZv4rZapyuq9qv5ww7FA/zrFPjBkPqiqcoxiSGlioct03uOaw5TYslPw39XRs9lqxm0UzfCWm/5puOzD3YBCCGGDZ2oH4gR03HJH5uVqa3dxvS1MRNOMav1oLcKnki3zMgM=","Q":"8z5hArowjESHtg+reKzuPYHSPPpJjRbjjEd720GvC7eFIKB0UbxiTEN7x40F9E870jyAP0GFs5SmosXUu/VUJyv5Tbf7lgFuMYXHN97lBi/JDzgXHujxQCTwUTKrVmDxMT1fJQG6RiZmEo2qA57I0KwLbr5zMUxLiAf9cAes8E8="}} -------------------------------------------------------------------------------- /sample/Czar.Sample.TestIds4/wwwroot/css/site.css: -------------------------------------------------------------------------------- 1 | /* Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification 2 | for details on configuring this project to bundle and minify static web assets. */ 3 | body { 4 | padding-top: 50px; 5 | padding-bottom: 20px; 6 | } 7 | 8 | /* Wrapping element */ 9 | /* Set some basic padding to keep content from hitting the edges */ 10 | .body-content { 11 | padding-left: 15px; 12 | padding-right: 15px; 13 | } 14 | 15 | /* Carousel */ 16 | .carousel-caption p { 17 | font-size: 20px; 18 | line-height: 1.4; 19 | } 20 | 21 | /* Make .svg files in the carousel display properly in older browsers */ 22 | .carousel-inner .item img[src$=".svg"] { 23 | width: 100%; 24 | } 25 | 26 | /* QR code generator */ 27 | #qrCode { 28 | margin: 15px; 29 | } 30 | 31 | /* Hide/rearrange for smaller screens */ 32 | @media screen and (max-width: 767px) { 33 | /* Hide captions */ 34 | .carousel-caption { 35 | display: none; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /sample/Czar.Sample.TestIds4/wwwroot/css/site.min.css: -------------------------------------------------------------------------------- 1 | body{padding-top:50px;padding-bottom:20px}.body-content{padding-left:15px;padding-right:15px}.carousel-caption p{font-size:20px;line-height:1.4}.carousel-inner .item img[src$=".svg"]{width:100%}#qrCode{margin:15px}@media screen and (max-width:767px){.carousel-caption{display:none}} -------------------------------------------------------------------------------- /sample/Czar.Sample.TestIds4/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinyancao/czar.gateway/aca749cf57a701b5124949a40bc74b73f6484486/sample/Czar.Sample.TestIds4/wwwroot/favicon.ico -------------------------------------------------------------------------------- /sample/Czar.Sample.TestIds4/wwwroot/images/banner2.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sample/Czar.Sample.TestIds4/wwwroot/js/site.js: -------------------------------------------------------------------------------- 1 | // Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification 2 | // for details on configuring this project to bundle and minify static web assets. 3 | 4 | // Write your Javascript code. 5 | -------------------------------------------------------------------------------- /sample/Czar.Sample.TestIds4/wwwroot/js/site.min.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinyancao/czar.gateway/aca749cf57a701b5124949a40bc74b73f6484486/sample/Czar.Sample.TestIds4/wwwroot/js/site.min.js -------------------------------------------------------------------------------- /sample/Czar.Sample.TestIds4/wwwroot/lib/bootstrap/.bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bootstrap", 3 | "description": "The most popular front-end framework for developing responsive, mobile first projects on the web.", 4 | "keywords": [ 5 | "css", 6 | "js", 7 | "less", 8 | "mobile-first", 9 | "responsive", 10 | "front-end", 11 | "framework", 12 | "web" 13 | ], 14 | "homepage": "http://getbootstrap.com", 15 | "license": "MIT", 16 | "moduleType": "globals", 17 | "main": [ 18 | "less/bootstrap.less", 19 | "dist/js/bootstrap.js" 20 | ], 21 | "ignore": [ 22 | "/.*", 23 | "_config.yml", 24 | "CNAME", 25 | "composer.json", 26 | "CONTRIBUTING.md", 27 | "docs", 28 | "js/tests", 29 | "test-infra" 30 | ], 31 | "dependencies": { 32 | "jquery": "1.9.1 - 3" 33 | }, 34 | "version": "3.3.7", 35 | "_release": "3.3.7", 36 | "_resolution": { 37 | "type": "version", 38 | "tag": "v3.3.7", 39 | "commit": "0b9c4a4007c44201dce9a6cc1a38407005c26c86" 40 | }, 41 | "_source": "https://github.com/twbs/bootstrap.git", 42 | "_target": "v3.3.7", 43 | "_originalSource": "bootstrap", 44 | "_direct": true 45 | } -------------------------------------------------------------------------------- /sample/Czar.Sample.TestIds4/wwwroot/lib/bootstrap/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2011-2016 Twitter, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /sample/Czar.Sample.TestIds4/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinyancao/czar.gateway/aca749cf57a701b5124949a40bc74b73f6484486/sample/Czar.Sample.TestIds4/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /sample/Czar.Sample.TestIds4/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinyancao/czar.gateway/aca749cf57a701b5124949a40bc74b73f6484486/sample/Czar.Sample.TestIds4/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /sample/Czar.Sample.TestIds4/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinyancao/czar.gateway/aca749cf57a701b5124949a40bc74b73f6484486/sample/Czar.Sample.TestIds4/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /sample/Czar.Sample.TestIds4/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinyancao/czar.gateway/aca749cf57a701b5124949a40bc74b73f6484486/sample/Czar.Sample.TestIds4/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /sample/Czar.Sample.TestIds4/wwwroot/lib/bootstrap/dist/js/npm.js: -------------------------------------------------------------------------------- 1 | // This file is autogenerated via the `commonjs` Grunt task. You can require() this file in a CommonJS environment. 2 | require('../../js/transition.js') 3 | require('../../js/alert.js') 4 | require('../../js/button.js') 5 | require('../../js/carousel.js') 6 | require('../../js/collapse.js') 7 | require('../../js/dropdown.js') 8 | require('../../js/modal.js') 9 | require('../../js/tooltip.js') 10 | require('../../js/popover.js') 11 | require('../../js/scrollspy.js') 12 | require('../../js/tab.js') 13 | require('../../js/affix.js') -------------------------------------------------------------------------------- /sample/Czar.Sample.TestIds4/wwwroot/lib/jquery-validation-unobtrusive/.bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jquery-validation-unobtrusive", 3 | "homepage": "https://github.com/aspnet/jquery-validation-unobtrusive", 4 | "version": "3.2.9", 5 | "_release": "3.2.9", 6 | "_resolution": { 7 | "type": "version", 8 | "tag": "v3.2.9", 9 | "commit": "a91f5401898e125f10771c5f5f0909d8c4c82396" 10 | }, 11 | "_source": "https://github.com/aspnet/jquery-validation-unobtrusive.git", 12 | "_target": "^3.2.9", 13 | "_originalSource": "jquery-validation-unobtrusive", 14 | "_direct": true 15 | } -------------------------------------------------------------------------------- /sample/Czar.Sample.TestIds4/wwwroot/lib/jquery-validation-unobtrusive/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) .NET Foundation. All rights reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 4 | these files except in compliance with the License. You may obtain a copy of the 5 | License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software distributed 10 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 11 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 12 | specific language governing permissions and limitations under the License. 13 | -------------------------------------------------------------------------------- /sample/Czar.Sample.TestIds4/wwwroot/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js: -------------------------------------------------------------------------------- 1 | // Unobtrusive validation support library for jQuery and jQuery Validate 2 | // Copyright (C) Microsoft Corporation. All rights reserved. 3 | // @version v3.2.9 4 | !function(a){"function"==typeof define&&define.amd?define("jquery.validate.unobtrusive",["jquery.validation"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery-validation")):jQuery.validator.unobtrusive=a(jQuery)}(function(a){function e(a,e,n){a.rules[e]=n,a.message&&(a.messages[e]=a.message)}function n(a){return a.replace(/^\s+|\s+$/g,"").split(/\s*,\s*/g)}function t(a){return a.replace(/([!"#$%&'()*+,.\/:;<=>?@\[\\\]^`{|}~])/g,"\\$1")}function r(a){return a.substr(0,a.lastIndexOf(".")+1)}function i(a,e){return 0===a.indexOf("*.")&&(a=a.replace("*.",e)),a}function o(e,n){var r=a(this).find("[data-valmsg-for='"+t(n[0].name)+"']"),i=r.attr("data-valmsg-replace"),o=i?a.parseJSON(i)!==!1:null;r.removeClass("field-validation-valid").addClass("field-validation-error"),e.data("unobtrusiveContainer",r),o?(r.empty(),e.removeClass("input-validation-error").appendTo(r)):e.hide()}function d(e,n){var t=a(this).find("[data-valmsg-summary=true]"),r=t.find("ul");r&&r.length&&n.errorList.length&&(r.empty(),t.addClass("validation-summary-errors").removeClass("validation-summary-valid"),a.each(n.errorList,function(){a("
  • ").html(this.message).appendTo(r)}))}function s(e){var n=e.data("unobtrusiveContainer");if(n){var t=n.attr("data-valmsg-replace"),r=t?a.parseJSON(t):null;n.addClass("field-validation-valid").removeClass("field-validation-error"),e.removeData("unobtrusiveContainer"),r&&n.empty()}}function l(e){var n=a(this),t="__jquery_unobtrusive_validation_form_reset";if(!n.data(t)){n.data(t,!0);try{n.data("validator").resetForm()}finally{n.removeData(t)}n.find(".validation-summary-errors").addClass("validation-summary-valid").removeClass("validation-summary-errors"),n.find(".field-validation-error").addClass("field-validation-valid").removeClass("field-validation-error").removeData("unobtrusiveContainer").find(">*").removeData("unobtrusiveContainer")}}function u(e){var n=a(e),t=n.data(v),r=a.proxy(l,e),i=f.unobtrusive.options||{},u=function(n,t){var r=i[n];r&&a.isFunction(r)&&r.apply(e,t)};return t||(t={options:{errorClass:i.errorClass||"input-validation-error",errorElement:i.errorElement||"span",errorPlacement:function(){o.apply(e,arguments),u("errorPlacement",arguments)},invalidHandler:function(){d.apply(e,arguments),u("invalidHandler",arguments)},messages:{},rules:{},success:function(){s.apply(e,arguments),u("success",arguments)}},attachValidation:function(){n.off("reset."+v,r).on("reset."+v,r).validate(this.options)},validate:function(){return n.validate(),n.valid()}},n.data(v,t)),t}var m,f=a.validator,v="unobtrusiveValidation";return f.unobtrusive={adapters:[],parseElement:function(e,n){var t,r,i,o=a(e),d=o.parents("form")[0];d&&(t=u(d),t.options.rules[e.name]=r={},t.options.messages[e.name]=i={},a.each(this.adapters,function(){var n="data-val-"+this.name,t=o.attr(n),s={};void 0!==t&&(n+="-",a.each(this.params,function(){s[this]=o.attr(n+this)}),this.adapt({element:e,form:d,message:t,params:s,rules:r,messages:i}))}),a.extend(r,{__dummy__:!0}),n||t.attachValidation())},parse:function(e){var n=a(e),t=n.parents().addBack().filter("form").add(n.find("form")).has("[data-val=true]");n.find("[data-val=true]").each(function(){f.unobtrusive.parseElement(this,!0)}),t.each(function(){var a=u(this);a&&a.attachValidation()})}},m=f.unobtrusive.adapters,m.add=function(a,e,n){return n||(n=e,e=[]),this.push({name:a,params:e,adapt:n}),this},m.addBool=function(a,n){return this.add(a,function(t){e(t,n||a,!0)})},m.addMinMax=function(a,n,t,r,i,o){return this.add(a,[i||"min",o||"max"],function(a){var i=a.params.min,o=a.params.max;i&&o?e(a,r,[i,o]):i?e(a,n,i):o&&e(a,t,o)})},m.addSingleVal=function(a,n,t){return this.add(a,[n||"val"],function(r){e(r,t||a,r.params[n])})},f.addMethod("__dummy__",function(a,e,n){return!0}),f.addMethod("regex",function(a,e,n){var t;return!!this.optional(e)||(t=new RegExp(n).exec(a),t&&0===t.index&&t[0].length===a.length)}),f.addMethod("nonalphamin",function(a,e,n){var t;return n&&(t=a.match(/\W/g),t=t&&t.length>=n),t}),f.methods.extension?(m.addSingleVal("accept","mimtype"),m.addSingleVal("extension","extension")):m.addSingleVal("extension","extension","accept"),m.addSingleVal("regex","pattern"),m.addBool("creditcard").addBool("date").addBool("digits").addBool("email").addBool("number").addBool("url"),m.addMinMax("length","minlength","maxlength","rangelength").addMinMax("range","min","max","range"),m.addMinMax("minlength","minlength").addMinMax("maxlength","minlength","maxlength"),m.add("equalto",["other"],function(n){var o=r(n.element.name),d=n.params.other,s=i(d,o),l=a(n.form).find(":input").filter("[name='"+t(s)+"']")[0];e(n,"equalTo",l)}),m.add("required",function(a){"INPUT"===a.element.tagName.toUpperCase()&&"CHECKBOX"===a.element.type.toUpperCase()||e(a,"required",!0)}),m.add("remote",["url","type","additionalfields"],function(o){var d={url:o.params.url,type:o.params.type||"GET",data:{}},s=r(o.element.name);a.each(n(o.params.additionalfields||o.element.name),function(e,n){var r=i(n,s);d.data[r]=function(){var e=a(o.form).find(":input").filter("[name='"+t(r)+"']");return e.is(":checkbox")?e.filter(":checked").val()||e.filter(":hidden").val()||"":e.is(":radio")?e.filter(":checked").val()||"":e.val()}}),e(o,"remote",d)}),m.add("password",["min","nonalphamin","regex"],function(a){a.params.min&&e(a,"minlength",a.params.min),a.params.nonalphamin&&e(a,"nonalphamin",a.params.nonalphamin),a.params.regex&&e(a,"regex",a.params.regex)}),m.add("fileextensions",["extensions"],function(a){e(a,"extension",a.params.extensions)}),a(function(){f.unobtrusive.parse(document)}),f.unobtrusive}); -------------------------------------------------------------------------------- /sample/Czar.Sample.TestIds4/wwwroot/lib/jquery-validation/.bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jquery-validation", 3 | "homepage": "https://jqueryvalidation.org/", 4 | "repository": { 5 | "type": "git", 6 | "url": "git://github.com/jquery-validation/jquery-validation.git" 7 | }, 8 | "authors": [ 9 | "Jörn Zaefferer " 10 | ], 11 | "description": "Form validation made easy", 12 | "main": "dist/jquery.validate.js", 13 | "keywords": [ 14 | "forms", 15 | "validation", 16 | "validate" 17 | ], 18 | "license": "MIT", 19 | "ignore": [ 20 | "**/.*", 21 | "node_modules", 22 | "bower_components", 23 | "test", 24 | "demo", 25 | "lib" 26 | ], 27 | "dependencies": { 28 | "jquery": ">= 1.7.2" 29 | }, 30 | "version": "1.17.0", 31 | "_release": "1.17.0", 32 | "_resolution": { 33 | "type": "version", 34 | "tag": "1.17.0", 35 | "commit": "fc9b12d3bfaa2d0c04605855b896edb2934c0772" 36 | }, 37 | "_source": "https://github.com/jzaefferer/jquery-validation.git", 38 | "_target": "^1.17.0", 39 | "_originalSource": "jquery-validation", 40 | "_direct": true 41 | } -------------------------------------------------------------------------------- /sample/Czar.Sample.TestIds4/wwwroot/lib/jquery-validation/LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | ===================== 3 | 4 | Copyright Jörn Zaefferer 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /sample/Czar.Sample.TestIds4/wwwroot/lib/jquery/.bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jquery", 3 | "main": "dist/jquery.js", 4 | "license": "MIT", 5 | "ignore": [ 6 | "package.json" 7 | ], 8 | "keywords": [ 9 | "jquery", 10 | "javascript", 11 | "browser", 12 | "library" 13 | ], 14 | "homepage": "https://github.com/jquery/jquery-dist", 15 | "version": "3.3.1", 16 | "_release": "3.3.1", 17 | "_resolution": { 18 | "type": "version", 19 | "tag": "3.3.1", 20 | "commit": "9e8ec3d10fad04748176144f108d7355662ae75e" 21 | }, 22 | "_source": "https://github.com/jquery/jquery-dist.git", 23 | "_target": "^3.3.1", 24 | "_originalSource": "jquery", 25 | "_direct": true 26 | } -------------------------------------------------------------------------------- /sample/Czar.Sample.TestIds4/wwwroot/lib/jquery/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright JS Foundation and other contributors, https://js.foundation/ 2 | 3 | This software consists of voluntary contributions made by many 4 | individuals. For exact contribution history, see the revision history 5 | available at https://github.com/jquery/jquery 6 | 7 | The following license applies to all parts of this software except as 8 | documented below: 9 | 10 | ==== 11 | 12 | Permission is hereby granted, free of charge, to any person obtaining 13 | a copy of this software and associated documentation files (the 14 | "Software"), to deal in the Software without restriction, including 15 | without limitation the rights to use, copy, modify, merge, publish, 16 | distribute, sublicense, and/or sell copies of the Software, and to 17 | permit persons to whom the Software is furnished to do so, subject to 18 | the following conditions: 19 | 20 | The above copyright notice and this permission notice shall be 21 | included in all copies or substantial portions of the Software. 22 | 23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 24 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 25 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 26 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 27 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 28 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 29 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 30 | 31 | ==== 32 | 33 | All files located in the node_modules and external directories are 34 | externally maintained libraries used by this software which have their 35 | own licenses; we recommend you read them, as their terms may differ from 36 | the terms above. 37 | -------------------------------------------------------------------------------- /src/Czar.Gateway/Authentication/CzarAuthenticationProcessor.cs: -------------------------------------------------------------------------------- 1 | using Czar.Gateway.Configuration; 2 | using Ocelot.Cache; 3 | using System; 4 | using System.Threading.Tasks; 5 | 6 | namespace Czar.Gateway.Authentication 7 | { 8 | /// 9 | /// 金焰的世界 10 | /// 2018-11-15 11 | /// 实现自定义授权处理器逻辑 12 | /// 13 | public class CzarAuthenticationProcessor : ICzarAuthenticationProcessor 14 | { 15 | private readonly IClientAuthenticationRepository _clientAuthenticationRepository; 16 | private readonly CzarOcelotConfiguration _options; 17 | private readonly IOcelotCache _ocelotCache; 18 | public CzarAuthenticationProcessor(IClientAuthenticationRepository clientAuthenticationRepository, CzarOcelotConfiguration options, IOcelotCache ocelotCache) 19 | { 20 | _clientAuthenticationRepository = clientAuthenticationRepository; 21 | _options = options; 22 | _ocelotCache = ocelotCache; 23 | } 24 | /// 25 | /// 校验当前的请求地址客户端是否有权限访问 26 | /// 27 | /// 客户端ID 28 | /// 请求地址 29 | /// 30 | public async Task CheckClientAuthenticationAsync(string clientid, string path) 31 | { 32 | var enablePrefix = CzarCacheRegion.AuthenticationRegion; 33 | var key = CzarOcelotHelper.ComputeCounterKey(enablePrefix, clientid, "", path); 34 | var cacheResult = _ocelotCache.Get(key, enablePrefix); 35 | if (cacheResult!=null) 36 | {//提取缓存数据 37 | return cacheResult.Role; 38 | } 39 | else 40 | {//重新获取认证信息 41 | var result = await _clientAuthenticationRepository.ClientAuthenticationAsync(clientid, path); 42 | //添加到缓存里 43 | _ocelotCache.Add(key, new ClientRoleModel() { CacheTime = DateTime.Now,Role=result }, TimeSpan.FromMinutes(_options.CzarCacheTime), enablePrefix); 44 | return result; 45 | } 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/Czar.Gateway/Authentication/IClientAuthenticationRepository.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Threading.Tasks; 5 | 6 | namespace Czar.Gateway.Authentication 7 | { 8 | /// 9 | /// 金焰的世界 10 | /// 2018-11-15 11 | /// 客户端授权仓储接口 12 | /// 13 | public interface IClientAuthenticationRepository 14 | { 15 | /// 16 | /// 校验当前的请求地址是否有权限访问 17 | /// 18 | /// 客户端ID 19 | /// 请求地址 20 | /// 21 | Task ClientAuthenticationAsync(string clientid, string path); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Czar.Gateway/Authentication/ICzarAuthenticationProcessor.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace Czar.Gateway.Authentication 4 | { 5 | /// 6 | /// 金焰的世界 7 | /// 2018-11-15 8 | /// 自定义授权处理器 9 | /// 10 | public interface ICzarAuthenticationProcessor 11 | { 12 | /// 13 | /// 校验当前的请求地址客户端是否有权限访问 14 | /// 15 | /// 客户端ID 16 | /// 请求地址 17 | /// 18 | Task CheckClientAuthenticationAsync(string clientid, string path); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Czar.Gateway/Authentication/Middleware/CzarAuthenticationMiddleware.cs: -------------------------------------------------------------------------------- 1 | using Czar.Gateway.Configuration; 2 | using Ocelot.Configuration; 3 | using Ocelot.Logging; 4 | using Ocelot.Middleware; 5 | using System.Linq; 6 | using System.Threading.Tasks; 7 | 8 | namespace Czar.Gateway.Authentication.Middleware 9 | { 10 | /// 11 | /// 金焰的世界 12 | /// 2018-11-15 13 | /// 自定义授权中间件 14 | /// 15 | public class CzarAuthenticationMiddleware : OcelotMiddleware 16 | { 17 | private readonly OcelotRequestDelegate _next; 18 | private readonly CzarOcelotConfiguration _options; 19 | private readonly ICzarAuthenticationProcessor _ahphAuthenticationProcessor; 20 | public CzarAuthenticationMiddleware(OcelotRequestDelegate next, 21 | IOcelotLoggerFactory loggerFactory, 22 | ICzarAuthenticationProcessor ahphAuthenticationProcessor, 23 | CzarOcelotConfiguration options) 24 | : base(loggerFactory.CreateLogger()) 25 | { 26 | _next = next; 27 | _ahphAuthenticationProcessor = ahphAuthenticationProcessor; 28 | _options = options; 29 | } 30 | 31 | public async Task Invoke(DownstreamContext context) 32 | { 33 | if (!context.IsError && context.HttpContext.Request.Method.ToUpper() != "OPTIONS" && IsAuthenticatedRoute(context.DownstreamReRoute)) 34 | { 35 | if (!_options.ClientAuthorization) 36 | { 37 | Logger.LogInformation($"未启用客户端认证中间件"); 38 | await _next.Invoke(context); 39 | } 40 | else 41 | { 42 | Logger.LogInformation($"{context.HttpContext.Request.Path} 是认证路由. {MiddlewareName} 开始校验授权信息"); 43 | #region 提取客户端ID 44 | var clientId = "client_cjy"; 45 | var path = context.DownstreamReRoute.UpstreamPathTemplate.OriginalValue; //路由地址 46 | var clientClaim = context.HttpContext.User.Claims.FirstOrDefault(p => p.Type == _options.ClientKey); 47 | if (!string.IsNullOrEmpty(clientClaim?.Value)) 48 | {//从Claims中提取客户端id 49 | clientId = clientClaim?.Value; 50 | } 51 | #endregion 52 | if (await _ahphAuthenticationProcessor.CheckClientAuthenticationAsync(clientId, path)) 53 | { 54 | await _next.Invoke(context); 55 | } 56 | else 57 | {//未授权直接返回错误 58 | var error = new UnauthenticatedError($"请求认证路由 {context.HttpContext.Request.Path}客户端未授权"); 59 | Logger.LogWarning($"路由地址 {context.HttpContext.Request.Path} 自定义认证管道校验失败. {error}"); 60 | SetPipelineError(context, error); 61 | } 62 | } 63 | } 64 | else 65 | { 66 | await _next.Invoke(context); 67 | } 68 | 69 | } 70 | private static bool IsAuthenticatedRoute(DownstreamReRoute reRoute) 71 | { 72 | return reRoute.IsAuthenticated; 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/Czar.Gateway/Authentication/Middleware/CzarAuthenticationMiddlewareExtensions.cs: -------------------------------------------------------------------------------- 1 | using Ocelot.Middleware.Pipeline; 2 | 3 | namespace Czar.Gateway.Authentication.Middleware 4 | { 5 | /// 6 | /// 金焰的世界 7 | /// 2018-11-15 8 | /// 使用自定义授权中间件 9 | /// 10 | public static class CzarAuthenticationMiddlewareExtensions 11 | { 12 | public static IOcelotPipelineBuilder UseAhphAuthenticationMiddleware(this IOcelotPipelineBuilder builder) 13 | { 14 | return builder.UseMiddleware(); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Czar.Gateway/Cache/CzarCacheController.cs: -------------------------------------------------------------------------------- 1 | using Czar.Gateway.Authentication; 2 | using Czar.Gateway.Configuration; 3 | using Czar.Gateway.RateLimit; 4 | using Czar.Gateway.Rpc; 5 | using Microsoft.AspNetCore.Authorization; 6 | using Microsoft.AspNetCore.Mvc; 7 | using Microsoft.Extensions.Caching.Memory; 8 | using Ocelot.Configuration; 9 | using Ocelot.Configuration.Creator; 10 | using Ocelot.Configuration.Repository; 11 | using System; 12 | using System.Threading.Tasks; 13 | 14 | namespace Czar.Gateway.Cache 15 | { 16 | /// 17 | /// 提供外部缓存处理接口 18 | /// 19 | [Authorize] 20 | [Route("CzarCache")] 21 | public class CzarCacheController : Controller 22 | { 23 | private readonly CzarOcelotConfiguration _options; 24 | private readonly IClientAuthenticationRepository _clientAuthenticationRepository; 25 | private IFileConfigurationRepository _fileConfigurationRepository; 26 | private IInternalConfigurationCreator _internalConfigurationCreator; 27 | private readonly IClientRateLimitRepository _clientRateLimitRepository; 28 | private readonly IRpcRepository _rpcRepository; 29 | private readonly IMemoryCache _cache; 30 | public CzarCacheController(IClientAuthenticationRepository clientAuthenticationRepository, CzarOcelotConfiguration options, 31 | IFileConfigurationRepository fileConfigurationRepository, 32 | IInternalConfigurationCreator internalConfigurationCreator, 33 | IClientRateLimitRepository clientRateLimitRepository, 34 | IRpcRepository rpcRepository, 35 | IMemoryCache cache) 36 | { 37 | _clientAuthenticationRepository = clientAuthenticationRepository; 38 | _options = options; 39 | _fileConfigurationRepository = fileConfigurationRepository; 40 | _internalConfigurationCreator = internalConfigurationCreator; 41 | _clientRateLimitRepository = clientRateLimitRepository; 42 | _rpcRepository = rpcRepository; 43 | _cache = cache; 44 | } 45 | 46 | /// 47 | /// 更新客户端地址访问授权接口 48 | /// 49 | /// 客户端ID 50 | /// 请求模板 51 | /// 52 | [HttpPost] 53 | [Route("ClientRule")] 54 | public async Task UpdateClientRuleCache(string clientid,string path) 55 | { 56 | var region = CzarCacheRegion.AuthenticationRegion; 57 | var key = CzarOcelotHelper.ComputeCounterKey(region, clientid, "", path); 58 | key = CzarOcelotHelper.GetKey(_options.RedisOcelotKeyPrefix, region, key); 59 | var result = await _clientAuthenticationRepository.ClientAuthenticationAsync(clientid, path); 60 | var data = new ClientRoleModel() { CacheTime = DateTime.Now, Role = result }; 61 | if (_options.ClusterEnvironment) 62 | { 63 | RedisHelper.Set(key, data); //加入redis缓存 64 | RedisHelper.Publish(key, data.ToJson()); //发布事件 65 | } 66 | else 67 | { 68 | _cache.Remove(key); 69 | } 70 | } 71 | 72 | /// 73 | /// 更新网关配置路由信息 74 | /// 75 | /// 76 | [HttpPost] 77 | [Route("InternalConfiguration")] 78 | public async Task UpdateInternalConfigurationCache() 79 | { 80 | var key = CzarCacheRegion.InternalConfigurationRegion; 81 | key = CzarOcelotHelper.GetKey(_options.RedisOcelotKeyPrefix, "", key); 82 | var fileconfig = await _fileConfigurationRepository.Get(); 83 | var internalConfig = await _internalConfigurationCreator.Create(fileconfig.Data); 84 | var config = (InternalConfiguration)internalConfig.Data; 85 | if (_options.ClusterEnvironment) 86 | { 87 | RedisHelper.Set(key, config); //加入redis缓存 88 | RedisHelper.Publish(key, config.ToJson()); //发布事件 89 | } 90 | else 91 | { 92 | _cache.Remove(key); 93 | } 94 | } 95 | 96 | /// 97 | /// 删除路由配合的缓存信息 98 | /// 99 | /// 区域 100 | /// 下端路由 101 | /// 102 | [HttpPost] 103 | [Route("Response")] 104 | public async Task DeleteResponseCache(string region,string downurl) 105 | { 106 | var key = CzarOcelotHelper.GetKey(_options.RedisOcelotKeyPrefix, region, downurl); 107 | if (_options.ClusterEnvironment) 108 | { 109 | await RedisHelper.DelAsync(key); 110 | RedisHelper.Publish(key, "");//发布时间 111 | } 112 | else 113 | { 114 | _cache.Remove(key); 115 | } 116 | } 117 | 118 | /// 119 | /// 更新客户端限流规则缓存 120 | /// 121 | /// 客户端ID 122 | /// 路由模板 123 | /// 124 | [HttpPost] 125 | [Route("RateLimitRule")] 126 | public async Task UpdateRateLimitRuleCache(string clientid, string path) 127 | { 128 | var region = CzarCacheRegion.RateLimitRuleModelRegion; 129 | var key = clientid + path; 130 | key = CzarOcelotHelper.GetKey(_options.RedisOcelotKeyPrefix, region, key); 131 | var result = await _clientRateLimitRepository.CheckClientRateLimitAsync(clientid, path); 132 | var data = new RateLimitRuleModel() { RateLimit = result.RateLimit, rateLimitOptions = result.rateLimitOptions }; 133 | if (_options.ClusterEnvironment) 134 | { 135 | RedisHelper.Set(key, data); //加入redis缓存 136 | RedisHelper.Publish(key, data.ToJson()); //发布事件 137 | } 138 | else 139 | { 140 | _cache.Remove(key); 141 | } 142 | } 143 | 144 | /// 145 | /// 更新客户端是否开启限流缓存 146 | /// 147 | /// 148 | /// 149 | [HttpPost] 150 | [Route("ClientRole")] 151 | public async Task UpdateClientRoleCache(string path) 152 | { 153 | var region = CzarCacheRegion.ClientRoleModelRegion; 154 | var key = path; 155 | key = CzarOcelotHelper.GetKey(_options.RedisOcelotKeyPrefix, region, key); 156 | var result = await _clientRateLimitRepository.CheckReRouteRuleAsync(path); 157 | var data = new ClientRoleModel() { CacheTime = DateTime.Now, Role = result }; 158 | if (_options.ClusterEnvironment) 159 | { 160 | RedisHelper.Set(key, data); //加入redis缓存 161 | RedisHelper.Publish(key, data.ToJson()); //发布事件 162 | } 163 | else 164 | { 165 | _cache.Remove(key); 166 | } 167 | } 168 | 169 | /// 170 | /// 更新呢客户端路由白名单缓存 171 | /// 172 | /// 173 | /// 174 | /// 175 | [HttpPost] 176 | [Route("ClientReRouteWhiteList")] 177 | public async Task UpdateClientReRouteWhiteListCache(string clientid, string path) 178 | { 179 | var region = CzarCacheRegion.ClientReRouteWhiteListRegion; 180 | var key = clientid + path; 181 | key = CzarOcelotHelper.GetKey(_options.RedisOcelotKeyPrefix, region, key); 182 | var result = await _clientRateLimitRepository.CheckClientReRouteWhiteListAsync(clientid, path); 183 | var data = new ClientRoleModel() { CacheTime = DateTime.Now, Role = result }; 184 | if (_options.ClusterEnvironment) 185 | { 186 | RedisHelper.Set(key, data); //加入redis缓存 187 | RedisHelper.Publish(key, data.ToJson()); //发布事件 188 | } 189 | else 190 | { 191 | _cache.Remove(key); 192 | } 193 | } 194 | 195 | [HttpPost] 196 | [Route("Rpc")] 197 | public async Task UpdateRpcCache(string UpUrl) 198 | { 199 | var region = CzarCacheRegion.RemoteInvokeMessageRegion; 200 | var key = UpUrl; 201 | key = CzarOcelotHelper.GetKey(_options.RedisOcelotKeyPrefix, region, key); 202 | var result = await _rpcRepository.GetRemoteMethodAsync(UpUrl); 203 | if (_options.ClusterEnvironment) 204 | { 205 | RedisHelper.Set(key, result); //加入redis缓存 206 | RedisHelper.Publish(key, result.ToJson()); //发布事件 207 | } 208 | else 209 | { 210 | _cache.Remove(key); 211 | } 212 | } 213 | } 214 | } 215 | -------------------------------------------------------------------------------- /src/Czar.Gateway/Cache/CzarMemoryCache.cs: -------------------------------------------------------------------------------- 1 | using Czar.Gateway.Configuration; 2 | using Czar.Gateway.RateLimit; 3 | using Microsoft.Extensions.Caching.Memory; 4 | using Ocelot.Cache; 5 | using System; 6 | 7 | namespace Czar.Gateway.Cache 8 | { 9 | /// 10 | /// 金焰的世界 11 | /// 2019-03-03 12 | /// 使用二级缓存解决集群环境问题 13 | /// 14 | public class CzarMemoryCache : IOcelotCache 15 | { 16 | private readonly CzarOcelotConfiguration _options; 17 | private readonly IMemoryCache _cache; 18 | public CzarMemoryCache(CzarOcelotConfiguration options,IMemoryCache cache) 19 | { 20 | _options = options; 21 | _cache = cache; 22 | } 23 | public void Add(string key, T value, TimeSpan ttl, string region) 24 | { 25 | key = CzarOcelotHelper.GetKey(_options.RedisOcelotKeyPrefix,region, key); 26 | if (_options.ClusterEnvironment) 27 | { 28 | var msg = value.ToJson(); 29 | if (typeof(T) == typeof(CachedResponse)) 30 | {//带过期时间的缓存 31 | _cache.Set(key, value, ttl); //添加本地缓存 32 | RedisHelper.Set(key, msg,(int)ttl.TotalSeconds); //加入redis缓存 33 | RedisHelper.Publish(key, msg); //发布 34 | } 35 | else if (typeof(T) == typeof(CzarClientRateLimitCounter?)) 36 | {//限流缓存,直接使用redis 37 | RedisHelper.Set(key, value, (int)ttl.TotalSeconds); 38 | } 39 | else 40 | {//正常缓存,发布 41 | _cache.Set(key, value, ttl); //添加本地缓存 42 | RedisHelper.Set(key, msg); //加入redis缓存 43 | RedisHelper.Publish(key, msg); //发布 44 | } 45 | } 46 | else 47 | { 48 | _cache.Set(key, value, ttl); //添加本地缓存 49 | } 50 | } 51 | 52 | public void AddAndDelete(string key, T value, TimeSpan ttl, string region) 53 | { 54 | Add(key, value, ttl, region); 55 | } 56 | 57 | public void ClearRegion(string region) 58 | { 59 | if (_options.ClusterEnvironment) 60 | { 61 | var keys = RedisHelper.Keys(region + "*"); 62 | RedisHelper.Del(keys); 63 | foreach (var key in keys) 64 | { 65 | RedisHelper.Publish(key, ""); //发布key值为空,处理时删除即可。 66 | } 67 | } 68 | else 69 | { 70 | _cache.Remove(region); 71 | } 72 | } 73 | 74 | public T Get(string key, string region) 75 | { 76 | key = CzarOcelotHelper.GetKey(_options.RedisOcelotKeyPrefix, region, key); 77 | if(region== CzarCacheRegion.CzarClientRateLimitCounterRegion&& _options.ClusterEnvironment) 78 | {//限流且开启了集群支持,默认从redis取 79 | return RedisHelper.Get(key); 80 | } 81 | var result = _cache.Get(key); 82 | if (result == null&& _options.ClusterEnvironment) 83 | { 84 | result= RedisHelper.Get(key); 85 | if (result != null) 86 | { 87 | if (typeof(T) == typeof(CachedResponse)) 88 | {//查看redis过期时间 89 | var second = RedisHelper.Ttl(key); 90 | if (second > 0) 91 | { 92 | _cache.Set(key, result, TimeSpan.FromSeconds(second)); 93 | } 94 | } 95 | else 96 | { 97 | _cache.Set(key, result, TimeSpan.FromSeconds(_options.CzarCacheTime)); 98 | } 99 | } 100 | } 101 | return result; 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/Czar.Gateway/Cache/RedisInternalConfigurationRepository.cs: -------------------------------------------------------------------------------- 1 | using Czar.Gateway.Configuration; 2 | using Ocelot.Cache; 3 | using Ocelot.Configuration; 4 | using Ocelot.Configuration.Creator; 5 | using Ocelot.Configuration.Repository; 6 | using Ocelot.Responses; 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Text; 10 | 11 | namespace Czar.Gateway.Cache 12 | { 13 | /// 14 | /// 金焰的世界 15 | /// 2018-11-14 16 | /// 使用redis存储内部配置信息 17 | /// 18 | public class RedisInternalConfigurationRepository : IInternalConfigurationRepository 19 | { 20 | private readonly CzarOcelotConfiguration _options; 21 | private IFileConfigurationRepository _fileConfigurationRepository; 22 | private IInternalConfigurationCreator _internalConfigurationCreator; 23 | private readonly IOcelotCache _ocelotCache; 24 | public RedisInternalConfigurationRepository(CzarOcelotConfiguration options,IFileConfigurationRepository fileConfigurationRepository, IInternalConfigurationCreator internalConfigurationCreator, IOcelotCache ocelotCache) 25 | { 26 | _fileConfigurationRepository = fileConfigurationRepository; 27 | _internalConfigurationCreator = internalConfigurationCreator; 28 | _options = options; 29 | _ocelotCache = ocelotCache; 30 | } 31 | 32 | /// 33 | /// 设置配置信息 34 | /// 35 | /// 配置信息 36 | /// 37 | public Response AddOrReplace(IInternalConfiguration internalConfiguration) 38 | { 39 | var key = CzarCacheRegion.InternalConfigurationRegion; 40 | _ocelotCache.Add(key, (InternalConfiguration)internalConfiguration, TimeSpan.FromSeconds(_options.CzarCacheTime), ""); 41 | return new OkResponse(); 42 | } 43 | 44 | /// 45 | /// 从缓存中获取配置信息 46 | /// 47 | /// 48 | public Response Get() 49 | { 50 | var key = CzarCacheRegion.InternalConfigurationRegion; 51 | var result = _ocelotCache.Get(key, ""); 52 | if (result!=null) 53 | { 54 | return new OkResponse(result); 55 | } 56 | var fileconfig= _fileConfigurationRepository.Get().Result; 57 | var internalConfig= _internalConfigurationCreator.Create(fileconfig.Data).Result; 58 | AddOrReplace(internalConfig.Data); 59 | return new OkResponse(internalConfig.Data); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/Czar.Gateway/Configuration/CzarCacheRegion.cs: -------------------------------------------------------------------------------- 1 | namespace Czar.Gateway.Configuration 2 | { 3 | /// 4 | /// 缓存所属区域 5 | /// 6 | public class CzarCacheRegion 7 | { 8 | /// 9 | /// 授权 10 | /// 11 | public const string AuthenticationRegion = "CacheClientAuthentication"; 12 | 13 | /// 14 | /// 路由配置 15 | /// 16 | public const string FileConfigurationRegion = "CacheFileConfiguration"; 17 | 18 | /// 19 | /// 内部配置 20 | /// 21 | public const string InternalConfigurationRegion = "CacheInternalConfiguration"; 22 | 23 | /// 24 | /// 客户端权限 25 | /// 26 | public const string ClientRoleModelRegion = "CacheClientRoleModel"; 27 | 28 | /// 29 | /// 客户端路由白名单 30 | /// 31 | public const string ClientReRouteWhiteListRegion = "CacheClientReRouteWhiteList"; 32 | 33 | /// 34 | /// 限流规则 35 | /// 36 | public const string RateLimitRuleModelRegion = "CacheRateLimitRuleModel"; 37 | 38 | /// 39 | /// Rpc远程调用 40 | /// 41 | public const string RemoteInvokeMessageRegion = "CacheRemoteInvokeMessage"; 42 | 43 | /// 44 | /// 客户端限流 45 | /// 46 | public const string CzarClientRateLimitCounterRegion = "CacheCzarClientRateLimitCounter"; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/Czar.Gateway/Configuration/CzarOcelotConfiguration.cs: -------------------------------------------------------------------------------- 1 | namespace Czar.Gateway.Configuration 2 | { 3 | /// 4 | /// 金焰的世界 5 | /// 2018-11-11 6 | /// 自定义配置信息 7 | /// 8 | public class CzarOcelotConfiguration 9 | { 10 | /// 11 | /// 数据库连接字符串,使用不同数据库时自行修改,默认实现了SQLSERVER 12 | /// 13 | public string DbConnectionStrings { get; set; } 14 | 15 | /// 16 | /// 金焰的世界 17 | /// 2018-11-12 18 | /// 是否启用定时器,默认不启动 19 | /// 20 | public bool EnableTimer { get; set; } = false; 21 | 22 | /// 23 | /// 金焰的世界 24 | /// 2018-11.12 25 | /// 定时器周期,单位(毫秒),默认30分总自动更新一次 26 | /// 27 | public int TimerDelay { get; set; } = 30 * 60 * 1000; 28 | 29 | /// 30 | /// 金焰的世界 31 | /// 2018-11-14 32 | /// Redis连接字符串 33 | /// 34 | public string RedisConnectionString { get; set; } 35 | 36 | /// 37 | /// 金焰的世界 38 | /// 2019-03-03 39 | /// 配置哨兵或分区时使用 40 | /// 41 | public string[] RedisSentinelOrPartitionConStr { get; set; } 42 | 43 | /// 44 | /// 金焰的世界 45 | /// 2019-03-03 46 | /// Redis部署方式,默认使用普通方式 47 | /// 48 | public RedisStoreMode RedisStoreMode { get; set; } = RedisStoreMode.Normal; 49 | 50 | /// 51 | /// 金焰的计界 52 | /// 2019-03-03 53 | /// 做集群缓存同步时使用,会订阅所有正则匹配的事件 54 | /// 55 | public string RedisOcelotKeyPrefix { get; set; } = "CzarOcelot"; 56 | 57 | /// 58 | /// 金焰的世界 59 | /// 2019-03-03 60 | /// 是否启用集群环境,如果非集群环境直接本地缓存+数据库即可 61 | /// 62 | public bool ClusterEnvironment { get; set; } = false; 63 | 64 | /// 65 | /// 金焰的世界 66 | /// 2018-11-15 67 | /// 是否启用客户端授权,默认不开启 68 | /// 69 | public bool ClientAuthorization { get; set; } = false; 70 | 71 | /// 72 | /// 金焰的世界 73 | /// 2018-11-15 74 | /// 服务器缓存时间,默认30分钟 75 | /// 76 | public int CzarCacheTime { get; set; } = 1800; 77 | /// 78 | /// 金焰的世界 79 | /// 2018-11-15 80 | /// 客户端标识,默认 client_id 81 | /// 82 | public string ClientKey { get; set; } = "client_id"; 83 | 84 | /// 85 | /// 金焰的世界 86 | /// 2018-11-18 87 | /// 是否开启自定义限流,默认不开启 88 | /// 89 | public bool ClientRateLimit { get; set; } = false; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/Czar.Gateway/Configuration/DbConfigurationPoller.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Hosting; 2 | using Ocelot.Configuration.Creator; 3 | using Ocelot.Configuration.Repository; 4 | using Ocelot.Logging; 5 | using System; 6 | using System.Linq; 7 | using System.Threading; 8 | using System.Threading.Tasks; 9 | 10 | namespace Czar.Gateway.Configuration 11 | { 12 | /// 13 | /// 金焰的世界 14 | /// 2018-11-12 15 | /// 数据库配置信息更新策略 16 | /// 17 | public class DbConfigurationPoller : IHostedService, IDisposable 18 | { 19 | private readonly IOcelotLogger _logger; 20 | private readonly IFileConfigurationRepository _repo; 21 | private readonly CzarOcelotConfiguration _option; 22 | private Timer _timer; 23 | private bool _polling; 24 | private readonly IInternalConfigurationRepository _internalConfigRepo; 25 | private readonly IInternalConfigurationCreator _internalConfigCreator; 26 | public DbConfigurationPoller(IOcelotLoggerFactory factory, 27 | IFileConfigurationRepository repo, 28 | IInternalConfigurationRepository internalConfigRepo, 29 | IInternalConfigurationCreator internalConfigCreator, 30 | CzarOcelotConfiguration option) 31 | { 32 | _internalConfigRepo = internalConfigRepo; 33 | _internalConfigCreator = internalConfigCreator; 34 | _logger = factory.CreateLogger(); 35 | _repo = repo; 36 | _option = option; 37 | } 38 | 39 | public void Dispose() 40 | { 41 | _timer?.Dispose(); 42 | } 43 | 44 | public Task StartAsync(CancellationToken cancellationToken) 45 | { 46 | if (_option.EnableTimer) 47 | {//判断是否启用自动更新 48 | _logger.LogInformation($"{nameof(DbConfigurationPoller)} is starting."); 49 | _timer = new Timer(async x => 50 | { 51 | if (_polling) 52 | { 53 | return; 54 | } 55 | _polling = true; 56 | await Poll(); 57 | _polling = false; 58 | }, null, _option.TimerDelay, _option.TimerDelay); 59 | } 60 | return Task.CompletedTask; 61 | } 62 | 63 | public Task StopAsync(CancellationToken cancellationToken) 64 | { 65 | if (_option.EnableTimer) 66 | {//判断是否启用自动更新 67 | _logger.LogInformation($"{nameof(DbConfigurationPoller)} is stopping."); 68 | _timer?.Change(Timeout.Infinite, 0); 69 | } 70 | return Task.CompletedTask; 71 | } 72 | 73 | private async Task Poll() 74 | { 75 | _logger.LogInformation("Started polling"); 76 | 77 | var fileConfig = await _repo.Get(); 78 | 79 | if (fileConfig.IsError) 80 | { 81 | _logger.LogWarning($"error geting file config, errors are {string.Join(",", fileConfig.Errors.Select(x => x.Message))}"); 82 | return; 83 | } 84 | else 85 | { 86 | var config = await _internalConfigCreator.Create(fileConfig.Data); 87 | if (!config.IsError) 88 | { 89 | _internalConfigRepo.AddOrReplace(config.Data); 90 | } 91 | } 92 | _logger.LogInformation("Finished polling"); 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/Czar.Gateway/Configuration/ErrorResult.cs: -------------------------------------------------------------------------------- 1 | namespace Czar.Gateway.Configuration 2 | { 3 | /// 4 | /// 金焰的世界 5 | /// 2018-11-15 6 | /// 统一的异常显示结构 7 | /// 8 | public class ErrorResult 9 | { 10 | /// 11 | /// 错误代码 12 | /// 13 | public int errcode { get; set; } 14 | 15 | /// 16 | /// 错误描述 17 | /// 18 | public string errmsg { get; set; } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Czar.Gateway/Configuration/Model/BaseResult.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Czar.Gateway.Configuration 6 | { 7 | /// 8 | /// 金焰的世界 9 | /// 2018-12-10 10 | /// 信息输出基类 11 | /// 12 | public class BaseResult 13 | { 14 | public BaseResult(int _errcode,string _errmsg) 15 | { 16 | errcode = _errcode; 17 | errmsg = _errmsg; 18 | } 19 | public BaseResult() 20 | { 21 | 22 | } 23 | /// 24 | /// 错误类型标识 25 | /// 26 | public int errcode { get; set; } 27 | 28 | /// 29 | /// 错误类型说明 30 | /// 31 | public string errmsg { get; set; } 32 | } 33 | 34 | /// 35 | /// 金焰的世界 36 | /// 2018-12-10 37 | /// 默认成功结果 38 | /// 39 | public class SuccessResult : BaseResult 40 | { 41 | public SuccessResult() : base(0, "成功") 42 | { 43 | 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Czar.Gateway/Configuration/Model/ClientRoleModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Czar.Gateway.Configuration 4 | { 5 | /// 6 | /// 金焰的世界 7 | /// 2018-11-15 8 | /// 是否存在的实体,缓存使用 9 | /// 10 | public class ClientRoleModel 11 | { 12 | /// 13 | /// 缓存时间 14 | /// 15 | public DateTime CacheTime { get; set; } 16 | /// 17 | /// 是否有访问权限 18 | /// 19 | public bool Role { get; set; } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Czar.Gateway/Configuration/RedisStoreMode.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Czar.Gateway.Configuration 6 | { 7 | /// 8 | /// 金焰的世界 9 | /// 2019-03-03 10 | /// Redis部署方式 11 | /// 12 | public enum RedisStoreMode 13 | { 14 | /// 15 | /// 普通方式 16 | /// 17 | Normal = 1, 18 | /// 19 | /// 官方集群方式 20 | /// 21 | Cluster=2, 22 | /// 23 | /// 哨兵方式 24 | /// 25 | Sentinel=3, 26 | /// 27 | /// 分区方式 28 | /// 29 | Partition=4 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Czar.Gateway/Czar.Gateway.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.2 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/Czar.Gateway/Errors/IdentityServer4Error.cs: -------------------------------------------------------------------------------- 1 | using Ocelot.Errors; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace Czar.Gateway.Errors 7 | { 8 | public class IdentityServer4Error:Error 9 | { 10 | public IdentityServer4Error(string message) : base(message, OcelotErrorCode.UnableToCreateAuthenticationHandlerError) 11 | { 12 | 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Czar.Gateway/Errors/InternalServerError.cs: -------------------------------------------------------------------------------- 1 | using Ocelot.Errors; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace Czar.Gateway.Errors 7 | { 8 | /// 9 | /// 金焰的世界 10 | /// 2018-12-10 11 | /// 服务不可用错误 12 | /// 13 | public class InternalServerError:Error 14 | { 15 | public InternalServerError(string message) : base(message, OcelotErrorCode.UnableToCompleteRequestError) 16 | { 17 | 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Czar.Gateway/Errors/RateLimitOptionsError.cs: -------------------------------------------------------------------------------- 1 | using Ocelot.Errors; 2 | 3 | namespace Czar.Gateway.Errors 4 | { 5 | /// 6 | /// 金焰的世界 7 | /// 2018-11-18 8 | /// 限流错误信息 9 | /// 10 | public class RateLimitOptionsError:Error 11 | { 12 | public RateLimitOptionsError(string message) : base(message, OcelotErrorCode.RateLimitOptionsError) 13 | { 14 | 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Czar.Gateway/Extensions/Ext.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using Newtonsoft.Json.Converters; 3 | using Newtonsoft.Json.Linq; 4 | using System.Collections.Generic; 5 | using System.Data; 6 | 7 | namespace Czar.Gateway 8 | { 9 | /// 10 | /// 自定义扩展实现序列化 11 | /// 12 | public static partial class Ext 13 | { 14 | public static object ToJson(this string Json) 15 | { 16 | return Json == null ? null : JsonConvert.DeserializeObject(Json); 17 | } 18 | public static string ToJson(this object obj) 19 | { 20 | var timeConverter = new IsoDateTimeConverter { DateTimeFormat = "yyyy-MM-dd HH:mm:ss" }; 21 | //var timeConverter = new IsoDateTimeConverter { DateTimeFormat = "yyyy-MM-dd" }; 22 | return JsonConvert.SerializeObject(obj, timeConverter); 23 | } 24 | public static string ToJson(this object obj, string datetimeformats) 25 | { 26 | var timeConverter = new IsoDateTimeConverter { DateTimeFormat = datetimeformats }; 27 | return JsonConvert.SerializeObject(obj, timeConverter); 28 | } 29 | public static T ToObject(this string Json) 30 | { 31 | return Json == null ? default(T) : JsonConvert.DeserializeObject(Json); 32 | } 33 | public static List ToList(this string Json) 34 | { 35 | return Json == null ? null : JsonConvert.DeserializeObject>(Json); 36 | } 37 | public static DataTable ToTable(this string Json) 38 | { 39 | return Json == null ? null : JsonConvert.DeserializeObject(Json); 40 | } 41 | public static JObject ToJObject(this string Json) 42 | { 43 | return Json == null ? JObject.Parse("{}") : JObject.Parse(Json.Replace(" ", "")); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Czar.Gateway/Helper/CzarOcelotHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Security.Cryptography; 4 | using System.Text; 5 | 6 | namespace Czar.Gateway 7 | { 8 | public static class CzarOcelotHelper 9 | { 10 | /// 11 | /// 获取加密后缓存的KEY 12 | /// 13 | /// 缓存前缀,防止重复 14 | /// 客户端ID 15 | /// 限流策略,如 1s 2m 3h 4d 16 | /// 限流地址,可使用通配符 17 | /// 18 | public static string ComputeCounterKey(string CounterPrefix, string ClientId, string Period, string Path) 19 | { 20 | var key = $"{CounterPrefix}_{ClientId}_{Period}_{Path}"; 21 | var idBytes = Encoding.UTF8.GetBytes(key); 22 | byte[] hashBytes; 23 | using (var algorithm = SHA1.Create()) 24 | { 25 | hashBytes = algorithm.ComputeHash(idBytes); 26 | } 27 | return BitConverter.ToString(hashBytes).Replace("-", string.Empty); 28 | } 29 | 30 | /// 31 | /// 金焰的世界 32 | /// 2018-11-19 33 | /// 根据限流标识,获取周期秒数 34 | /// 35 | /// 标识 36 | /// 37 | public static int ConvertToSecond(string timeSpan) 38 | { 39 | var l = timeSpan.Length - 1; 40 | var value = timeSpan.Substring(0, l); 41 | var type = timeSpan.Substring(l, 1); 42 | 43 | switch (type) 44 | { 45 | case "d": 46 | return Convert.ToInt32(double.Parse(value) * 24 * 3600); 47 | case "h": 48 | return Convert.ToInt32(double.Parse(value) * 3600); 49 | case "m": 50 | return Convert.ToInt32(double.Parse(value) * 60); 51 | case "s": 52 | return Convert.ToInt32(value); 53 | default: 54 | throw new FormatException($"{timeSpan} can't be converted to TimeSpan, unknown type {type}"); 55 | } 56 | } 57 | 58 | /// 59 | /// 获取格式化后的key 60 | /// 61 | /// 系统标识 62 | /// 分类标识 63 | /// key 64 | /// 65 | public static string GetKey(string prefix,string region, string key) 66 | { 67 | return prefix + "-" + region + "-" + key; 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/Czar.Gateway/Middleware/CzarOcelotMiddlewareExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using Ocelot.Configuration; 3 | using Ocelot.Logging; 4 | using Ocelot.Middleware; 5 | using Ocelot.Middleware.Pipeline; 6 | using System; 7 | using System.Threading.Tasks; 8 | using System.Linq; 9 | using Ocelot.Configuration.Creator; 10 | using Microsoft.Extensions.DependencyInjection; 11 | using Ocelot.Configuration.Repository; 12 | using Ocelot.Responses; 13 | using Microsoft.AspNetCore.Hosting; 14 | using System.Diagnostics; 15 | using Czar.Gateway.Configuration; 16 | using Microsoft.Extensions.Caching.Memory; 17 | 18 | namespace Czar.Gateway.Middleware 19 | { 20 | /// 21 | /// 金焰的世界 22 | /// 2018-11-10 23 | /// 基于Ocelot扩展网关中间件,主要是重写了CreateConfiguration方法,其他内容保持与原有的中间件内容不变。 24 | /// 25 | public static class CzarOcelotMiddlewareExtensions 26 | { 27 | 28 | public static async Task UseCzarOcelot(this IApplicationBuilder builder) 29 | { 30 | await builder.UseCzarOcelot(new OcelotPipelineConfiguration()); 31 | return builder; 32 | } 33 | 34 | public static async Task UseCzarOcelot(this IApplicationBuilder builder, Action pipelineConfiguration) 35 | { 36 | var config = new OcelotPipelineConfiguration(); 37 | pipelineConfiguration?.Invoke(config); 38 | return await builder.UseCzarOcelot(config); 39 | } 40 | 41 | public static async Task UseCzarOcelot(this IApplicationBuilder builder, OcelotPipelineConfiguration pipelineConfiguration) 42 | { 43 | //重写创建配置方法 44 | var configuration = await CreateConfiguration(builder); 45 | 46 | ConfigureDiagnosticListener(builder); 47 | CacheChangeListener(builder); 48 | return CreateOcelotPipeline(builder, pipelineConfiguration); 49 | } 50 | 51 | /// 52 | /// 金焰的世界 53 | /// 2019-03-03 54 | /// 添加缓存数据变更订阅 55 | /// 56 | /// 57 | /// 58 | private static void CacheChangeListener(IApplicationBuilder builder) 59 | { 60 | var config= builder.ApplicationServices.GetService(); 61 | var _cache= builder.ApplicationServices.GetService(); 62 | if (config.ClusterEnvironment) 63 | { 64 | //订阅满足条件的所有事件 65 | RedisHelper.PSubscribe(new[] { config.RedisOcelotKeyPrefix + "*" }, message => 66 | { 67 | var key = message.Channel; 68 | _cache.Remove(key); //直接移除,如果有请求从redis里取 69 | }); 70 | } 71 | } 72 | 73 | private static IApplicationBuilder CreateOcelotPipeline(IApplicationBuilder builder, OcelotPipelineConfiguration pipelineConfiguration) 74 | { 75 | var pipelineBuilder = new OcelotPipelineBuilder(builder.ApplicationServices); 76 | 77 | //pipelineBuilder.BuildOcelotPipeline(pipelineConfiguration); 78 | //使用自定义管道扩展 2018-11-15 金焰的世界 79 | pipelineBuilder.BuildCzarOcelotPipeline(pipelineConfiguration); 80 | 81 | var firstDelegate = pipelineBuilder.Build(); 82 | 83 | /* 84 | inject first delegate into first piece of asp.net middleware..maybe not like this 85 | then because we are updating the http context in ocelot it comes out correct for 86 | rest of asp.net.. 87 | */ 88 | 89 | builder.Properties["analysis.NextMiddlewareName"] = "TransitionToOcelotMiddleware"; 90 | 91 | builder.Use(async (context, task) => 92 | { 93 | var downstreamContext = new DownstreamContext(context); 94 | await firstDelegate.Invoke(downstreamContext); 95 | }); 96 | 97 | return builder; 98 | } 99 | 100 | private static async Task CreateConfiguration(IApplicationBuilder builder) 101 | { 102 | //提取文件配置信息 103 | var fileConfig = await builder.ApplicationServices.GetService().Get(); 104 | var internalConfigCreator = builder.ApplicationServices.GetService(); 105 | var internalConfig = await internalConfigCreator.Create(fileConfig.Data); 106 | //如果配置文件错误直接抛出异常 107 | if (internalConfig.IsError) 108 | { 109 | ThrowToStopOcelotStarting(internalConfig); 110 | } 111 | //配置信息缓存,这块需要注意实现方式,因为后期我们需要改造下满足分布式架构,这篇不做讲解 112 | var internalConfigRepo = builder.ApplicationServices.GetService(); 113 | internalConfigRepo.AddOrReplace(internalConfig.Data); 114 | //获取中间件配置委托 115 | var configurations = builder.ApplicationServices.GetServices(); 116 | foreach (var configuration in configurations) 117 | { 118 | await configuration(builder); 119 | } 120 | return GetOcelotConfigAndReturn(internalConfigRepo); 121 | } 122 | 123 | private static bool IsError(Response response) 124 | { 125 | return response == null || response.IsError; 126 | } 127 | 128 | private static IInternalConfiguration GetOcelotConfigAndReturn(IInternalConfigurationRepository provider) 129 | { 130 | var ocelotConfiguration = provider.Get(); 131 | 132 | if (ocelotConfiguration?.Data == null || ocelotConfiguration.IsError) 133 | { 134 | ThrowToStopOcelotStarting(ocelotConfiguration); 135 | } 136 | 137 | return ocelotConfiguration.Data; 138 | } 139 | 140 | private static void ThrowToStopOcelotStarting(Response config) 141 | { 142 | throw new Exception($"Unable to start Ocelot, errors are: {string.Join(",", config.Errors.Select(x => x.ToString()))}"); 143 | } 144 | 145 | private static void ConfigureDiagnosticListener(IApplicationBuilder builder) 146 | { 147 | var env = builder.ApplicationServices.GetService(); 148 | var listener = builder.ApplicationServices.GetService(); 149 | var diagnosticListener = builder.ApplicationServices.GetService(); 150 | diagnosticListener.SubscribeWithAdapter(listener); 151 | } 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /src/Czar.Gateway/Middleware/OcelotPipelineExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using Czar.Gateway.Authentication.Middleware; 4 | using Czar.Gateway.RateLimit.Middleware; 5 | using Czar.Gateway.Requester.Middleware; 6 | using Czar.Gateway.Responder.Middleware; 7 | using Czar.Gateway.Rpc.Middleware; 8 | using Ocelot.Authentication.Middleware; 9 | using Ocelot.Authorisation.Middleware; 10 | using Ocelot.Cache.Middleware; 11 | using Ocelot.DownstreamRouteFinder.Middleware; 12 | using Ocelot.DownstreamUrlCreator.Middleware; 13 | using Ocelot.Errors.Middleware; 14 | using Ocelot.Headers.Middleware; 15 | using Ocelot.LoadBalancer.Middleware; 16 | using Ocelot.Middleware; 17 | using Ocelot.Middleware.Pipeline; 18 | using Ocelot.RateLimit.Middleware; 19 | using Ocelot.Request.Middleware; 20 | using Ocelot.Requester.Middleware; 21 | using Ocelot.RequestId.Middleware; 22 | using Ocelot.Responder.Middleware; 23 | using Ocelot.WebSockets.Middleware; 24 | 25 | namespace Czar.Gateway.Middleware 26 | { 27 | /// 28 | /// 金焰的世界 29 | /// 2018-11-15 30 | /// 网关管道扩展 31 | /// 32 | public static class OcelotPipelineExtensions 33 | { 34 | public static OcelotRequestDelegate BuildCzarOcelotPipeline(this IOcelotPipelineBuilder builder, 35 | OcelotPipelineConfiguration pipelineConfiguration) 36 | { 37 | 38 | // 注册一个全局异常 39 | builder.UseExceptionHandlerMiddleware(); 40 | 41 | // 如果请求是websocket使用单独的管道 42 | builder.MapWhen(context => context.HttpContext.WebSockets.IsWebSocketRequest, 43 | app => 44 | { 45 | app.UseDownstreamRouteFinderMiddleware(); 46 | app.UseDownstreamRequestInitialiser(); 47 | app.UseLoadBalancingMiddleware(); 48 | app.UseDownstreamUrlCreatorMiddleware(); 49 | app.UseWebSocketsProxyMiddleware(); 50 | }); 51 | 52 | // 添加自定义的错误管道 53 | builder.UseIfNotNull(pipelineConfiguration.PreErrorResponderMiddleware); 54 | 55 | //使用自定义的输出管道 56 | builder.UseCzarResponderMiddleware(); 57 | 58 | // 下游路由匹配管道 59 | builder.UseDownstreamRouteFinderMiddleware(); 60 | 61 | //增加自定义扩展管道 62 | if (pipelineConfiguration.MapWhenOcelotPipeline != null) 63 | { 64 | foreach (var pipeline in pipelineConfiguration.MapWhenOcelotPipeline) 65 | { 66 | builder.MapWhen(pipeline); 67 | } 68 | } 69 | 70 | // 使用Http头部转换管道 71 | builder.UseHttpHeadersTransformationMiddleware(); 72 | 73 | // 初始化下游请求管道 74 | builder.UseDownstreamRequestInitialiser(); 75 | 76 | // 使用自定义限流管道 77 | builder.UseRateLimiting(); 78 | 79 | //使用请求ID生成管道 80 | builder.UseRequestIdMiddleware(); 81 | 82 | //使用自定义授权前管道 83 | builder.UseIfNotNull(pipelineConfiguration.PreAuthenticationMiddleware); 84 | 85 | //根据请求判断是否启用授权来使用管道 86 | if (pipelineConfiguration.AuthenticationMiddleware == null) 87 | { 88 | builder.UseAuthenticationMiddleware(); 89 | } 90 | else 91 | { 92 | builder.Use(pipelineConfiguration.AuthenticationMiddleware); 93 | } 94 | 95 | //添加自定义限流中间件 2018-11-18 金焰的世界 96 | builder.UseCzarClientRateLimitMiddleware(); 97 | 98 | //添加自定义授权中间件 2018-11-15 金焰的世界 99 | builder.UseAhphAuthenticationMiddleware(); 100 | 101 | //启用自定义的认证之前中间件 102 | builder.UseIfNotNull(pipelineConfiguration.PreAuthorisationMiddleware); 103 | 104 | //是否使用自定义的认证中间件 105 | if (pipelineConfiguration.AuthorisationMiddleware == null) 106 | { 107 | builder.UseAuthorisationMiddleware(); 108 | } 109 | else 110 | { 111 | builder.Use(pipelineConfiguration.AuthorisationMiddleware); 112 | } 113 | 114 | // 使用自定义的参数构建中间件 115 | builder.UseIfNotNull(pipelineConfiguration.PreQueryStringBuilderMiddleware); 116 | 117 | // 使用负载均衡中间件 118 | builder.UseLoadBalancingMiddleware(); 119 | 120 | // 使用下游地址创建中间件 121 | builder.UseDownstreamUrlCreatorMiddleware(); 122 | 123 | // 使用缓存中间件 124 | builder.UseOutputCacheMiddleware(); 125 | 126 | //判断下游的是否启用rpc通信,切换到RPC处理 127 | builder.MapWhen(context => context.DownstreamReRoute.DownstreamScheme.Equals("rpc", StringComparison.OrdinalIgnoreCase), app => 128 | { 129 | app.UseCzarRpcMiddleware(); 130 | }); 131 | 132 | //使用下游请求中间件 133 | builder.UseCzaHttpRequesterMiddleware(); 134 | 135 | return builder.Build(); 136 | } 137 | 138 | private static void UseIfNotNull(this IOcelotPipelineBuilder builder, 139 | Func, Task> middleware) 140 | { 141 | if (middleware != null) 142 | { 143 | builder.Use(middleware); 144 | } 145 | } 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /src/Czar.Gateway/Middleware/ServiceCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using Czar.Gateway.Authentication; 2 | using Czar.Gateway.Cache; 3 | using Czar.Gateway.Configuration; 4 | using Czar.Gateway.Stores.MySql; 5 | using Czar.Gateway.Stores.SqlServer; 6 | using Czar.Gateway.RateLimit; 7 | using Czar.Gateway.Responder; 8 | using Microsoft.Extensions.DependencyInjection; 9 | using Ocelot.Cache; 10 | using Ocelot.Configuration.File; 11 | using Ocelot.Configuration.Repository; 12 | using Ocelot.DependencyInjection; 13 | using Ocelot.Responder; 14 | using System; 15 | using Czar.Gateway.Rpc; 16 | using Czar.Rpc.Message; 17 | using Ocelot.Configuration; 18 | using Czar.Rpc.DotNetty.Extensions; 19 | using Microsoft.Extensions.Configuration; 20 | using System.Linq; 21 | 22 | namespace Czar.Gateway.Middleware 23 | { 24 | /// 25 | /// 金焰的世界 26 | /// 2018-11-12 27 | /// 扩展Ocelot实现的自定义的注入 28 | /// 29 | public static class ServiceCollectionExtensions 30 | { 31 | /// 32 | /// 添加默认的注入方式,所有需要传入的参数都是用默认值 33 | /// 34 | /// 35 | /// 36 | public static IOcelotBuilder AddCzarOcelot(this IServiceCollection builder, Action option) 37 | { 38 | var options = new CzarOcelotConfiguration(); 39 | builder.AddSingleton(options); 40 | option?.Invoke(options); 41 | 42 | //配置文件仓储注入 43 | builder.AddSingleton(); 44 | builder.AddSingleton(); 45 | builder.AddSingleton(); 46 | 47 | //注册后端服务 48 | builder.AddHostedService(); 49 | builder.AddMemoryCache(); //添加本地缓存 50 | #region 启动Redis缓存,并支持普通模式 官方集群模式 哨兵模式 分区模式 51 | if (options.ClusterEnvironment) 52 | { 53 | //默认使用普通模式 54 | var csredis = new CSRedis.CSRedisClient(options.RedisConnectionString); 55 | switch (options.RedisStoreMode) 56 | { 57 | case RedisStoreMode.Partition: 58 | var NodesIndex = options.RedisSentinelOrPartitionConStr; 59 | Func nodeRule = null; 60 | csredis = new CSRedis.CSRedisClient(nodeRule, options.RedisSentinelOrPartitionConStr); 61 | break; 62 | case RedisStoreMode.Sentinel: 63 | csredis = new CSRedis.CSRedisClient(options.RedisConnectionString, options.RedisSentinelOrPartitionConStr); 64 | break; 65 | } 66 | //初始化 RedisHelper 67 | RedisHelper.Initialization(csredis); 68 | } 69 | #endregion 70 | builder.AddSingleton, CzarMemoryCache>(); 71 | builder.AddSingleton, CzarMemoryCache>(); 72 | builder.AddSingleton, CzarMemoryCache>(); 73 | builder.AddSingleton(); 74 | builder.AddSingleton, CzarMemoryCache>(); 75 | builder.AddSingleton, CzarMemoryCache>(); 76 | builder.AddSingleton, CzarMemoryCache>(); 77 | builder.AddSingleton, CzarMemoryCache>(); 78 | //注入授权 79 | builder.AddSingleton(); 80 | //注入限流实现 81 | builder.AddSingleton(); 82 | 83 | //重写错误状态码 84 | builder.AddSingleton(); 85 | 86 | //http输出转换类 87 | builder.AddSingleton(); 88 | 89 | var service = builder.First(x => x.ServiceType == typeof(IConfiguration)); 90 | var configuration = (IConfiguration)service.ImplementationInstance; 91 | //Rpc应用 92 | builder.AddSingleton(); 93 | builder.AddSingleton(); 94 | builder.AddLibuvTcpClient(configuration); 95 | 96 | return new OcelotBuilder(builder, configuration); 97 | } 98 | 99 | /// 100 | /// 扩展使用Mysql存储。 101 | /// 102 | /// 103 | /// 104 | public static IOcelotBuilder UseMySql(this IOcelotBuilder builder) 105 | { 106 | builder.Services.AddSingleton(); 107 | builder.Services.AddSingleton(); 108 | builder.Services.AddSingleton(); 109 | builder.Services.AddSingleton(); 110 | return builder; 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/Czar.Gateway/Model/CzarAuthGroup.cs: -------------------------------------------------------------------------------- 1 | namespace Czar.Gateway.Model 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.ComponentModel.DataAnnotations; 6 | using System.ComponentModel.DataAnnotations.Schema; 7 | 8 | public partial class CzarAuthGroup 9 | { 10 | [Key] 11 | public int GroupId { get; set; } 12 | 13 | [Required] 14 | [StringLength(100)] 15 | public string GroupName { get; set; } 16 | 17 | [StringLength(500)] 18 | public string GroupDetail { get; set; } 19 | 20 | public int InfoStatus { get; set; } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Czar.Gateway/Model/CzarClientGroup.cs: -------------------------------------------------------------------------------- 1 | namespace Czar.Gateway.Model 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.ComponentModel.DataAnnotations; 6 | using System.ComponentModel.DataAnnotations.Schema; 7 | 8 | public partial class CzarClientGroup 9 | { 10 | [Key] 11 | public int ClientGroupId { get; set; } 12 | 13 | public int? Id { get; set; } 14 | 15 | public int? GroupId { get; set; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Czar.Gateway/Model/CzarClientLimitGroup.cs: -------------------------------------------------------------------------------- 1 | namespace Czar.Gateway.Model 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.ComponentModel.DataAnnotations; 6 | using System.ComponentModel.DataAnnotations.Schema; 7 | 8 | public partial class CzarClientLimitGroup 9 | { 10 | [Key] 11 | public int ClientLimitGroupId { get; set; } 12 | 13 | public int? Id { get; set; } 14 | 15 | public int? LimitGroupId { get; set; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Czar.Gateway/Model/CzarClientReRouteWhiteList.cs: -------------------------------------------------------------------------------- 1 | namespace Czar.Gateway.Model 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.ComponentModel.DataAnnotations; 6 | using System.ComponentModel.DataAnnotations.Schema; 7 | public partial class CzarClientReRouteWhiteList 8 | { 9 | [Key] 10 | public int WhiteListId { get; set; } 11 | 12 | public int? ReRouteId { get; set; } 13 | 14 | public int? Id { get; set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Czar.Gateway/Model/CzarClients.cs: -------------------------------------------------------------------------------- 1 | namespace Czar.Gateway.Model 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.ComponentModel.DataAnnotations; 6 | using System.ComponentModel.DataAnnotations.Schema; 7 | 8 | public partial class CzarClients 9 | { 10 | [Key] 11 | public int Id { get; set; } 12 | 13 | public int AbsoluteRefreshTokenLifetime { get; set; } 14 | 15 | public int AccessTokenLifetime { get; set; } 16 | 17 | public int AccessTokenType { get; set; } 18 | 19 | public bool AllowAccessTokensViaBrowser { get; set; } 20 | 21 | public bool AllowOfflineAccess { get; set; } 22 | 23 | public bool AllowPlainTextPkce { get; set; } 24 | 25 | public bool AllowRememberConsent { get; set; } 26 | 27 | public bool AlwaysIncludeUserClaimsInIdToken { get; set; } 28 | 29 | public bool AlwaysSendClientClaims { get; set; } 30 | 31 | public int AuthorizationCodeLifetime { get; set; } 32 | 33 | public bool? BackChannelLogoutSessionRequired { get; set; } 34 | 35 | [StringLength(500)] 36 | public string BackChannelLogoutUri { get; set; } 37 | 38 | [Required] 39 | [StringLength(50)] 40 | public string ClientClaimsPrefix { get; set; } 41 | 42 | [Required] 43 | [StringLength(50)] 44 | public string ClientId { get; set; } 45 | 46 | [Required] 47 | [StringLength(200)] 48 | public string ClientName { get; set; } 49 | 50 | [StringLength(100)] 51 | public string ClientUri { get; set; } 52 | 53 | public int? ConsentLifetime { get; set; } 54 | 55 | [StringLength(500)] 56 | public string Description { get; set; } 57 | 58 | public bool EnableLocalLogin { get; set; } 59 | 60 | public bool Enabled { get; set; } 61 | 62 | public bool FrontChannelLogoutSessionRequired { get; set; } 63 | 64 | [StringLength(100)] 65 | public string FrontChannelLogoutUri { get; set; } 66 | 67 | public int IdentityTokenLifetime { get; set; } 68 | 69 | public bool IncludeJwtId { get; set; } 70 | 71 | [StringLength(150)] 72 | public string LogoUri { get; set; } 73 | 74 | [StringLength(200)] 75 | public string PairWiseSubjectSalt { get; set; } 76 | 77 | [StringLength(50)] 78 | public string ProtocolType { get; set; } 79 | 80 | public int RefreshTokenExpiration { get; set; } 81 | 82 | public int RefreshTokenUsage { get; set; } 83 | 84 | public bool RequireClientSecret { get; set; } 85 | 86 | public bool RequireConsent { get; set; } 87 | 88 | public bool RequirePkce { get; set; } 89 | 90 | public int SlidingRefreshTokenLifetime { get; set; } 91 | 92 | public bool UpdateAccessTokenClaimsOnRefresh { get; set; } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/Czar.Gateway/Model/CzarConfigReRoutes.cs: -------------------------------------------------------------------------------- 1 | namespace Czar.Gateway.Model 2 | { 3 | using System.ComponentModel.DataAnnotations; 4 | 5 | public partial class CzarConfigReRoutes 6 | { 7 | [Key] 8 | public int CtgRouteId { get; set; } 9 | 10 | public int? AhphId { get; set; } 11 | 12 | public int? ReRouteId { get; set; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/Czar.Gateway/Model/CzarGlobalConfiguration.cs: -------------------------------------------------------------------------------- 1 | namespace Czar.Gateway.Model 2 | { 3 | using System.ComponentModel.DataAnnotations; 4 | public partial class CzarGlobalConfiguration 5 | { 6 | [Key] 7 | public int AhphId { get; set; } 8 | 9 | [Required] 10 | [StringLength(100)] 11 | public string GatewayName { get; set; } 12 | 13 | [StringLength(100)] 14 | public string RequestIdKey { get; set; } 15 | 16 | [StringLength(100)] 17 | public string BaseUrl { get; set; } 18 | 19 | [StringLength(50)] 20 | public string DownstreamScheme { get; set; } 21 | 22 | [StringLength(500)] 23 | public string ServiceDiscoveryProvider { get; set; } 24 | 25 | [StringLength(500)] 26 | public string LoadBalancerOptions { get; set; } 27 | 28 | [StringLength(500)] 29 | public string HttpHandlerOptions { get; set; } 30 | 31 | [StringLength(200)] 32 | public string QoSOptions { get; set; } 33 | 34 | public int IsDefault { get; set; } 35 | 36 | public int InfoStatus { get; set; } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Czar.Gateway/Model/CzarLimitGroup.cs: -------------------------------------------------------------------------------- 1 | namespace Czar.Gateway.Model 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.ComponentModel.DataAnnotations; 6 | using System.ComponentModel.DataAnnotations.Schema; 7 | public partial class CzarLimitGroup 8 | { 9 | [Key] 10 | public int LimitGroupId { get; set; } 11 | 12 | [Required] 13 | [StringLength(100)] 14 | public string LimitGroupName { get; set; } 15 | 16 | [StringLength(500)] 17 | public string LimitGroupDetail { get; set; } 18 | 19 | public int InfoStatus { get; set; } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Czar.Gateway/Model/CzarLimitGroupRule.cs: -------------------------------------------------------------------------------- 1 | namespace Czar.Gateway.Model 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.ComponentModel.DataAnnotations; 6 | using System.ComponentModel.DataAnnotations.Schema; 7 | public partial class CzarLimitGroupRule 8 | { 9 | [Key] 10 | public int GroupRuleId { get; set; } 11 | 12 | public int? LimitGroupId { get; set; } 13 | 14 | public int? ReRouteLimitId { get; set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Czar.Gateway/Model/CzarLimitRule.cs: -------------------------------------------------------------------------------- 1 | namespace Czar.Gateway.Model 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.ComponentModel.DataAnnotations; 6 | using System.ComponentModel.DataAnnotations.Schema; 7 | public partial class CzarLimitRule 8 | { 9 | [Key] 10 | public int RuleId { get; set; } 11 | 12 | [Required] 13 | [StringLength(200)] 14 | public string LimitName { get; set; } 15 | 16 | [Required] 17 | [StringLength(50)] 18 | public string LimitPeriod { get; set; } 19 | 20 | public int LimitNum { get; set; } 21 | 22 | public int InfoStatus { get; set; } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Czar.Gateway/Model/CzarReRoute.cs: -------------------------------------------------------------------------------- 1 | namespace Czar.Gateway.Model 2 | { 3 | using System.ComponentModel.DataAnnotations; 4 | 5 | public partial class CzarReRoute 6 | { 7 | [Key] 8 | public int ReRouteId { get; set; } 9 | 10 | public int? ItemId { get; set; } 11 | 12 | [Required] 13 | [StringLength(150)] 14 | public string UpstreamPathTemplate { get; set; } 15 | 16 | [Required] 17 | [StringLength(50)] 18 | public string UpstreamHttpMethod { get; set; } 19 | 20 | [StringLength(100)] 21 | public string UpstreamHost { get; set; } 22 | 23 | [Required] 24 | [StringLength(50)] 25 | public string DownstreamScheme { get; set; } 26 | 27 | [Required] 28 | [StringLength(200)] 29 | public string DownstreamPathTemplate { get; set; } 30 | 31 | [StringLength(500)] 32 | public string DownstreamHostAndPorts { get; set; } 33 | 34 | [StringLength(300)] 35 | public string AuthenticationOptions { get; set; } 36 | 37 | [StringLength(100)] 38 | public string RequestIdKey { get; set; } 39 | 40 | [StringLength(200)] 41 | public string CacheOptions { get; set; } 42 | 43 | [StringLength(100)] 44 | public string ServiceName { get; set; } 45 | 46 | [StringLength(500)] 47 | public string LoadBalancerOptions { get; set; } 48 | 49 | [StringLength(200)] 50 | public string QoSOptions { get; set; } 51 | 52 | [StringLength(200)] 53 | public string DelegatingHandlers { get; set; } 54 | 55 | public int? Priority { get; set; } 56 | 57 | public int InfoStatus { get; set; } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/Czar.Gateway/Model/CzarReRouteGroupAuth.cs: -------------------------------------------------------------------------------- 1 | namespace Czar.Gateway.Model 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.ComponentModel.DataAnnotations; 6 | using System.ComponentModel.DataAnnotations.Schema; 7 | 8 | public partial class CzarReRouteGroupAuth 9 | { 10 | [Key] 11 | public int AuthId { get; set; } 12 | 13 | public int? GroupId { get; set; } 14 | 15 | public int? ReRouteId { get; set; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Czar.Gateway/Model/CzarReRouteLimitRule.cs: -------------------------------------------------------------------------------- 1 | namespace Czar.Gateway.Model 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.ComponentModel.DataAnnotations; 6 | using System.ComponentModel.DataAnnotations.Schema; 7 | public partial class CzarReRouteLimitRule 8 | { 9 | [Key] 10 | public int ReRouteLimitId { get; set; } 11 | 12 | public int? RuleId { get; set; } 13 | 14 | public int? ReRouteId { get; set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Czar.Gateway/Model/CzarReRouteRpcConfig.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.Text; 5 | 6 | namespace Czar.Gateway.Model 7 | { 8 | public partial class CzarReRouteRpcConfig 9 | { 10 | [Key] 11 | public int RpcId { get; set; } 12 | 13 | 14 | public int ReRouteId { get; set; } 15 | 16 | [Required] 17 | [StringLength(100)] 18 | public string ServantName { get; set; } 19 | 20 | [Required] 21 | [StringLength(100)] 22 | public string FuncName { get; set; } 23 | 24 | 25 | public bool IsOneway { get; set; } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Czar.Gateway/Model/CzarReRoutesItem.cs: -------------------------------------------------------------------------------- 1 | namespace Czar.Gateway.Model 2 | { 3 | using System.ComponentModel.DataAnnotations; 4 | public partial class CzarReRoutesItem 5 | { 6 | [Key] 7 | public int ItemId { get; set; } 8 | 9 | [Required] 10 | [StringLength(100)] 11 | public string ItemName { get; set; } 12 | 13 | [StringLength(500)] 14 | public string ItemDetail { get; set; } 15 | 16 | public int? ItemParentId { get; set; } 17 | 18 | public int InfoStatus { get; set; } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Czar.Gateway/RateLimit/CzarClientRateLimitCounter.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | 4 | namespace Czar.Gateway.RateLimit 5 | { 6 | /// 7 | /// 金焰的世界 8 | /// 2018-11-19 9 | /// 客户端限流计数器 10 | /// 11 | public struct CzarClientRateLimitCounter 12 | { 13 | [JsonConstructor] 14 | public CzarClientRateLimitCounter(DateTime timestamp, long totalRequests) 15 | { 16 | Timestamp = timestamp; 17 | TotalRequests = totalRequests; 18 | } 19 | 20 | /// 21 | /// 最后请求时间 22 | /// 23 | public DateTime Timestamp { get; private set; } 24 | 25 | /// 26 | /// 请求总数 27 | /// 28 | public long TotalRequests { get; private set; } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Czar.Gateway/RateLimit/CzarClientRateLimitOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Czar.Gateway.RateLimit 6 | { 7 | /// 8 | /// 金焰的世界 9 | /// 2018-11.19 10 | /// 限流相关选项 11 | /// 12 | public class CzarClientRateLimitOptions 13 | { 14 | /// 15 | /// 限流请求的地址 16 | /// 17 | public string RateLimitPath { get; set; } 18 | 19 | /// 20 | /// 限制的访问次数 21 | /// 22 | public int Limit { get; set; } 23 | 24 | /// 25 | /// 限流的策略,如 1s 2m 3h 4d 26 | /// 27 | public string Period { get; set; } 28 | 29 | /// 30 | /// 客户端ID 31 | /// 32 | public string ClientId { get; set; } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Czar.Gateway/RateLimit/CzarClientRateLimitProcessor.cs: -------------------------------------------------------------------------------- 1 | using Czar.Gateway.Cache; 2 | using Czar.Gateway.Configuration; 3 | using Ocelot.Cache; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace Czar.Gateway.RateLimit 10 | { 11 | /// 12 | /// 金焰的世界 13 | /// 2018-11-19 14 | /// 实现客户端限流处理器 15 | /// 16 | public class CzarClientRateLimitProcessor : IClientRateLimitProcessor 17 | { 18 | private readonly CzarOcelotConfiguration _options; 19 | private readonly IOcelotCache _ocelotCache; 20 | private readonly IOcelotCache _rateLimitRuleCache; 21 | private readonly IOcelotCache _clientRateLimitCounter; 22 | private readonly IClientRateLimitRepository _clientRateLimitRepository; 23 | private static readonly object _processLocker = new object(); 24 | public CzarClientRateLimitProcessor(CzarOcelotConfiguration options,IClientRateLimitRepository clientRateLimitRepository, IOcelotCache clientRateLimitCounter, IOcelotCache ocelotCache, IOcelotCache rateLimitRuleCache) 25 | { 26 | _options = options; 27 | _clientRateLimitRepository = clientRateLimitRepository; 28 | _clientRateLimitCounter = clientRateLimitCounter; 29 | _ocelotCache = ocelotCache; 30 | _rateLimitRuleCache = rateLimitRuleCache; 31 | } 32 | 33 | /// 34 | /// 校验客户端限流结果 35 | /// 36 | /// 客户端ID 37 | /// 请求地址 38 | /// 39 | public async Task CheckClientRateLimitResultAsync(string clientid, string path) 40 | { 41 | var result = false; 42 | var clientRule = new List(); 43 | //1、校验路由是否有限流策略 44 | result = !await CheckReRouteRuleAsync(path); 45 | if (!result) 46 | {//2、校验客户端是否被限流了 47 | var limitResult = await CheckClientRateLimitAsync(clientid, path); 48 | result = !limitResult.RateLimit; 49 | clientRule = limitResult.rateLimitOptions; 50 | } 51 | if (!result) 52 | {//3、校验客户端是否启动白名单 53 | result = await CheckClientReRouteWhiteListAsync(clientid, path); 54 | } 55 | if (!result) 56 | {//4、校验是否触发限流及计数 57 | result = CheckRateLimitResult(clientRule); 58 | } 59 | return result; 60 | 61 | } 62 | 63 | /// 64 | /// 检验是否启用限流规则 65 | /// 66 | /// 请求地址 67 | /// 68 | private async Task CheckReRouteRuleAsync(string path) 69 | { 70 | var region = CzarCacheRegion.ClientRoleModelRegion; 71 | var key = path; 72 | var cacheResult = _ocelotCache.Get(key, region); 73 | if (cacheResult != null) 74 | {//提取缓存数据 75 | return cacheResult.Role; 76 | } 77 | else 78 | {//重新获取限流策略 79 | var result = await _clientRateLimitRepository.CheckReRouteRuleAsync(path); 80 | _ocelotCache.Add(key, new ClientRoleModel() { CacheTime = DateTime.Now, Role = result }, TimeSpan.FromSeconds(_options.CzarCacheTime), region); 81 | return result; 82 | } 83 | 84 | } 85 | 86 | /// 87 | /// 校验客户端限流规则 88 | /// 89 | /// 客户端ID 90 | /// 请求地址 91 | /// 92 | private async Task<(bool RateLimit, List rateLimitOptions)> CheckClientRateLimitAsync(string clientid, string path) 93 | { 94 | var region = CzarCacheRegion.RateLimitRuleModelRegion; 95 | var key = clientid + path; 96 | var cacheResult = _rateLimitRuleCache.Get(key, region); 97 | if (cacheResult != null) 98 | {//提取缓存数据 99 | return (cacheResult.RateLimit, cacheResult.rateLimitOptions); 100 | } 101 | else 102 | {//重新获取限流策略 103 | var result = await _clientRateLimitRepository.CheckClientRateLimitAsync(clientid, path); 104 | _rateLimitRuleCache.Add(key, new RateLimitRuleModel() { RateLimit=result.RateLimit, rateLimitOptions=result.rateLimitOptions }, TimeSpan.FromSeconds(_options.CzarCacheTime), region); 105 | return result; 106 | } 107 | } 108 | 109 | /// 110 | /// 校验是否设置了路由白名单 111 | /// 112 | /// 客户端ID 113 | /// 请求地址 114 | /// 115 | private async Task CheckClientReRouteWhiteListAsync(string clientid, string path) 116 | { 117 | var region = CzarCacheRegion.ClientReRouteWhiteListRegion; 118 | var key = clientid+ path; 119 | var cacheResult = _ocelotCache.Get(key, region); 120 | if (cacheResult != null) 121 | {//提取缓存数据 122 | return cacheResult.Role; 123 | } 124 | else 125 | {//重新获取限流策略 126 | var result = await _clientRateLimitRepository.CheckClientReRouteWhiteListAsync(clientid,path); 127 | _ocelotCache.Add(key, new ClientRoleModel() { CacheTime = DateTime.Now, Role = result }, TimeSpan.FromSeconds(_options.CzarCacheTime), region); 128 | return result; 129 | } 130 | } 131 | 132 | /// 133 | /// 校验完整的限流规则 134 | /// 135 | /// 限流配置 136 | /// 137 | private bool CheckRateLimitResult(List rateLimitOptions) 138 | { 139 | bool result = true; 140 | if (rateLimitOptions != null && rateLimitOptions.Count > 0) 141 | {//校验策略 142 | foreach (var op in rateLimitOptions) 143 | { 144 | CzarClientRateLimitCounter counter = new CzarClientRateLimitCounter(DateTime.UtcNow, 1); 145 | //分别对每个策略校验 146 | var enablePrefix = CzarCacheRegion.CzarClientRateLimitCounterRegion; 147 | var key = CzarOcelotHelper.ComputeCounterKey(enablePrefix, op.ClientId, op.Period, op.RateLimitPath); 148 | var periodTimestamp = CzarOcelotHelper.ConvertToSecond(op.Period); 149 | lock (_processLocker) 150 | { 151 | var rateLimitCounter = _clientRateLimitCounter.Get(key, enablePrefix); 152 | if (rateLimitCounter.HasValue) 153 | {//提取当前的计数情况 154 | // 请求次数增长 155 | var totalRequests = rateLimitCounter.Value.TotalRequests + 1; 156 | // 深拷贝 157 | counter = new CzarClientRateLimitCounter(rateLimitCounter.Value.Timestamp, totalRequests); 158 | } 159 | else 160 | {//写入限流策略 161 | _clientRateLimitCounter.Add(key, counter,TimeSpan.FromSeconds(periodTimestamp), enablePrefix); 162 | } 163 | } 164 | if (counter.TotalRequests > op.Limit) 165 | {//更新请求记录,并标记为失败 166 | result = false; 167 | } 168 | if (counter.TotalRequests > 1 && counter.TotalRequests <= op.Limit) 169 | {//更新缓存配置信息 170 | //获取限流剩余时间 171 | var cur = (int)(counter.Timestamp.AddSeconds(periodTimestamp) - DateTime.UtcNow).TotalSeconds; 172 | _clientRateLimitCounter.Add(key, counter, TimeSpan.FromSeconds(cur), enablePrefix); 173 | } 174 | } 175 | } 176 | return result; 177 | } 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /src/Czar.Gateway/RateLimit/IClientRateLimitProcessor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Threading.Tasks; 5 | 6 | namespace Czar.Gateway.RateLimit 7 | { 8 | /// 9 | /// 金焰的世界 10 | /// 2018-11-18 11 | /// 客户端限流处理器 12 | /// 13 | public interface IClientRateLimitProcessor 14 | { 15 | /// 16 | /// 校验客户端限流结果 17 | /// 18 | /// 客户端ID 19 | /// 应用策略地址 20 | /// 21 | Task CheckClientRateLimitResultAsync(string clientid, string path); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Czar.Gateway/RateLimit/IClientRateLimitRepository.cs: -------------------------------------------------------------------------------- 1 |  2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | 5 | namespace Czar.Gateway.RateLimit 6 | { 7 | /// 8 | /// 金焰的世界 9 | /// 2018-11-19 10 | /// 客户端限流相关仓储接口 11 | /// 12 | public interface IClientRateLimitRepository 13 | { 14 | /// 15 | /// 校验是否启用限流规则 16 | /// 17 | /// 请求地址 18 | /// 19 | Task CheckReRouteRuleAsync(string path); 20 | 21 | /// 22 | /// 校验客户端限流规则 23 | /// 24 | /// 客户端ID 25 | /// 请求地址 26 | /// 27 | Task<(bool RateLimit, List rateLimitOptions)> CheckClientRateLimitAsync(string clientid, string path); 28 | 29 | /// 30 | /// 校验是否设置了路由白名单 31 | /// 32 | /// 客户端ID 33 | /// 请求地址 34 | /// 35 | Task CheckClientReRouteWhiteListAsync(string clientid, string path); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Czar.Gateway/RateLimit/Middleware/CzarClientRateLimitMiddleware.cs: -------------------------------------------------------------------------------- 1 | using Czar.Gateway.Configuration; 2 | using Czar.Gateway.Errors; 3 | using Ocelot.Logging; 4 | using Ocelot.Middleware; 5 | using System.Linq; 6 | using System.Threading.Tasks; 7 | 8 | namespace Czar.Gateway.RateLimit.Middleware 9 | { 10 | 11 | /// 12 | /// 金焰的世界 13 | /// 2018-11-18 14 | /// 自定义客户端限流中间件 15 | /// 16 | public class CzarClientRateLimitMiddleware : OcelotMiddleware 17 | { 18 | private readonly IClientRateLimitProcessor _clientRateLimitProcessor; 19 | private readonly OcelotRequestDelegate _next; 20 | private readonly CzarOcelotConfiguration _options; 21 | public CzarClientRateLimitMiddleware(OcelotRequestDelegate next, 22 | IOcelotLoggerFactory loggerFactory, 23 | IClientRateLimitProcessor clientRateLimitProcessor, 24 | CzarOcelotConfiguration options) 25 | : base(loggerFactory.CreateLogger()) 26 | { 27 | _next = next; 28 | _clientRateLimitProcessor = clientRateLimitProcessor; 29 | _options = options; 30 | } 31 | 32 | public async Task Invoke(DownstreamContext context) 33 | { 34 | var clientId = "client_cjy"; //使用默认的客户端 35 | if (!context.IsError) 36 | { 37 | if (!_options.ClientRateLimit) 38 | { 39 | Logger.LogInformation($"未启用客户端限流中间件"); 40 | await _next.Invoke(context); 41 | } 42 | else 43 | { 44 | //非认证的渠道 45 | if (!context.DownstreamReRoute.IsAuthenticated) 46 | { 47 | if (context.HttpContext.Request.Headers.Keys.Contains(_options.ClientKey)) 48 | { 49 | clientId = context.HttpContext.Request.Headers[_options.ClientKey].First(); 50 | } 51 | } 52 | else 53 | {//认证过的渠道,从Claim中提取 54 | var clientClaim = context.HttpContext.User.Claims.FirstOrDefault(p => p.Type == _options.ClientKey); 55 | if (!string.IsNullOrEmpty(clientClaim?.Value)) 56 | { 57 | clientId = clientClaim?.Value; 58 | } 59 | } 60 | //路由地址 61 | var path = context.DownstreamReRoute.UpstreamPathTemplate.OriginalValue; 62 | //1、校验路由是否有限流策略 63 | //2、校验客户端是否被限流了 64 | //3、校验客户端是否启动白名单 65 | //4、校验是否触发限流及计数 66 | if (await _clientRateLimitProcessor.CheckClientRateLimitResultAsync(clientId, path)) 67 | { 68 | await _next.Invoke(context); 69 | } 70 | else 71 | { 72 | var error = new RateLimitOptionsError($"请求路由 {context.HttpContext.Request.Path}触发限流策略"); 73 | Logger.LogWarning($"路由地址 {context.HttpContext.Request.Path} 触发限流策略. {error}"); 74 | SetPipelineError(context, error); 75 | } 76 | } 77 | } 78 | else 79 | { 80 | await _next.Invoke(context); 81 | } 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/Czar.Gateway/RateLimit/Middleware/CzarClientRateLimitMiddlewareExtensions.cs: -------------------------------------------------------------------------------- 1 | using Ocelot.Middleware.Pipeline; 2 | 3 | namespace Czar.Gateway.RateLimit.Middleware 4 | { 5 | /// 6 | /// 金焰的世界 7 | /// 2018-11-18 8 | /// 限流中间件扩展 9 | /// 10 | public static class CzarClientRateLimitMiddlewareExtensions 11 | { 12 | public static IOcelotPipelineBuilder UseCzarClientRateLimitMiddleware(this IOcelotPipelineBuilder builder) 13 | { 14 | return builder.UseMiddleware(); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Czar.Gateway/RateLimit/RateLimitRuleModel.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Czar.Gateway.RateLimit 4 | { 5 | public class RateLimitRuleModel 6 | { 7 | /// 8 | /// 是否启用限流 9 | /// 10 | public bool RateLimit { get; set; } 11 | 12 | /// 13 | /// 限流配置信息 14 | /// 15 | public List rateLimitOptions { get; set; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Czar.Gateway/Requester/Middleware/CzarHttpRequesterMiddleware.cs: -------------------------------------------------------------------------------- 1 | using Czar.Gateway.Errors; 2 | using Newtonsoft.Json.Linq; 3 | using Ocelot.Logging; 4 | using Ocelot.Middleware; 5 | using Ocelot.Requester; 6 | using System.Threading.Tasks; 7 | 8 | namespace Czar.Gateway.Requester.Middleware 9 | { 10 | /// 11 | /// 金焰的世界 12 | /// 2018-12-10 13 | /// 自定义请求中间件 14 | /// 15 | public class CzarHttpRequesterMiddleware : OcelotMiddleware 16 | { 17 | private readonly OcelotRequestDelegate _next; 18 | private readonly IHttpRequester _requester; 19 | 20 | public CzarHttpRequesterMiddleware(OcelotRequestDelegate next, 21 | IOcelotLoggerFactory loggerFactory, 22 | IHttpRequester requester) 23 | : base(loggerFactory.CreateLogger()) 24 | { 25 | _next = next; 26 | _requester = requester; 27 | } 28 | 29 | public async Task Invoke(DownstreamContext context) 30 | { 31 | var response = await _requester.GetResponse(context); 32 | 33 | if (response.IsError) 34 | { 35 | Logger.LogDebug("IHttpRequester returned an error, setting pipeline error"); 36 | 37 | SetPipelineError(context, response.Errors); 38 | return; 39 | } 40 | else if(response.Data.StatusCode != System.Net.HttpStatusCode.OK) 41 | {//如果后端未处理异常,设置异常信息,统一输出,防止暴露敏感信息 42 | if (response.Data.StatusCode == System.Net.HttpStatusCode.BadRequest) 43 | {//提取Ids4相关的异常(400) 44 | var result = await response.Data.Content.ReadAsStringAsync(); 45 | JObject jobj = JObject.Parse(result); 46 | var errorMsg = jobj["error"]?.ToString(); 47 | var error = new IdentityServer4Error(errorMsg??"未知异常"); 48 | SetPipelineError(context, error); 49 | return; 50 | } 51 | else 52 | { 53 | var error = new InternalServerError($"请求服务异常"); 54 | Logger.LogWarning($"路由地址 {context.HttpContext.Request.Path} 请求服务异常. {error}"); 55 | SetPipelineError(context, error); 56 | return; 57 | } 58 | } 59 | Logger.LogDebug("setting http response message"); 60 | 61 | context.DownstreamResponse = new DownstreamResponse(response.Data); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/Czar.Gateway/Requester/Middleware/CzarHttpRequesterMiddlewareExtensions.cs: -------------------------------------------------------------------------------- 1 | using Ocelot.Middleware.Pipeline; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace Czar.Gateway.Requester.Middleware 7 | { 8 | public static class CzarHttpRequesterMiddlewareExtensions 9 | { 10 | public static IOcelotPipelineBuilder UseCzaHttpRequesterMiddleware(this IOcelotPipelineBuilder builder) 11 | { 12 | return builder.UseMiddleware(); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Czar.Gateway/Responder/CzarErrorsToHttpStatusCodeMapper.cs: -------------------------------------------------------------------------------- 1 | using Ocelot.Errors; 2 | using Ocelot.Responder; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | 8 | namespace Czar.Gateway.Responder 9 | { 10 | /// 11 | /// 金焰的世界 12 | /// 2018-11-19 13 | /// 错误信息转换成输出状态码 14 | /// 15 | public class CzarErrorsToHttpStatusCodeMapper : IErrorsToHttpStatusCodeMapper 16 | { 17 | public int Map(List errors) 18 | { 19 | if (errors.Any(e => e.Code == OcelotErrorCode.UnauthenticatedError)) 20 | { 21 | return 401; 22 | } 23 | 24 | if (errors.Any(e => e.Code == OcelotErrorCode.UnauthorizedError 25 | || e.Code == OcelotErrorCode.ClaimValueNotAuthorisedError 26 | || e.Code == OcelotErrorCode.ScopeNotAuthorisedError 27 | || e.Code == OcelotErrorCode.UserDoesNotHaveClaimError 28 | || e.Code == OcelotErrorCode.CannotFindClaimError)) 29 | { 30 | return 403; 31 | } 32 | 33 | if (errors.Any(e => e.Code == OcelotErrorCode.RequestTimedOutError)) 34 | { 35 | return 503; 36 | } 37 | 38 | if (errors.Any(e => e.Code == OcelotErrorCode.UnableToFindDownstreamRouteError)) 39 | { 40 | return 404; 41 | } 42 | 43 | if (errors.Any(e => e.Code == OcelotErrorCode.UnableToCompleteRequestError)) 44 | { 45 | return 500; 46 | } 47 | 48 | if (errors.Any(e => e.Code == OcelotErrorCode.RateLimitOptionsError)) 49 | { 50 | return 429; 51 | } 52 | 53 | if (errors.Any(e => e.Code == OcelotErrorCode.UnableToCreateAuthenticationHandlerError)) 54 | { 55 | return 400; 56 | } 57 | 58 | return 404; 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/Czar.Gateway/Responder/CzarHttpContextResponder.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Http; 2 | using Microsoft.AspNetCore.Http.Features; 3 | using Microsoft.Extensions.Primitives; 4 | using Ocelot.Headers; 5 | using Ocelot.Middleware; 6 | using Ocelot.Responder; 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Linq; 10 | using System.Net; 11 | using System.Text; 12 | using System.Threading.Tasks; 13 | 14 | namespace Czar.Gateway.Responder 15 | { 16 | public class CzarHttpContextResponder : IHttpResponder 17 | { 18 | private readonly IRemoveOutputHeaders _removeOutputHeaders; 19 | 20 | public CzarHttpContextResponder(IRemoveOutputHeaders removeOutputHeaders) 21 | { 22 | _removeOutputHeaders = removeOutputHeaders; 23 | } 24 | 25 | public async Task SetResponseOnHttpContext(HttpContext context, DownstreamResponse response) 26 | { 27 | _removeOutputHeaders.Remove(response.Headers); 28 | 29 | foreach (var httpResponseHeader in response.Headers) 30 | { 31 | AddHeaderIfDoesntExist(context, httpResponseHeader); 32 | } 33 | 34 | foreach (var httpResponseHeader in response.Content.Headers) 35 | { 36 | AddHeaderIfDoesntExist(context, new Header(httpResponseHeader.Key, httpResponseHeader.Value)); 37 | } 38 | 39 | var content = await response.Content.ReadAsStreamAsync(); 40 | 41 | if (response.Content.Headers.ContentLength != null) 42 | { 43 | AddHeaderIfDoesntExist(context, new Header("Content-Length", new[] { content.Length.ToString() })); 44 | } 45 | 46 | SetStatusCode(context, (int)response.StatusCode); 47 | 48 | context.Response.HttpContext.Features.Get().ReasonPhrase = response.ReasonPhrase; 49 | 50 | using (content) 51 | { 52 | if (response.StatusCode != HttpStatusCode.NotModified && context.Response.ContentLength != 0) 53 | { 54 | await content.CopyToAsync(context.Response.Body); 55 | } 56 | } 57 | } 58 | 59 | public void SetErrorResponseOnContext(HttpContext context, int statusCode) 60 | { 61 | SetStatusCode(context, statusCode); 62 | } 63 | 64 | private void SetStatusCode(HttpContext context, int statusCode) 65 | { 66 | if (!context.Response.HasStarted) 67 | { 68 | context.Response.StatusCode = statusCode; 69 | } 70 | } 71 | 72 | private static void AddHeaderIfDoesntExist(HttpContext context, Header httpResponseHeader) 73 | { 74 | if (!context.Response.Headers.ContainsKey(httpResponseHeader.Key)) 75 | { 76 | context.Response.Headers.Add(httpResponseHeader.Key, new StringValues(httpResponseHeader.Values.ToArray())); 77 | } 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/Czar.Gateway/Responder/Middleware/CzarResponderMiddleware.cs: -------------------------------------------------------------------------------- 1 | using Czar.Gateway.Configuration; 2 | using Microsoft.AspNetCore.Http; 3 | using Ocelot.Errors; 4 | using Ocelot.Logging; 5 | using Ocelot.Middleware; 6 | using Ocelot.Responder; 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Net; 10 | using System.Threading.Tasks; 11 | 12 | namespace Czar.Gateway.Responder.Middleware 13 | { 14 | /// 15 | /// 金焰的世界 16 | /// 2018-12-10 17 | /// 统一输出中间件 18 | /// 19 | public class CzarResponderMiddleware: OcelotMiddleware 20 | { 21 | private readonly OcelotRequestDelegate _next; 22 | private readonly IHttpResponder _responder; 23 | private readonly IErrorsToHttpStatusCodeMapper _codeMapper; 24 | 25 | public CzarResponderMiddleware(OcelotRequestDelegate next, 26 | IHttpResponder responder, 27 | IOcelotLoggerFactory loggerFactory, 28 | IErrorsToHttpStatusCodeMapper codeMapper 29 | ) 30 | : base(loggerFactory.CreateLogger()) 31 | { 32 | _next = next; 33 | _responder = responder; 34 | _codeMapper = codeMapper; 35 | } 36 | 37 | public async Task Invoke(DownstreamContext context) 38 | { 39 | await _next.Invoke(context); 40 | 41 | if (context.IsError) 42 | {//自定义输出结果 43 | var errmsg = context.Errors[0].Message; 44 | int httpstatus = _codeMapper.Map(context.Errors); 45 | var errResult = new BaseResult() { errcode = httpstatus, errmsg = errmsg }; 46 | var message = errResult.ToJson(); 47 | context.HttpContext.Response.StatusCode = (int)HttpStatusCode.OK; 48 | await context.HttpContext.Response.WriteAsync(message); 49 | return; 50 | } 51 | else if (context.HttpContext.Response.StatusCode != (int)HttpStatusCode.OK) 52 | {//标记失败,不做任何处理,自定义扩展时预留 53 | 54 | } 55 | else if (context.DownstreamResponse == null) 56 | {//添加如果管道强制终止,不做任何处理,修复未将对象实例化错误 57 | 58 | } 59 | else 60 | {//继续请求下游地址返回 61 | Logger.LogDebug("no pipeline errors, setting and returning completed response"); 62 | await _responder.SetResponseOnHttpContext(context.HttpContext, context.DownstreamResponse); 63 | } 64 | } 65 | 66 | private void SetErrorResponse(HttpContext context, List errors) 67 | { 68 | var statusCode = _codeMapper.Map(errors); 69 | _responder.SetErrorResponseOnContext(context, statusCode); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/Czar.Gateway/Responder/Middleware/CzarResponderMiddlewareExtensions.cs: -------------------------------------------------------------------------------- 1 | using Ocelot.Middleware.Pipeline; 2 | 3 | namespace Czar.Gateway.Responder.Middleware 4 | { 5 | /// 6 | /// 金焰的世界 7 | /// 2018-12-10 8 | /// 应用输出中间件扩展 9 | /// 10 | public static class CzarResponderMiddlewareExtensions 11 | { 12 | public static IOcelotPipelineBuilder UseCzarResponderMiddleware(this IOcelotPipelineBuilder builder) 13 | { 14 | return builder.UseMiddleware(); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Czar.Gateway/Rpc/CzarRpcProcessor.cs: -------------------------------------------------------------------------------- 1 | using Czar.Gateway.Configuration; 2 | using Czar.Rpc.Message; 3 | using Ocelot.Cache; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace Czar.Gateway.Rpc 10 | { 11 | public class CzarRpcProcessor: ICzarRpcProcessor 12 | { 13 | private readonly IRpcRepository _rpcRepository; 14 | private readonly CzarOcelotConfiguration _options; 15 | private readonly IOcelotCache _ocelotCache; 16 | public CzarRpcProcessor(IRpcRepository rpcRepository, CzarOcelotConfiguration options, IOcelotCache ocelotCache) 17 | { 18 | _rpcRepository = rpcRepository; 19 | _options = options; 20 | _ocelotCache = ocelotCache; 21 | } 22 | 23 | /// 24 | /// 根据模板地址获取RPC请求方法 25 | /// 26 | /// 上游模板 27 | /// 28 | public async Task GetRemoteMethodAsync(string UpUrl) 29 | { 30 | var region = CzarCacheRegion.RemoteInvokeMessageRegion; 31 | var key = UpUrl; 32 | var cacheResult = _ocelotCache.Get(key, region); 33 | if (cacheResult != null) 34 | {//提取缓存数据 35 | return cacheResult; 36 | } 37 | else 38 | { 39 | cacheResult = await _rpcRepository.GetRemoteMethodAsync(UpUrl); 40 | _ocelotCache.Add(key, cacheResult, TimeSpan.FromSeconds(_options.CzarCacheTime), region); 41 | return cacheResult; 42 | } 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Czar.Gateway/Rpc/ICzarRpcProcessor.cs: -------------------------------------------------------------------------------- 1 | using Czar.Rpc.Message; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Czar.Gateway.Rpc 8 | { 9 | public interface ICzarRpcProcessor 10 | { 11 | /// 12 | /// 根据模板地址获取RPC请求方法 13 | /// 14 | /// 上游模板 15 | /// 16 | Task GetRemoteMethodAsync(string UpUrl); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Czar.Gateway/Rpc/IRpcRepository.cs: -------------------------------------------------------------------------------- 1 | using Czar.Rpc.Message; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Czar.Gateway.Rpc 8 | { 9 | public interface IRpcRepository 10 | { 11 | /// 12 | /// 根据模板地址获取RPC请求方法 13 | /// 14 | /// 上游模板 15 | /// 16 | Task GetRemoteMethodAsync(string UpUrl); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Czar.Gateway/Rpc/Middleware/CzarRpcMiddleware.cs: -------------------------------------------------------------------------------- 1 | using Czar.Gateway.Errors; 2 | using Czar.Rpc.Clients; 3 | using Ocelot.Logging; 4 | using Ocelot.Middleware; 5 | using Ocelot.Responses; 6 | using System.Collections.Generic; 7 | using System.Net; 8 | using System.Threading.Tasks; 9 | 10 | namespace Czar.Gateway.Rpc.Middleware 11 | { 12 | public class CzarRpcMiddleware : OcelotMiddleware 13 | { 14 | private readonly OcelotRequestDelegate _next; 15 | private readonly IRpcClientFactory _clientFactory; 16 | private readonly ICzarRpcProcessor _czarRpcProcessor; 17 | public CzarRpcMiddleware(OcelotRequestDelegate next, IRpcClientFactory clientFactory, 18 | IOcelotLoggerFactory loggerFactory, ICzarRpcProcessor czarRpcProcessor) : base(loggerFactory.CreateLogger()) 19 | { 20 | _next = next; 21 | _clientFactory = clientFactory; 22 | _czarRpcProcessor = czarRpcProcessor; 23 | } 24 | 25 | public async Task Invoke(DownstreamContext context) 26 | { 27 | var httpStatusCode = HttpStatusCode.OK; 28 | var _param = new List(); 29 | //1、提取路由参数 30 | var tmpInfo = context.TemplatePlaceholderNameAndValues; 31 | if (tmpInfo != null && tmpInfo.Count > 0) 32 | { 33 | foreach (var tmp in tmpInfo) 34 | { 35 | _param.Add(tmp.Value); 36 | } 37 | } 38 | //2、提取query参数 39 | foreach (var _q in context.HttpContext.Request.Query) 40 | { 41 | _param.Add(_q.Value.ToString()); 42 | } 43 | //3、从body里提取内容 44 | if (context.HttpContext.Request.Method.ToUpper() != "GET") 45 | { 46 | context.DownstreamRequest.Scheme = "http"; 47 | var requert = context.DownstreamRequest.ToHttpRequestMessage(); 48 | if (requert.Content!=null) 49 | { 50 | var json = "{}"; 51 | json = await requert.Content.ReadAsStringAsync(); 52 | _param.Add(json); 53 | } 54 | } 55 | //从缓存里提取 56 | var req = await _czarRpcProcessor.GetRemoteMethodAsync(context.DownstreamReRoute.UpstreamPathTemplate.OriginalValue); 57 | if (req != null) 58 | { 59 | req.Parameters = _param.ToArray(); 60 | var result = await _clientFactory.SendAsync(req, GetEndPoint(context.DownstreamRequest.Host, context.DownstreamRequest.Port)); 61 | OkResponse httpResponse; 62 | if (result.CzarCode == Czar.Rpc.Utilitys.RpcStatusCode.Success) 63 | { 64 | httpResponse = new OkResponse(new RpcHttpContent(result.CzarResult?.ToString())); 65 | } 66 | else 67 | { 68 | httpResponse = new OkResponse(new RpcHttpContent(result)); 69 | } 70 | context.HttpContext.Response.ContentType = "application/json"; 71 | context.DownstreamResponse = new DownstreamResponse(httpResponse.Data, httpStatusCode, httpResponse.Data.Headers, "OK"); 72 | } 73 | else 74 | {//输出错误 75 | var error = new InternalServerError($"请求路由 {context.HttpContext.Request.Path}未配置后端转发"); 76 | Logger.LogWarning($"{error}"); 77 | SetPipelineError(context, error); 78 | } 79 | } 80 | private EndPoint GetEndPoint(string ipaddress, int port) 81 | { 82 | if (IPAddress.TryParse(ipaddress, out IPAddress ip)) 83 | { 84 | return new IPEndPoint(ip, port); 85 | } 86 | else 87 | { 88 | return new DnsEndPoint(ipaddress, port); 89 | } 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/Czar.Gateway/Rpc/Middleware/CzarRpcMiddlewareExtensions.cs: -------------------------------------------------------------------------------- 1 | using Ocelot.Middleware.Pipeline; 2 | 3 | namespace Czar.Gateway.Rpc.Middleware 4 | { 5 | public static class CzarRpcMiddlewareExtensions 6 | { 7 | public static IOcelotPipelineBuilder UseCzarRpcMiddleware(this IOcelotPipelineBuilder builder) 8 | { 9 | return builder.UseMiddleware(); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/Czar.Gateway/Rpc/RpcHttpContent.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Net; 3 | using System.Net.Http; 4 | using System.Threading.Tasks; 5 | 6 | namespace Czar.Gateway.Rpc 7 | { 8 | public class RpcHttpContent : HttpContent 9 | { 10 | private string result; 11 | 12 | public RpcHttpContent(string result) 13 | { 14 | this.result = result; 15 | } 16 | 17 | public RpcHttpContent(object result) 18 | { 19 | this.result = Newtonsoft.Json.JsonConvert.SerializeObject(result); 20 | } 21 | 22 | protected override async Task SerializeToStreamAsync(Stream stream, TransportContext context) 23 | { 24 | var writer = new StreamWriter(stream); 25 | await writer.WriteAsync(result); 26 | await writer.FlushAsync(); 27 | } 28 | 29 | protected override bool TryComputeLength(out long length) 30 | { 31 | length = result.Length; 32 | return true; 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Czar.Gateway/Stores/MySql/MySqlClientRateLimitRepository.cs: -------------------------------------------------------------------------------- 1 | using Czar.Gateway.Configuration; 2 | using Czar.Gateway.RateLimit; 3 | using Dapper; 4 | using System.Collections.Generic; 5 | using System.Threading.Tasks; 6 | using MySql.Data.MySqlClient; 7 | 8 | namespace Czar.Gateway.Stores.SqlServer 9 | { 10 | /// 11 | /// 金焰的世界 12 | /// 2018-11-19 13 | /// 客户端限流信息提取 14 | /// 15 | public class MySqlClientRateLimitRepository : IClientRateLimitRepository 16 | { 17 | private readonly CzarOcelotConfiguration _option; 18 | public MySqlClientRateLimitRepository(CzarOcelotConfiguration option) 19 | { 20 | _option = option; 21 | } 22 | 23 | /// 24 | /// 校验客户端限流规则 25 | /// 26 | /// 客户端ID 27 | /// 请求地址 28 | /// 29 | public async Task<(bool RateLimit, List rateLimitOptions)> CheckClientRateLimitAsync(string clientid, string path) 30 | { 31 | using (var connection = new MySqlConnection(_option.DbConnectionStrings)) 32 | { 33 | string sql = @"SELECT DISTINCT UpstreamPathTemplate AS RateLimitPath,LimitPeriod AS Period,LimitNum AS Limit,ClientId FROM AhphReRoute T1 INNER JOIN AhphReRouteLimitRule T2 ON T1.ReRouteId=T2.ReRouteId 34 | INNER JOIN AhphLimitRule T3 ON T2.RuleId=T3.RuleId INNER JOIN AhphLimitGroupRule T4 ON 35 | T2.ReRouteLimitId=T4.ReRouteLimitId INNER JOIN AhphLimitGroup T5 ON T4.LimitGroupId=T5.LimitGroupId 36 | INNER JOIN AhphClientLimitGroup T6 ON T5.LimitGroupId=T6.LimitGroupId INNER JOIN 37 | AhphClients T7 ON T6.Id=T7.Id 38 | WHERE T1.InfoStatus=1 AND T1.UpstreamPathTemplate=@path AND T3.InfoStatus=1 AND T5.InfoStatus=1 39 | AND ClientId=@clientid AND Enabled=1"; 40 | var result = (await connection.QueryAsync(sql, new { clientid, path }))?.AsList(); 41 | if (result != null && result.Count > 0) 42 | { 43 | return (true, result); 44 | } 45 | else 46 | { 47 | return (false, null); 48 | } 49 | } 50 | } 51 | 52 | /// 53 | /// 校验是否设置了路由白名单 54 | /// 55 | /// 客户端ID 56 | /// 请求地址 57 | /// 58 | public async Task CheckClientReRouteWhiteListAsync(string clientid, string path) 59 | { 60 | using (var connection = new MySqlConnection(_option.DbConnectionStrings)) 61 | { 62 | string sql = @"SELECT COUNT(1) FROM AhphReRoute T1 INNER JOIN AhphClientReRouteWhiteList T2 ON T1.ReRouteId=T2.ReRouteId 63 | INNER JOIN AhphClients T3 ON T2.Id=T3.Id WHERE T1.InfoStatus=1 AND UpstreamPathTemplate=@path AND 64 | ClientId=@clientid AND Enabled=1"; 65 | var result = await connection.QueryFirstOrDefaultAsync(sql, new { clientid,path }); 66 | return result > 0; 67 | } 68 | } 69 | 70 | /// 71 | /// 校验是否启用限流规则 72 | /// 73 | /// 请求地址 74 | /// 75 | public async Task CheckReRouteRuleAsync(string path) 76 | { 77 | using (var connection = new MySqlConnection(_option.DbConnectionStrings)) 78 | { 79 | string sql = @"SELECT COUNT(1) FROM AhphReRoute T1 INNER JOIN AhphReRouteLimitRule T2 ON T1.ReRouteId=T2.ReRouteId 80 | INNER JOIN AhphLimitRule T3 ON T2.RuleId=T3.RuleId WHERE T1.InfoStatus=1 AND UpstreamPathTemplate=@path 81 | AND T3.InfoStatus=1"; 82 | var result = await connection.QueryFirstOrDefaultAsync(sql, new { path }); 83 | return result > 0; 84 | } 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/Czar.Gateway/Stores/MySql/MySqlFileConfigurationRepository.cs: -------------------------------------------------------------------------------- 1 | using Czar.Gateway.Configuration; 2 | using Czar.Gateway.Model; 3 | using Dapper; 4 | using MySql.Data.MySqlClient; 5 | using Ocelot.Configuration.File; 6 | using Ocelot.Configuration.Repository; 7 | using Ocelot.Responses; 8 | using System; 9 | using System.Collections.Generic; 10 | using System.Threading.Tasks; 11 | 12 | namespace Czar.Gateway.Stores.MySql 13 | { 14 | /// 15 | /// 金焰的世界 16 | /// 2018-11-13 17 | /// 使用MySql来实现配置文件仓储接口 18 | /// 19 | public class MySqlFileConfigurationRepository : IFileConfigurationRepository 20 | { 21 | private readonly CzarOcelotConfiguration _option; 22 | public MySqlFileConfigurationRepository(CzarOcelotConfiguration option) 23 | { 24 | _option = option; 25 | } 26 | 27 | /// 28 | /// 从数据库中获取配置信息 29 | /// 30 | /// 31 | public async Task> Get() 32 | { 33 | #region 提取配置信息 34 | var file = new FileConfiguration(); 35 | //提取默认启用的路由配置信息 36 | string glbsql = "select * from AhphGlobalConfiguration where IsDefault=1 and InfoStatus=1"; 37 | //提取全局配置信息 38 | using (var connection = new MySqlConnection(_option.DbConnectionStrings)) 39 | { 40 | var result = await connection.QueryFirstOrDefaultAsync(glbsql); 41 | if (result != null) 42 | { 43 | var glb = new FileGlobalConfiguration(); 44 | //赋值全局信息 45 | glb.BaseUrl = result.BaseUrl; 46 | glb.DownstreamScheme = result.DownstreamScheme; 47 | glb.RequestIdKey = result.RequestIdKey; 48 | if (!String.IsNullOrEmpty(result.HttpHandlerOptions)) 49 | { 50 | glb.HttpHandlerOptions = result.HttpHandlerOptions.ToObject(); 51 | } 52 | if (!String.IsNullOrEmpty(result.LoadBalancerOptions)) 53 | { 54 | glb.LoadBalancerOptions = result.LoadBalancerOptions.ToObject(); 55 | } 56 | if (!String.IsNullOrEmpty(result.QoSOptions)) 57 | { 58 | glb.QoSOptions = result.QoSOptions.ToObject(); 59 | } 60 | if (!String.IsNullOrEmpty(result.ServiceDiscoveryProvider)) 61 | { 62 | glb.ServiceDiscoveryProvider = result.ServiceDiscoveryProvider.ToObject(); 63 | } 64 | file.GlobalConfiguration = glb; 65 | 66 | //提取所有路由信息 67 | string routesql = "select T2.* from AhphConfigReRoutes T1 inner join AhphReRoute T2 on T1.ReRouteId=T2.ReRouteId where AhphId=@AhphId and InfoStatus=1"; 68 | var routeresult = (await connection.QueryAsync(routesql, new { result.AhphId }))?.AsList(); 69 | if (routeresult != null && routeresult.Count > 0) 70 | { 71 | var reroutelist = new List(); 72 | foreach (var model in routeresult) 73 | { 74 | var m = new FileReRoute(); 75 | if (!String.IsNullOrEmpty(model.AuthenticationOptions)) 76 | { 77 | m.AuthenticationOptions = model.AuthenticationOptions.ToObject(); 78 | } 79 | if (!String.IsNullOrEmpty(model.CacheOptions)) 80 | { 81 | m.FileCacheOptions = model.CacheOptions.ToObject(); 82 | } 83 | if (!String.IsNullOrEmpty(model.DelegatingHandlers)) 84 | { 85 | m.DelegatingHandlers = model.DelegatingHandlers.ToObject>(); 86 | } 87 | if (!String.IsNullOrEmpty(model.LoadBalancerOptions)) 88 | { 89 | m.LoadBalancerOptions = model.LoadBalancerOptions.ToObject(); 90 | } 91 | if (!String.IsNullOrEmpty(model.QoSOptions)) 92 | { 93 | m.QoSOptions = model.QoSOptions.ToObject(); 94 | } 95 | if (!String.IsNullOrEmpty(model.DownstreamHostAndPorts)) 96 | { 97 | m.DownstreamHostAndPorts = model.DownstreamHostAndPorts.ToObject>(); 98 | } 99 | //开始赋值 100 | m.DownstreamPathTemplate = model.DownstreamPathTemplate; 101 | m.DownstreamScheme = model.DownstreamScheme; 102 | m.Key = model.RequestIdKey; 103 | m.Priority = model.Priority ?? 0; 104 | m.RequestIdKey = model.RequestIdKey; 105 | m.ServiceName = model.ServiceName; 106 | m.UpstreamHost = model.UpstreamHost; 107 | m.UpstreamHttpMethod = model.UpstreamHttpMethod?.ToObject>(); 108 | m.UpstreamPathTemplate = model.UpstreamPathTemplate; 109 | reroutelist.Add(m); 110 | } 111 | file.ReRoutes = reroutelist; 112 | } 113 | } 114 | else 115 | { 116 | throw new Exception("未监测到任何可用的配置信息"); 117 | } 118 | } 119 | #endregion 120 | if (file.ReRoutes == null || file.ReRoutes.Count == 0) 121 | { 122 | return new OkResponse(null); 123 | } 124 | return new OkResponse(file); 125 | } 126 | 127 | //由于数据库存储可不实现Set接口直接返回 128 | public async Task Set(FileConfiguration fileConfiguration) 129 | { 130 | return new OkResponse(); 131 | } 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/Czar.Gateway/Stores/MySql/MySqlRpcRepository.cs: -------------------------------------------------------------------------------- 1 | using Czar.Gateway.Configuration; 2 | using Czar.Gateway.Rpc; 3 | using Czar.Rpc.Message; 4 | using Dapper; 5 | using MySql.Data.MySqlClient; 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | 11 | namespace Czar.Gateway.Stores.MySql 12 | { 13 | /// 14 | /// 金焰的世界 15 | /// 2019-01-03 16 | /// Rpc相关仓储 17 | /// 18 | public class MySqlRpcRepository : IRpcRepository 19 | { 20 | private readonly CzarOcelotConfiguration _option; 21 | public MySqlRpcRepository(CzarOcelotConfiguration option) 22 | { 23 | _option = option; 24 | } 25 | 26 | /// 27 | /// 获取RPC调用方法 28 | /// 29 | /// 30 | /// 31 | public async Task GetRemoteMethodAsync(string UpUrl) 32 | { 33 | using (var connection = new MySqlConnection(_option.DbConnectionStrings)) 34 | { 35 | string sql = @"select T4.* from AhphGlobalConfiguration t1 inner join AhphConfigReRoutes T2 on 36 | T1.AhphId=t2.AhphId inner join AhphReRoute T3 on T2.ReRouteId=T3.ReRouteId 37 | INNER JOIN AhphReRouteRpcConfig T4 ON T3.ReRouteId=T4.ReRouteId 38 | where IsDefault=1 and T1.InfoStatus=1 AND T3.InfoStatus=1 AND UpstreamPathTemplate=@URL"; 39 | var result = await connection.QueryFirstOrDefaultAsync(sql, new { URL = UpUrl }); 40 | return result; 41 | } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Czar.Gateway/Stores/MySql/MysqlClientAuthenticationRepository.cs: -------------------------------------------------------------------------------- 1 | using Czar.Gateway.Authentication; 2 | using Czar.Gateway.Configuration; 3 | using MySql.Data.MySqlClient; 4 | using System.Threading.Tasks; 5 | using Dapper; 6 | namespace Czar.Gateway.Stores.SqlServer 7 | { 8 | /// 9 | /// 金焰的世界 10 | /// 2018-11-16 11 | /// 使用sqlserver实现客户端授权仓储 12 | /// 13 | public class MySqlClientAuthenticationRepository : IClientAuthenticationRepository 14 | { 15 | private readonly CzarOcelotConfiguration _option; 16 | public MySqlClientAuthenticationRepository(CzarOcelotConfiguration option) 17 | { 18 | _option = option; 19 | } 20 | /// 21 | /// 校验获取客户端是否有访问权限 22 | /// 23 | /// 客户端ID 24 | /// 请求路由 25 | /// 26 | public async Task ClientAuthenticationAsync(string clientid, string path) 27 | { 28 | using (var connection = new MySqlConnection(_option.DbConnectionStrings)) 29 | { 30 | string sql = @"SELECT COUNT(1) FROM AhphClients T1 INNER JOIN AhphClientGroup T2 ON T1.Id=T2.Id INNER JOIN AhphAuthGroup T3 ON T2.GroupId = T3.GroupId INNER JOIN AhphReRouteGroupAuth T4 ON T3.GroupId = T4.GroupId INNER JOIN AhphReRoute T5 ON T4.ReRouteId = T5.ReRouteId WHERE Enabled = 1 AND ClientId = @ClientId AND T5.InfoStatus = 1 AND UpstreamPathTemplate = @Path"; 31 | var result= await connection.QueryFirstOrDefaultAsync(sql, new { ClientId = clientid, Path = path }); 32 | return result > 0 ? true : false; 33 | } 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Czar.Gateway/Stores/SqlServer/SqlServerClientAuthenticationRepository.cs: -------------------------------------------------------------------------------- 1 | using Czar.Gateway.Authentication; 2 | using Czar.Gateway.Configuration; 3 | using System.Data.SqlClient; 4 | using System.Threading.Tasks; 5 | using Dapper; 6 | namespace Czar.Gateway.Stores.SqlServer 7 | { 8 | /// 9 | /// 金焰的世界 10 | /// 2018-11-16 11 | /// 使用sqlserver实现客户端授权仓储 12 | /// 13 | public class SqlServerClientAuthenticationRepository : IClientAuthenticationRepository 14 | { 15 | private readonly CzarOcelotConfiguration _option; 16 | public SqlServerClientAuthenticationRepository(CzarOcelotConfiguration option) 17 | { 18 | _option = option; 19 | } 20 | /// 21 | /// 校验获取客户端是否有访问权限 22 | /// 23 | /// 客户端ID 24 | /// 请求路由 25 | /// 26 | public async Task ClientAuthenticationAsync(string clientid, string path) 27 | { 28 | using (var connection = new SqlConnection(_option.DbConnectionStrings)) 29 | { 30 | string sql = @"SELECT COUNT(1) FROM AhphClients T1 INNER JOIN AhphClientGroup T2 ON T1.Id=T2.Id INNER JOIN AhphAuthGroup T3 ON T2.GroupId = T3.GroupId INNER JOIN AhphReRouteGroupAuth T4 ON T3.GroupId = T4.GroupId INNER JOIN AhphReRoute T5 ON T4.ReRouteId = T5.ReRouteId WHERE Enabled = 1 AND ClientId = @ClientId AND T5.InfoStatus = 1 AND UpstreamPathTemplate = @Path"; 31 | var result= await connection.QueryFirstOrDefaultAsync(sql, new { ClientId = clientid, Path = path }); 32 | return result > 0; 33 | } 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Czar.Gateway/Stores/SqlServer/SqlServerClientRateLimitRepository.cs: -------------------------------------------------------------------------------- 1 | using Czar.Gateway.Configuration; 2 | using Czar.Gateway.RateLimit; 3 | using Dapper; 4 | using System.Collections.Generic; 5 | using System.Data.SqlClient; 6 | using System.Threading.Tasks; 7 | 8 | namespace Czar.Gateway.Stores.SqlServer 9 | { 10 | /// 11 | /// 金焰的世界 12 | /// 2018-11-19 13 | /// 客户端限流信息提取 14 | /// 15 | public class SqlServerClientRateLimitRepository : IClientRateLimitRepository 16 | { 17 | private readonly CzarOcelotConfiguration _option; 18 | public SqlServerClientRateLimitRepository(CzarOcelotConfiguration option) 19 | { 20 | _option = option; 21 | } 22 | 23 | /// 24 | /// 校验客户端限流规则 25 | /// 26 | /// 客户端ID 27 | /// 请求地址 28 | /// 29 | public async Task<(bool RateLimit, List rateLimitOptions)> CheckClientRateLimitAsync(string clientid, string path) 30 | { 31 | using (var connection = new SqlConnection(_option.DbConnectionStrings)) 32 | { 33 | string sql = @"SELECT DISTINCT UpstreamPathTemplate AS RateLimitPath,LimitPeriod AS Period,LimitNum AS Limit,ClientId FROM AhphReRoute T1 INNER JOIN AhphReRouteLimitRule T2 ON T1.ReRouteId=T2.ReRouteId 34 | INNER JOIN AhphLimitRule T3 ON T2.RuleId=T3.RuleId INNER JOIN AhphLimitGroupRule T4 ON 35 | T2.ReRouteLimitId=T4.ReRouteLimitId INNER JOIN AhphLimitGroup T5 ON T4.LimitGroupId=T5.LimitGroupId 36 | INNER JOIN AhphClientLimitGroup T6 ON T5.LimitGroupId=T6.LimitGroupId INNER JOIN 37 | AhphClients T7 ON T6.Id=T7.Id 38 | WHERE T1.InfoStatus=1 AND T1.UpstreamPathTemplate=@path AND T3.InfoStatus=1 AND T5.InfoStatus=1 39 | AND ClientId=@clientid AND Enabled=1"; 40 | var result = (await connection.QueryAsync(sql, new { clientid, path }))?.AsList(); 41 | if (result != null && result.Count > 0) 42 | { 43 | return (true, result); 44 | } 45 | else 46 | { 47 | return (false, null); 48 | } 49 | } 50 | } 51 | 52 | /// 53 | /// 校验是否设置了路由白名单 54 | /// 55 | /// 客户端ID 56 | /// 请求地址 57 | /// 58 | public async Task CheckClientReRouteWhiteListAsync(string clientid, string path) 59 | { 60 | using (var connection = new SqlConnection(_option.DbConnectionStrings)) 61 | { 62 | string sql = @"SELECT COUNT(1) FROM AhphReRoute T1 INNER JOIN AhphClientReRouteWhiteList T2 ON T1.ReRouteId=T2.ReRouteId 63 | INNER JOIN AhphClients T3 ON T2.Id=T3.Id WHERE T1.InfoStatus=1 AND UpstreamPathTemplate=@path AND 64 | ClientId=@clientid AND Enabled=1"; 65 | var result = await connection.QueryFirstOrDefaultAsync(sql, new { clientid,path }); 66 | return result > 0; 67 | } 68 | } 69 | 70 | /// 71 | /// 校验是否启用限流规则 72 | /// 73 | /// 请求地址 74 | /// 75 | public async Task CheckReRouteRuleAsync(string path) 76 | { 77 | using (var connection = new SqlConnection(_option.DbConnectionStrings)) 78 | { 79 | string sql = @"SELECT COUNT(1) FROM AhphReRoute T1 INNER JOIN AhphReRouteLimitRule T2 ON T1.ReRouteId=T2.ReRouteId 80 | INNER JOIN AhphLimitRule T3 ON T2.RuleId=T3.RuleId WHERE T1.InfoStatus=1 AND UpstreamPathTemplate=@path 81 | AND T3.InfoStatus=1"; 82 | var result = await connection.QueryFirstOrDefaultAsync(sql, new { path }); 83 | return result > 0; 84 | } 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/Czar.Gateway/Stores/SqlServer/SqlServerFileConfigurationRepository.cs: -------------------------------------------------------------------------------- 1 | using Czar.Gateway.Configuration; 2 | using Czar.Gateway.Model; 3 | using Dapper; 4 | using Ocelot.Configuration.File; 5 | using Ocelot.Configuration.Repository; 6 | using Ocelot.Responses; 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Data.SqlClient; 10 | using System.Threading.Tasks; 11 | 12 | namespace Czar.Gateway.Stores.SqlServer 13 | { 14 | /// 15 | /// 金焰的世界 16 | /// 2018-11-11 17 | /// 使用SqlServer来实现配置文件仓储接口 18 | /// 19 | public class SqlServerFileConfigurationRepository : IFileConfigurationRepository 20 | { 21 | private readonly CzarOcelotConfiguration _option; 22 | public SqlServerFileConfigurationRepository(CzarOcelotConfiguration option) 23 | { 24 | _option = option; 25 | } 26 | 27 | /// 28 | /// 从数据库中获取配置信息 29 | /// 30 | /// 31 | public async Task> Get() 32 | { 33 | #region 提取配置信息 34 | var file = new FileConfiguration(); 35 | //提取默认启用的路由配置信息 36 | string glbsql = "select * from AhphGlobalConfiguration where IsDefault=1 and InfoStatus=1"; 37 | //提取全局配置信息 38 | using (var connection = new SqlConnection(_option.DbConnectionStrings)) 39 | { 40 | var result = await connection.QueryFirstOrDefaultAsync(glbsql); 41 | if (result != null) 42 | { 43 | var glb = new FileGlobalConfiguration(); 44 | //赋值全局信息 45 | glb.BaseUrl = result.BaseUrl; 46 | glb.DownstreamScheme = result.DownstreamScheme; 47 | glb.RequestIdKey = result.RequestIdKey; 48 | //glb.HttpHandlerOptions = result.HttpHandlerOptions?.ToObject(); 49 | //glb.LoadBalancerOptions = result.LoadBalancerOptions?.ToObject(); 50 | //glb.QoSOptions = result.QoSOptions?.ToObject(); 51 | //glb.ServiceDiscoveryProvider = result.ServiceDiscoveryProvider?.ToObject(); 52 | if (!String.IsNullOrEmpty(result.HttpHandlerOptions)) 53 | { 54 | glb.HttpHandlerOptions = result.HttpHandlerOptions.ToObject(); 55 | } 56 | if (!String.IsNullOrEmpty(result.LoadBalancerOptions)) 57 | { 58 | glb.LoadBalancerOptions = result.LoadBalancerOptions.ToObject(); 59 | } 60 | if (!String.IsNullOrEmpty(result.QoSOptions)) 61 | { 62 | glb.QoSOptions = result.QoSOptions.ToObject(); 63 | } 64 | if (!String.IsNullOrEmpty(result.ServiceDiscoveryProvider)) 65 | { 66 | glb.ServiceDiscoveryProvider = result.ServiceDiscoveryProvider.ToObject(); 67 | } 68 | file.GlobalConfiguration = glb; 69 | 70 | //提取所有路由信息 71 | string routesql = "select T2.* from AhphConfigReRoutes T1 inner join AhphReRoute T2 on T1.ReRouteId=T2.ReRouteId where AhphId=@AhphId and InfoStatus=1"; 72 | var routeresult = (await connection.QueryAsync(routesql, new { result.AhphId }))?.AsList(); 73 | if (routeresult != null && routeresult.Count > 0) 74 | { 75 | var reroutelist = new List(); 76 | foreach (var model in routeresult) 77 | { 78 | var m = new FileReRoute(); 79 | //m.AuthenticationOptions = model.AuthenticationOptions?.ToObject(); 80 | //m.FileCacheOptions = model.CacheOptions?.ToObject(); 81 | //m.DelegatingHandlers = model.DelegatingHandlers?.ToObject>(); 82 | //m.LoadBalancerOptions = model.LoadBalancerOptions?.ToObject(); 83 | //m.QoSOptions = model.QoSOptions?.ToObject(); 84 | //m.DownstreamHostAndPorts = model.DownstreamHostAndPorts?.ToObject>(); 85 | if (!String.IsNullOrEmpty(model.AuthenticationOptions)) 86 | { 87 | m.AuthenticationOptions = model.AuthenticationOptions.ToObject(); 88 | } 89 | if (!String.IsNullOrEmpty(model.CacheOptions)) 90 | { 91 | m.FileCacheOptions = model.CacheOptions.ToObject(); 92 | } 93 | if (!String.IsNullOrEmpty(model.DelegatingHandlers)) 94 | { 95 | m.DelegatingHandlers = model.DelegatingHandlers.ToObject>(); 96 | } 97 | if (!String.IsNullOrEmpty(model.LoadBalancerOptions)) 98 | { 99 | m.LoadBalancerOptions = model.LoadBalancerOptions.ToObject(); 100 | } 101 | if (!String.IsNullOrEmpty(model.QoSOptions)) 102 | { 103 | m.QoSOptions = model.QoSOptions.ToObject(); 104 | } 105 | if (!String.IsNullOrEmpty(model.DownstreamHostAndPorts)) 106 | { 107 | m.DownstreamHostAndPorts = model.DownstreamHostAndPorts.ToObject>(); 108 | } 109 | //开始赋值 110 | m.DownstreamPathTemplate = model.DownstreamPathTemplate; 111 | m.DownstreamScheme = model.DownstreamScheme; 112 | m.Key = model.RequestIdKey; 113 | m.Priority = model.Priority ?? 0; 114 | m.RequestIdKey = model.RequestIdKey; 115 | m.ServiceName = model.ServiceName; 116 | m.UpstreamHost = model.UpstreamHost; 117 | m.UpstreamHttpMethod = model.UpstreamHttpMethod?.ToObject>(); 118 | m.UpstreamPathTemplate = model.UpstreamPathTemplate; 119 | reroutelist.Add(m); 120 | } 121 | file.ReRoutes = reroutelist; 122 | } 123 | } 124 | else 125 | { 126 | throw new Exception("未监测到任何可用的配置信息"); 127 | } 128 | } 129 | #endregion 130 | if (file.ReRoutes == null || file.ReRoutes.Count == 0) 131 | { 132 | return new OkResponse(null); 133 | } 134 | return new OkResponse(file); 135 | } 136 | 137 | //由于数据库存储可不实现Set接口直接返回 138 | public async Task Set(FileConfiguration fileConfiguration) 139 | { 140 | return new OkResponse(); 141 | } 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /src/Czar.Gateway/Stores/SqlServer/SqlServerRpcRepository.cs: -------------------------------------------------------------------------------- 1 | using Czar.Gateway.Configuration; 2 | using Czar.Gateway.Rpc; 3 | using Czar.Rpc.Message; 4 | using Dapper; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Data.SqlClient; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | 11 | namespace Czar.Gateway.Stores.SqlServer 12 | { 13 | /// 14 | /// 金焰的世界 15 | /// 2019-01-03 16 | /// Rpc相关仓储 17 | /// 18 | public class SqlServerRpcRepository : IRpcRepository 19 | { 20 | private readonly CzarOcelotConfiguration _option; 21 | public SqlServerRpcRepository(CzarOcelotConfiguration option) 22 | { 23 | _option = option; 24 | } 25 | 26 | /// 27 | /// 获取RPC调用方法 28 | /// 29 | /// 30 | /// 31 | public async Task GetRemoteMethodAsync(string UpUrl) 32 | { 33 | using (var connection = new SqlConnection(_option.DbConnectionStrings)) 34 | { 35 | string sql = @"select T4.* from AhphGlobalConfiguration t1 inner join AhphConfigReRoutes T2 on 36 | T1.AhphId=t2.AhphId inner join AhphReRoute T3 on T2.ReRouteId=T3.ReRouteId 37 | INNER JOIN AhphReRouteRpcConfig T4 ON T3.ReRouteId=T4.ReRouteId 38 | where IsDefault=1 and T1.InfoStatus=1 AND T3.InfoStatus=1 AND UpstreamPathTemplate=@URL"; 39 | var result = await connection.QueryFirstOrDefaultAsync(sql, new { URL = UpUrl }); 40 | return result; 41 | } 42 | } 43 | } 44 | } 45 | --------------------------------------------------------------------------------