id4 使用 授权码模式授权

1 添加客户端

设置好clientid,添加客户端密钥,设置允许的作用域(scope)加上 openid 和 profile以及其它允许访问的scope,允许离线访问(请求 token 的时候scope要加上offline_access 会返回 refresh token),设置好重定向url(接收code的地址),设置好允许的授权类型为 authorization_code

2 mvc 客户端相关代码

startup.cs 代码如下:

using IdentityModel;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System.IdentityModel.Tokens.Jwt;

namespace MvcClient
{
    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.AddControllersWithViews();

            // 添加认证
            services.AddAuthentication("IdentityCookieAuthenScheme").AddCookie("IdentityCookieAuthenScheme", options =>
            {
                // sso 登录
                options.LoginPath = "/Login/Index"; // 未登陆的回调地址
                // 本地登录
                //options.LoginPath = "/Login/LocalIndex";

                options.Cookie.Name = "AuthCookie";
            });

            //JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); // 包 System.IdentityModel.Tokens.Jwt;
            //services.AddAuthentication(options =>
            //{
            //    options.DefaultScheme = "Cookies";
            //    options.DefaultChallengeScheme = "oidc";
            //})
            //.AddCookie("Cookies", options =>
            //{
            //    options.LoginPath = "/Login/Index"; // 未认证的回跳地址
            //    options.Cookie.Name = "AuthCookie";
            //})
            //.AddOpenIdConnect("oidc", options => // 安装包 Microsoft.AspNetCore.Authentication.OpenIdConnect
            //{
            //    options.Authority = "https://localhost:44310"; // identity server 服务器地址
            //    options.SignInScheme = "Cookies";
            //    options.ClientId = "mvc_client";
            //    options.ClientSecret = "mvc_secret";
            //    options.ResponseType = "code";
            //    //options.RequireHttpsMetadata = false;
            //    options.SaveTokens = true; // 把获取到的token写入到cookie
            //    options.Scope.Clear();
            //    options.Scope.Add("openid");
            //    options.Scope.Add("profile");
            //    options.Scope.Add(OidcConstants.StandardScopes.OfflineAccess);
            //});
        }

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

            app.UseRouting();

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

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Home}/{action=Index}/{id?}");
            });
        }
    }
}
using IdentityModel;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System.IdentityModel.Tokens.Jwt;

namespace MvcClient
{
    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.AddControllersWithViews();

            // 添加认证
            services.AddAuthentication("IdentityCookieAuthenScheme").AddCookie("IdentityCookieAuthenScheme", options =>
            {
                // sso 登录
                options.LoginPath = "/Login/Index"; // Authorize 失败后的回调地址
                // 本地登录
                //options.LoginPath = "/Login/LocalIndex";

                options.Cookie.Name = "AuthCookie";
            });

            //JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); // 包 System.IdentityModel.Tokens.Jwt;
            //services.AddAuthentication(options =>
            //{
            //    options.DefaultScheme = "Cookies";
            //    options.DefaultChallengeScheme = "oidc";
            //})
            //.AddCookie("Cookies", options =>
            //{
            //    options.LoginPath = "/Login/Index"; // 未认证的回跳地址
            //    options.Cookie.Name = "AuthCookie";
            //})
            //.AddOpenIdConnect("oidc", options => // 安装包 Microsoft.AspNetCore.Authentication.OpenIdConnect
            //{
            //    options.Authority = "https://localhost:44310"; // identity server 服务器地址
            //    options.SignInScheme = "Cookies";
            //    options.ClientId = "mvc_client";
            //    options.ClientSecret = "mvc_secret";
            //    options.ResponseType = "code";
            //    //options.RequireHttpsMetadata = false;
            //    options.SaveTokens = true; // 把获取到的token写入到cookie
            //    options.Scope.Clear();
            //    options.Scope.Add("openid");
            //    options.Scope.Add("profile");
            //    options.Scope.Add(OidcConstants.StandardScopes.OfflineAccess);
            //});
        }

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

            app.UseRouting();

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

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

未认证用户处理 LoginController 代码如下:

using Microsoft.AspNetCore.Mvc;
using System.Text;
using System.Net.Http;
using System.IO;
using Newtonsoft.Json.Linq;

namespace MvcClient.Controllers
{
    public class LoginController : Controller
    {
        private static string clientId = "mvc_client";
        private static string clientSecret = "mvc_secret";
        private static string id4Server = "https://localhost:44310";// 5001  44310
        private static string scope = "openid profile offline_access client_credentials_apis.WeatherForecastController.scope"; // 获取指定scope的token才有权调用该scope对应的apiresource 加上 offline_access 会返回 refresh token
        private static string redirectUri = "https://localhost:7001/Login/Callback"; // 接口code的地址

        public IActionResult Index()
        {
            //// 未登录
            // 从授权服务器获取 code
            string url = $"{id4Server}/connect/authorize?client_id={clientId}&response_type=code&scope={scope}&redirect_uri={redirectUri}&state=custom_state";
            return Redirect(url);
        }

        /// <summary>
        /// code 回调
        /// </summary>
        /// <param name="code"></param>
        /// <param name="state">自定义值</param>
        /// <returns></returns>
        public IActionResult Callback(string code, string state)
        {
            if (string.IsNullOrWhiteSpace(code))
                return Json(new { code = 0,msg="获取 code 失败"});

            string accessToken = string.Empty;
            string refreshToken = string.Empty;

            /*
             * 根据code 换取 access token
             */
            var postData = $"grant_type=authorization_code&code={code}&client_id={clientId}&client_secret={clientSecret}&redirect_uri={redirectUri}";
            var requestBody = Encoding.UTF8.GetBytes(postData);
            // 设置请求体类型
            string requestContentType = "application/x-www-form-urlencoded";
            using (HttpClient http = new HttpClient())
            {
                HttpResponseMessage message = null;
                using (Stream requestBodyStream = new MemoryStream(requestBody ?? new byte[0]))
                {
                    using (HttpContent content = new StreamContent(requestBodyStream))
                    {
                        content.Headers.Add("Content-Type", requestContentType);
                        var task = http.PostAsync(id4Server + "/connect/token", content);
                        message = task.Result;
                    }
                }
                using (message)
                {
                    string result = message.Content.ReadAsStringAsync().Result;
                    var json = JObject.Parse(result);
                    accessToken = json.GetValue("access_token").ToString();
                    refreshToken = json["refresh_token"].ToString();
                }
            }

            /*
             * 使用 access token 访问受保护资源
             */
            using (HttpClient httpclient = new HttpClient())
            {
                // 不使用token访问
                // 401 (Unauthorized).)
                //string res1 = httpclient.GetStringAsync("https://localhost:6001/Identity/Get").Result;

                // 使用 token 访问
                httpclient.DefaultRequestHeaders.Add("Authorization", "Bearer " + accessToken);
                var apiRes1 = httpclient.GetStringAsync($"https://localhost:6001/Test/Ping").Result;
                // 有token就能访问且 client allowedscope 包含 client_credentials_apis.WeatherForecastController.scope 才能访问
                var res_res3 = httpclient.GetStringAsync($"https://localhost:6001/WeatherForecast/Ping").Result;
                // 有token就能访问且 client allowedscope 包含 client_credentials_apis.IdentityUserController.scope 才能访问
                //var apiRes2 = httpclient.GetStringAsync($"https://localhost:6001/IdentityUser/Ping").Result;
            }

            // 通过 refresh token 刷新 access token
            var postData1 = $"grant_type=refresh_token&client_id={clientId}&client_secret={clientSecret}&refresh_token={refreshToken}";
            var requestBody1 = Encoding.UTF8.GetBytes(postData1);
            using (HttpClient http = new HttpClient())
            {
                HttpResponseMessage message = null;
                using (Stream requestBodyStream = new MemoryStream(requestBody1 ?? new byte[0]))
                {
                    using (HttpContent content = new StreamContent(requestBodyStream))
                    {
                        content.Headers.Add("Content-Type", requestContentType);
                        var task = http.PostAsync(id4Server + "/connect/token", content);
                        message = task.Result;
                    }
                }
                using (message)
                {
                    string result = message.Content.ReadAsStringAsync().Result;
                    var json = JObject.Parse(result);
                    accessToken = json.GetValue("access_token").ToString();

                    http.DefaultRequestHeaders.Add("Authorization", "Bearer " + accessToken);
                    // 有token就能访问
                    var userInfoRes = http.GetStringAsync($"{id4Server}/connect/userinfo").Result;
                }
            }

            return Json(new { code = 0, msg = "OK", data = new { code, state } });
        }
    }
}

 

posted @ 2022-01-25 22:18  温故纳新  阅读(671)  评论(1编辑  收藏  举报