带有 OWIN 的 webapi 中的复杂 JSON Web 令牌数组

Posted

技术标签:

【中文标题】带有 OWIN 的 webapi 中的复杂 JSON Web 令牌数组【英文标题】:Complex JSON Web Token Array in webapi with OWIN 【发布时间】:2015-06-25 05:56:56 【问题描述】:

我正在尝试学习 JWT 和 ouath。我遇到了 JWT 的形式,它可以帮助我开发我的授权服务器。

我遇到的格式如下:


  iat: 1416929061, 
  jti: "802057ff9b5b4eb7fbb8856b6eb2cc5b",
  scopes: 
    users: 
      actions: ['read', 'create']
    ,
    users_app_metadata: 
      actions: ['read', 'create']
    
  

但是,既然在添加声明时我们只能添加简单的字符串,那么如何实现这样的事情呢?

到目前为止我看到的唯一方法是使用 JSON.serialization - 来自https://***.com/a/27279400/2476347

new Claim(someClass,JsonConvert.SerializeObject(result)

任何指导方针将不胜感激!谢谢!

用于测试的代码

我想在 JWT 中使用的类

public class MyTes

    public string       area  get; set; 
    public List<string> areapermissions  get; set; 

然后我使用以下代码生成令牌

        var identity = new ClaimsIdentity("JWT");

        var cos = new List<string>();

        cos.Add("aaa");
        cos.Add("bbb");

        MyTes vario = new MyTes()
        
            area = "someregion",
            areapermissions = cos
        ;




        identity.AddClaim(new Claim(ClaimTypes.Name, context.UserName));
        identity.AddClaim(new Claim("sub", context.UserName));
        identity.AddClaim(new Claim(ClaimTypes.Role, "Manager"));
        identity.AddClaim(new Claim(ClaimTypes.Role, "Supervisor"));
        identity.AddClaim(new Claim("scope", "xyz1"));
        identity.AddClaim(new Claim("scope", "xyz2"));
        identity.AddClaim(new Claim("scope", "xyz3"));
        identity.AddClaim(new Claim("APIs", JsonConvert.SerializeObject(cos)));
        identity.AddClaim(new Claim("APIs2", JsonConvert.SerializeObject(vario)));

这没有错误,当我解码我现在得到的票时:


  "unique_name": "Rafski",
  "sub": "Rafski",
  "role": [
    "Manager",
    "Supervisor"
  ],
  "scope": [
    "xyz1",
    "xyz2",
    "xyz3"
  ],
  "APIs": "[\"aaa\",\"bbb\"]",
  "APIs2": "\"area\":\"someregion\",\"areapermissions\":[\"aaa\",\"bbb\"]",
  "iss": "http://kurwa.mac",
  "aud": "7aaa70ed8f0b4807a01596e2abfbd44d",
  "exp": 1429351056,
  "nbf": 1429349256

【问题讨论】:

如果您可以提供一些示例代码来展示您尝试过的内容,这将为回答这个问题提供一个很好的起点。 我已经添加了我正在尝试的代码。 你不应该接受@stephane 的回答,因为它似乎完全按照你的要求做。至少它对我有用.. 【参考方案1】:

以下是使用 .Net 创建具有复杂 JSON 声明的 JWT 令牌的方法。

使用 Nuget 获取库:System.IdentityModel.Tokens.Jwt

然后使用以下代码创建 JWT 令牌。

var keybytes = Convert.FromBase64String(YOUR_CLIENT_SECRET);
        var signingCredentials = new SigningCredentials(
                    new InMemorySymmetricSecurityKey(keybytes),
                    SecurityAlgorithms.HmacSha256Signature,
                    SecurityAlgorithms.Sha256Digest);

        var nbf = DateTime.UtcNow.AddSeconds(-1);
        var exp = DateTime.UtcNow.AddSeconds(120);
        var payload = new JwtPayload(null, "", new List<Claim>(), nbf, exp);

        var users = new Dictionary<string, object>();
        users.Add("actions", new List<string>()  "read", "create" );
        var scopes = new Dictionary<string, object>();
        scopes.Add("users", users);
        payload.Add("scopes", scopes);

        var jwtToken = new JwtSecurityToken(new JwtHeader(signingCredentials), payload);
        var jwtTokenHandler = new JwtSecurityTokenHandler();
        return jwtTokenHandler.WriteToken(jwtToken);

这会产生一个令牌,例如


  "typ": "JWT",
  "alg": "HS256"
    

      "exp": 1433254394,
      "nbf": 1433254273,
      "scopes": 
        "users": 
          "actions": [
            "read", "create"
          ]
        
      
    

【讨论】:

【参考方案2】:

这些天来,这从来都不是问题。可以使用以下方法解决 令牌的有效负载部分。

**using System.IdentityModel.Tokens.Jwt;** //Vesrion 5.5.0

示例代码

        public static string Generate()
    

        IList<User> users = new List<User> 
            new User  Id = 1, Name = "User One" ,
            new User  Id = 2, Name = "User Two" ,
            new User  Id = 3, Name = "User Three" 
        ;

        IList<Company> companies = new List<Company>
        
            new Company Id = 1, Code = "C01", Name = "Company I", Users = users ,
            new Company Id = 2, Code = "C03", Name = "Company II", Users = null ,
            new Company Id = 3, Code = "C03", Name = "Company III", Users = users 
        ;

        IList<Branch> branches = new List<Branch>
        
            new BranchId = 1, CompanyId = 1, Code="B01", Name = "Branch 1.1",
            new BranchId = 2, CompanyId = 1, Code="B02", Name = "Branch 1.2",
            new BranchId = 3, CompanyId = 1, Code="B03", Name = "Branch 1.3",
            new BranchId = 4, CompanyId = 2, Code="B04", Name = "Branch 2.1",
            new BranchId = 5, CompanyId = 2, Code="B05", Name = "Branch 2.2",
        ;

        var payload = new JwtPayload       
             "companies", companies ,
             "branches", branches 
        ;

        string key = "eyJjb21wYW5pZXMiOlt7IklkIjoxLCJDb2RlIjoiQzAxIiwiTmFtZSI6IkNvbXBhbnkgSSIsIkJyYW5jaGVzIjpudWxsLCJVc2VycyI6W3siSWQiOjEsIk5hbWUiOiJV";
        var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key));
        var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256Signature);
        var header = new JwtHeader(credentials);

        var secToken = new JwtSecurityToken(header, payload);

        var handler = new JwtSecurityTokenHandler();
        var tokenString = handler.WriteToken(secToken);

        Console.WriteLine(secToken);
        Console.WriteLine(tokenString);

        return tokenString;
    

输出


  "companies": [
    
      "Id": 1,
      "Code": "C01",
      "Name": "Company I",
      "Branches": null,
      "Users": [
        
          "Id": 1,
          "Name": "User One"
        ,
        
          "Id": 2,
          "Name": "User Two"
        ,
        
          "Id": 3,
          "Name": "User Three"
        
      ]
    ,
    
      "Id": 2,
      "Code": "C03",
      "Name": "Company II",
      "Branches": null,
      "Users": null
    ,
    
      "Id": 3,
      "Code": "C03",
      "Name": "Company III",
      "Branches": null,
      "Users": [
        
          "Id": 1,
          "Name": "User One"
        ,
        
          "Id": 2,
          "Name": "User Two"
        ,
        
          "Id": 3,
          "Name": "User Three"
        
      ]
    
  ],
  "branches": [
    
      "Id": 1,
      "CompanyId": 1,
      "Code": "B01",
      "Name": "Branch 1.1"
    ,
    
      "Id": 2,
      "CompanyId": 1,
      "Code": "B02",
      "Name": "Branch 1.2"
    ,
    
      "Id": 3,
      "CompanyId": 1,
      "Code": "B03",
      "Name": "Branch 1.3"
    ,
    
      "Id": 4,
      "CompanyId": 2,
      "Code": "B04",
      "Name": "Branch 2.1"
    ,
    
      "Id": 5,
      "CompanyId": 2,
      "Code": "B05",
      "Name": "Branch 2.2"
    
  ]

令牌

eyJhbGciOiJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGRzaWctbW9yZSNobWFjLXNoYTI1NiIsInR5cCI6IkpXVCJ9.eyJjb21wYW5pZXMiOlt7IklkIjoxLCJDb2RlIjoiQzAxIiwiTmFtZSI6IkNvbXBhbnkgSSIsIkJyYW5jaGVzIjpudWxsLCJVc2VycyI6W3siSWQiOjEsIk5hbWUiOiJVc2VyIE9uZSJ9LHsiSWQiOjIsIk5hbWUiOiJVc2VyIFR3byJ9LHsiSWQiOjMsIk5hbWUiOiJVc2VyIFRocmVlIn1dfSx7IklkIjoyLCJDb2RlIjoiQzAzIiwiTmFtZSI6IkNvbXBhbnkgSUkiLCJCcmFuY2hlcyI6bnVsbCwiVXNlcnMiOm51bGx9LHsiSWQiOjMsIkNvZGUiOiJDMDMiLCJOYW1lIjoiQ29tcGFueSBJSUkiLCJCcmFuY2hlcyI6bnVsbCwiVXNlcnMiOlt7IklkIjoxLCJOYW1lIjoiVXNlciBPbmUifSx7IklkIjoyLCJOYW1lIjoiVXNlciBUd28ifSx7IklkIjozLCJOYW1lIjoiVXNlciBUaHJlZSJ9XX1dLCJicmFuY2hlcyI6W3siSWQiOjEsIkNvbXBhbnlJZCI6MSwiQ29kZSI6IkIwMSIsIk5hbWUiOiJCcmFuY2ggMS4xIn0seyJJZCI6MiwiQ29tcGFueUlkIjoxLCJDb2RlIjoiQjAyIiwiTmFtZSI6IkJyYW5jaCAxLjIifSx7IklkIjozLCJDb21wYW55SWQiOjEsIkNvZGUiOiJCMDMiLCJOYW1lIjoiQnJhbmNoIDEuMyJ9LHsiSWQiOjQsIkNvbXBhbnlJZCI6MiwiQ29kZSI6IkIwNCIsIk5hbWUiOiJCcmFuY2ggMi4xIn0seyJJZCI6NSwiQ29tcGFueUlkIjoyLCJDb2RlIjoiQjA1IiwiTmFtZSI6IkJyYW5jaCAyLjIifV19.ysjwBa3YeYNmVB0fVEh95wL0zt8Krb-T4VRpWKWIfbU

【讨论】:

【参考方案3】:

所以这个问题的关键是理解:) 首先应该注意的是下面的escape characters。

这基本上让我明白,我所需要的只是自定义数组对象的正确序列化/反序列化。

所以我创建了以下内容作为每个范围的基础

                Dictionary<string, List<string>> xx3 = new Dictionary<string, List<string>>()
                
                    
                        "s3",new List<string>()
                        
                            "access1" , "access2"
                        
                    
                ;

然后简单地序列化这个对象并添加为声明

            var cos3 = JsonConvert.SerializeObject(xx3, Formatting.Indented);

            identity.AddClaim(new Claim("scopes", cos1));

现在剩下的挑战是对我的资源进行适当的授权。所以我添加了自定义 AuthorizationFilterAttribute

在其中,我使用来自here 的代码对声明进行了反序列化

对于那些想要寻找更多代码的人,这是我的自定义过滤器中的 sn-p:

    // Check if we have scopes 
    var AllScopes = principal.Claims.Where(p => p.Type == "scopes");

    // Check if we have desired scope 
    foreach (var singlescope in AllScopes)
    
        Dictionary<string, List<string>> userscopes = JsonConvert.DeserializeObject<Dictionary<string, List<string>>>(singlescope.Value);

        if (userscopes.Single(kvp => kvp.Key == ScopeName).Value.Contains(ScopeAccess))
        
            //User is Authorized, complete execution
            return Task.FromResult<object>(null);
        
    

我希望这会对某人有所帮助!

【讨论】:

【参考方案4】:

这是我创建复杂令牌的方式:

var securityKey = new InMemorySymmetricSecurityKey(Encoding.Default.GetBytes("401b09eab3c013d4ca54922bb802bec8fd5318192b0a75f201d8b3727429090fb337591abd3e44453b954555b7a0812e1081c39b740293f765eae731f5a65ed1"));
var signingCredentials = new SigningCredentials(securityKey,
                SecurityAlgorithms.HmacSha256Signature,
                SecurityAlgorithms.Sha256Digest);

var header = new JwtHeader(signingCredentials);
var payload = new JwtPayload();

payload.AddClaims(claims);
payload.Add("tags", _tags.ToArray()); // this guy accepts object!

var token = new JwtSecurityToken(header, payload);
var tokenString = securityTokenHandler.WriteToken(token);

【讨论】:

你能发布示例输出吗? @erPe 更新。如果您需要其他任何东西,请告诉我。你也可以看看这个***.com/questions/27090547/…

以上是关于带有 OWIN 的 webapi 中的复杂 JSON Web 令牌数组的主要内容,如果未能解决你的问题,请参考以下文章

带有 OWIN 的 WebApi 2 - 授权挑战不会重定向到登录

[翻译]自托管WebApi使用OWIN和Unity

ApiController 中长时间运行的任务(使用 WebAPI,自托管 OWIN)

授权角色 WebAPI oauth owin

授权角色 WebAPI oauth owin

带有 Owin JWT 身份的 MVC