zoukankan      html  css  js  c++  java
  • .Net Core 商城微服务项目系列(一):使用IdentityServer4构建基础登录验证

    这里第一次搭建,所以IdentityServer端比较简单,后期再进行完善。

    1.新建API项目MI.Service.Identity,NuGet引用IdentityServer4,添加类InMemoryConfiguration用于配置api和客户端资源:

    public class InMemoryConfiguration
        {
            public static IConfiguration Configuration { get; set; }
            /// <summary>
            /// Define which APIs will use this IdentityServer
            /// </summary>
            /// <returns></returns>
            public static IEnumerable<ApiResource> GetApiResources()
            {
                return new[]
                {
                    new ApiResource("MI.Service", "MI.Service"),
                };
            }
    
            /// <summary>
            /// Define which Apps will use thie IdentityServer
            /// </summary>
            /// <returns></returns>
            public static IEnumerable<Client> GetClients()
            {
                return new[]
                {
                    new Client
                    {
                        ClientId = "MI.Web",
                        ClientSecrets = new [] { new Secret("miwebsecret".Sha256()) },
                        AllowedGrantTypes = GrantTypes.ClientCredentials,
                        AllowedScopes = new [] { "MI.Service" }
                    }
                };
            }
    
            public static IEnumerable<IdentityResource> GetIdentityResources()
            {
                return new List<IdentityResource>
                {
                    new IdentityResources.OpenId(),
                    new IdentityResources.Profile(),
                };
            }
    
            /// <summary>
            /// Define which uses will use this IdentityServer
            /// </summary>
            /// <returns></returns>
            public static IEnumerable<TestUser> GetUsers()
            {
                return new[]
                {
                    new TestUser
                    {
                        SubjectId = "10001",
                        Username = "admin",
                        Password = "admin"
                    },
                    new TestUser
                    {
                        SubjectId = "10002",
                        Username = "wei",
                        Password = "123"
                    },
                    new TestUser
                    {
                        SubjectId = "10003",
                        Username = "test",
                        Password = "123"
                    }
                };
            }
        }

    简单介绍一下,既然是微服务项目,比如有需要的API,ApiResource即我们要使用的API资源,这里我用“MI.Service”,后面的API项目也需要和这里配置的相同。当前也可以每一个API项目都新建一个ApiResource的名称。

    Client是发起调用发,比如我们的Web系统会调用API,那Web系统就是一个Client,也可以理解为一个角色,Client Id是角色标识,这个也需要在发起调用方那边配置,ClientSecrets是私钥,这里使用最简单的自带私钥,AllowedScopes是当前这个Client可以访问的ApiResource。

    TestUser是IdentityServer自带的测试用户类,用户使用用户名和密码的方式登录使用。

    然后需要在Startup中添加IdentityServer配置:

    在ConfigureServices方法中添加如下:

    services.AddIdentityServer()
               .AddDeveloperSigningCredential()
               .AddTestUsers(InMemoryConfiguration.GetUsers().ToList())
               .AddInMemoryClients(InMemoryConfiguration.GetClients())
               .AddInMemoryApiResources(InMemoryConfiguration.GetApiResources());

    这里我们使用的均是内存级别的配置,在实际项目里建议改为数据库中读取。

    然后在Configure方法中启用IdentityServer:

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
            {
                if (env.IsDevelopment())
                {
                    app.UseDeveloperExceptionPage();
                }
    
                app.UseIdentityServer();
                app.UseStaticFiles();
                app.UseMvcWithDefaultRoute();
            }

    到此IdentityServer验证端配置完毕。

    2.新建API项目MI.Service.Account,NuGet引用 IdentityServer4.AccessTokenValidation。

    在Startup的ConfigureServices方法中进行IdentityServer4配置:

     services.AddAuthentication(Configuration["Identity:Scheme"])  //
                .AddIdentityServerAuthentication(options =>
                {
                    options.RequireHttpsMetadata = false; // for dev env
                    options.Authority = $"http://{Configuration["Identity:IP"]}:{Configuration["Identity:Port"]}";  //IdnetityServer项目IP和端口
                    options.ApiName = Configuration["Service:Name"]; // match with configuration in IdentityServer  //当前API项目的ApiResource的名称 即我们上个项目的“MI.Service”
                });

    在Configure中启用验证:

            public void Configure(IApplicationBuilder app, IHostingEnvironment env)
            {
                if (env.IsDevelopment())
                {
                    app.UseDeveloperExceptionPage();
                }
                else
                {
                    app.UseHsts();
                }
                app.UseAuthentication();  //启用验证
                app.UseMvcWithDefaultRoute();
                
            }

    我们整理用的是appsettings.json的配置,配置如下:

    {
      "Service": {
        "Name": "MI.Service",
        "Port": "7001",
        "DocName": "Account Service",
        "Version": "v1",
        "Title": "Account Service API",
        "Description": "CAS Client Service API provide some API to help you get client information from CAS"
        //"XmlFile": "Manulife.DNC.MSAD.IdentityServer4Test.ApiService01.xml"
      },
      "Identity": {
        "IP": "localhost",
        "Port": "7000",
        "Scheme": "Bearer"
      }
    }

    我们的IdentityServer项目运行在7000端口,当前API项目运行在70001端口,大家可以根据需要自行配置。

    在当前API项目新增控制器MiUserController,并新增一个测试方法和一个登陆方法:

    [EnableCors("AllowCors")]
        [Authorize]  //这里添加验证标签
        public class MiUserController : Controller
        {
    //实体上下文类
    public MIContext _context; public MiUserController(MIContext _context) { this._context = _context; } //这个方法用来进行测试 public IActionResult Index() { return Json("Successful"); } public async Task<SSOLoginResponse> SSOLogin(SSOLoginRequest request) { SSOLoginResponse response = new SSOLoginResponse(); try { if (!string.IsNullOrEmpty(request.UserName) && !string.IsNullOrEmpty(request.Password)) { var user = _context.UserEntities.FirstOrDefault(a => a.CustomerPhone.Equals(request.UserName)); if (user == null) { response.Successful = false; response.Message = "用户名或密码错误!"; return response; } if (user.CustomerPwd == request.Password) { //将用户名存储硬盘cookie 30分钟 作用域为整个网站 HttpContext.Response.Cookies.Append("MIUserName", user.CustomerPhone, new Microsoft.AspNetCore.Http.CookieOptions { Expires = DateTime.Now.AddMinutes(30), Path = "/", }); return response; } } response.Successful = false; response.Message = "用户名密码不能为空!"; } catch (Exception ex) { response.Successful = false; response.Message = ex.Message; } return response; } }

    现在配置完成,我们现在PostMan中测试一下请求IdentityServer项目获取Token,下面请求参数分别是我们之前配置的:

    不出意外我们能够获取到对应的Token。

    拿到Token后我们可以使用它来请求API项目:MI.Service.Account:

    Token前我们必须要有Bearer这个,我们之前在API项目的appsettings.json中也加过这个配置,如果一切正常我们能够获取当测试方法Index返回的“Successful”。

    3.新建Web项目MI.Web,毕竟这些API项目需要有调用方,要么是Web端,要么是移动端,既然是商城就要有一个Web端界面。

    通过Nuget添加 IdentityModel。

    在Web项目的Startup.cs的ConfigureServices方法中注册缓存使用,我们获取的Token需要存储在缓存中重复使用:

            public void ConfigureServices(IServiceCollection services)
            {
                services.AddMvc();
                services.AddMemoryCache(); //注册缓存
            }
    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
            {
                if (env.IsDevelopment())
                {
                    app.UseBrowserLink();
                    app.UseDeveloperExceptionPage();
                }
    
                app.UseStaticFiles();
    
                app.UseMvcWithDefaultRoute(); //添加默认的MVC请求路由
            }

    在Web项目的appsettings.json中配置对应的API项目地址:

    {
      "Logging": {
        "IncludeScopes": false,
        "LogLevel": {
          "Default": "Warning"
        }
      },
      "ServiceAddress": {
        "Service.Identity": "http://localhost:7000/",
        "Service.Account": "http://localhost:7001/"
      },
      "MehtodName": {
        "Account.MiUser.SSOLogin": "MiUser/SSOLogin", //登录
        "Identity.Connect.Token": "connect/token"  //获取token
      }
    }

    接下来我们需要在Web中获取Token就需要有一个公用的方法,我在ApiHelper中添加了一个方法如下,这里使用了IdentityModel提供的方法来获取Token:

            //获取Token
            public static async Task<string> GetToken()
            {
                string token = null;
                if (cache.TryGetValue<string>("Token", out token))
                {
                    return token;
                }
                try
                {
                    //DiscoveryClient类:IdentityModel提供给我们通过基础地址(如:http://localhost:5000)就可以访问令牌服务端;
                    //当然可以根据上面的restful api里面的url自行构建;上面就是通过基础地址,获取一个TokenClient;(对应restful的url:token_endpoint   "http://localhost:5000/connect/token")
                    //RequestClientCredentialsAsync方法:请求令牌;
                    //获取令牌后,就可以通过构建http请求访问API接口;这里使用HttpClient构建请求,获取内容;
                    var dico = await DiscoveryClient.GetAsync("http://localhost:7000");
                    var tokenClient = new TokenClient(dico.TokenEndpoint, "MI.Web", "miwebsecret");
                    var tokenResponse = await tokenClient.RequestClientCredentialsAsync("MI.Service");
                    if (tokenResponse.IsError)
                    {
                        throw new Exception(tokenResponse.Error);
                        
                    }
                    token = tokenResponse.AccessToken;
                    cache.Set<string>("Token", token, TimeSpan.FromSeconds(tokenResponse.ExpiresIn));
                }
                catch (Exception ex)
                {
                    throw new Exception(ex.Message);
                }
                return token;
            }

    有了获取令牌的方法还需要有一个请求API的POST帮助方法,如下:(大家可以根据自己的习惯替换,重点是要加入Token)

    private static MemoryCache cache = new MemoryCache(new MemoryCacheOptions());
    
            /// <summary>
            /// HttpClient实现Post请求
            /// </summary>
            public static async Task<T> PostAsync<T>(string url, Dictionary<string, string> dic)
            {
                
                //设置HttpClientHandler的AutomaticDecompression
                var handler = new HttpClientHandler() { AutomaticDecompression = DecompressionMethods.GZip };
                //创建HttpClient(注意传入HttpClientHandler)
                using (var http = new HttpClient(handler))
                {
                    //添加Token
                 var token = await GetToken();
                 http.SetBearerToken(token);
                 //使用FormUrlEncodedContent做HttpContent
                    var content = new FormUrlEncodedContent(dic);
                    //await异步等待回应
                    var response = await http.PostAsync(url, content);
    
                    //确保HTTP成功状态值
                    response.EnsureSuccessStatusCode();
    
                    //await异步读取最后的JSON(注意此时gzip已经被自动解压缩了,因为上面的AutomaticDecompression = DecompressionMethods.GZip)
                    string Result = await response.Content.ReadAsStringAsync();
    
                    var Item = JsonConvert.DeserializeObject<T>(Result);
    
                    return Item;
                }
            }

    有了这些之后我们新建一个登陆控制器 LoginController,新建登陆方法:

            public async Task<JsonResult> UserLogin(string UserName, string UserPwd)
            {
                string url = $"{configuration["ServiceAddress:Service.Account"]}{configuration["MehtodName:Account.MiUser.SSOLogin"]}";
                var dictionary = new Dictionary<string, string>();
                dictionary.Add("UserName", UserName);
                dictionary.Add("Password", MD5Helper.Get_MD5(UserPwd));
                SSOLoginResponse response = null;
                try
                {
                    response = await ApiHelper.PostAsync<SSOLoginResponse>(url, dictionary);
                }
                catch(Exception ex)
                {
                    return Json(ex.Message);
                }
                if(response.Successful)
                {
                    return Json("ok");
                }
                return Json(response.Message);
            }

    然后将三个项目分别发布在IIS中,访问Web登陆页面:

    输入用户密码登陆测试,这里我们会请求MI.Service.Account这个API项目的登陆方法:

     登陆成功即说明通过了验证,下一步将加入Ocelot,结合IdentityServer4实现网关转发请求并验证。

  • 相关阅读:
    spring 任务调度quartz
    java增强型for循环
    DateTimeFormat
    Java的同步和异步
    HTTP Status 400,400 (Bad Request)
    com.mysql.jdbc.exceptions.jdbc4.MySQLDataException: '2.34435678977654336E17' in column '3' is outside valid range for the datatype INTEGER.
    Servlet.service() for servlet [appServlet] in context with path [/item] threw exception [Request processing failed
    mysql调优
    Windows nexus 启动失败
    NFS客户端访问行为相关的几个参数解释
  • 原文地址:https://www.cnblogs.com/weiBlog/p/9822416.html
Copyright © 2011-2022 走看看