public async Task <ApiResultUrl> Logout() { var userId = HttpContext.User.GetUserId(); if (userId >= 0) { await _tokenService.RevokeRefreshTokenAsync(userId); await _userService.SignOutAsync(userId); await _userActionLogService.SaveAsync(new UserActionLogInput { UserId = userId, ActionTypeId = 2, ClientTypeId = null, // TODO: (alby)ClientTypeInput ClientAgent = null, Remark = "后台注销" }, ModelState); } var result = new ApiResultUrl { Code = 200, Message = "注销成功", Url = _frontendSettings.CoreEnvironment.IsDevelopment ? _frontendSettings.CoreEnvironment.DevelopmentHost + "/modules/login.html" : Url.Action("Login", "View"), }; return(result); }
public async Task <ApiResultUrl> ChangeUserLogo([FromForm] UserImageInput userImageInput) { var result = new ApiResultUrl(); var url = await _userService.ChangeLogoAsync(userImageInput, ModelState); if (!ModelState.IsValid) { result.Code = 400; result.Message = $"修改 Logo 失败:{ModelState.FirstErrorMessage()}"; return(result); } result.Url = url; result.Code = 200; result.Message = "修改 Logo 成功"; return(result); }
public async Task <ApiResultUrl> ChangeAvatar(IFormFile file) { var result = new ApiResultUrl(); var url = await _userService.ChangeAvatarAsync(HttpContext.User.GetUserId(), file, ModelState); if (!ModelState.IsValid) { result.Code = 400; result.Message = $"修改头像失败:{ModelState.FirstErrorMessage()}"; return(result); } result.Url = url; result.Code = 200; result.Message = "修改头像成功"; return(result); }
public async Task <ApiResultUrl> Logout() { var userId = HttpContext.User.GetUserId(); if (userId >= 0) { await _tokenService.RevokeRefreshTokenAsync(userId); await _userService.SignOutAsync(userId); } var result = new ApiResultUrl { Code = 200, Message = "注销成功", Url = _frontendSettings.CoreEnvironment.IsDevelopment ? _frontendSettings.CoreEnvironment.DevelopmentHost + "/modules/login.html" : Url.Action("Login", "View"), }; return(result); }
/// <summary> /// ConfigureServices /// </summary> /// <param name="services"></param> public override void ConfigureServices(IServiceCollection services) { // Background Service services.AddSingleton <IBackgroundTask, IdleBackgroundTask>(); // Cache services.AddDistributedRedisCache(options => { options.Configuration = "localhost"; options.InstanceName = _environment.ApplicationName + ":"; }); services.AddMemoryCache(); // Cors services.AddCors(options => options.AddPolicy("DefaultPolicy", builder => builder.WithOrigins("http://localhost:9090", "http://localhost:8080").AllowAnyMethod().AllowAnyHeader().AllowCredentials()) // builder => builder.AllowAnyOrigin.AllowAnyMethod().AllowAnyHeader().AllowCredentials()) ); // Cookie services.Configure <CookiePolicyOptions>(options => { options.CheckConsentNeeded = context => false; // 需保持为 false, 否则 Web API 不会 Set-Cookie 。 options.MinimumSameSitePolicy = SameSiteMode.None; }); // Session services.AddSession(options => { options.IdleTimeout = TimeSpan.FromMinutes(10); options.Cookie.Name = ".Tubumu.Session"; options.Cookie.HttpOnly = true; }); // HTTP Client services.AddHttpClient(); // ApiBehaviorOptions services.Configure <ApiBehaviorOptions>(options => { options.InvalidModelStateResponseFactory = context => new OkObjectResult(new ApiResult { Code = 400, Message = context.ModelState.FirstErrorMessage() }); }); // Authentication var registeredServiceDescriptor = services.FirstOrDefault(s => s.Lifetime == ServiceLifetime.Transient && s.ServiceType == typeof(IApplicationModelProvider) && s.ImplementationType == typeof(AuthorizationApplicationModelProvider)); if (registeredServiceDescriptor != null) { services.Remove(registeredServiceDescriptor); } services.AddTransient <IApplicationModelProvider, PermissionAuthorizationApplicationModelProvider>(); services.AddSingleton <ITokenService, TokenService>(); var tokenValidationSettings = _configuration.GetSection("TokenValidationSettings").Get <TokenValidationSettings>(); services.AddSingleton(tokenValidationSettings); services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { ValidIssuer = tokenValidationSettings.ValidIssuer, ValidateIssuer = true, ValidAudience = tokenValidationSettings.ValidAudience, ValidateAudience = true, IssuerSigningKey = SignatureHelper.GenerateSigningKey(tokenValidationSettings.IssuerSigningKey), ValidateIssuerSigningKey = tokenValidationSettings.ValidateLifetime, ValidateLifetime = true, ClockSkew = TimeSpan.FromSeconds(tokenValidationSettings.ClockSkewSeconds), }; // We have to hook the OnMessageReceived event in order to // allow the JWT authentication handler to read the access // token from the query string when a WebSocket or // Server-Sent Events request comes in. options.Events = new JwtBearerEvents { OnMessageReceived = context => { var accessToken = context.Request.Query["access_token"]; // If the request is for our hub... var path = context.HttpContext.Request.Path; if (!string.IsNullOrEmpty(accessToken) && path.StartsWithSegments("/hubs")) { // Read the token out of the query string context.Token = accessToken; } return(Task.CompletedTask); }, OnAuthenticationFailed = context => { _logger.LogError($"Authentication Failed(OnAuthenticationFailed): {context.Request.Path} Error: {context.Exception}"); if (context.Exception.GetType() == typeof(SecurityTokenExpiredException)) { context.Response.Headers.Add("Token-Expired", "true"); } return(Task.CompletedTask); }, OnChallenge = context => { _logger.LogError($"Authentication Challenge(OnChallenge): {context.Request.Path}"); // TODO: (alby)为不同客户端返回不同的内容 var result = new ApiResultUrl() { Code = 400, Message = "Authentication Challenge", Url = _environment.IsProduction() ? tokenValidationSettings.LoginUrl : null, }; var body = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(result)); context.Response.StatusCode = StatusCodes.Status401Unauthorized; context.Response.ContentType = "application/json"; context.Response.Body.Write(body, 0, body.Length); context.HandleResponse(); return(Task.CompletedTask); } }; }); // JSON Date format void JsonSetup(MvcJsonOptions options) => options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss"; services.Configure((Action <MvcJsonOptions>)JsonSetup); // SignalR services.AddSignalR(); services.Replace(ServiceDescriptor.Singleton(typeof(IUserIdProvider), typeof(NameUserIdProvider))); // AutoMapper services.AddAutoMapper(); Initalizer.Initialize(); // Swagger services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new Info { Title = _environment.ApplicationName + " API", Version = "v1" }); c.AddSecurityDefinition("Bearer", new ApiKeyScheme { Description = "权限认证(数据将在请求头中进行传输) 参数结构: \"Authorization: Bearer {token}\"", Name = "Authorization", In = "header", Type = "apiKey" }); var security = new Dictionary <string, IEnumerable <string> > { { "Bearer", new string[] { } }, }; c.AddSecurityRequirement(security); c.DescribeAllEnumsAsStrings(); c.DocumentFilter <HiddenApiDocumentFilter>(); IncludeXmlCommentsForModules(c); c.OrderActionsBy(m => m.ActionDescriptor.DisplayName); }); }
/// <summary> /// ConfigureServices /// </summary> /// <param name="services"></param> public override void ConfigureServices(IServiceCollection services) { // Background Service services.AddSingleton <IBackgroundTask, IdleBackgroundTask>(); services.AddSingleton <IBackgroundTask, DailyBackgroundTask>(); // StackExchange.Redis.Extensions // https://github.com/imperugo/StackExchange.Redis.Extensions var redisConfiguration = _configuration.GetSection("RedisSettings").Get <RedisConfiguration>(); services.AddSingleton(redisConfiguration); services.AddSingleton <IRedisCacheClient, RedisCacheClient>(); services.AddSingleton <IRedisCacheConnectionPoolManager, RedisCacheConnectionPoolManager>(); services.AddSingleton <IRedisDefaultCacheClient, RedisDefaultCacheClient>(); services.AddSingleton <ISerializer, NewtonsoftSerializer>(); var redisKeyPrefix = !redisConfiguration.KeyPrefix.IsNullOrWhiteSpace() ? redisConfiguration.KeyPrefix : _environment.ApplicationName; // Cache services.AddDistributedRedisCache(options => { options.Configuration = "localhost"; options.InstanceName = redisKeyPrefix + ":"; }); services.AddMemoryCache(); // Cors services.AddCors(options => options.AddPolicy("DefaultPolicy", builder => builder.WithOrigins("http://localhost:9090", "http://localhost:8080").AllowAnyMethod().AllowAnyHeader().AllowCredentials()) // builder => builder.AllowAnyOrigin.AllowAnyMethod().AllowAnyHeader().AllowCredentials()) ); // Cookie services.Configure <CookiePolicyOptions>(options => { options.CheckConsentNeeded = context => false; // 需保持为 false, 否则 Web API 不会 Set-Cookie 。 options.MinimumSameSitePolicy = SameSiteMode.None; }); // Session services.AddSession(options => { options.IdleTimeout = TimeSpan.FromMinutes(10); options.Cookie.Name = ".Tubumu.Session"; options.Cookie.HttpOnly = true; }); // HTTP Client services.AddHttpClient(); // ApiBehaviorOptions services.Configure <ApiBehaviorOptions>(options => { options.InvalidModelStateResponseFactory = context => new OkObjectResult(new ApiResult { Code = 400, Message = context.ModelState.FirstErrorMessage() }); }); // Authentication services.AddSingleton <IAuthorizationPolicyProvider, TubumuAuthorizationPolicyProvider>(); services.AddSingleton <ITokenService, TokenService>(); var tokenValidationSettings = _configuration.GetSection("TokenValidationSettings").Get <TokenValidationSettings>(); services.AddSingleton(tokenValidationSettings); services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { ValidIssuer = tokenValidationSettings.ValidIssuer, ValidateIssuer = true, ValidAudience = tokenValidationSettings.ValidAudience, ValidateAudience = true, IssuerSigningKey = SignatureHelper.GenerateSigningKey(tokenValidationSettings.IssuerSigningKey), ValidateIssuerSigningKey = true, ValidateLifetime = tokenValidationSettings.ValidateLifetime, ClockSkew = TimeSpan.FromSeconds(tokenValidationSettings.ClockSkewSeconds), }; // We have to hook the OnMessageReceived event in order to // allow the JWT authentication handler to read the access // token from the query string when a WebSocket or // Server-Sent Events request comes in. options.Events = new JwtBearerEvents { OnMessageReceived = context => { var accessToken = context.Request.Query["access_token"]; // If the request is for our hub... var path = context.HttpContext.Request.Path; if (!string.IsNullOrEmpty(accessToken) && path.StartsWithSegments("/hubs")) { // Read the token out of the query string context.Token = accessToken; } return(Task.CompletedTask); }, OnAuthenticationFailed = context => { _logger.LogError($"Authentication Failed(OnAuthenticationFailed): {context.Request.Path} Error: {context.Exception}"); if (context.Exception.GetType() == typeof(SecurityTokenExpiredException)) { context.Response.Headers.Add("Token-Expired", "true"); } return(Task.CompletedTask); }, OnChallenge = context => { _logger.LogError($"Authentication Challenge(OnChallenge): {context.Request.Path}"); // TODO: (alby)为不同客户端返回不同的内容 var result = new ApiResultUrl() { Code = 400, Message = "Authentication Challenge", // TODO: (alby)前端 IsDevelopment 为 true 时不返回 Url Url = _environment.IsProduction() ? tokenValidationSettings.LoginUrl : null, }; var body = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(result)); context.Response.StatusCode = StatusCodes.Status401Unauthorized; context.Response.ContentType = "application/json"; context.Response.Body.Write(body, 0, body.Length); context.HandleResponse(); return(Task.CompletedTask); } }; }); // JSON Date format void JsonSetup(MvcJsonOptions options) => options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss"; services.Configure((Action <MvcJsonOptions>)JsonSetup); // SignalR services.AddSignalR(); services.Replace(ServiceDescriptor.Singleton(typeof(IUserIdProvider), typeof(NameUserIdProvider))); // AutoMapper services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies()); AutoMapperInitalizer.Initialize(); // RabbitMQ services.AddSingleton <IConnectionPool, ConnectionPool>(); services.AddSingleton <IChannelPool, ChannelPool>(); // Swagger services.AddSwaggerGen(c => { c.SwaggerDoc("v1.0", new Info { Title = _environment.ApplicationName + " API", Version = "v1.0" }); c.AddSecurityDefinition("Bearer", new ApiKeyScheme { Description = "权限认证(数据将在请求头中进行传输) 参数结构: \"Authorization: Bearer {token}\"", Name = "Authorization", In = "header", Type = "apiKey" }); c.AddSecurityRequirement(new Dictionary <string, IEnumerable <string> > { { "Bearer", new string[] { } }, }); c.DescribeAllEnumsAsStrings(); c.DocumentFilter <HiddenApiDocumentFilter>(); IncludeXmlCommentsForModules(c); c.OrderActionsBy(m => m.ActionDescriptor.DisplayName); c.ApplyGrouping(); }); // Add Hangfire services. //services.AddHangfire(configuration => configuration // .SetDataCompatibilityLevel(CompatibilityLevel.Version_170) // .UseSimpleAssemblyNameTypeSerializer() // .UseRecommendedSerializerSettings() // .UseSqlServerStorage(_configuration.GetConnectionString("Tubumu"), new SqlServerStorageOptions // { // CommandBatchMaxTimeout = TimeSpan.FromMinutes(5), // SlidingInvisibilityTimeout = TimeSpan.FromMinutes(5), // QueuePollInterval = TimeSpan.FromSeconds(15), // 作业队列轮询间隔。默认值为15秒。 // JobExpirationCheckInterval = TimeSpan.FromHours(1), // 作业到期检查间隔(管理过期记录)。默认值为1小时。 // UseRecommendedIsolationLevel = true, // UsePageLocksOnDequeue = true, // DisableGlobalLocks = true // })); services.AddHangfire(configuration => { // 推荐使用 ConnectionMultiplexer,见:https://github.com/marcoCasamento/Hangfire.Redis.StackExchange 。 // 但是存在 StackExchange.Redis.StrongName 和 StackExchange.Redis 冲突问题。 configuration.UseRedisStorage("localhost", new RedisStorageOptions { Prefix = $"{redisKeyPrefix}:hangfire:", Db = 9, }); }); // Add the processing server as IHostedService services.AddHangfireServer(); // Data version services.AddSingleton <IDataVersionService, DataVersionService>(); }