你可以将Thread.CurrentPrincipal与F#异步工作流一起使用吗?

时间:2014-03-26 13:33:13

标签: .net asynchronous f# authorization

是否有人在F#异步代码中使用设置Thread.CurrentPrincipal进行身份验证?

是否有任何陷阱需要注意,或者只是按预期工作?

2 个答案:

答案 0 :(得分:4)

这听起来不是一个好主意,因为Thread.CurrentPrincipal依赖于与给定线程关联的IPrincipal,但在异步工作流内,您可能无法在同一个线程上运行所有时间。

考虑这个例子:

open System.Threading

let collectThreadInformation jobId =
    async {
        let tId1 = Thread.CurrentThread.ManagedThreadId
        do! Async.Sleep 1000
        let tId2 = Thread.CurrentThread.ManagedThreadId
        return jobId, tId1, tId2 }

这是一个简单的异步工作流,可在睡眠前后报告线程ID一秒钟。以下是并行运行其中10个的看法:

> [1 .. 10]
  |> List.map collectThreadInformation
  |> Async.Parallel
  |> Async.RunSynchronously;;
val it : (int * int * int) [] =
  [|(1, 15, 15); (2, 14, 15); (3, 15, 15); (4, 15, 15); (5, 15, 15);
    (6, 15, 15); (7, 15, 15); (8, 14, 15); (9, 15, 15); (10, 15, 15)|]

正如您所看到的,某些线程ID在Sleep之前和之后发生了变化,例如第二个元组:(2, 14, 15)。在Sleep运行主题14 之前,但在睡眠之后,它在主题15 上重新启动。

我认为将IPrincipal实例作为函数参数传递会更安全。

答案 1 :(得分:1)

我在不同的地方对此有各种相互矛盾的回应,所以我实际上构建了一个测试程序。运行下面的代码表明,当async工作流在ThreadPool上运行时,它们确实保留了线程上下文。

module TestCurrentPrinciple

open System
open System.Security.Principal
open System.Threading

let rand = Random()

let spawnWorkflow userName = 
    async { 
        let identity = GenericIdentity(userName)
        let principle = GenericPrincipal(identity, [||])
        Thread.CurrentPrincipal <- principle
        for i in 1..10000 do
            let principleName = Thread.CurrentPrincipal.Identity.Name
            if principleName <> userName then 
                failwithf "Mismatch! Principle name %s does not match username %s (Iteration %d)"
                    principleName userName i
            do! Async.Sleep(rand.Next(10))
    }

let names = 
    [ "mavnn"; "ploeh"; "dsyme"; "biboudis"; "MattDrivenDev"; "fssnip"; "marprz_93"; "skillsmatter"; "thinkb4coding" ]

printfn "Starting"
names
|> List.map spawnWorkflow
|> Async.Parallel
|> Async.RunSynchronously
|> ignore
printfn "Done"
Console.ReadLine() |> ignore

这对我目前的项目可能很有意思。