.Net core IdentityServer4+OAuth2.0身份验证框架详解

这两天在新公司看了个MIS系统,其中登录用的Oauth2协议的授权码认证模式。感觉蛮有意思的,就去了解了下,写了个小例子:

该模式大致意思就是:首先用户访客户端,客户端将用户导向认证服务器,用户在认证服务器输入用户名密码授权得到认证码,再携带认证码再次请求认证服务器得到token,用户再携带token请求客户端资源。

这里配一张图来对照着看:

从图上可以清晰的看出来,客户端在向资源拥有者(服务端)请求获得许可后,再去向认证服务端获取Token令牌,然后拿着Token令牌去资源服务器请求数据。

这里有三块:

1、API资源端;

2、Client客户端;

3、IdentityServer服务端;

1、首先我们来新建:IdentityServer服务端

首先修改服务端打开端口,这里我用的5001,可以在Properties文件夹下的launchSettings.json中设置:

创建配置类:Con­fig

using IdentityServer4.Models;
using IdentityServer4.Test;
using System.Collections.Generic;

namespace IdentityServer
{
    public static class Config
    {
        /*配置允许登录的用户信息*/
        public static List<TestUser> TestUsers =>
      new List<TestUser>
      {

         new TestUser
        {
            SubjectId = "1",
            Username = "alice",
            Password = "password"
        },
        new TestUser
        {
            SubjectId = "2",
            Username = "200607157",
            Password = "bob"
        }

      };
        public static IEnumerable<IdentityResource> IdentityResources =>
        new IdentityResource[]
        {
            new IdentityResources.OpenId()
        };
        //定义如下API
        public static IEnumerable<ApiScope> ApiScopes =>
        new List<ApiScope>
        {
            new ApiScope("api1", "My API")
        };
        //定义客户端账户client 密码secret 运行接口范围api1
        public static IEnumerable<Client> Clients =>
        new List<Client>
        {
            new Client
            {
            ClientId = "roclient",

            // no interactive user, use the clientid/secret for authentication
            AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
            // secret for authentication
            ClientSecrets =
            {
            new Secret("secret".Sha256())
            },

            // scopes that client has access to
            AllowedScopes = { "api1" }
            }
        };
    }
}

这里我配置了认证方式及登录用户信息,具体的代码逻辑大家可以去看看OAuth2.0客户端模式、密码模式,我就不做详细解说了。

然后在S­tar­tup下的­Con­fig­ure­Ser­vices注入依赖:

   public void ConfigureServices(IServiceCollection services)
        {
            var builder = services.AddIdentityServer(options =>
            {
                options.EmitStaticAudienceClaim = true;
            })
                .AddInMemoryIdentityResources(Config.IdentityResources)
                .AddInMemoryApiScopes(Config.ApiScopes)
                .AddInMemoryClients(Config.Clients).AddTestUsers(Config.TestUsers);


            builder.AddDeveloperSigningCredential();
        }

这里还需要在­Con­fig­ure下添加验证:

app.UseIdentityServer();

到这里服务端就配置完成了。

2、新建:Client客户端

这里主要就是获取相应­To­ken提供个API使用。

using IdentityModel;
using IdentityModel.Client;
using Newtonsoft.Json.Linq;
using System;
using System.Net.Http;

namespace Client
{
    class Program
    {
        static async System.Threading.Tasks.Task Main(string[] args)
        {
            string re= Console.ReadLine();
            if (re == "123")
            {
                var client = new HttpClient();

                //https://localhost:5001 验证服务器是否开启
                var disco = await client.GetDiscoveryDocumentAsync("https://localhost:5001");
                if (disco.IsError)
                {
                    Console.WriteLine(disco.Error);
                    return;
                }

                 //获取token
                //var tokenResponse = await client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest
                //{
                //    Address = disco.TokenEndpoint,
                   
                //    ClientId = "client",
                //    ClientSecret = "secret",
                //    Scope = "api1"
                //});

                //if (tokenResponse.IsError)
                //{
                //    Console.WriteLine(tokenResponse.Error);
                //    return;
                //}


                var tokenurl = disco.TokenEndpoint;
                // 封装请求参数,获取授权码
                // 资源服务器会去授权服务器认证
                var tokenResponse = client.RequestPasswordTokenAsync(new PasswordTokenRequest
                {
                    Address = tokenurl,
                    UserName = "alice",
                    Password = "password",

                    ClientId = "roclient",
                    ClientSecret = "secret",
                    Scope = "api1"       
                }).Result;

              

                Console.WriteLine(tokenResponse.Json);
             
                //携带token访问API客户端,验证API是否开启  https://localhost:6001/identity
                client.SetBearerToken(tokenResponse.AccessToken);

                 var response = await client.GetAsync("https://localhost:6001/identity");
              
                if (!response.IsSuccessStatusCode)
                {
                    Console.WriteLine(response.StatusCode);
                    Console.ReadKey();
                }
                else
                {
                    var content = await response.Content.ReadAsStringAsync();
                    Console.WriteLine(JArray.Parse(content));
                    Console.ReadKey();
                }
            }
           
            
        }
    }
}

3、新建:API资源管理端

这里我用的端口为6001,端口可以随意设置,但一定要与服务端跟客户端请求的API端口保持一致。

设置端口可以在Properties下的launchSettings.json中设置。

新建I­den­ti­ty­Con­troller控制器:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;

namespace Api.Controllers
{

    [Route("identity")]
    [Authorize]
    public class IdentityController : ControllerBase
    {
        [HttpGet]
        public IActionResult Get()
        {
            return new JsonResult(from c in User.Claims select new { c.Type, c.Value });
        }
    }
}

这里设置的路由细心的小伙伴应该发现了,我限定的一下,而且还加了需要验证的标签,这里对应这Client客户端的 var response = await client.GetAsync(“https://localhost:6001/identity”);这一步,至于为什么要这样,你可以认为是用户信息的校验。

这里还需要在S­tar­tup中配置依赖:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.IdentityModel.Tokens;

namespace Api
{
    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)
        {
            services.AddControllers();
            services.AddAuthentication("Bearer")
            .AddJwtBearer("Bearer", options =>
            {
                //这里指向服务端地址 因为token是该服务端颁发的
                options.Authority = "https://localhost:5001";

                options.TokenValidationParameters = new TokenValidationParameters
                {
                    ValidateAudience = false
                };
            });
        }

        // 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();

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

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }
    }
}

到了这里具体的例子就讲完了,可能大家还有很多没理解为什么要这样的,大家可以去看看IdentityServer4、OAuth2.0鉴权模式就会了解。我这里可以作为参照学习。

具体实例大家可以下载下来:

为您推荐

发表评论

您的电子邮箱地址不会被公开。