1. authentication
When the client passes Ocelot When accessing downstream Services , Authentication will be performed to protect downstream resource servers , You need to be in Ocelot Add authentication service . After adding authentication service , Subsequent use Ocelot Any function based on declaration , Such as authorizing or using Token Value modification request in . Users have to be in their Startup.cs Register authentication service in , But they offer a plan for each registration ( Authentication provider key ), for example :
public void ConfigureServices(IServiceCollection services) { var authenticationProviderKey = "TestKey"; services.AddAuthentication() .AddJwtBearer(authenticationProviderKey, x => { }); }
Here it is Ocelot Certification program Example ,TestKey Is a scheme that has registered this provider . Then we map it to the Routes route , for example :
{ "Routes": [ { "DownstreamPathTemplate": "/api/customers", "DownstreamScheme": "http", "DownstreamHost": "localhost", "DownstreamPort": 9001, "UpstreamPathTemplate": "/customers", "UpstreamHttpMethod": [ "Get" ], "AuthenticationOptions": { "AuthenticationProviderKey": "TestKey", "AllowedScopes": [] } } ] }
Ocelot Runtime , It will look at Routes.AuthenticationOptions.AuthenticationProviderKey And check if there is an authentication provider registered with the given key . If it doesn't exist , be Ocelot Will not start , If there is , be Routes The provider will be used at execution time .
If the route is authenticated ,Ocelot Any scheme associated with authentication middleware will be called when the authentication middleware is executed . If the request fails to pass authentication ,Ocelot Will return http The status code 401.
2.JWT Tokens Bearer authentication
Json Web Token (JWT), Is a kind of implementation based on the JSON Open standards for (RFC 7519). The token Designed to be compact and safe , Especially for single sign in of distributed sites (SSO) scene .JWT The declaration of is generally used to pass the authenticated user identity information between the identity provider and the service provider , To get resources from the resource server , You can also add some additional declaration information that other business logic requires , The token It can also be used directly for authentication , It can also be encrypted .
2.1JWT Token structure
In a compact form ,JSON Web Tokens from dot(.) The three separate parts make up , They are :
Header head 、Payload Payload 、Signature Signature
therefore ,JWT Usually as follows :xxxxx.yyyyy.zzzzz(Header.Payload.Signature)
2.1.1Header head
The header usually consists of two parts : Type of token , namely JWT, And the signature algorithm being used , for example HMAC SHA256 or RSA. for example :
{ "alg": "HS256", "typ": "JWT" }
then , This JSON Encoded as Base64Url, formation JWT The first part of .
2.1.2Payload Payload
Payload Part of it is also a JSON object , It is used to store the data that needs to be transferred .JWT Specifies the 7 Official fields , For selection .
iss (issuer): Issued by people
exp (expiration time): Expiration time
sub (subject): The theme
aud (audience): Audience
nbf (Not Before): entry-into-force time
iat (Issued At): The issuance of time
jti (JWT ID): Number
Except for official fields , You can also define private fields in this section , Here is an example . for example :
{ "sub": "1234567890", "name": "John Doe", "admin": true }
Be careful ,JWT It is not encrypted by default , Anyone can read it , So don't put secret information in this part . This JSON Objects also use Base64URL Algorithm to string .
2.1.3.Signature Signature
Signature Part is the signature of the first two parts , Prevent data tampering .
First , A key needs to be specified (secret). This key is only known to the server , Do not disclose to users . then , Use Header The signature algorithm specified in ( The default is HMAC SHA256), Follow the formula below to generate a signature .
HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
The signature is used to verify that the message was not changed during this process , also , In the case of a token signed with a private key , It can also be verified JWT Whether the sender of is the person it claims .
Put all three of them together , The output is three dots separated Base64-URL character string , Can be in HTML and HTTP Easy delivery in the environment , And based on XML Standards for ( Such as SAML) More compact than .
Here's a JWT, It has previous headers and payload encoding , And use secret signatures .
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoid3prNzAzIiwibmJmIjoiMTU5MjE0MzkzNyIsImV4cCI6MTU5MjE0Mzk5OCwiaXNzIjoiYXV0aC5qd3QuY2MiLCJhdWQiOiJkZW5nd3V8MjAyMC82LzE0IDIyOjEyOjE5In0
.4RiwhRy0rQkZjclOFWyTpmW7v0AMaL3aeve1L-eWIz0
In fact, it usually sends the user name and password to get token That's because Identity4 To complete , Including authenticating users , Generate JwtToken. But the project here is made up of System.IdentityModel.Tokens Class library to generate JwtToken. Finally back to jwt token token To the user .JwtToken Decoding can be done by https://jwt.io/ To see .
3. Project presentations
3.1APIGateway project
Enable authentication in this project to protect downstream api service , Use JwtBearer authentication , Set the default authentication scheme to TestKey. stay appsettings.json File configuration authentication key (Secret) Follow the audience (Aud) Information :
{ "Audience": { "Secret": "Y2F0Y2hlciUyMHdvbmclMjBsb3ZlJTIwLm5ldA==", "Iss": "http://www.c-sharpcorner.com/members/catcher-wong", "Aud": "Catcher Wong" } }
Startup Add the authentication code as follows :
public void ConfigureServices(IServiceCollection services) { // obtain appsettings.json File configuration authentication key (Secret) Follow the audience (Aud) Information var audienceConfig = Configuration.GetSection("Audience"); // Get the security key var signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(audienceConfig["Secret"])); //token Parameter set to verify var tokenValidationParameters = new TokenValidationParameters { // The security key must be verified ValidateIssuerSigningKey = true, // Assign the security key IssuerSigningKey = signingKey, // The issuer must be verified ValidateIssuer = true, // Assignment issuer ValidIssuer = audienceConfig["Iss"], // You have to verify the audience ValidateAudience = true, // Assign audience ValidAudience = audienceConfig["Aud"], // Whether the validation Token The period of validity , Use the current time and Token Of Claims Medium NotBefore and Expires contrast ValidateLifetime = true, // Server time offset allowed ClockSkew = TimeSpan.Zero, // Whether to ask for Token Of Claims Must include Expires RequireExpirationTime = true, }; // Add service validation , The plan is TestKey services.AddAuthentication(o => { o.DefaultAuthenticateScheme = "TestKey"; }) .AddJwtBearer("TestKey", x => { x.RequireHttpsMetadata = false; // stay JwtBearerOptions Configuration in progress ,IssuerSigningKey( Signature secret key )、ValidIssuer(Token Issuing authority )、ValidAudience( To whom ) Three parameters are necessary . x.TokenValidationParameters = tokenValidationParameters; }); // add to Ocelot Gateway Service , Include Secret Secret key 、Iss Issued by people 、Aud Audience services.AddOcelot(Configuration); } public async void Configure(IApplicationBuilder app, IHostingEnvironment env) { // Using authentication services app.UseAuthentication(); // Use Ocelot middleware await app.UseOcelot(); }
3.1.1Identity Server bearing JWT Token
In the second section, introduce JWT Token When it comes to certification , We all know that sending user name and password to get Token That's because Identity4 To complete , Including authenticating users , Generate JWT Token. in other words Identity Server Carrying JWT Token Authentication function . In order to use IdentityServer bearing Token, Please be at... As usual ConfigureServices Using solutions in ( secret key ) register IdentityServer service . If you don't know how to do this , Please refer to IdentityServer file .
public void ConfigureServices(IServiceCollection services) { var authenticationProviderKey = "TestKey"; Action<IdentityServerAuthenticationOptions> options = o => { o.Authority = "https://whereyouridentityserverlives.com"; o.ApiName = "api"; o.SupportedTokens = SupportedTokens.Both; o.ApiSecret = "secret"; }; services.AddAuthentication() .AddIdentityServerAuthentication(authenticationProviderKey, options); services.AddOcelot(); }
stay Identity4 It's from Authority Parameter assignment OIDC Service address ,OIDC Auto discovery Issuer, IssuerSigningKey Other configuration , and o.Audience And x.TokenValidationParameters = new TokenValidationParameters { ValidAudience = "api" } It's equivalent .
3.2AuthServer project
This service is mainly used when clients request protected resource servers , After authentication, the client needs JWT Token, Generate JWT Token The key codes are as follows :
[Route("api/[controller]")] public class AuthController : Controller { private IOptions<Audience> _settings; public AuthController(IOptions<Audience> settings) { this._settings = settings; } /// <summary> /// The user to use User name, password To request the server /// The server verifies the user's information /// The server is sent to the user by authentication token /// Client storage token, And attach this to every request token value , headers: {'Authorization': 'Bearer ' + token} /// Server side validation token value , And return the data /// </summary> /// <param name="name"></param> /// <param name="pwd"></param> /// <returns></returns> [HttpGet] public IActionResult Get(string name, string pwd) { // Verify login user name and password if (name == "catcher" && pwd == "123") { var now = DateTime.UtcNow; // Add user information , Turn into a group of statements , You can also write more user information statements var claims = new Claim[] { // The subject of the statement new Claim(JwtRegisteredClaimNames.Sub, name), //JWT ID Unique identifier new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()), // Release timestamp issued timestamp new Claim(JwtRegisteredClaimNames.Iat, now.ToUniversalTime().ToString(), ClaimValueTypes.Integer64) }; // Use Microsoft.IdentityModel.Tokens Help the class under the library to create JwtToken // Security key var signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(_settings.Value.Secret)); // Statement jwt Verify parameters var tokenValidationParameters = new TokenValidationParameters { // The security key must be verified ValidateIssuerSigningKey = true, // Assign the security key IssuerSigningKey = signingKey, // The issuer must be verified ValidateIssuer = true, // Assignment issuer ValidIssuer = _settings.Value.Iss, // You have to verify the audience ValidateAudience = true, // Assign audience ValidAudience = _settings.Value.Aud, // Whether the validation Token The period of validity , Use the current time and Token Of Claims Medium NotBefore and Expires contrast ValidateLifetime = true, // Server time offset allowed ClockSkew = TimeSpan.Zero, // Whether to ask for Token Of Claims Must include Expires RequireExpirationTime = true, }; var jwt = new JwtSecurityToken( //jwt Issued by people issuer: _settings.Value.Iss, //jwt Audience audience: _settings.Value.Aud, //jwt A set of statements claims: claims, notBefore: now, //jwt Token expiration time expires: now.Add(TimeSpan.FromMinutes(2)), // Certificate of signature : Security key 、 Signature algorithm signingCredentials: new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256) ); // Generate jwt token (json web token) var encodedJwt = new JwtSecurityTokenHandler().WriteToken(jwt); var responseJson = new { access_token = encodedJwt, expires_in = (int)TimeSpan.FromMinutes(2).TotalSeconds }; return Json(responseJson); } else { return Json(""); } } } public class Audience { public string Secret { get; set; } public string Iss { get; set; } public string Aud { get; set; } }
appsettings.json File configuration authentication key (Secret) Follow the audience (Aud) Information :
{ "Audience": { "Secret": "Y2F0Y2hlciUyMHdvbmclMjBsb3ZlJTIwLm5ldA==", "Iss": "http://www.c-sharpcorner.com/members/catcher-wong", "Aud": "Catcher Wong" } }
3.3CustomerAPIServices project
The project follows APIGateway The project is the same , To protect the downstream api service , Use JwtBearer authentication , Set the default authentication scheme to TestKey. stay appsettings.json File configuration authentication key (Secret) Follow the audience (Aud) Information :
{ "Audience": { "Secret": "Y2F0Y2hlciUyMHdvbmclMjBsb3ZlJTIwLm5ldA==", "Iss": "http://www.c-sharpcorner.com/members/catcher-wong", "Aud": "Catcher Wong" } }
Startup Add the authentication code as follows :
public void ConfigureServices(IServiceCollection services) { // obtain appsettings.json File configuration authentication key (Secret) Follow the audience (Aud) Information var audienceConfig = Configuration.GetSection("Audience"); // Get the security key var signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(audienceConfig["Secret"])); //token Parameter set to verify var tokenValidationParameters = new TokenValidationParameters { // The security key must be verified ValidateIssuerSigningKey = true, // Assign the security key IssuerSigningKey = signingKey, // The issuer must be verified ValidateIssuer = true, // Assignment issuer ValidIssuer = audienceConfig["Iss"], // You have to verify the audience ValidateAudience = true, // Assign audience ValidAudience = audienceConfig["Aud"], // Whether the validation Token The period of validity , Use the current time and Token Of Claims Medium NotBefore and Expires contrast ValidateLifetime = true, // Server time offset allowed ClockSkew = TimeSpan.Zero, // Whether to ask for Token Of Claims Must include Expires RequireExpirationTime = true, }; // Add service validation , The plan is TestKey services.AddAuthentication(o => { o.DefaultAuthenticateScheme = "TestKey"; }) .AddJwtBearer("TestKey", x => { x.RequireHttpsMetadata = false; // stay JwtBearerOptions Configuration in progress ,IssuerSigningKey( Signature secret key )、ValidIssuer(Token Issuing authority )、ValidAudience( To whom ) Three parameters are necessary . x.TokenValidationParameters = tokenValidationParameters; }); services.AddMvc(); } public void Configure(IApplicationBuilder app) { // Using authentication services app.UseAuthentication(); app.UseMvc(); }
stay CustomersController Next, add a method that requires authentication , A method that doesn't require authentication :
[Route("api/[controller]")] public class CustomersController : Controller { // Add authentication properties [Authorize] [HttpGet] public IEnumerable<string> Get() { return new string[] { "Catcher Wong", "James Li" }; } [HttpGet("{id}")] public string Get(int id) { return $"Catcher Wong - {id}"; } }
3.4ClientApp project
This project is used to simulate the client access to the resource server entire authentication process test project , stay Program The main program can see the following code :
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("/customers").Result; Console.WriteLine($"Sending Request to /customers , 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("/customers").Result; Console.WriteLine($"\nSend Request to /customers , 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("/customers/1").Result; Console.WriteLine($"Send Request to /customers/1"); 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("/api/auth?name=catcher&pwd=123").Result; dynamic jwt = JsonConvert.DeserializeObject(res2.Content.ReadAsStringAsync().Result); return jwt.access_token; } }
Run the project to see the test results :
Combining with the code , We can see when the client passes through Ocelot Gateway access downstream Services http://localhost:9000/api/Customers/Get When it comes to methods , Because the method needs to pass authentication to return the processing results , So it's going to be JWT Token authentication , If you find that there is no Token,Ocelot Then return to http The status code 401 Access denied . If we pass GetJwt Method in AuthServer Login authentication on the service to obtain authorization Token, Then access the resource server interface , The processing results will be returned immediately , By following without authentication http://localhost:9000/api/Customers/Get/{id} Method comparison , We knew ,Ocelot The certification has been successful !
4. summary
This chapter is just a combination of demo The brief introduction of the project is in Ocelot How to use JWT Token authentication . In fact, in the formal environment ,Ocelot It should be integrated IdentityServer Authorized , Again, by rewriting Ocelot Middleware, we can also put configuration.json The configuration information of is stored in the database or cached to Redis in .
reference :
Ocelot Official website