如何在IdentityServer 3中确定范围并将声明保留给不同的客户端?

时间:2016-09-18 20:23:50

标签: identityserver3 claims

我是IdentityServer3的新手,拥有多个MVC客户端,用户声称可能存在冲突并提供不受欢迎的授权。

以下是两个能够向用户发送电子邮件和通知的客户端的示例。用户可能有权访问这两个应用程序,但应该只能在应用程序A中接收通知。我们如何阻止用户在应用程序B中收到通知?

Application A
Claim Type: ApplicationFunctionality Claim Value:
RecieveNotifications
Claim Type: ApplicationFunctionality Claim Value: RecieveEmails

Application B
Claim Type: ApplicationFunctionality Claim Value: RecieveEmails

一个合理的解决方案是使用IUserService接口在类中实现某些逻辑吗?

在多个客户端上以上述方式使用声明是否正确,我们有时会重复声明跨客户端功能。我想这需要我将声明命名空间(可能使用客户端发送给IdentityServer的请求范围名称),以便区分不同客户端的声明并防止客户端之间的未授权访问。

以下是用户声明类型/声明值的示例:

Name: John Doe
Email: john.doe@acme.com
PreferedLanguages: English,Swedish,Spanish
ApplicationFunctionality: ClientA.RecieveEmails
ApplicationFunctionality: ClientB.RecieveEmails
ApplicationFunctionality: ClientA.RecieveNotifications
ApplicationFunctionality: ClientB.RecieveNotifications
ApplicationFunctionality: ClientA.ViewBackorders
ApplicationFunctionality: ClientA.DeleteBackorder
ApplicationFunctionality: ClientB.SearchProductInformation
CompanyID: 1145
CompanyID: 6785
CompanyName: Acme Inc
ApplicationLicense: ClientA.PayingNormalUser
ApplicationLicense: ClientB.FreeUser

来自公司Acme Inc的用户有几个CompanyID,用于过滤我们从数据层中的webservices \ databases请求的数据。用户可以访问多个应用程序,根据他们在应用程序中购买的许可证,他/她可以具有不同级别的功能。某些功能存在于多个客户端中,但这并不意味着用户在他/她有权访问的所有客户端中被授权使用相同的功能。

我会很感激有关索赔的一些指导方针,或者可能会指出一些关于这个主题的好资源。我已经读过声明主要用于身份相关信息(电子邮件,姓名,部门,喜欢的颜色,鞋子尺寸等),但如果没有角色\权限样式声明应该与声明一起使用,那么应该如何获得有关用户授权的信息在客户端中要做的是持久化以及如何在Web服务/数据库(资源提供者)中过滤数据,以便用户只能看到他/她有权查看的数据?

我首先想到的是id_token和访问令牌可以方便地使用,因为它们是由STS(IdentityServer)发出的,然后保存在cookie中。首先需要STS在Active Directory中执行用户帐户查找,其中包含用户身份相关信息以及自定义数据库中的查找(使用Active Directory用户帐户的用户名),其中包含有关角色\权限和用户声明的信息

如果不使用IdentityServer提供的Cookie持久令牌,我应该如何保留用户的角色/权限和声明?

2 个答案:

答案 0 :(得分:1)

客户端(应用程序)和用户可以拥有自己的一组声明。看起来您希望拥有可以访问不同资源的应用程序。这是您应该使用范围的地方。基本上定义两个范围来定义对该资源的操作(这是一种常见的方式),即一个用于阅读'电子邮件和一个用于撰写'电子邮件(例如Get-Content "P:\PowerShell\jv\servers.txt" | ForEach-Object{ #Write-Host $_ $intNumberofServers++ $strServer =$_ #setting the psobject server value #$strserver $objPrintdata.Server = $strServer $blnHasOldQueues = $false #Setting server value $strComputer = $strServer.server $PingResult = Test-Connection -ComputerName $strServer -Count 1 -Quiet If($PingResult){ #Outputting server status $objPrintdata.ServerStatus = "Workstation is UP" try{ If($strHklm = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('LocalMachine',$strServer )){ #executes when it can open HKLM $objPrintdata.RegistryStatus = "Was able to open the registry" #set the path of the registry $PrinterRegKey = 'SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Print\\Providers\\Client Side Rendering Print Provider' $regPrinterRef = $strHklm.OpenSubKey($PrinterRegKey) #debug #$regPrinterRef } #setting this to output to csv # $resultsarray =@() #$strPrinters = $regPrinterRef.GetSubKeyNames() #check if regprinterref is not null if($regPrinterRef){ #executes when there are keys #region Loop thru all child keys. These contain the calculable UNC paths to 2003 $regPrinterRef.GetSubKeyNames() | ForEach-Object{ #debug # $_ #concatinating to get to the connections key $strPrinterpath =$PrinterRegKey+"\\"+ $_ + "\\Printers\\Connections" #$strPrinterPath if ($strPrinterpath -notlike "*servers*"){ #this value is the sid # $_ will give us the sids. Here I am storing the SIDs into strUserSID to use later on $strUserSID = $_ #debug $strUserSID # The logic below will convert SID to username #pass the SID to the secrity principal SID being struserSID #$objSID = New-Object System.Security.Principal.SecurityIdentifier("$strUserSID") #using a try catch to filter out deleted SIDs, otherwise powershell will throw an error saying it is null Try{ $objSID=get-aduser -filter {sid -eq $strUserSID} if($objSID-eq $null) { #does a test AD lookup to see if the user still exists baseed on their sid. Returns the user object if it exists & null if not $objPrintdata.User_SID_Status ="Invalid SID" $objPrintdata.User = "Invalid SID" $objPrintdata.Does_it_Have_2003_Queues ="Invalid SID" $objPrintdata.UNC_2003_Queues = "Invalid SID" $objPrintdata | Export-Csv P:\powershell\jv\results.csv -NoTypeInformation -Append } else { #executes when the sid is still valid. Displays a subset of the user object properties $strUser= $objSID.SamAccountName $objPrintdata.User_SID_Status ="Valid SID" } } Catch{ #$strUserID = $objSID.Value Write-Host "Lost connection to AD" #exit } #$strUser $regPrinterDetails = $Strhklm.OpenSubKey($strPrinterPath) #debug #$strPrinterPath #$regPrinterDetails $regPrinterDetails.GetSubKeyNames() |ForEach-Object{ #looping through each key at the connections level to search for the 2003 print server names if($_ -like "*sabppprt2*"){ $objPrintdata.Does_it_Have_2003_Queues = "Yes" #this value is the printer if it exists # $_ will give us the printers. Here I am storing the printers into strUserPrinters to user later on $strUserPrinters = $_ #$strUserPrinters $blnHasOldQueues = $true #The code below is to build the printer UNC to make it more legible $intPrinterLength= $strUserPrinters.Length $strPrintServer= $strUserPrinters.Substring(2,10) #Doing the -13 because we have to limit the length of the substring statement to the length minus the starting poistion of the substring $strPrinterShareName =$strUserPrinters.Substring(13,$intPrinterLength-13) $strPrintUNC = "\\"+$strPrintServer+"\"+$strPrinterShareName $objPrintdata.UNC_2003_Queues = $strPrintUNC $objPrintdata.User = $strUser # Write-Host $strServer $blnHasOldQueues $strUser $strPrintUNC $objPrintdata | Export-Csv P:\powershell\jv\results.csv -NoTypeInformation -Append } # Write-Host $strServer $blnHasOldQueues $strUserSID $strUserPrinters } } else { #Write-host "No 2003 Queues Detected" #Write-host "no 2003 Queues detected" $strUserSID,$strUser,$strPrintUNC $objPrintdata.User = $strUser $objPrintdata.Does_it_Have_2003_Queues = "No 2003 Queues Detected for this user or vsabppprt* queue" $objPrintdata.UNC_2003_Queues = "No 2003 Queues Detected for this user or vsabppprt* queue" $objPrintdata | Export-Csv P:\powershell\jv\results.csv -NoTypeInformation -Append } } #endregion } } catch{ Write-Host "Can Not access this machines registry: " $strServer $objPrintdata.RegistryStatus = "Can not open the registry" $objPrintdata.User_SID_Status = "Can not open the registry" $objPrintdata.User = "Can not open the registry" $objPrintdata.UNC_2003_Queues = "Could not open the registry" $objPrintdata.Does_it_Have_2003_Queues = "Could not open the registry" $objPrintdata | Export-Csv P:\powershell\jv\results.csv -NoTypeInformation -Append } } #if ends here else{ #executes when the machine is unpingable Write-Host "This machine is currently not on the network: " $strServer $objPrintdata.ServerStatus = "Workstation is Down" $objPrintdata.RegistryStatus = "Workstation is Down" $objPrintdata.User_SID_Status = "Workstation is Down" $objPrintdata.User = "Workstation is Down" $objPrintdata.UNC_2003_Queues = "Workstation is Down" $objPrintdata.Does_it_Have_2003_Queues = "Workstation is Down" $objPrintdata | Export-Csv P:\powershell\jv\results.csv -NoTypeInformation -Append } #Doesn't the Else need to be here } $intNumberofServers emails.read)随后其他范围可以是emails.writebackorders.read。在这里进行一致的命名是一种很好的做法。

现在好了,因为我们已经定义了这两个范围,你现在可以定义两个客户端,一个只有backorders.delete范围,另一个同时具有读写范围。所有这些意味着一个客户端可以访问比另一个客户端更多的资源。

所有用户身份声明都应该坚持用户自己。特定应用程序/客户端永远不应与用户关联。 emails.readNameEmailApplicationLicense都是对用户有效的声明,因为它们描述了用户本身以及可以对其声明的内容。

对于"复杂"授权您可能需要查看this示例,了解有关如何设置复合安全策略或授权的一些​​想法。

答案 1 :(得分:1)

听起来您希望为同一声明向不同的客户端公开不同的值。这似乎是一件合乎逻辑的事情,特别是如果您正在与不受您控制的客户进行集成,因此无法向他们指示每项索赔中的预期或要求的范围。一个简单的例子可能是"角色"声明 - 您可能希望根据发出请求的应用发送不同的值。如果您要加入其他人的企业,可能还有多个OpenID Connect提供商,那么您不必总是选择范围或声明名称。

我觉得Nat Sakimura在OpenID Connect常见问题解答视频https://www.youtube.com/watch?v=Kb56GzQ2pSk(1分40秒)中对此有所了解,认为实体可能希望向不同的客户端公开不同的身份。

在实施方面,我们添加了一个包含[identityId,clientId,attributeName,attributeValue]的表,以允许我们为不同的客户端存储相同的身份属性。在我们的例子中,这些身份属性成为传出JWT中的声明。由于关于用户的大多数属性是全局的(即不是特定于客户端的),我们将此表中的数据视为对基本集的覆盖,这样可以不必要地为每个客户端复制相同的数据。 iUserService.GetProfileDataAsync()方法可以访问客户端,因此可以根据数据的使用者来定制其响应。