Authentication, authorization

What is identity authentication

Identity authentication is a mechanism to verify the validity of a client when it accesses server resources

What is authorization

Authorization is a mechanism by which clients can access server resources only after being authenticated

Why use authentication and authorization

In order to ensure the security of server-side resources, we must understand it from the real project

What are the ways of identity authentication and authorization

1. Base authentication

Base64 Id authentication == HTTPS

2. Digest authentication

MD5 message digest authentication == HTTPS

3. Bearer certification

Token (electronic ID card) authentication is based on the user information and other information into a token, and then the token authentication

Token authentication is a stateless authentication mode that can be extended indefinitely and is particularly suitable for single sign-on (SSO)

3.1 OAuth 2.0 ==== Authentication mode

3.2 JWT is also a form of identification

There are two types of tokens

Reference type Token == OAuth 2.0

None User information

Self contained token

There is user related information JWT === address, phone, ID, etc

In actual distributed projects, Bearer is mostly used for identity authentication and authorization

In distributed projects or microservice projects, to ensure unified system login (SSO login),

Use the OpenID protocol standard to standardize the identity authentication function === specification

It also uses the OAuth 2.0 protocol standard to standardize access === specification

To combine authentication (single sign-on) with authorization, the OpenID Connect protocol standard === interface emerged

​ OpenID Connect = OpenID + OAuth 2.0

SSO+OAuth 2.0 enables single sign-on and authorization

Single sign-on and authorization are also available for IdentityServer4

And then we implemented all of these frameworks together and we’re going to talk about IdentityServer4

How do I use IdentityServer4 in a project

What is IdentityServer4

IdentityServer is an identity authentication and authorization program based on the OpenID Connect protocol standard, which implements the OpenID Connect and OAuth 2.0 protocols.

IdentityServer4 function

Secure your resources Authenticate users using local accounts or through external identity providers provide session management and single sign-on management and authentication Clients issue identity and access tokens to customers authentication tokens

IdentityServer4 Internal concepts

The following terms are explained

User A User is a person who accesses resources using a registered client (that is, a registered client in ID4).

Client

A client is a piece of software that requests tokens from IdentityServer, authenticating users with tokens, and accessing server resources with authorization tokens. However, the client must first be registered with the identityserver service before applying for a token.

The actual client can not only be a Web application, APP or desktop application (you can think of it as PC software), SPA, server process, etc.

Resources

A resource is something you want to protect with IdentityServer. It could be the user’s identity data or an API resource. Each resource has a unique name, use this unique name to identify the client which want to access resources (before the visit, the actual identityserver server has been configured for which the client can access which resources, so you don’t have to understand as long as you specify a name for the client they can access any resource) casually.

The user’s identity information actually consists of a set of claims. For example, the name or email will be included in the identity information (which will be returned to the called client after verification by IdentityServer).

The API resource is the function that the client wants to call (usually returned to the client in JSON or XML format, such as WebAPI, WCF, webService). It is usually modeled through webAPI, but not necessarily webAPI. I have emphasized that you can make other types of formats, This depends on the specific use scenario.

Id_token JWT An identity token is a description of the authentication process. It must at least identify the master identity information of a user (Called the sub aka subject claim), and the authentication time and authentication method of the user. However, the identity token can contain additional identity data, which can be customized by the developer. However, in order to ensure the efficiency of data transfer, the developer usually does not do too much extra Settings.

Access_token Oauth 2.0 Access token allows a client to access an API resource. The client requests an access token, which is then used to access API resources. The access token contains information about the client and the user (if any, depending on whether the business needs it, but not usually necessary) that the API uses to grant data access to the client.

IdentityServer4 Role logical diagram

See figure part

IdentityServer4 is used in microservices

IdentityServer4 official website address

Chinese address: www.identityserver.com.cn/Home/Detail…

English address: identityserver4. Readthedocs. IO/en / 3.1.0 /

Client authentication mode

conditions

IdentityServer4 Authentication Server

2. Client

3. Microservices (Resources)

4, the user

steps

1, NutGet download IdentityServer4 in resource server

1.1. Configure IdentityServer4 in the IOC container

/ / 1, the ioc container, add IdentityServer4 services. AddIdentityServer () AddDeveloperSigningCredential () / / 1, the user login configuration .addinmemoryApiResources (config.getapiResources ()).addinMemoryClients (config.getClients ())Copy the code

1.2. Use IdentityServer4

// 1, use IdentityServe4 app.useidentityServer (); //2, add static resource access app.usestaticFiles ();Copy the code

1.3. Configure the test user Config class

// </summary> public class Config {/// </summary> // 1, micro service API resources /// </summary> /// 1, micro service API resources /// </summary> /// <returns></returns> public static IEnumerable<ApiResource> GetApiResources() { return new List<ApiResource> { new ApiResource("TeamService", "TeamService API needs to be protected ")}; /// </summary> /// </returns> public static IEnumerable<Client> GetClients() {return </ // </summary> // </returns> public static IEnumerable<Client> GetClients() {return New List<Client> {new Client {ClientId = "Client ", // No interactive user, use ClientId /secret to implement authentication. AllowedGrantTypes = GrantTypes ClientCredentials, / / for the password authentication ClientSecrets = {new Secret (" Secret ". Sha256 ())}, // Scopes that the client has access to (Scopes) AllowedScopes = {"TeamService"}}}; }}Copy the code

2. Micro-service configuration

2.1 download IdentityServer4 Nuget. AccessTokenValidation

2.2 Configuring Identity and Authorization Authentication (Configure Authentication resources to be protected by resource Servers)

/ / 6, calibration AccessToken, calibration services from the identity check center. AddAuthentication (IdentityServerAuthenticationDefaults. AuthenticationScheme) .AddIdentityServerAuthentication(options => { options.Authority = "http://localhost:5005"; Options. ApiName = "TeamService"; / / 2, name of the API (project name) options. RequireHttpsMetadata = false; });Copy the code

2.3 Enabling Identity Authentication

app.UseAuthentication(); // 1. Use authenticationCopy the code

3. Client configuration

3.1 Nuget Download IdentityModel

3.2 Call the code

The default client authentication mode is described above

Client password authentication mode

conditions

1. Test

steps

1. Add a new client in Config

New Client {ClientId = "Client - password", / / use the user name password interactive authentication AllowedGrantTypes = GrantTypes. ResourceOwnerPassword, // ClientSecrets = {new Secret(" Secret ".sha256 ())}, // AllowedScopes = {"TeamService"}},Copy the code

2. In config, add the test user

/// </summary> // </returns> public static List<TestUser> GetUsers() {return new List<TestUser>() { new TestUser { SubjectId="1", Username="tony", Password="123456" } }; }Copy the code

3. Add the configuration in startup

/ / 1, the ioc container, add IdentityServer4 services. AddIdentityServer () AddDeveloperSigningCredential () / / 1, the user login configuration .addinmemoryApiResources (config.getapiResources ()).addinMemoryClients (config.getClients ()) .addTestUsers (config.getUsers ())//Copy the code

4. Start the call

The call procedure is already written in the code

Authorization code mode authentication (obtain the assess_token by code)

conditions

1, OpenIdConnect

steps

1, download Microsoft Nuget. AspNetCore. Authentication. OpenIdConnect

2. Configure the authentication server

2.1 Add the OpenID identity resource statement in config

public static IEnumerable<IdentityResource> Ids => new List<IdentityResource>
        {
            new IdentityResources.OpenId(),
            new IdentityResources.Profile()
        };	
Copy the code

2.2 Adding a Client in Config

// OpenId new Client {ClientId="client-code", ClientSecrets={new Secret(" Secret ".sha256 ())}, AllowedGrantTypes=GrantTypes.Code, RequireConsent=false, RequirePkce=true, RedirectUris={ "https://localhost:5006/signin-oidc"}, PostLogoutRedirectUris={ "https://localhost:5006/signout-callback-oidc"}, AllowedScopes=new List<string>{ IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants. StandardScopes. Profile, "TeamService" / / to enable support for the refresh token}, / / add grant access AllowOfflineAccess = true}Copy the code

2.3 Adding an OpenidTest User

// OpenID authentication new TestUser{SubjectId = "818727", Username = "SubjectId ", Password = "123456", Claims = {new Claim(JwtClaimTypes.Name, "three "), new Claim(JwtClaimTypes.GivenName," three "), New Claim (JwtClaimTypes FamilyName, "zhang"), a new Claim (JwtClaimTypes. Email, "zhangsan.com"), new Claim(JwtClaimTypes.EmailVerified, "true", ClaimValueTypes.Boolean), new Claim(JwtClaimTypes.WebSite, "http://zhangsan.com"), // new Claim(jwtclaimtypes.address, @"{' city ': 'hangzhou ',' zip ': '310000' }", IdentityServer4.IdentityServerConstants.ClaimValueTypes.Json) } }Copy the code

2.4. Add the configuration to startup

/ / 1, the ioc container, add IdentityServer4 services. AddIdentityServer () AddDeveloperSigningCredential () / / 1, the user login configuration .addinmemoryApiResources (config.getapiResources ()).addinMemoryClients (config.getClients ()) AddTestUsers (Config. GetUsers ()) / / 4, add the logged in user (patterns). AddInMemoryIdentityResources (Config. Ids); // use openID modeCopy the code

2.5 Preparing for logging in to the UI

It’s ready in the code

3. Client configuration

3.1 Configuration in startup.cs File

// We use Cookies to log in to users locally (through "Cookies" as DefaultScheme) and set DefaultChallengeScheme to OIDC. Because when we need users to log in, we will use the OpenID Connect protocol. services.AddAuthentication(options => { options.DefaultScheme = "Cookies"; options.DefaultChallengeScheme = "oidc"; }) // Add a handler that can handle Cookies.AddCookie("Cookies") // Use the handler to configure the implementation of OpenID Connect protocol. options => { options.Authority = "http://localhost:5005"; / / trusted token service address options. RequireHttpsMetadata = false; options.ClientId = "client-code"; options.ClientSecret = "secret"; options.ResponseType = "code"; options.SaveTokens = true; Add("TeamService"); // Add("TeamService"); // Add("TeamService"); options.Scope.Add("offline_access"); });Copy the code

3.2 Using the Configuration

// 1, use static page app.usestaticFiles (); // 2. Enable app.useAuthentication ();Copy the code

IdentityServer4 persistence

It is mainly to store resource data, user data and identity data in the database.

conditions

1, ConfigurationDbContext

Used to configure data such as Clients, Resources, and scopes.

2, PersistedGrantDbContext

Used for temporary manipulation of data, such as authorization codes and refreshing tokens

steps

1, install IdentityServer4 Nuge. EntityFramework

​ Microsoft.EntityFrameworkCore.SqlServer

​ Microsoft.EntityFrameworkCore.Tools

2. Reconfigure the startup. cs file

Var migrationsAssembly = typeof(Startup).gettypeInfo ().assembly.getName ().name; var connectionString = Configuration.GetConnectionString("DefaultConnection"); services.AddIdentityServer() .AddConfigurationStore(options => { options.ConfigureDbContext = builder => { builder.UseSqlServer(connectionString, options => options.MigrationsAssembly(migrationsAssembly)); }; }) .AddOperationalStore(options => { options.ConfigureDbContext = builder => { builder.UseSqlServer(connectionString, options => options.MigrationsAssembly(migrationsAssembly)); }; }) .AddTestUsers(Config.GetUsers()) .AddDeveloperSigningCredential();Copy the code

3. Configuration in AppSettings. json

{ "ConnectionStrings": { "DefaultConnection": "Data Source=.; Initial Catalog=IndentityServer4; Persist Security Info=True; User ID=sa; Password=tony" } }Copy the code

4. Execute the migration script

dotnet ef migrations add PersistedGrantDbMigration -c PersistedGrantDbContext -o Data/Migrations/IdentityServer/PersistedGrantDb
dotnet ef migrations add ConfigurationDbMigration -c ConfigurationDbContext -o Data/Migrations/IdentityServer/ConfigurationDb
Copy the code

5. Migration scripts generate databases and tables

dotnet ef database update -c PersistedGrantDbContext 
dotnet ef database update -c ConfigurationDbContext 
Copy the code

6. Sample data store

Private void InitializeDatabase(IApplicationBuilder app) {using (var serviceScope = app.ApplicationServices.GetRequiredService<IServiceScopeFactory>().CreateScope()) { serviceScope.ServiceProvider.GetService<PersistedGrantDbContext>().Database.Migrate(); var context = serviceScope.ServiceProvider.GetService<ConfigurationDbContext>(); context.Database.Migrate(); if (! context.Clients.Any()) { foreach (var client in Config.GetClients()) { context.Clients.Add(client.ToEntity()); } context.SaveChanges(); } if (! context.IdentityResources.Any()) { foreach (var resource in Config.Ids) { context.IdentityResources.Add(resource.ToEntity()); } context.SaveChanges(); } if (! context.ApiResources.Any()) { foreach (var resource in Config.GetApiResources()) { context.ApiResources.Add(resource.ToEntity()); } context.SaveChanges(); }}}Copy the code

6. Access from the client

Persistent user IdentityServer4 (data store)

conditions

1, the Identity

steps

1, install Microsoft Nuget. AspNetCore. Identity. EntityFrameworkCore

2. Add context classes

public class IdentityServerUserDbContext : IdentityDbContext<IdentityUser> { public IdentityServerUserDbContext(DbContextOptions<IdentityServerUserDbContext> options) : base(options) { } protected override void OnModelCreating(ModelBuilder builder) { base.OnModelCreating(builder); }}Copy the code

3. Configure startup.cs file

services.AddDbContext<IdentityServerUserDbContext>(options => { options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")); }); AddIdentity<IdentityUser, IdentityRole > (options = > {/ / 1.2 Password complexity configuration options. Password. RequireDigit = true; options.Password.RequiredLength = 6; options.Password.RequiredUniqueChars = 1; options.Password.RequireLowercase = false; options.Password.RequireNonAlphanumeric = false; options.Password.RequireUppercase = false; }) .AddEntityFrameworkStores<IdentityServerUserDbContext>() .AddDefaultTokenProviders();Copy the code

4. Execute the migration script

	dotnet ef migrations add IdentityServerUserDbMigration -c IdentityServerUserDbContext -o Data/Migrations/IdentityServer/IdentityServerUserDb
Copy the code

5. Migration scripts generate databases and tables

dotnet ef database update -c IdentityServerUserDbContext
Copy the code

6. Sample data store

Private void InitializeUserDatabase(IApplicationBuilder app) {using (var serviceScope = app.ApplicationServices.GetRequiredService<IServiceScopeFactory>().CreateScope()) { var context = serviceScope.ServiceProvider.GetService<IdentityServerUserDbContext>(); context.Database.Migrate(); var userManager = serviceScope.ServiceProvider.GetRequiredService<UserManager<IdentityUser>>(); var idnetityUser = userManager.FindByNameAsync("tony").Result; if (idnetityUser == null) { idnetityUser = new IdentityUser { UserName = "zhangsan", Email = "[email protected]" }; var result = userManager.CreateAsync(idnetityUser, "123456").Result; if (! result.Succeeded) { throw new Exception(result.Errors.First().Description); } result = userManager.AddClaimsAsync(idnetityUser, new Claim[] { new Claim(JwtClaimTypes.Name, "tony"), new Claim(JwtClaimTypes.GivenName, "tony"), new Claim(JwtClaimTypes.FamilyName, "tony"), new Claim(JwtClaimTypes.Email, "[email protected]"), new Claim(JwtClaimTypes.EmailVerified, "true", ClaimValueTypes.Boolean), new Claim(JwtClaimTypes.WebSite, "http://tony.com") }).Result; if (! result.Succeeded) { throw new Exception(result.Errors.First().Description); }}}}Copy the code

6. AccountController Changes the Login code

public class AccountController : Controller { private UserManager<ApplicationUser> _userManager; private SignInManager<ApplicationUser> _signInManager; private IIdentityServerInteractionService _interaction; public AccountController(UserManager<ApplicationUser> userManager, SignInManager<ApplicationUser> signInManager, IIdentityServerInteractionService interaction) { _userManager = userManager; _signInManager = signInManager; _interaction = interaction; } public IActionResult Login(string returnUrl) { ViewData["ReturnUrl"] = returnUrl; return View(); } [HttpPost] public async Task<IActionResult> Login(LoginViewModel loginViewModel, string returnUrl) { // check if we are in the context of an authorization request var context = await _interaction.GetAuthorizationContextAsync(model.ReturnUrl); // the user clicked the "cancel" button if (button ! = "login") { if (context ! = null) { // if the user cancels, send a result back into IdentityServer as if they // denied the consent (even if this client does not require consent). // this will send back an access denied OIDC error response to the client. await _interaction.GrantConsentAsync(context,  ConsentResponse.Denied); // we can trust model.ReturnUrl since GetAuthorizationContextAsync returned non-null if (await _clientStore.IsPkceClientAsync(context.ClientId)) { // if the client is PKCE then we assume it's native, so this change in how to // return the response is for better UX for the end user. return this.LoadingPage("Redirect", model.ReturnUrl); } return Redirect(model.ReturnUrl); } else { // since we don't have a valid context, then we just go back to the home page return Redirect("~/"); } } if (ModelState.IsValid) { var user = await _userManager.FindByNameAsync(model.Username); If (user == null) {modelstate.addModelError (nameof(model.username), $" Username {model.username} not exists"); } else { if (await _userManager.CheckPasswordAsync(user, model.Password)) { AuthenticationProperties props = null; if (model.RememberLogin) { props = new AuthenticationProperties { IsPersistent = true, ExpiresUtc = DateTimeOffset.UtcNow.Add(TimeSpan.FromMinutes(30)) }; } var isuser = new IdentityServerUser(user.Id) { DisplayName = user.UserName }; await HttpContext.SignInAsync(isuser, props); if (_interaction.IsValidReturnUrl(model.ReturnUrl)) { return Redirect(model.ReturnUrl); } return Redirect("~/"); } modelstate.addModelError (nameof(model.password), "Password error "); } } // something went wrong, show form with error var vm = await BuildLoginViewModelAsync(model); return View(vm); }}Copy the code

Ocelot integration IdentityServer4

conditions

1, IdentityServer4

steps

1, install IdentityServer4 Nuget. AccessTokenValidation

2. Configure IndentityServer4 in the startup file

/ / 1, configuration IdentityServer var authenticationProviderKey = "OcelotKey"; services.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme) .AddIdentityServerAuthentication(options => { options.Authority = "http://localhost:5005"; Options. ApiName = "TeamService"; / / 2, name of the API (project name) options. RequireHttpsMetadata = false; Options.SupportedTokens = SupportedTokens.Both; });Copy the code

3. Configure authentication in Ocelot

"ReRoutes": [
{
"AuthenticationOptions": {
		"AuthenticationProviderKey": "OcelotKey",
		"AllowedScopes": []
	}
}
]
Copy the code

Ocelot integrates IdentityServer4 configuration encapsulation

conditions

1, IdentityServerOptions class

steps

1. Create IdentityServerOptions class

/// </summary> public class IdentityServerOptions {/// </summary> /// authorization server address ///  </summary> public int AuthorityAddress { get; set; } // <summary> // access_token type, /// </summary> Public string IdentityScheme {get; set; } /// <summary> // ResourceName, authentication service registered resource list name is the same, /// </summary> public string ResourceName {get; set; }}Copy the code

2. Configure IdentityServerOptions

Var identityServerOptions = new identityServerOptions (); Configuration.Bind("IdentityServerOptions", identityServerOptions); var authenticationProviderKey = "OcelotKey"; services.AddAuthentication(identityServerOptions.IdentityScheme) .AddIdentityServerAuthentication(options => { options.Authority = identityServerOptions.AuthorityAddress; / / 1, authorization center address options. ApiName = identityServerOptions. ResourceName; / / 2, name of the API (project name) options. RequireHttpsMetadata = false; Options.SupportedTokens = SupportedTokens.Both; });Copy the code

Ocelot dynamic routing

conditions

1, the consul

steps

1. Configure dynamic routes

{
  "ReRoutes": [],
  "Aggregates": [],
  "GlobalConfiguration": {
    "RequestIdKey": null,
    "ServiceDiscoveryProvider": {
      "Host": "localhost",
      "Port": 8500,
      "Type": "Consul",
      "Token": null,
      "ConfigurationKey": null
    },
    "RateLimitOptions": {
      "ClientIdHeader": "ClientId",
      "QuotaExceededMessage": null,
      "RateLimitCounterPrefix": "ocelot",
      "DisableRateLimitHeaders": false,
      "HttpStatusCode": 429
    },
    "QoSOptions": {
      "ExceptionsAllowedBeforeBreaking": 0,
      "DurationOfBreak": 0,
      "TimeoutValue": 0
    },
    "BaseUrl": null,
    "LoadBalancerOptions": {
      "Type": "LeastConnection",
      "Key": null,
      "Expiry": 0
    },
    "DownstreamScheme": "https",
    "HttpHandlerOptions": {
      "AllowAutoRedirect": false,
      "UseCookieContainer": false,
      "UseTracing": false
    }
  }
}
Copy the code

2. Configure traffic limiting for dynamic routing services

{
  "DynamicReRoutes": [
    {
      "ServiceName": "TeamService",
      "RateLimitRule": {
        "ClientWhitelist": [],
        "EnableRateLimiting": true,
        "Period": "1s",
        "PeriodTimespan": 3000,
        "Limit": 3
      }
    }
  ],
  "GlobalConfiguration": {
    "RequestIdKey": null,
    "ServiceDiscoveryProvider": {
      "Host": "localhost",
      "Port": 8500,
      "Type": "Consul"
    },
    "RateLimitOptions": {
      "ClientIdHeader": "ClientId",
      "QuotaExceededMessage": "",
      "RateLimitCounterPrefix": "",
      "DisableRateLimitHeaders": false,
      "HttpStatusCode": 428
    },
    "DownstreamScheme": "http"
  }
}
Copy the code

The specific code is as follows:

IdentityServer4 Authentication center

/// <summary>
    ///Identity Test use
    /// </summary>
    public class Config
    {
        /// <summary>
        ///1. Microservice API resources
        /// </summary>
        /// <returns></returns>
        public static IEnumerable<ApiResource> GetApiResources()
        {
            return new List<ApiResource>
            {
                new ApiResource("TeamService"."TeamService API needs to be protected".new List<string> {"role"."admin"})}; }/// <summary>
        ///2. Client
        /// </summary>
        /// <returns></returns>
        public static IEnumerable<Client> GetClients()
        {
            return new List<Client>
            {
                new Client
                {
                    ClientId = "client".// Without interactivity, use clientid/secret for authentication.
                    // 1. Client authentication mode
                    // 2. Client user password authentication mode
                    // 3. Authorization Code Authentication Mode
                    // Simple authentication mode (js)
                    AllowedGrantTypes = GrantTypes.ClientCredentials,

                    // Password for authentication
                    ClientSecrets =
                    {
                        new Secret("secret".Sha256())
                    },
                    // Scopes that the client has access to (Scopes)
                    AllowedScopes = { "TeamService"."MemberService"}},new Client
                {
                    ClientId = "client-password".// Use the username and password for interactive authentication
	                AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,

	                // Password for authentication
	                ClientSecrets =
                    {
                        new Secret("secret".Sha256())
                    },
	                // Scopes that the client has access to (Scopes)
	                AllowedScopes = { "TeamService"}},// OpenID client
                new Client
                {
                    ClientId="client-code",
                    ClientSecrets={new Secret("secret".Sha256())},
                    AllowedGrantTypes=GrantTypes.Code,
                    RequireConsent=false,
                    RequirePkce=true,

                    RedirectUris={ "https://localhost:5006/signin-oidc"}, // 1. Client address

                    PostLogoutRedirectUris={ "https://localhost:5006/signout-callback-oidc"},// 2. Login and exit address

                    AllowedScopes=new List<string>{
                    IdentityServerConstants.StandardScopes.OpenId,
                    IdentityServerConstants.StandardScopes.Profile,
                    "TeamService" // Enable service authorization support
                    },

                    // Add authorized access
                    AllowOfflineAccess=true}}; }/// <summary>
        ///2.1 OpenID Identity Resources
        /// </summary>
        public static IEnumerable<IdentityResource> Ids => new List<IdentityResource>
        {
            new IdentityResources.OpenId(),
            new IdentityResources.Profile()
        };

        /// <summary>
        ///3. Test users
        /// </summary>
        /// <returns></returns>
        public static List<TestUser> GetUsers()
        {
            return new List<TestUser>()
            {
                new TestUser
                {
                    SubjectId="1",
                    Username="tony",
                    Password="123456"
                },
                // OpenID authentication
                new TestUser{SubjectId = "818727", Username = "tony-1", Password = "123456",
                    Claims =
                    {
                        new Claim(JwtClaimTypes.Name, "tony-1"),
                        new Claim(JwtClaimTypes.GivenName, "tony-1"),
                        new Claim(JwtClaimTypes.FamilyName, "tony-1"),
                        new Claim(JwtClaimTypes.Email, "[email protected]"),
                        new Claim(JwtClaimTypes.EmailVerified, "true", ClaimValueTypes.Boolean),
                        new Claim(JwtClaimTypes.WebSite, "http://tony-1.com"),
                      // new Claim(jwtclaimtypes. Address, @"{' city ': 'hangzhou ',' zip ': '310000' }", IdentityServer4.IdentityServerConstants.ClaimValueTypes.Json)}}}; }}Copy the code
public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            // add IdentityServer4 to ioc container
           / * services. AddIdentityServer (.) AddDeveloperSigningCredential () / / 1, the user login configuration .addinmemoryApiResources (config.getapiResources ()).addinMemoryClients (config.getClients ()) AddTestUsers (Config. GetUsers ()) / / 4, the client user. AddInMemoryIdentityResources (Config. Ids); // 5. Openid */


            // 2, how to persist Config data to Sqlserver
            // 2. Resource client persistence
            var migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name;
            var connectionString = Configuration.GetConnectionString("DefaultConnection");
            services.AddIdentityServer()
                .AddConfigurationStore(options =>
                {
                    options.ConfigureDbContext = builder =>
                    {
                        builder.UseSqlServer(connectionString, options =>
                             options.MigrationsAssembly(migrationsAssembly));
                    };
                })
                .AddOperationalStore(options =>
                {
                    options.ConfigureDbContext = builder =>
                    {
                        builder.UseSqlServer(connectionString, options =>
                  options.MigrationsAssembly(migrationsAssembly));
                    };
                })
                .AddDeveloperSigningCredential();


            // 2. User related configuration
            services.AddDbContext<IdentityServerUserDbContext>(options =>
            {
                options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"));
            });
            1.1 Adding a User
            services.AddIdentity<IdentityUser, IdentityRole>(options => {
                1.2 Password complexity Configuration
                options.Password.RequireDigit = true;
                options.Password.RequiredLength = 6;
                options.Password.RequiredUniqueChars = 1;
                options.Password.RequireLowercase = false;
                options.Password.RequireNonAlphanumeric = false;
                options.Password.RequireUppercase = false;
            })
            .AddEntityFrameworkStores<IdentityServerUserDbContext>()
            .AddDefaultTokenProviders();

            services.AddControllersWithViews();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            // 1. Initialize data
            InitializeDatabase(app);

            // 2. Initialize user data
            InitializeUserDatabase(app);

            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                app.UseHsts();
            }
            app.UseHttpsRedirection();
            app.UseStaticFiles();

            // 1、使用IdentityServe4
            app.UseIdentityServer();

            app.UseRouting();
            app.UseAuthentication();
            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Home}/{action=Index}/{id? }");
            });
        }


        // 1. Store the data in config
        private void InitializeDatabase(IApplicationBuilder app)
        {
            using (var serviceScope = app.ApplicationServices.GetRequiredService<IServiceScopeFactory>().CreateScope())
            {
                serviceScope.ServiceProvider.GetService<PersistedGrantDbContext>().Database.Migrate();

                var context = serviceScope.ServiceProvider.GetService<ConfigurationDbContext>();
                context.Database.Migrate();
                if(! context.Clients.Any()) {foreach (var client in Config.GetClients())
                    {
                        context.Clients.Add(client.ToEntity());
                    }
                    context.SaveChanges();
                }

                if(! context.IdentityResources.Any()) {foreach (var resource in Config.Ids)
                    {
                        context.IdentityResources.Add(resource.ToEntity());
                    }
                    context.SaveChanges();
                }

                if(! context.ApiResources.Any()) {foreach (var resource inConfig.GetApiResources()) { context.ApiResources.Add(resource.ToEntity()); } context.SaveChanges(); }}}// 2. Store the user's data
        private void InitializeUserDatabase(IApplicationBuilder app)
        {
            using (var serviceScope = app.ApplicationServices.GetRequiredService<IServiceScopeFactory>().CreateScope())
            {
                var context = serviceScope.ServiceProvider.GetService<IdentityServerUserDbContext>();
                context.Database.Migrate();

                var userManager = serviceScope.ServiceProvider.GetRequiredService<UserManager<IdentityUser>>();
                var idnetityUser = userManager.FindByNameAsync("tony").Result;
                if (idnetityUser == null)
                {
                    idnetityUser = new IdentityUser
                    {
                        UserName = "tony",
                        Email = "[email protected]"
                    };
                    var result = userManager.CreateAsync(idnetityUser, "123456").Result;
                    if(! result.Succeeded) {throw new Exception(result.Errors.First().Description);
                    }
                    result = userManager.AddClaimsAsync(idnetityUser, new Claim[] {
                        new Claim(JwtClaimTypes.Name, "tony"),
                        new Claim(JwtClaimTypes.GivenName, "tony"),
                        new Claim(JwtClaimTypes.FamilyName, "tony"),
                        new Claim(JwtClaimTypes.Email, "[email protected]"),
                        new Claim(JwtClaimTypes.EmailVerified, "true", ClaimValueTypes.Boolean),
                        new Claim(JwtClaimTypes.WebSite, "http://tony.com")
                    }).Result;

                    if(! result.Succeeded) {throw new Exception(result.Errors.First().Description);
                    }
                }
            }
        }
    }
Copy the code

The gateway layer

public class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                    webBuilder.ConfigureAppConfiguration((hostingContext, config) =>
                    {
                        // Load the Ocelot configuration file
                        // config.AddJsonFile("ocelot.json");
                        config
                             .SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
                             .AddJsonFile("appsettings.json".true.true)
                            .AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json".true.true)
                            .AddJsonFile("ocelot.json".true.true) // Dynamic routing configuration
                           // .AddOcelot(hostingContext.HostingEnvironment)
                            .AddEnvironmentVariables();
                    });
                });
                
    }
Copy the code
public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
        public void ConfigureServices(IServiceCollection services)
        {
            // 1
            var identityServerOptions = new IdentityServerOptions();
            Configuration.Bind("IdentityServerOptions", identityServerOptions);
            services.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)
                    .AddIdentityServerAuthentication(identityServerOptions.IdentityScheme, options =>
                    {
                        options.Authority = identityServerOptions.AuthorityAddress; // 1. Address of authorization center
                        options.ApiName = identityServerOptions.ResourceName; // 2. API name (project name)
                        options.RequireHttpsMetadata = false; // 3. HTTPS metadata is not required
                    });

            // add gateway Ocelot to ioc container
            services.AddOcelot().AddConsul().AddPolly();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            // 2
            app.UseOcelot((build, config) =>
            {
                build.BuildCustomeOcelotPipeline(config); 2.1 Customization of Ocelot middleware is complete
            }).Wait();

            app.UseRouting();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapGet("/".async context =>
                {
                    await context.Response.WriteAsync("Hello World!"); }); }); }}Copy the code
{
  "ReRoutes": []."Aggregates": []."DynamicReRoutes": [{"ServiceName": "TeamService"."RateLimitRule": {
        "ClientWhitelist": []."EnableRateLimiting": true."Period": "1s"."PeriodTimespan": 3000."Limit": 3}}]."GlobalConfiguration": {
    "RequestIdKey": null."ServiceDiscoveryProvider": {
      "Host": "localhost"."Port": 8500."Type": "Consul"."Token": null."ConfigurationKey": null
    },
    "RateLimitOptions": {
      "ClientIdHeader": "ClientId"."QuotaExceededMessage": "3123123"."RateLimitCounterPrefix": "ocelot"."DisableRateLimitHeaders": false."HttpStatusCode": 429
    },
    "QoSOptions": {
      "ExceptionsAllowedBeforeBreaking": 0."DurationOfBreak": 0."TimeoutValue": 0
    },
    "BaseUrl": null."LoadBalancerOptions": {
      "Type": "LeastConnection"."Key": null."Expiry": 0
    },
    "DownstreamScheme": "https"."HttpHandlerOptions": {
      "AllowAutoRedirect": false."UseCookieContainer": false."UseTracing": false
    },
    "AuthenticationOptions": {
      "AuthenticationProviderKey": "OcelotKey"."AllowedScopes": ["memberservice"."teamserver"]}}}Copy the code
/// <summary>
    ///Register ocelot middleware
    /// </summary>
    public static class DemoOcelotExtension
    {
        public static IOcelotPipelineBuilder UseDemoResponseMiddleware(this IOcelotPipelineBuilder builder)
        {
            returnbuilder.UseMiddleware<DemoResponseMiddleware>(); }}Copy the code
/// <summary>
    ///Custom Ocelot middleware
    /// </summary>
    public class DemoResponseMiddleware : OcelotMiddleware
    {
        private readonly OcelotRequestDelegate _next;
        public DemoResponseMiddleware(OcelotRequestDelegate next, IOcelotLoggerFactory loggerFactory)
            : base(loggerFactory.CreateLogger<DemoResponseMiddleware>())
        {
            _next = next;
        }


        public async Task Invoke(DownstreamContext context)
        {
            if(! context.IsError && context.HttpContext.Request.Method.ToUpper() ! ="OPTIONS")
            {
                Console.WriteLine("Custom Middleware");
                Console.WriteLine("Custom business logic processing");
                // 1
                // resutList resultMap 
                // 2
                // 3. Perform link monitoring
                // 4. Performance monitoring
                // 5. Traffic statistics

            }
            else
            {
                await_next.Invoke(context); }}}Copy the code
public static class OcelotPipelineExtensions
    {
        public static OcelotRequestDelegate BuildCustomeOcelotPipeline(this IOcelotPipelineBuilder builder,
            OcelotPipelineConfiguration pipelineConfiguration)
        {
            // This is registered to catch any global exceptions that are not handled
            // It also sets the Request Id if anything is set globally
            builder.UseExceptionHandlerMiddleware();

            // If the request is for websockets upgrade we fork into a different pipeline
            builder.MapWhen(context => context.HttpContext.WebSockets.IsWebSocketRequest,
                app =>
                {
                    app.UseDownstreamRouteFinderMiddleware();
                    app.UseDownstreamRequestInitialiser();
                    app.UseLoadBalancingMiddleware();
                    app.UseDownstreamUrlCreatorMiddleware();
                    app.UseWebSocketsProxyMiddleware();
                });

            // Allow the user to respond with absolutely anything they want.
            builder.UseIfNotNull(pipelineConfiguration.PreErrorResponderMiddleware);

            // This is registered first so it can catch any errors and issue an appropriate response
            builder.UseResponderMiddleware();

            // Then we get the downstream route information
            builder.UseDownstreamRouteFinderMiddleware();

            // This security module, IP whitelist blacklist, extended security mechanism
            builder.UseSecurityMiddleware();

            //Expand other branch pipes
            if(pipelineConfiguration.MapWhenOcelotPipeline ! =null)
            {
                foreach (var pipeline inpipelineConfiguration.MapWhenOcelotPipeline) { builder.MapWhen(pipeline); }}// Now we have the ds route we can transform headers and stuff?
            builder.UseHttpHeadersTransformationMiddleware();

            // Initialises downstream request
            builder.UseDownstreamRequestInitialiser();

            // We check whether the request is ratelimit, and if there is no continue processing
            builder.UseRateLimiting();

            // This adds or updates the request id (initally we try and set this based on global config in the error handling middleware)
            // If anything was set at global level and we have a different setting at re route level the global stuff will be overwritten
            // This means you can get a scenario where you have a different request id from the first piece of middleware to the request id middleware.
            builder.UseRequestIdMiddleware();

            // Allow pre authentication logic. The idea being people might want to run something custom before what is built in.
            builder.UseIfNotNull(pipelineConfiguration.PreAuthenticationMiddleware);

            // Now we know where the client is going to go we can authenticate them.
            // We allow the ocelot middleware to be overriden by whatever the
            // user wants
            if (pipelineConfiguration.AuthenticationMiddleware == null)
            {
                builder.UseAuthenticationMiddleware();
            }
            else
            {
                builder.Use(pipelineConfiguration.AuthenticationMiddleware);
            }

            // The next thing we do is look at any claims transforms in case this is important for authorisation
            builder.UseClaimsToClaimsMiddleware();

            // Allow pre authorisation logic. The idea being people might want to run something custom before what is built in.
            builder.UseIfNotNull(pipelineConfiguration.PreAuthorisationMiddleware);

            // Now we have authenticated and done any claims transformation we
            // can authorise the request
            // We allow the ocelot middleware to be overriden by whatever the
            // user wants
            if (pipelineConfiguration.AuthorisationMiddleware == null)
            {
                builder.UseAuthorisationMiddleware();
            }
            else
            {
                builder.Use(pipelineConfiguration.AuthorisationMiddleware);
            }

            // Now we can run the claims to headers transformation middleware
            builder.UseClaimsToHeadersMiddleware();

            // Allow the user to implement their own query string manipulation logic
            builder.UseIfNotNull(pipelineConfiguration.PreQueryStringBuilderMiddleware);

            // Now we can run any claims to query string transformation middleware
            builder.UseClaimsToQueryStringMiddleware();

            // Get the load balancer for this request
            builder.UseLoadBalancingMiddleware();

            // This takes the downstream route we retrieved earlier and replaces any placeholders with the variables that should be used
            builder.UseDownstreamUrlCreatorMiddleware();

            // Not sure if this is the best place for this but we use the downstream url
            // as the basis for our cache key.
            builder.UseOutputCacheMiddleware();

            //We fire off the request and set the response on the scoped data repo
            builder.UseHttpRequesterMiddleware();

            // Add custom test middleware
            builder.UseDemoResponseMiddleware();

            return builder.Build();
        }

        private static void UseIfNotNull(this IOcelotPipelineBuilder builder,
            Func<DownstreamContext, Func<Task>, Task> middleware)
        {
            if(middleware ! =null) { builder.Use(middleware); }}}Copy the code
/// <summary>
    ///Configuration options for IdentityServer
    /// </summary>
    public class IdentityServerOptions
    {
       /// <summary>
        ///Address of the authorization server
        /// </summary>
        public string AuthorityAddress { get; set; }
        /// <summary>
        ///Type of access_token. The token_type value in the access_token parameter is the same
        /// </summary>
        public string IdentityScheme  { get; set; }
        /// <summary>
        ///The resource name is the same as the resource list registered by the authentication service.
        /// </summary>
        public string ResourceName { get; set; }}Copy the code

The end of the service

public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
             // add authentication
            // We use Cookies to log in users locally (through "Cookies" as DefaultScheme) and set DefaultChallengeScheme to OIDC. Because when we need users to log in, we will use the OpenID Connect protocol.
            services.AddAuthentication(options =>
            {
                options.DefaultScheme = "Cookies";
                options.DefaultChallengeScheme = "oidc"; // openid connect
            })
            // Add a handler that can handle cookies
            .AddCookie("Cookies")
            // Used to configure the handler that executes the OpenID Connect protocol
            .AddOpenIdConnect("oidc", options =>
            {
                // 1. Generate id_token
                options.Authority = "http://localhost:5005";    // Address of trusted token service
                options.RequireHttpsMetadata = false;
                options.ClientId = "client-code";
                options.ClientSecret = "secret";
                options.ResponseType = "code";
                options.SaveTokens = true;  // Used to keep tokens from IdentityServer in cookies
               
                // Add access_token to access_token API
                options.Scope.Add("TeamService");
                options.Scope.Add("offline_access");
            });

            services.AddControllersWithViews();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                app.UseHsts();
            }
            app.UseHttpsRedirection();

            // 1
            app.UseStaticFiles();
            
            app.UseRouting();
            app.UseAuthentication(); // 1. Add authentication
            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Home}/{action=Index}/{id? }").RequireAuthorization(); }); }}Copy the code
/// <summary>
    ///MVC client
    /// </summary>
    public class HomeController : Controller
    {
        private readonly ILogger<HomeController> _logger;

        public HomeController(ILogger<HomeController> logger)
        {
            _logger = logger;
        }

        public async Task<IActionResult> Index()
        {
            #regionToken mode
            {
               String access_token = await GetAccessToken(); access_token = await GetAccessToken(); access_token = await GetAccessToken(); String result = await UseAccessToken(access_token); // Add("Json", result); * /
            }
            #endregion

            #regionOpenid connect agreement
            {
                // 1, obtain the token(id_token, access_token,refresh_token)
                var accessToken = await HttpContext.GetTokenAsync("access_token"); // ("id_token")
                Console.WriteLine($"accessToken:{accessToken}");
                // var refreshToken =await HttpContext.GetTokenAsync("refresh_token");
                var client = new HttpClient();
                client.SetBearerToken(accessToken);
                /*client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", accessToken); * /

                // 2
                var result = await client.GetStringAsync("https://localhost:5004/TeamService/teams");// ==== https://localhost:5001/teams

                // 3. Return the result to the page
                ViewData.Add("Json", result);
            }
            #endregion
           
            return View();
        }

        /// <summary>
        ///1. Generate tokens
        /// </summary>
        /// <returns></returns>
        public static async Task<string> GetAccessToken()
        {
            // Create a connection
            HttpClient client = new HttpClient();
            DiscoveryDocumentResponse disco = await client.GetDiscoveryDocumentAsync("http://localhost:5005");
            if (disco.IsError)
            {
                Console.WriteLine($"[DiscoveryDocumentResponse Error]: {disco.Error}");
            }
            Access AccessToken from the client
           /* TokenResponse tokenResponse = await client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest { AccessToken center ClientId = "client", Scope = "TeamService" Scope = "TeamService" Scope = "TeamService"}); * /

            1.2 Obtaining AccessToken using the client user password
            TokenResponse tokenResponse = await client.RequestPasswordTokenAsync(new PasswordTokenRequest
            {
                Address = disco.TokenEndpoint,
                ClientId = "client-password",
                ClientSecret = "secret",
                Scope = "TeamService",
                UserName = "tony",
                Password = "123456"
            });

            // 1.3 Obtaining AccessToken with Authorization Code [login required]
            /* TokenResponse tokenResponse = await client.RequestAuthorizationCodeTokenAsync (new AuthorizationCodeTokenRequest { Address = disco.TokenEndpoint, ClientId = "client-code", ClientSecret = "secret", Code = "12", RedirectUri = "http://localhost:5005" }); * /
            if (tokenResponse.IsError)
            {
                //ClientId and ClientSecret error: invalid_client
                Scope error: invalid_scope
                //UserName and Password error: invalid_grant
                string errorDesc = tokenResponse.ErrorDescription;
                if (string.IsNullOrEmpty(errorDesc)) errorDesc = "";
                if (errorDesc.Equals("invalid_username_or_password"))
                {
                    Console.WriteLine("Wrong username or password, please re-enter!");
                }
                else
                {
                    Console.WriteLine($"[TokenResponse Error]: {tokenResponse.Error}, [TokenResponse Error Description]: {errorDesc}"); }}else
            {
                Console.WriteLine($"Access Token: {tokenResponse.Json}");
                Console.WriteLine($"Access Token: {tokenResponse.RefreshToken}");
                Console.WriteLine($"Access Token: {tokenResponse.ExpiresIn}");
            }
            return tokenResponse.AccessToken;
        }

        /// <summary>
        ///2. Use tokens
        /// </summary>
        public static async Task<string> UseAccessToken(string AccessToken)
        {
            HttpClient apiClient = new HttpClient();
            apiClient.SetBearerToken(AccessToken); // set token to request header
            HttpResponseMessage response = await apiClient.GetAsync("https://localhost:5001/teams");
            if(! response.IsSuccessStatusCode) { Console.WriteLine($"API Request Error, StatusCode is : {response.StatusCode}");
            }
            else
            {
                string content = await response.Content.ReadAsStringAsync();
                Console.WriteLine("");
                Console.WriteLine($"Result: {JArray.Parse(content)}");

                // 3. Output the result to the page
                return JArray.Parse(content).ToString();
            }
            return "";
            
        }

        public IActionResult Privacy()
        {
            return View();
        }

        [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
        public IActionResult Error()
        {
            return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
        }
    }
Copy the code

The service side

public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            // register the context to the IOC container
            services.AddDbContext<TeamContext>(options => {
                options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"));
            });
            // 2
            services.AddScoped<ITeamService, TeamServiceImpl>();

            // 3. Register team storage
            services.AddScoped<ITeamRepository, TeamRepository>();

            // add a mapping
            //services.AddAutoMapper();

            // 5. Add a service registration condition
            services.AddConsulRegistry(Configuration);

            // 6. Verify AccessToken from the identity verification center
            /*services.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme) .AddIdentityServerAuthentication(options => { options.Authority = "http://localhost:5005"; Options. ApiName = "TeamService"; / / 2, name of the API (project name) options. RequireHttpsMetadata = false; }); * /

            services.AddControllers();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseHttpsRedirection();

            } // 1. Consul service registration
            app.UseConsulRegistry();

            app.UseRouting();

          // app.UseAuthentication(); // enable authentication
            app.UseAuthorization();// 2app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); }}Copy the code
/// <summary>
    ///Team microservices API
    /// </summary>
    [Route("Teams")]
  // [Authorize] // 1
    [ApiController]
    public class TeamsController : ControllerBase
    {
        private readonly ITeamService teamService;

        public TeamsController(ITeamService teamService)
        {
            this.teamService = teamService;
        }

        // GET: api/Teams
        [HttpGet]
        public ActionResult<IEnumerable<Team>> GetTeams()
        {
            // Thread.Sleep(10000000);
            // 1
            Console.WriteLine($" Query team information");
            return teamService.GetTeams().ToList();
        }

        // GET: api/Teams/5
        [HttpGet("{id}")]
        public ActionResult<Team> GetTeam(int id)
        {
            Team team = teamService.GetTeamById(id);

            if (team == null)
            {
                return NotFound();
            }
            return team;
        }

        // PUT: api/Teams/5
        // To protect from overposting attacks, please enable the specific properties you want to bind to, for
        // more details see https://aka.ms/RazorPagesCRUD.
        [HttpPut("{id}")]
        public IActionResult PutTeam(int id, Team team)
        {
            if(id ! = team.Id) {return BadRequest();
            }

            try
            {
                teamService.Update(team);
            }
            catch (DbUpdateConcurrencyException)
            {
                if(! teamService.TeamExists(id)) {return NotFound();
                }
                else
                {
                    throw; }}return NoContent();
        }

        // POST: api/Teams
        // To protect from overposting attacks, please enable the specific properties you want to bind to, for
        // more details see https://aka.ms/RazorPagesCRUD.
        [HttpPost]
        public ActionResult<Team> PostTeam(Team team)
        {
            teamService.Create(team);

            return CreatedAtAction("GetTeam".new { id = team.Id }, team);
        }

        // DELETE: api/Teams/5
        [HttpDelete("{id}")]
        public ActionResult<Team> DeleteTeam(int id)
        {
            var team = teamService.GetTeamById(id);
            if (team == null)
            {
                return NotFound();
            }

            teamService.Delete(team);
            returnteam; }}Copy the code