在CKFinder v3中验证网站成员作为用户

时间:2016-05-19 09:52:45

标签: c# asp.net asp-classic ckfinder

在开始这个问题之前,我应该指出我对ASP.NET& C#几乎没有。

我正在尝试将ASP.NET版本的CKFinder v3集成到一个用不同语言构建的网站中,到目前为止一切顺利;我有所有设置,因为我想要它,并且当我授予对CKF的无限制访问权限时它正在工作但是我现在停留在尝试通过仅验证我网站的某些成员来使用它来限制对它的访问。 CKFinder在我的网站上出现的所有页面只能由那些特定成员访问,但是如果有人知道我的“ckfinder.html”文件的直接路径,我需要额外的安全级别。

在CKFinder的ASP版本中,我只是在检查我的成员权限的函数中添加了这一行,其中isEditor是一个布尔值,其值是根据我的数据库中的信息为每个成员分配的:

session("accessckf")=isEditor

然后在CKFinder的“config.asp”文件中编辑CheckAuthentication()函数,以读取:

function CheckAuthentication()
    CheckAuthentication=session("accessckf")
end function

通过this "Howto"阅读,身份验证在v3中似乎很多更复杂,但在a lot of trial and errorsome help from Lesiman之后,我创建了这个C#文件,位于我的CKF目录中:

<%@page codepage="65001" debug="true" language="c#" lcid="6153"%>
<%@import namespace="CKSource.CKFinder.Connector.Core"%>
<%@import namespace="CKSource.CKFinder.Connector.Core.Authentication"%>
<%@import namespace="CKSource.CKFinder.Connector.Core.Builders"%>
<%@import namespace="CKSource.CKFinder.Connector.Host.Owin"%>
<%@import namespace="Owin"%>
<%@import namespace="System.Data.Odbc"%>
<%@import namespace="System.Threading"%>
<%@import namespace="System.Threading.Tasks"%>
<script runat="server">
    public void Configuration(IAppBuilder appBuilder){
        var connectorBuilder=ConfigureConnector();
        var connector=connectorBuilder.Build(new OwinConnectorFactory());
        appBuilder.Map("/path/to/connector",builder=>builder.UseConnector(connector));
    }
    public ConnectorBuilder ConfigureConnector(){
        var connectorBuilder=new ConnectorBuilder();
        connectorBuilder.SetAuthenticator(new MyAuthenticator());
        return connectorBuilder;
    }
    public class MyAuthenticator:IAuthenticator{
        public Task<IUser> AuthenticateAsync(ICommandRequest commandRequest,CancellationToken cancellationToken){
            var domain=HttpContext.Current.Request.Url.Host;
            var cookie=HttpContext.Current.Request.Cookies[urlDomain];
            var password="";
            var username="";
            var user=new User(false,null);
            if (cookie!=null){
                if (cookie["username"]!=null)
                    username=cookie["username"];
                if (cookie["password"]!=null)
                    password=cookie["password"];
                if(username!=""&&password!=""){
                    var connection=new OdbcConnection("database=[database];driver=MySQL;pwd=[pwd];server=[server];uid=[uid];");
                    connection.Open();
                    OdbcDataReader records=new OdbcCommand("SELECT ISEDITOR FROM MEMBERS WHERE USERNAME='"+username+"' AND PASSWORD='"+password+"'",connection).ExecuteReader();
                    if(records.HasRows){
                        records.Read();
                        bool isEditor=records.GetString(0)=="1";
                        var roles="member";
                        if(isEditor)
                            roles="editor,member";
                            user=new User(isEditor,roles.Split(','));
                        }
                        records.Close();
                        connection.Close();
                }
            }
            return Task.FromResult((IUser)user);
        }
    }
</script>

加载该页面不会产生任何错误(这并不一定意味着它正在尝试从public class内部向屏幕写入任何内容,因为某些原因无效)所以现在我处于舞台以某种方式检查该文件进行身份验证。

最初,我曾尝试从我的函数中通过XMLHttp加载它来检查网站的会员权限,但是,正如我怀疑和Lesmian确认的那样,这是行不通的。经过更多的试验和错误,我添加了代码来检查网站成员对C#文件的权限,这导致我到了现在的位置:卡住了!

我需要在CKFinder中编辑什么才能让它使用此自定义文件来检查用户是否经过身份验证?

2 个答案:

答案 0 :(得分:3)

首先,您需要ASP会话和CKFinder的.Net身份验证器之间的连接器。这是一个将ASP Session内容序列化为JSON的示例。

connector.asp放入可公开访问的位置。例如http://myaspwebsite.com/connector.asp

<强> connector.asp

<%@Language=VBScript CodePage=65001%>
<% Option Explicit %>
<!--#include file="JSON.asp"-->
<%
' obtain JSON.asp from https://github.com/tugrul/aspjson/archive/master.zip

' just for testing, must be removed in the production environment
Session("isEditor") = True
Session("isMember") = True

' only local requests allowed
' instead of local and remote ip comparison, a secret key can be used
If Request.ServerVariables("LOCAL_ADDR") <> Request.ServerVariables("REMOTE_ADDR") Then
    Response.Status = "403 Forbidden"
    Response.End
End If

Response.ContentType = "application/json"
Response.Charset = "utf-8"

Dim JSONObject, Key
Set JSONObject = jsObject()

For Each Key In Session.Contents
    If Not IsObject(Session.Contents(Key)) Then 'skip the objects cannot be serialized
        JSONObject(Key) = Session.Contents(Key)
    End If
Next

JSONObject.Flush
%>

CKFinder 3.3.0附带一个默认连接器,可在/ckfinder/bin/CKSource.CKFinder.Connector.WebApp.dll中找到,将其删除

检查以下程序,并记住将builder.Map("/connector", SetupConnector);new Uri("http://myaspwebsite.com/connector.asp");替换为您自己的值。

它只是通过isEditor检查ASP会话变量isMemberconnector.asp来验证用户,最后声明角色editormember或者没有。< / p>

我假设您已在editor中配置了角色memberweb.config

然后将Shaggy.cs放入/ckfinder/App_Code。如果不存在,请创建App_Code目录。此文件夹中的.Net文件将即时编译。

有关详细信息,请查看Shared Code Folders in ASP.NET Web Projects

Shaggy.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Cookies;
using Newtonsoft.Json.Linq;
using Owin;

[assembly: Microsoft.Owin.OwinStartup(typeof(CKSource.CKFinder.Connector.Shaggy.Startup))]
namespace CKSource.CKFinder.Connector.Shaggy
{
    using FileSystem.Local;
    using FileSystem.Dropbox;
    using Core;
    using Core.Authentication;
    using Config;
    using Core.Builders;
    using Core.Logs;
    using Host.Owin;
    using Logs.NLog;
    using KeyValue.EntityFramework;

    public class Startup
    {
        public void Configuration(IAppBuilder builder)
        {
            LoggerManager.LoggerAdapterFactory = new NLogLoggerAdapterFactory();

            RegisterFileSystems();

            builder.UseCookieAuthentication(new CookieAuthenticationOptions
            {
                AuthenticationType = "ApplicationCookie",
                AuthenticationMode = AuthenticationMode.Active
            });

            //replace connector path with yours
            builder.Map("/connector", SetupConnector);
        }

        private static void RegisterFileSystems()
        {
            FileSystemFactory.RegisterFileSystem<LocalStorage>();
            FileSystemFactory.RegisterFileSystem<DropboxStorage>();
        }

        private static void SetupConnector(IAppBuilder builder)
        {
            var keyValueStoreProvider = new EntityFrameworkKeyValueStoreProvider("CacheConnectionString");
            var authenticator = new ShaggysAuthenticator();

            var connectorFactory = new OwinConnectorFactory();
            var connectorBuilder = new ConnectorBuilder();
            var connector = connectorBuilder
                .LoadConfig()
                .SetAuthenticator(authenticator)
                .SetRequestConfiguration(
                    (request, config) =>
                    {
                        config.LoadConfig();
                        config.SetKeyValueStoreProvider(keyValueStoreProvider);
                    })
                .Build(connectorFactory);

            builder.UseConnector(connector);
        }
    }

    public class ShaggysAuthenticator : IAuthenticator
    {
        // this method makes an http request on the background to gather ASP's all session contents and returns a JSON object
        // if the request contains ASP's session cookie(s)
        private static JObject GetAspSessionState(ICommandRequest requestContext)
        {
            // building Cookie header with ASP's session cookies
            var aspSessionCookies = string.Join(";",
                requestContext.Cookies.Where(cookie => cookie.Key.StartsWith("ASPSESSIONID"))
                    .Select(cookie => string.Join("=", cookie.Key, cookie.Value)));

            if (aspSessionCookies.Length == 0)
            {
                // logs can be found in /ckfinder/App_Data/logs
                LoggerManager.GetLoggerForCurrentClass().Info("No ASP session cookie found");
                // don't make an extra request to the connector.asp, there's no session initiated
                return new JObject();
            }

            //replace this URL with your connector.asp's
            var publicAspSessionConnectorUrl = new Uri("http://myaspwebsite.com/connector.asp");
            var localSafeAspSessionConnectorUrl = new UriBuilder(publicAspSessionConnectorUrl) { Host = requestContext.LocalIpAddress };

            using (var wCli = new WebClient())
                try
                {
                    wCli.Headers.Add(HttpRequestHeader.Cookie, aspSessionCookies);
                    wCli.Headers.Add(HttpRequestHeader.Host, publicAspSessionConnectorUrl.Host);
                    return JObject.Parse(wCli.DownloadString(localSafeAspSessionConnectorUrl.Uri));
                }
                catch (Exception ex) // returning an empty JObject object in any fault
                {
                    // logs can be found in /ckfinder/App_Data/logs
                    LoggerManager.GetLoggerForCurrentClass().Error(ex);
                    return new JObject();
                }
        }

        public Task<IUser> AuthenticateAsync(ICommandRequest commandRequest, CancellationToken cancellationToken)
        {
            var aspSessionState = GetAspSessionState(commandRequest);

            var roles = new List<string>();
            var isEditor = aspSessionState.GetNullSafeValue("isEditor", false);
            var isMember = aspSessionState.GetNullSafeValue("isMember", false);

            if (isEditor) roles.Add("editor");
            if (isMember) roles.Add("member");

            var isAuthenticated = isEditor || isMember;
            var user = new User(isAuthenticated, roles);
            return Task.FromResult((IUser)user);
        }
    }

    public static class JObjectExtensions
    {
        // an extension method to help case insensitive lookups with a default value to get avoid NullReferenceException
        public static T GetNullSafeValue<T>(this JObject jobj, string key, T defaultValue = default(T))
        {
            dynamic val = jobj.GetValue(key, StringComparison.OrdinalIgnoreCase);
            if (val == null) return defaultValue;
            return (T)val;
        }
    }
}

现在你应该有一个正常工作的CKFinder连接器。如果需要,可以更改方法AuthenticateAsync中的逻辑,并查看CKFinder如何处理您的Classic ASP成员资格管理。

答案 1 :(得分:2)

您是否使用ConnectorBuilder设置了自定义身份验证提供程序?

public ConnectorBuilder ConfigureConnector()
{
   var connectorBuilder = new ConnectorBuilder();
   connectorBuilder.SetAuthenticator(new MyAuthenticator());

   return connectorBuilder;
}

您可以在此处找到完整示例:http://docs.cksource.com/ckfinder3-net/configuration_by_code.html

<强>更新

此外,您应该在Startup类中注册ConnectorBuilder以将其添加到请求管道:

public void Configuration(IAppBuilder appBuilder)
{
   var connectorBuilder = ConfigureConnector();
   var connector = connectorBuilder.Build(new OwinConnectorFactory());
   appBuilder.Map("/CKFinder/connector", builder => builder.UseConnector(connector));
}

所有这些都来自我之前提供的文档链接。