在开始这个问题之前,我应该指出我对 ASP.NET 和 C# 的了解几乎为零。
我正在尝试将 CKFinder v3 的 ASP.NET 版本集成到用不同语言构建的网站中,到目前为止一切进展顺利;我已经按照我想要的方式设置了所有内容,并且当我授予对 CKF 的不受限制的访问权限时,它正在工作,但我现在陷入了尝试通过仅验证网站的某些成员来使用它来限制对它的访问的地步。 CKFinder 出现在我网站上的所有页面只能由某些特定成员访问,但我需要额外的安全级别,例如,如果有人找出我的“ckfinder.html”文件的直接路径。
在 CKFinder 的 ASP 版本中,我只是在检查会员权限的函数中添加了这一行,其中
isEditor
是一个布尔值,其值是根据我的数据库中的信息为每个会员分配的:
session("accessckf")=isEditor
然后编辑CKFinder的“config.asp”文件中的
CheckAuthentication()
函数读取:
function CheckAuthentication()
CheckAuthentication=session("accessckf")
end function
阅读此“Howto”,身份验证在 v3 中似乎更加复杂,但是,经过大量的试验和错误以及 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 中编辑什么才能让它使用此自定义文件来检查用户是否经过身份验证?
首先,您需要在 ASP 的会话和 CKFinder 的 .Net 身份验证器之间建立一个连接器。下面是一个将 ASP Session 内容序列化为 JSON 的示例。
将
connector.asp
放入公共可访问的位置。例如http://myaspwebsite.com/connector.asp
。
连接器.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 会话变量 isMember
和 connector.asp
对用户进行身份验证,并最终声明角色 editor
、 member
或无角色。
我假设您已在
editor
中配置了角色 member
和 web.config
。
然后将
Shaggy.cs
放入/ckfinder/App_Code
。如果 App_Code
目录不存在,则创建。此文件夹中的 .Net 文件将即时编译。
有关更多信息,请查看 ASP.NET Web 项目中的共享代码文件夹
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 如何处理经典 ASP 成员资格管理。
您是否使用 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));
}
所有这些都来自我之前提供的文档链接。