修改接口项目
在上次的项目基础上,分别修改两个api项目的startup.cs
public void ConfigureServices(IServiceCollection services) { var audienceConfig = Configuration.GetSection("Audience"); var signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(audienceConfig["Secret"])); var tokenValidationParameters = new TokenValidationParameters { ValidateIssuerSigningKey = true, IssuerSigningKey = signingKey, ValidateIssuer = true, ValidIssuer = audienceConfig["Iss"], ValidateAudience = true, ValidAudience = audienceConfig["Aud"], ValidateLifetime = true, ClockSkew = TimeSpan.Zero, RequireExpirationTime = true, }; services.AddAuthentication(o => { o.DefaultAuthenticateScheme = "TestKey"; }) .AddJwtBearer("TestKey", x => { x.RequireHttpsMetadata = false; x.TokenValidationParameters = tokenValidationParameters; }); //services.AddConsulConfig(Configuration); services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2); }
修改配置文件
{ "Logging": { "LogLevel": { "Default": "Warning" } }, "AllowedHosts": "*", //"Consul": { // "Host": "http://192.168.2.29:8500" //}, "Service": { "Name": "ApiService", "IP": "192.168.2.16", "Port": "9001" }, "Consul": { "IP": "192.168.2.29", "Port": "8500" }, "Audience": { "Secret": "Y2F0Y2hlciUyMHdvbmclMjBsb3ZlJTIwLm5ldA==", "Iss": "http://www.c-sharpcorner.com/members/catcher-wong", "Aud": "Catcher Wong" } }
在接口的action中加入[Authorize]属性
[Authorize] [HttpGet] public string Count() { return $"Count {++_count} from ApiServices1"; }
加入IdentityServer4
新建webapi项目 ,nuget安装identityserver。将authapi项目也加入到consul中。所以要新建health控制器,新建一个授权控制器,修改startup.cs
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; namespace Test.WebApi.AuthServer.Controllers { [Produces("application/json")] [Route("api/[controller]")] [ApiController] public class HealthController : ControllerBase { [HttpGet] public IActionResult Get() => Ok("ok"); } }
using System; using System.Collections.Generic; using System.IdentityModel.Tokens.Jwt; using System.Linq; using System.Security.Claims; using System.Text; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Options; using Microsoft.IdentityModel.Tokens; namespace Test.WebApi.AuthServer.Controllers { [Route("authapi/[controller]")] [ApiController] public class AuthController : ControllerBase { private IOptions<Audience> _settings; public AuthController(IOptions<Audience> settings) { this._settings = settings; } [HttpGet] public ActionResult Get(string name, string pwd) { //just hard code here. if (name == "catcher" && pwd == "123") { var now = DateTime.UtcNow; var claims = new Claim[] { new Claim(JwtRegisteredClaimNames.Sub, name), new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()), new Claim(JwtRegisteredClaimNames.Iat, now.ToUniversalTime().ToString(), ClaimValueTypes.Integer64) }; var signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(_settings.Value.Secret)); var tokenValidationParameters = new TokenValidationParameters { ValidateIssuerSigningKey = true, IssuerSigningKey = signingKey, ValidateIssuer = true, ValidIssuer = _settings.Value.Iss, ValidateAudience = true, ValidAudience = _settings.Value.Aud, ValidateLifetime = true, ClockSkew = TimeSpan.Zero, RequireExpirationTime = true, }; var jwt = new JwtSecurityToken( issuer: _settings.Value.Iss, audience: _settings.Value.Aud, claims: claims, notBefore: now, expires: now.Add(TimeSpan.FromMinutes(2)), signingCredentials: new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256) ); var encodedJwt = new JwtSecurityTokenHandler().WriteToken(jwt); var responseJson = new { access_token = encodedJwt, expires_in = (int)TimeSpan.FromMinutes(2).TotalSeconds }; return new JsonResult(responseJson); } else { return new JsonResult(""); } } } public class Audience { public string Secret { get; set; } public string Iss { get; set; } public string Aud { get; set; } } }
修改 startup.cs
// This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2); services.AddOptions(); services.Configure<Controllers.Audience>(Configuration.GetSection("Audience")); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IHostingEnvironment env, IApplicationLifetime lifetime) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. app.UseHsts(); } ConsulService consulService = new ConsulService() { IP = Configuration["Consul:IP"], Port = Convert.ToInt32(Configuration["Consul:Port"]) }; HealthService healthService = new HealthService() { IP = Configuration["Service:IP"], Port = Convert.ToInt32(Configuration["Service:Port"]), Name = Configuration["Service:Name"], }; app.RegisterConsul(lifetime, healthService, consulService); app.UseHttpsRedirection(); app.UseMvc(); }
配置文件
{ "Logging": { "LogLevel": { "Default": "Warning" } }, "AllowedHosts": "*", "Service": { "Name": "AuthService", "IP": "192.168.2.16", "Port": "9003" }, "Consul": { "IP": "192.168.2.29", "Port": "8500" }, "Audience": { "Secret": "Y2F0Y2hlciUyMHdvbmclMjBsb3ZlJTIwLm5ldA==", "Iss": "http://www.c-sharpcorner.com/members/catcher-wong", "Aud": "Catcher Wong" } }
发布后,部署到IIS中,端口9003
参考链接:
https://www.cnblogs.com/xlxr45/p/11321134.html
修改网关项目
配置文件configuration.json
{ "ReRoutes": [ { "UseServiceDiscovery": true, "DownstreamPathTemplate": "/api/{url}", "DownstreamScheme": "http", "ServiceName": "ApiService", "LoadBalancerOptions": { "Type": "RoundRobin" }, "UpstreamPathTemplate": "/api/{url}", "UpstreamHttpMethod": [ "Get" ], "ReRoutesCaseSensitive": false }, { "UseServiceDiscovery": true, "DownstreamPathTemplate": "/authapi/{url}", "DownstreamScheme": "http", "ServiceName": "AuthService", "LoadBalancerOptions": { "Type": "RoundRobin" }, "UpstreamPathTemplate": "/authapi/{url}", "UpstreamHttpMethod": [ "Get" ], "ReRoutesCaseSensitive": false } ], "GlobalConfiguration": { "ServiceDiscoveryProvider": { "Host": "192.168.2.29", "Port": 8500, "Type": "PollConsul", "PollingInterval": 100 } } }
运行效果
新建一个cmd项目,测试下
class Program { static void Main(string[] args) { HttpClient client = new HttpClient(); client.DefaultRequestHeaders.Clear(); client.BaseAddress = new Uri("http://localhost:9000"); // 1. without access_token will not access the service // and return 401 . var resWithoutToken = client.GetAsync("/api/Counter/Count").Result; Console.WriteLine($"Sending Request to /api/Counter/Count , without token."); Console.WriteLine($"Result : {resWithoutToken.StatusCode}"); //2. with access_token will access the service // and return result. client.DefaultRequestHeaders.Clear(); Console.WriteLine("\nBegin Auth...."); var jwt = GetJwt(); Console.WriteLine("End Auth...."); Console.WriteLine($"\nToken={jwt}"); client.DefaultRequestHeaders.Add("Authorization", $"Bearer {jwt}"); var resWithToken = client.GetAsync("/api/Counter/Count").Result; Console.WriteLine($"\nSend Request to /api/Counter/Count , with token."); Console.WriteLine($"Result : {resWithToken.StatusCode}"); Console.WriteLine(resWithToken.Content.ReadAsStringAsync().Result); //3. visit no auth service Console.WriteLine("\nNo Auth Service Here "); client.DefaultRequestHeaders.Clear(); var res = client.GetAsync("/api/Counter/Count").Result; Console.WriteLine($"Send Request to /api/Counter/Count"); Console.WriteLine($"Result : {res.StatusCode}"); Console.WriteLine(res.Content.ReadAsStringAsync().Result); Console.Read(); } private static string GetJwt() { HttpClient client = new HttpClient(); client.BaseAddress = new Uri( "http://localhost:9000"); client.DefaultRequestHeaders.Clear(); var res2 = client.GetAsync("/authapi/auth?name=catcher&pwd=123").Result; dynamic jwt = JsonConvert.DeserializeObject(res2.Content.ReadAsStringAsync().Result); return jwt.access_token; } }
postman测试下。
先获取access_token
将access_token放到header中
如果不加入header中,则会报500错误