11.Feign组件介入Nacos微服务
创建三个项目Summerboot.Feign,Summerboot.Client(http://192.168.10.13:5197),NacosService(http://192.168.10.13:5199)
注意Summerboot.Client依赖Summerboot.Feign。
安装包:SummerBoot
Summerboot.Client客户端是用来调用Feign组件
先添加一个JWT配置类
/// <summary> /// JWT配置类 /// </summary> public class JwtTokenOption { /// <summary> /// Token 过期时间,默认为60分钟 /// </summary> public int TokenExpireTime { get; set; } = 60; /// <summary> /// 接收人 /// </summary> public string Audience { get; set; } /// <summary> /// 秘钥 /// </summary> public string SecurityKey { get; set; } /// <summary> /// 签发人 /// </summary> public string Issuer { get; set; } }
NacosService项目
UserController控制器
[ApiController] [Route("[controller]/[action]")] public class UserController:ControllerBase { [HttpGet] public List<UserInfo> GetUsers() { List<UserInfo> result = new() { new(){Id = 1,NickName = "张三"}, new(){Id = 2,NickName = "李四"} }; return result; } [HttpPost] public UserInfo PostUser([FromBody]UserInfo user) { return user; } /// <summary> /// 测试Feign调用受保护的资源 /// </summary> /// <param name="user"></param> /// <returns></returns> [HttpPost] [Authorize] public UserInfo TestAuthentication([FromBody] UserInfo user) { return user; } }
appsettings.json
{ "Logging": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning" } }, "AllowedHosts": "*", "nacos": { "ServerAddresses": [ "http://127.0.0.1:8848" ], "DefaultTimeOut": 15000, //请求超时时间 "Namespace": "6c0da0b7-eb66-4c15-b73e-15b983d07548", //命名空间id "ListenInterval": 1000, //监听间隔时间 "ServiceName": "NacosService", //服务名称 "GroupName": "DEFAULT_GROUP", // 默认分组名称 "ClusterName": "DEFAULT", // 如果去掉这个配置项,系统会自动获取服务IP(建议去掉这个配置) // "Ip": "localhost",//注册中心,服务调用的IP地址 // "PreferredNetworks": "localhost", //首选网络 // // 如果去掉这个配置项,系统会自动获取服务运行的端口号(建议去掉这个配置) // "Port": 5199, // // 写0 表示80端口() "Weight": 100, "RegisterEnabled": true, "InstanceEnabled": true, "Ephemeral": true, "Secure": false, //表示当前服务是否时安全实例,用于标识访问的时候是否要启用 https "AccessKey": "", "SecretKey": "", "UserName": "", "Password": "", "ConfigUseRpc": false, "NamingUseRpc": false, "NamingLoadCacheAtStart": "", "LBStrategy": "WeightRandom", // 负载均衡策略:WeightRandom(随机), WeightRoundRobin(轮询) "Metadata": { "aa": "bb", "cc": "dd" } } }
Summerboot.Client项目
appsettings.Development.json
{ "Logging": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning" } }, "nacos": { //--------使用nacos则serviceAddress和namespaceId必填------ //nacos服务地址,如http://172.16.189.242:8848 "serviceAddress": "http://127.0.0.1:8848/", //命名空间id,如832e754e-e845-47db-8acc-46ae3819b638或者public "namespaceId": "6c0da0b7-eb66-4c15-b73e-15b983d07548", //--------如果只是访问nacos中的微服务,则仅配置lbStrategy即可,defaultNacosGroupName和defaultNacosNamespaceId选填------ //客户端负载均衡算法,一个服务下有多个实例,lbStrategy用来挑选服务下的实例,默认为Random(随机),也可以选择WeightRandom(根据服务权重加权后再随机) "lbStrategy": "Random", //defaultNacosGroupName,选填,为FeignClient注解中NacosGroupName的默认值,为空则默认为DEFAULT_GROUP "defaultNacosGroupName": "", //defaultNacosNamespaceId,选填,为FeignClient注解中NacosNamespaceId的默认值,为空则默认为public "defaultNacosNamespaceId": "", //--------如果需要使用nacos配置中心,则ConfigurationOption必填,允许监听多个配置------ "configurationOption": [ { "namespaceId": "6c0da0b7-eb66-4c15-b73e-15b983d07548", //配置的分组 "groupName": "DEFAULT_GROUP", //配置的dataId, "dataId": "app1-dev.json" } ], //-------如果是要将本应用注册为服务实例,则全部参数均需配置-------------- //是否要把应用注册为服务实例 "registerInstance": true, //要注册的服务名 "serviceName": "Summerboot.Client", //服务的分组名 "groupName": "DEFAULT_GROUP", //权重,一个服务下有多个实例,权重越高,访问到该实例的概率越大,比如有些实例所在的服务器配置高,那么权重就可以大一些,多引流到该实例,与上面的参数lbStrategy设置为WeightRandom搭配使用 "weight": 1, //本应用对外的网络协议,http或https "protocol": "http" //本应用对外的端口号,比如5000 // ,"port": 5000 } }
创建MicroFeignClientController控制器
[ApiController] [Route("[controller]/[action]")] public class MicroFeignClientController { private readonly INacosMicroService _nacosMicroService; private readonly IHttpContextAccessor _httpContextAccessor; public MicroFeignClientController( INacosMicroService nacosMicroService , IHttpContextAccessor httpContextAccessor) { _nacosMicroService = nacosMicroService; _httpContextAccessor = httpContextAccessor; } [HttpGet] public async Task<List<UserInfo>> GetUsers() { return await _nacosMicroService.GetUsers(); } /// <summary> /// 测试Feign调用受保护的资源 /// </summary> /// <param name="user"></param> /// <returns></returns> [Authorize] [HttpPost] public async Task<UserInfo> TestAuthentication([FromBody]UserInfo user) { // var token = await _httpContextAccessor.HttpContext.GetTokenAsync("access_token"); // return await _nacosMicroService.TestAuthentication(user,token); return await _nacosMicroService.TestAuthentication(user); } }
Program.cs配置
var builder = WebApplication.CreateBuilder(args); builder.Services.AddControllers(); builder.Services.AddSwaggerGen(p => { p.SwaggerDoc("v1", new OpenApiInfo() { Contact = new() { Email = "1310734881@qq.com", Name = "Feign组件学习", Url = new Uri("http://baidu.com") }, Description = "Feign_Description", Title = "Feign_Title" }); //Bearer 的scheme定义 var securityScheme = new OpenApiSecurityScheme() { Description = "JWT Authorization 头使用Bearer 体系方案. 例: \"Authorization: Bearer 你的token\"", Name = "Authorization", //参数添加在头部 In = ParameterLocation.Header, //使用Authorize头部 Type = SecuritySchemeType.Http, //内容为以 bearer开头 Scheme = "Bearer", BearerFormat = "JWT" }; //把所有方法配置为增加bearer头部信息 var securityRequirement = new OpenApiSecurityRequirement { { new OpenApiSecurityScheme { Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "JWT认证" } }, new string[] {} } }; //注册到swagger中 p.AddSecurityDefinition("JWT认证", securityScheme); p.AddSecurityRequirement(securityRequirement); // 加载xml文档注释 p.IncludeXmlComments(AppContext.BaseDirectory + Assembly.GetExecutingAssembly().GetName().Name + ".xml", true); }); builder.Host.UseNacosConfiguration(); builder.Services.AddSummerBoot();
//启用配置中心 builder.Services.AddSummerBootFeign(p => { p.AddNacos(builder.Configuration,true); }); builder.Services.AddHttpContextAccessor(); #region JWT认证 var jwtOption = builder.Configuration.GetSection("JwtTokenOption"); builder.Services.Configure<JwtTokenOption>(jwtOption); JwtTokenOption jwtTokenOption = jwtOption.Get<JwtTokenOption>()!; // 添加认证服务 builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(p => { var rsa = RSA.Create(); rsa.ImportRSAPrivateKey(Convert.FromBase64String(jwtTokenOption.SecurityKey), out _); SecurityKey securityKey = new RsaSecurityKey(rsa); // 校验JWT是否合法 p.TokenValidationParameters = new TokenValidationParameters() { ValidAlgorithms = new string[] { "RS256" }, ValidateIssuer = true, //是否验证Issuer ValidateAudience = true, //是否验证Audience ValidateLifetime = true, //是否验证失效时间 ClockSkew = TimeSpan.Zero, //时钟脉冲相位差 ValidateIssuerSigningKey = true, //是否验证SecurityKey ValidAudience = jwtTokenOption.Audience, //Audience ValidIssuer = jwtTokenOption.Issuer, //Issuer,这两项和前面签发jwt的设置一致 IssuerSigningKey = securityKey, //拿到SecurityKey }; }); builder.Services.AddAuthorization(); #endregion var app = builder.Build(); if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); } app.UseHttpsRedirection(); app.UseAuthentication(); app.UseAuthorization(); app.MapControllers(); app.Run();
Summerboot.Feign项目
自定义拦截器对接口下的所有方法均生效,拦截器的应用场景主要是在请求前做一些操作,比如请求第三方业务接口前,需要先登录第三方系统,那么就可以在拦截器里先请求第三方登录接口,获取到凭证以后,放到header里,拦截器需要实现IRequestInterceptor接口
/// <summary> /// 用于传递Token的拦截器 /// </summary> public class TokenInterceptor:IRequestInterceptor { private readonly IHttpContextAccessor _httpContextAccessor; public TokenInterceptor(IHttpContextAccessor httpContextAccessor) { _httpContextAccessor = httpContextAccessor; } public async Task ApplyAsync(RequestTemplate requestTemplate) { if (_httpContextAccessor.HttpContext != null) { var token = await _httpContextAccessor.HttpContext.GetTokenAsync("access_token"); if (!string.IsNullOrWhiteSpace(token)) { requestTemplate.Headers.Add("Authorization",new List<string>{$"Bearer {token}"}); } } } }
添加一个INacosMicroService接口
[FeignClient(ServiceName = "NacosService" ,MicroServiceMode = true , NacosNamespaceId = "${nacos:namespaceId}" ,NacosGroupName = "${nacos:groupName}" ,InterceptorType = typeof(TokenInterceptor) )] public interface INacosMicroService { [GetMapping("/user/GetUsers")] Task<List<UserInfo>> GetUsers(); [PostMapping("/user/PostUser")] Task<UserInfo> PostUser([Body] UserInfo user); // /// <summary> // /// 测试Feign调用受保护的资源 // /// </summary> // /// <param name="user"></param> // /// <returns></returns> // [PostMapping("/user/TestAuthentication")] // [Headers("Authorization:Bearer {{token}}")] // Task<UserInfo> TestAuthentication([Body] UserInfo user,string token); /// <summary> /// 测试Feign调用受保护的资源 /// </summary> /// <param name="user"></param> /// <returns></returns> [PostMapping("/user/TestAuthentication")] Task<UserInfo> TestAuthentication([Body] UserInfo user); }