committed by
GitHub
27 changed files with 688 additions and 61 deletions
@ -0,0 +1,56 @@ |
|||||
|
using DotNetCore.CAP.Messages; |
||||
|
using System; |
||||
|
using Volo.Abp; |
||||
|
|
||||
|
namespace LINGYUN.Abp.EventBus.CAP |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// AbpECAPExecutionFailedException
|
||||
|
/// </summary>
|
||||
|
public class AbpCAPExecutionFailedException : AbpException |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// MessageType
|
||||
|
/// </summary>
|
||||
|
public MessageType MessageType { get; set; } |
||||
|
/// <summary>
|
||||
|
/// Message
|
||||
|
/// </summary>
|
||||
|
public Message Origin { get; set; } |
||||
|
/// <summary>
|
||||
|
/// constructor
|
||||
|
/// </summary>
|
||||
|
/// <param name="messageType"></param>
|
||||
|
/// <param name="prigin"></param>
|
||||
|
public AbpCAPExecutionFailedException(MessageType messageType, Message prigin) |
||||
|
{ |
||||
|
MessageType = messageType; |
||||
|
Origin = prigin; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// constructor
|
||||
|
/// </summary>
|
||||
|
/// <param name="messageType"></param>
|
||||
|
/// <param name="prigin"></param>
|
||||
|
/// <param name="message"></param>
|
||||
|
public AbpCAPExecutionFailedException(MessageType messageType, Message prigin, string message) : base(message) |
||||
|
{ |
||||
|
MessageType = messageType; |
||||
|
Origin = prigin; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// constructor
|
||||
|
/// </summary>
|
||||
|
/// <param name="messageType"></param>
|
||||
|
/// <param name="prigin"></param>
|
||||
|
/// <param name="message"></param>
|
||||
|
/// <param name="innerException"></param>
|
||||
|
public AbpCAPExecutionFailedException(MessageType messageType, Message prigin, string message, Exception innerException) : base(message, innerException) |
||||
|
{ |
||||
|
MessageType = messageType; |
||||
|
Origin = prigin; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,93 @@ |
|||||
|
using Hangfire; |
||||
|
using Hangfire.Annotations; |
||||
|
using Hangfire.Dashboard; |
||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using Microsoft.Extensions.Options; |
||||
|
using NUglify.Helpers; |
||||
|
using System.Linq; |
||||
|
using Volo.Abp.Authorization.Permissions; |
||||
|
using Volo.Abp.Threading; |
||||
|
|
||||
|
namespace LINGYUN.Abp.MessageService.Authorization |
||||
|
{ |
||||
|
public class HangfireDashboardAuthorizationFilter : IDashboardAuthorizationFilter |
||||
|
{ |
||||
|
protected string[] AllowGrantPath { get; } |
||||
|
public HangfireDashboardAuthorizationFilter() |
||||
|
{ |
||||
|
AllowGrantPath = new string[] { "/css", "/js", "/fonts", "/stats" }; |
||||
|
} |
||||
|
|
||||
|
public bool Authorize([NotNull] DashboardContext context) |
||||
|
{ |
||||
|
// 本地请求
|
||||
|
if (LocalRequestOnlyAuthorize(context)) |
||||
|
{ |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
// 放行路径
|
||||
|
if (AllowGrantPath.Contains(context.Request.Path)) |
||||
|
{ |
||||
|
return true; |
||||
|
} |
||||
|
var httpContext = context.GetHttpContext(); |
||||
|
|
||||
|
var options = httpContext.RequestServices.GetService<IOptions<HangfireDashboardRouteOptions>>()?.Value; |
||||
|
|
||||
|
if (options != null) |
||||
|
{ |
||||
|
// 请求路径对应的权限检查
|
||||
|
// TODO: 怎么来传递用户身份令牌?
|
||||
|
var permission = options.GetPermission(context.Request.Path); |
||||
|
if (!permission.IsNullOrWhiteSpace()) |
||||
|
{ |
||||
|
var permissionChecker = httpContext.RequestServices.GetRequiredService<IPermissionChecker>(); |
||||
|
return AsyncHelper.RunSync(async () => await permissionChecker.IsGrantedAsync(permission)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
public override int GetHashCode() |
||||
|
{ |
||||
|
// 类型相同就行了
|
||||
|
return GetType().FullName.GetHashCode(); |
||||
|
} |
||||
|
|
||||
|
public override bool Equals(object obj) |
||||
|
{ |
||||
|
if (obj == null) |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
// 类型相同就行了
|
||||
|
if (GetType().Equals(obj.GetType())) |
||||
|
{ |
||||
|
return true; |
||||
|
} |
||||
|
return base.Equals(obj); |
||||
|
} |
||||
|
|
||||
|
protected virtual bool LocalRequestOnlyAuthorize(DashboardContext context) |
||||
|
{ |
||||
|
if (string.IsNullOrEmpty(context.Request.RemoteIpAddress)) |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
if (context.Request.RemoteIpAddress == "127.0.0.1" || context.Request.RemoteIpAddress == "::1") |
||||
|
{ |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
if (context.Request.RemoteIpAddress == context.Request.LocalIpAddress) |
||||
|
{ |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,74 @@ |
|||||
|
using Hangfire.Dashboard; |
||||
|
using JetBrains.Annotations; |
||||
|
using System.Collections.Generic; |
||||
|
using Volo.Abp; |
||||
|
|
||||
|
namespace Hangfire |
||||
|
{ |
||||
|
public static class DashboardOptionsExtensions |
||||
|
{ |
||||
|
public static DashboardOptions AddAuthorization( |
||||
|
[NotNull] this DashboardOptions options, |
||||
|
[NotNull] IDashboardAuthorizationFilter authorizationFilter) |
||||
|
{ |
||||
|
Check.NotNull(options, nameof(options)); |
||||
|
Check.NotNull(authorizationFilter, nameof(authorizationFilter)); |
||||
|
|
||||
|
List<IDashboardAuthorizationFilter> filters = new List<IDashboardAuthorizationFilter>(); |
||||
|
filters.AddRange(options.Authorization); |
||||
|
filters.AddIfNotContains(authorizationFilter); |
||||
|
|
||||
|
options.Authorization = filters; |
||||
|
|
||||
|
return options; |
||||
|
} |
||||
|
|
||||
|
public static DashboardOptions AddAuthorizations( |
||||
|
[NotNull] this DashboardOptions options, |
||||
|
[NotNull] IEnumerable<IDashboardAuthorizationFilter> authorizationFilters) |
||||
|
{ |
||||
|
Check.NotNull(options, nameof(options)); |
||||
|
Check.NotNull(authorizationFilters, nameof(authorizationFilters)); |
||||
|
|
||||
|
List<IDashboardAuthorizationFilter> filters = new List<IDashboardAuthorizationFilter>(); |
||||
|
filters.AddRange(options.Authorization); |
||||
|
filters.AddIfNotContains(authorizationFilters); |
||||
|
|
||||
|
options.Authorization = filters; |
||||
|
|
||||
|
return options; |
||||
|
} |
||||
|
|
||||
|
public static DashboardOptions UseAuthorization( |
||||
|
[NotNull] this DashboardOptions options, |
||||
|
[NotNull] IDashboardAuthorizationFilter authorizationFilter) |
||||
|
{ |
||||
|
Check.NotNull(options, nameof(options)); |
||||
|
Check.NotNull(authorizationFilter, nameof(authorizationFilter)); |
||||
|
|
||||
|
List<IDashboardAuthorizationFilter> filters = new List<IDashboardAuthorizationFilter> |
||||
|
{ |
||||
|
authorizationFilter |
||||
|
}; |
||||
|
|
||||
|
options.Authorization = filters; |
||||
|
|
||||
|
return options; |
||||
|
} |
||||
|
|
||||
|
public static DashboardOptions UseAuthorizations( |
||||
|
[NotNull] this DashboardOptions options, |
||||
|
[NotNull] IEnumerable<IDashboardAuthorizationFilter> authorizationFilters) |
||||
|
{ |
||||
|
Check.NotNull(options, nameof(options)); |
||||
|
Check.NotNull(authorizationFilters, nameof(authorizationFilters)); |
||||
|
|
||||
|
List<IDashboardAuthorizationFilter> filters = new List<IDashboardAuthorizationFilter>(); |
||||
|
filters.AddRange(authorizationFilters); |
||||
|
|
||||
|
options.Authorization = filters; |
||||
|
|
||||
|
return options; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,47 @@ |
|||||
|
using JetBrains.Annotations; |
||||
|
using Microsoft.AspNetCore.Builder; |
||||
|
using System; |
||||
|
using Volo.Abp; |
||||
|
|
||||
|
namespace Hangfire |
||||
|
{ |
||||
|
public static class HangfireApplicationBuilderExtensions |
||||
|
{ |
||||
|
public static IApplicationBuilder UseHangfireJwtToken( |
||||
|
[NotNull] this IApplicationBuilder app) |
||||
|
{ |
||||
|
return app.UseMiddleware<HangfireJwtTokenMiddleware>(); |
||||
|
} |
||||
|
|
||||
|
public static IApplicationBuilder UseHangfireDashboard( |
||||
|
[NotNull] this IApplicationBuilder app, |
||||
|
[CanBeNull] Action<DashboardOptions> setup = null) |
||||
|
{ |
||||
|
Check.NotNull(app, nameof(app)); |
||||
|
return app.UseHangfireDashboard("/hangfire", setup, null); |
||||
|
} |
||||
|
|
||||
|
public static IApplicationBuilder UseHangfireDashboard( |
||||
|
[NotNull] this IApplicationBuilder app, |
||||
|
[CanBeNull] string pathMatch = "/hangfire", |
||||
|
[CanBeNull] Action<DashboardOptions> setup = null) |
||||
|
{ |
||||
|
Check.NotNull(app, nameof(app)); |
||||
|
return app.UseHangfireDashboard(pathMatch, setup, null); |
||||
|
} |
||||
|
|
||||
|
public static IApplicationBuilder UseHangfireDashboard( |
||||
|
[NotNull] this IApplicationBuilder app, |
||||
|
[CanBeNull] string pathMatch = "/hangfire", |
||||
|
[CanBeNull] Action<DashboardOptions> setup = null, |
||||
|
[CanBeNull] JobStorage storage = null) |
||||
|
{ |
||||
|
Check.NotNull(app, nameof(app)); |
||||
|
|
||||
|
var options = new DashboardOptions(); |
||||
|
setup?.Invoke(options); |
||||
|
|
||||
|
return app.UseHangfireDashboard(pathMatch: pathMatch, options: options, storage: storage); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,55 @@ |
|||||
|
using LINGYUN.Abp.MessageService.Permissions; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
|
||||
|
namespace Hangfire |
||||
|
{ |
||||
|
public class HangfireDashboardRouteOptions |
||||
|
{ |
||||
|
public IList<string> AllowFrameOrigins { get; } |
||||
|
public IDictionary<string, string> RoutePermissions { get; } |
||||
|
public HangfireDashboardRouteOptions() |
||||
|
{ |
||||
|
AllowFrameOrigins = new List<string>(); |
||||
|
RoutePermissions = new Dictionary<string, string>(); |
||||
|
InitDefaultRoutes(); |
||||
|
} |
||||
|
|
||||
|
public void WithOrigins(params string[] origins) |
||||
|
{ |
||||
|
AllowFrameOrigins.AddIfNotContains(origins); |
||||
|
} |
||||
|
|
||||
|
public void WithPermission(string route, string permission) |
||||
|
{ |
||||
|
RoutePermissions.Add(route, permission); |
||||
|
} |
||||
|
|
||||
|
public string GetPermission(string route) |
||||
|
{ |
||||
|
var permission = RoutePermissions |
||||
|
.Where(x => x.Key.StartsWith(route)) |
||||
|
.Select(x => x.Value) |
||||
|
.FirstOrDefault(); |
||||
|
|
||||
|
return permission; |
||||
|
} |
||||
|
|
||||
|
private void InitDefaultRoutes() |
||||
|
{ |
||||
|
WithPermission("/hangfire", AbpMessageServicePermissions.Hangfire.Default); |
||||
|
WithPermission("/stats", AbpMessageServicePermissions.Hangfire.Default); |
||||
|
WithPermission("/servers", AbpMessageServicePermissions.Hangfire.Default); |
||||
|
WithPermission("/retries", AbpMessageServicePermissions.Hangfire.Default); |
||||
|
WithPermission("/recurring", AbpMessageServicePermissions.Hangfire.Default); |
||||
|
WithPermission("/jobs/enqueued", AbpMessageServicePermissions.Hangfire.ManageQueue); |
||||
|
WithPermission("/jobs/processing", AbpMessageServicePermissions.Hangfire.ManageQueue); |
||||
|
WithPermission("/jobs/scheduled", AbpMessageServicePermissions.Hangfire.ManageQueue); |
||||
|
WithPermission("/jobs/failed", AbpMessageServicePermissions.Hangfire.ManageQueue); |
||||
|
WithPermission("/jobs/deleted", AbpMessageServicePermissions.Hangfire.ManageQueue); |
||||
|
WithPermission("/jobs/awaiting", AbpMessageServicePermissions.Hangfire.ManageQueue); |
||||
|
WithPermission("/jobs/actions", AbpMessageServicePermissions.Hangfire.ManageQueue); |
||||
|
WithPermission("/jobs/details", AbpMessageServicePermissions.Hangfire.ManageQueue); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,31 @@ |
|||||
|
using Microsoft.AspNetCore.Http; |
||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using Microsoft.Extensions.Options; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
|
||||
|
namespace Hangfire |
||||
|
{ |
||||
|
public class HangfireJwtTokenMiddleware : IMiddleware, ITransientDependency |
||||
|
{ |
||||
|
public async Task InvokeAsync(HttpContext context, RequestDelegate next) |
||||
|
{ |
||||
|
// 通过 iframe 加载页面的话,需要手动传递 access_token 到参数列表
|
||||
|
if (context.Request.Path.StartsWithSegments("/hangfire") && context.User.Identity?.IsAuthenticated != true) |
||||
|
{ |
||||
|
if (context.Request.Query.TryGetValue("access_token", out var accessTokens)) |
||||
|
{ |
||||
|
context.Request.Headers.Add("Authorization", accessTokens); |
||||
|
} |
||||
|
var options = context.RequestServices.GetService<IOptions<HangfireDashboardRouteOptions>>()?.Value; |
||||
|
if (options != null && options.AllowFrameOrigins.Count > 0) |
||||
|
{ |
||||
|
// 跨域 iframe
|
||||
|
context.Response.Headers.TryAdd("X-Frame-Options", $"\"ALLOW-FROM {options.AllowFrameOrigins.JoinAsString(",")}\""); |
||||
|
} |
||||
|
} |
||||
|
await next(context); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,22 @@ |
|||||
|
using LINGYUN.Abp.MessageService.Localization; |
||||
|
using Volo.Abp.Authorization.Permissions; |
||||
|
using Volo.Abp.Localization; |
||||
|
|
||||
|
namespace LINGYUN.Abp.MessageService.Permissions |
||||
|
{ |
||||
|
public class AbpMessageServicePermissionDefinitionProvider : PermissionDefinitionProvider |
||||
|
{ |
||||
|
public override void Define(IPermissionDefinitionContext context) |
||||
|
{ |
||||
|
var group = context.GetGroup(MessageServicePermissions.GroupName); |
||||
|
|
||||
|
var hangfirePermission = group.AddPermission(AbpMessageServicePermissions.Hangfire.Default, L("Permission:Hangfire")); |
||||
|
hangfirePermission.AddChild(AbpMessageServicePermissions.Hangfire.ManageQueue, L("Permission:ManageQueue")); |
||||
|
} |
||||
|
|
||||
|
private static LocalizableString L(string name) |
||||
|
{ |
||||
|
return LocalizableString.Create<MessageServiceResource>(name); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,12 @@ |
|||||
|
namespace LINGYUN.Abp.MessageService.Permissions |
||||
|
{ |
||||
|
public class AbpMessageServicePermissions |
||||
|
{ |
||||
|
public class Hangfire |
||||
|
{ |
||||
|
public const string Default = MessageServicePermissions.GroupName + ".Hangfire"; |
||||
|
|
||||
|
public const string ManageQueue = Default + ".ManageQueue"; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,49 @@ |
|||||
|
<template> |
||||
|
<div class="dashboard-container"> |
||||
|
<iframe |
||||
|
id="iframe-hangifre" |
||||
|
ref="hangfireIframe" |
||||
|
:src="hangfireUrl" |
||||
|
scrolling="auto" |
||||
|
frameborder="0" |
||||
|
class="iframe-hangifre" |
||||
|
/> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script lang="ts"> |
||||
|
import { Component, Prop, Vue } from 'vue-property-decorator' |
||||
|
|
||||
|
@Component({ |
||||
|
name: 'HangfireDashboard' |
||||
|
}) |
||||
|
export default class extends Vue { |
||||
|
@Prop({ default: '' }) |
||||
|
private hangfireUrl!: string |
||||
|
|
||||
|
get showIframe() { |
||||
|
if (this.hangfireUrl) { |
||||
|
return true |
||||
|
} |
||||
|
return false |
||||
|
} |
||||
|
|
||||
|
mounted() { |
||||
|
const hangfireFrame = this.$refs.hangfireIframe as any |
||||
|
if (hangfireFrame) { |
||||
|
const deviceWidth = document.documentElement.clientWidth |
||||
|
const deviceHeight = document.documentElement.clientHeight |
||||
|
hangfireFrame.style.width = deviceWidth + 'px' |
||||
|
hangfireFrame.style.height = deviceHeight + 'px' |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss" scoped> |
||||
|
.iframe-hangifre { |
||||
|
width: 100%; |
||||
|
height: 100%; |
||||
|
overflow: hidden; |
||||
|
} |
||||
|
</style> |
||||
Loading…
Reference in new issue