gpt4 book ai didi

phoenix-framework - Phoenix 框架是否支持 Windows 身份验证?

转载 作者:行者123 更新时间:2023-12-01 03:38:11 38 4
gpt4 key购买 nike

我们的 Web 应用程序目前在 C# 中运行在 Windows 和 IIS 上。我们严重依赖此环境中包含的 Windows 身份验证方案。启用 Windows 身份验证后,我们可以检测连接用户的身份,并对他们能够使用的屏幕和操作进行授权。

如果我设置了一个 Phoenix Web 应用程序,是否可以根据当前的 Windows 登录来检测已连接用户的身份?如果没有,是否有易于使用的 Windows 身份验证替代品?

最佳答案

我刚刚在周末做了这个。对的,这是可能的。您必须使用 HttpPlatformHandler IIS 的附加组件以使其工作。 HttpPlatformHandler 具有 forwardWindowsAuthToken配置设置,可用于将经过身份验证的用户的 Windows 用户 token 从 IIS 转发到作为子进程运行的 Phoenix 应用程序。您必须使用 NIF 来处理 token 并获取 Windows 用户名或 SID。正如您将在文档中注意到的,您需要调用 CloseHandle 来为每个请求释放 Windows 用户 token 。

(如果下面的代码不符合最佳实践,我提前道歉。我是 Elixir 的新手,正在积极尝试学习如何编写更好的代码。这也是本周末试图找出解决方案的黑客 session ,所以它也不一定是抛光的。)

为此,我将所有内容打包到一个可以放入管道的自定义插件中(我删除了 Logger 语句以压缩示例的大小):

defmodule MyApp.WindowsAuthentication do
import Plug.Conn

require Logger

@on_load :load_nifs

def load_nifs do
if match? {:win32, _}, :os.type do
:erlang.load_nif("./priv/windows_authentication", 0)
else
:ok
end
end

def init(options), do: options

def call(conn, _options) do
if match? {:win32, _}, :os.type do
case get_req_header(conn, "x-iis-windowsauthtoken") do
[token_handle_string] ->
# token_handle_string is a hex string
token_handle = String.to_integer(token_handle_string, 16)
case do_get_windows_username(token_handle) do
{:ok, {domain_name, username}} ->
conn = assign(conn, :windows_user, {domain_name, username})
error ->
Logger.error IO.inspect(error)
end

do_close_handle(token_handle)
[] ->
Logger.debug "X-IIS-WindowsAuthToken was not present"
end
end

conn
end

def do_get_windows_username(_token_handle) do
raise "do_get_windows_username/1 is only available on Windows"
end

def do_close_handle(_handle) do
raise "do_close_handle/1 is only available on Windows"
end
end

NIF 的 C 源代码如下:
#include <Windows.h>
#include <erl_nif.h>

static const char* error_atom = "error";
static const char* invalid_token_handle_atom = "invalid_token_handle";
static const char* ok_atom = "ok";
static const char* win32_error_atom = "win32_error";

#define MAX_NAME 256

static HANDLE get_user_token(ErlNifEnv *env, ERL_NIF_TERM token) {
HANDLE token_handle;

if (!enif_get_ulong(env, token, (unsigned long *)&token_handle)) {
return NULL;
}

return token_handle;
}

static ERL_NIF_TERM make_win32_error_tuple(ErlNifEnv* env, DWORD error_code) {
return enif_make_tuple2(
env,
enif_make_atom(env, error_atom),
enif_make_ulong(env, error_code)
);
}

static ERL_NIF_TERM make_invalid_token_handle_error(ErlNifEnv* env) {
return enif_make_tuple2(
env,
enif_make_atom(env, error_atom),
enif_make_atom(env, invalid_token_handle_atom)
);
}

static ERL_NIF_TERM do_get_windows_username(ErlNifEnv* env, int argc, ERL_NIF_TERM argv[]) {
HANDLE token_handle;
DWORD token_user_length;
PTOKEN_USER token_user;
DWORD last_error;
WCHAR username[MAX_NAME];
DWORD username_length = MAX_NAME;
WCHAR domain_name[MAX_NAME];
DWORD domain_name_length = MAX_NAME;
size_t converted_chars;
char converted_username[MAX_NAME * 2];
char converted_domain_name[MAX_NAME * 2];
errno_t err;
BOOL succeeded;
SID_NAME_USE sid_name_use;

token_handle = get_user_token(env, argv[0]);
if (!token_handle) {
return make_invalid_token_handle_error(env);
}

if (!GetTokenInformation(token_handle, TokenUser, NULL, 0, &token_user_length)) {
last_error = GetLastError();
if (ERROR_INSUFFICIENT_BUFFER != last_error) {
return make_win32_error_tuple(env, last_error);
}
}

token_user = (PTOKEN_USER)malloc(token_user_length);
if (!GetTokenInformation(token_handle, TokenUser, token_user, token_user_length, &token_user_length)) {
free(token_user);
return make_win32_error_tuple(env, GetLastError());
}

succeeded = LookupAccountSidW(
NULL,
token_user->User.Sid,
username,
&username_length,
domain_name,
&domain_name_length,
&sid_name_use);
if (!succeeded) {
free(token_user);
return make_win32_error_tuple(env, GetLastError());
}

err = wcstombs_s(&converted_chars, converted_username, 512, username, username_length);
err = wcstombs_s(&converted_chars, converted_domain_name, 512, domain_name, domain_name_length);

free(token_user);
return enif_make_tuple2(
env,
enif_make_atom(env, ok_atom),
enif_make_tuple2(
env,
enif_make_string(env, converted_domain_name, ERL_NIF_LATIN1),
enif_make_string(env, converted_username, ERL_NIF_LATIN1)
)
);
}

static ERL_NIF_TERM do_close_handle(ErlNifEnv* env, int argc, ERL_NIF_TERM argv[]) {
HANDLE token_handle;

token_handle = get_user_token(env, argv[0]);
if (!token_handle) {
return make_invalid_token_handle_error(env);
}

if (!CloseHandle(token_handle)) {
return make_win32_error_tuple(env, GetLastError());
}

return enif_make_atom(env, ok_atom);
}

static ErlNifFunc nif_functions[] = {
{ "do_close_handle", 1, do_close_handle },
{ "do_get_windows_username", 1, do_get_windows_username }
};

ERL_NIF_INIT(
Elixir.MyApp.WindowsAuthentication,
nif_functions,
NULL,
NULL,
NULL,
NULL
)

您可以使用 64 位 Visual Studio C++ 工具编译 C 代码(打开 x64 VS 命令提示符)。我用新的 VS2017 工具对此进行了尝试。将 DLL 放入 priv您的应用程序目录。
cl /LD /I "C:\Program Files\erl-8.2\erts-8.2\include" /DDEBUG windows_authentication.c advapi32.lib

要运行插件,请将其添加到 web/router.ex 中的管道中:
pipeline :browser do
plug :accepts, ["html"]
plug MyApp.WindowsAuthentication
plug :fetch_session
plug :fetch_flash
plug :protect_from_forgery
plug :put_secure_browser_headers
end

这样做的最终结果是 conn.assigns.windows_user将包含形式为 {domain_name, username} 的元组具有经过身份验证的用户的 Windows 域用户名。

注意:当我尝试这个时,我发现作为 IIS 的子进程运行时 erl.exe 存在 CPU 和内存泄漏问题。我仍在努力弄清楚,以防你看到它。我发布了一个关于它的问题 here .

当我清理它并修复内存/CPU 问题时,我可能会将它作为一个库发布到 hex.pm 上,但现在,这里的代码可以让您使用 Phoenix 的 Windows 身份验证。

关于phoenix-framework - Phoenix 框架是否支持 Windows 身份验证?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32973404/

38 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com