9

我将资源所有者流程与 IdentityServer3 一起使用,并使用 javascript 中的用户名和密码将获取令牌请求发送到身份服务器令牌端点,如下所示:

        function getToken() {
        var uid = document.getElementById("username").value;
        var pwd = document.getElementById("password").value;
        var xhr = new XMLHttpRequest();
        xhr.onload = function (e) {
            console.log(xhr.status);
            console.log(xhr.response);
            var response_data = JSON.parse(xhr.response);
            if (xhr.status === 200 && response_data.access_token) {
                getUserInfo(response_data.access_token);
                getValue(response_data.access_token);
            }
        }
        xhr.open("POST", tokenUrl);
        var data = {
            username: uid,
            password: pwd,
            grant_type: "password",
            scope: "openid profile roles",
            client_id: 'client_id'
        };
        var body = "";
        for (var key in data) {
            if (body.length) {
                body += "&";
            }
            body += key + "=";
            body += encodeURIComponent(data[key]);
        }
        xhr.setRequestHeader("Authorization", "Basic " + btoa(client_id + ":" + client_secret));
        xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
        xhr.send(body);
    }

访问令牌从身份服务器返回,用户通过身份验证。然后我使用这个令牌向我的 Web Api 发送请求。

问题是,当我检查是否为用户分配了角色时,我发现声明不存在。

    [Authorize]
    // GET api/values
    public IEnumerable<string> Get()
    {
        var id = RequestContext.Principal as ClaimsPrincipal;
        bool geek = id.HasClaim("role", "Geek");  // false here
        bool asset_mgr = id.HasClaim("role", "asset_manager"); // false here
        return new string[] { "value1", "value2" };
    }

这是客户端在身份服务器中的定义方式。

new Client 
            {
                ClientName = "Client",
                ClientId = "client_id",
                Flow = Flows.ResourceOwner,
                RequireConsent = false,
                AllowRememberConsent = false,

                AllowedScopes = new List<string>
                {
                    "openid",
                    "profile",
                    "roles",
                    "sampleApi"
                },
                AbsoluteRefreshTokenLifetime = 86400,
                SlidingRefreshTokenLifetime = 43200,
                RefreshTokenUsage = TokenUsage.OneTimeOnly,
                RefreshTokenExpiration = TokenExpiration.Sliding,
                ClientSecrets = new List<Secret>
                {
                    new Secret("4C701024-0770-4794-B93D-52B5EB6487A0".Sha256())
                },
            },

这就是定义用户的方式:

new InMemoryUser
            {
                Username = "bob",
                Password = "secret",
                Subject = "1",

                Claims = new[]
                {
                    new Claim(Constants.ClaimTypes.GivenName, "Bob"),
                    new Claim(Constants.ClaimTypes.FamilyName, "Smith"),
                    new Claim(Constants.ClaimTypes.Role, "Geek"),
                    new Claim(Constants.ClaimTypes.Role, "Foo")
                }
            }

在这种情况下,如何向 access_token 添加声明?非常感谢!

4

3 回答 3

17

我自己花了一段时间才弄清楚这一点。@leastprivilege 对杨的回答的评论有线索,这个答案只是在扩展它。
这完全取决于 oAuth 和 OIDC 规范是如何演变的,它不是 IdentityServer 的人工制品(太棒了)。首先,这是关于身份令牌和访问令牌之间差异的相当不错的讨论:https ://github.com/IdentityServer/IdentityServer3/issues/2015 ,值得一读。

使用资源所有者流程,就像您正在做的那样,您将始终获得访问令牌。默认情况下,根据规范,您不应在该令牌中包含声明(请参阅上面的链接了解原因)。但是,在实践中,如果可以的话,那就太好了;它为您在客户端和服务器上节省了额外的精力。

Leastprivilege 指的是您需要创建一个范围,如下所示:

new Scope
{
    Name = "member",
    DisplayName = "member",
    Type = ScopeType.Resource,

    Claims = new List<ScopeClaim>
        {
              new ScopeClaim("role"),
              new ScopeClaim(Constants.ClaimTypes.Name),
              new ScopeClaim(Constants.ClaimTypes.Email)
        },

    IncludeAllClaimsForUser = true
}

然后,当您请求令牌时,您需要请求该范围。即您的行 scope: "openid profile roles",应该更改为scope: "member",(好吧,我说-范围在这里扮演双重角色,据我所知-它们也是一种控制形式,即客户要求某些范围,如果是,则可以被拒绝不允许那些,但那是另一个话题)。

请注意我有一段时间没有注意到的重要行,即Type = ScopeType.Resource(因为访问令牌是关于控制对资源的访问)。这意味着它将适用于访问令牌,并且指定的声明将包含在令牌中(我认为,可能与规范相反,但非常好)。

最后,在我的示例中,我包含了一些具体的声明以及IncludeAllClaimsForUser显然很愚蠢的声明,但只是想向您展示一些选项。

于 2016-02-21T17:06:49.637 回答
2

我发现我可以通过替换 IdentityServerServiceFactory 的默认 IClaimsProvider 来实现这一点。

自定义的 IClaimsProvider 如下:

public class MyClaimsProvider : DefaultClaimsProvider
{
    public MaccapClaimsProvider(IUserService users) : base(users)
    {
    }

    public override Task<IEnumerable<Claim>> GetAccessTokenClaimsAsync(ClaimsPrincipal subject, Client client, IEnumerable<Scope> scopes, ValidatedRequest request)
    {
        var baseclaims = base.GetAccessTokenClaimsAsync(subject, client, scopes, request);

        var claims = new List<Claim>();
        if (subject.Identity.Name == "bob")
        {
            claims.Add(new Claim("role", "super_user"));
            claims.Add(new Claim("role", "asset_manager"));
        }

        claims.AddRange(baseclaims.Result);

        return Task.FromResult(claims.AsEnumerable());
    }

    public override Task<IEnumerable<Claim>> GetIdentityTokenClaimsAsync(ClaimsPrincipal subject, Client client, IEnumerable<Scope> scopes, bool includeAllIdentityClaims, ValidatedRequest request)
    {
        var rst = base.GetIdentityTokenClaimsAsync(subject, client, scopes, includeAllIdentityClaims, request);
        return rst;
    }
}

然后,像这样替换 IClaimsProvider:

// custom claims provider
factory.ClaimsProvider = new Registration<IClaimsProvider>(typeof(MyClaimsProvider));

结果是,当访问令牌的请求被发送到令牌端点时,声明被添加到 access_token。

于 2015-11-27T04:49:03.020 回答
0

我不仅尝试了其他方法,还尝试了所有可能的范围组合等。我在访问令牌中可以读取的只是“范围”、“范围名称”,对于资源流,我没有添加任何声明。

我不得不做这一切

  1. 添加自定义 UserServiceBase 并覆盖 AuthenticateLocalAsync 因为我在那里有用户名/密码,我需要两者都从数据库中获取内容
  2. 在同一个函数中添加我需要的声明(这本身不会向访问令牌添加声明,但是您可以在各种 ClaimsPrincipal 参数中读取它们)
  3. 添加自定义 DefaultClaimsProvider 并覆盖 GetAccessTokenClaimsAsync ,其中 ClaimsPrincipal 主题包含我之前设置的声明,我只是将它们取出并再次放入 ølist of claim 以获得结果。

我猜这最后一步可能会覆盖自定义 UserServiceBase 中的 GetProfileDataAsync 完成,但以上只是工作,所以我不想打扰。

一般问题不在于如何设置声明,而在于您填充它们的位置。你必须在某处覆盖某些东西。

这对我有用,因为我需要来自数据库的数据,其他人应该在其他地方填充声明。但它们不会仅仅因为您很好地设置了 Scopes 和 Claims Identity Server 配置而神奇地出现。

大多数答案都没有说明在哪里正确设置声明值。在您完成的每个特定覆盖中,传递的参数(当它们具有声明时)在函数中附加到身份访问令牌。

只要照顾好它,一切都会好起来的。

于 2016-08-18T14:26:34.670 回答