Loading [MathJax]/jax/output/CommonHTML/config.js
社区首页 >问答首页 >ASP.NET核心JWT将角色声明映射到ClaimsIdentity

ASP.NET核心JWT将角色声明映射到ClaimsIdentity
EN

Stack Overflow用户
提问于 2017-02-04 05:24:52
回答 3查看 86K关注 0票数 36

我想使用JWT保护ASP.NET核心Web API。此外,我希望在控制器操作属性中直接使用令牌有效负载中的角色。

现在,虽然我确实找到了如何将其与策略一起使用:

代码语言:javascript
代码运行次数:0
复制
Authorize(Policy="CheckIfUserIsOfRoleX")
ControllerAction()...

我希望有更好的选项来使用一些常用的东西,比如:

代码语言:javascript
代码运行次数:0
复制
Authorize(Role="RoleX")

其中角色将从JWT有效负载自动映射。

代码语言:javascript
代码运行次数:0
复制
{
    name: "somename",
    roles: ["RoleX", "RoleY", "RoleZ"]
}

那么,在ASP.NET核心中实现这一点的最简单方法是什么?有没有办法通过一些设置/映射让它自动工作(如果有,在哪里设置?)或者,在令牌验证后,我是否应该拦截ClaimsIdentity的生成并手动添加角色声明(如果是,在哪里/如何执行?)?

EN

回答 3

Stack Overflow用户

发布于 2017-02-04 07:09:28

在生成JWT时,您需要获取有效的声明。以下是示例代码:

登录逻辑:

代码语言:javascript
代码运行次数:0
复制
[HttpPost]
[AllowAnonymous]
public async Task<IActionResult> Login([FromBody] ApplicationUser applicationUser) {
    var result = await _signInManager.PasswordSignInAsync(applicationUser.UserName, applicationUser.Password, true, false);
    if(result.Succeeded) {
        var user = await _userManager.FindByNameAsync(applicationUser.UserName);

        // Get valid claims and pass them into JWT
        var claims = await GetValidClaims(user);

        // Create the JWT security token and encode it.
        var jwt = new JwtSecurityToken(
            issuer: _jwtOptions.Issuer,
            audience: _jwtOptions.Audience,
            claims: claims,
            notBefore: _jwtOptions.NotBefore,
            expires: _jwtOptions.Expiration,
            signingCredentials: _jwtOptions.SigningCredentials);
        //...
    } else {
        throw new ApiException('Wrong username or password', 403);
    }
}

获取基于用户声明的UserRolesRoleClaimsUserClaims表(ASP.NET身份):

代码语言:javascript
代码运行次数:0
复制
private async Task<List<Claim>> GetValidClaims(ApplicationUser user)
{
    IdentityOptions _options = new IdentityOptions();
    var claims = new List<Claim>
        {
            new Claim(JwtRegisteredClaimNames.Sub, user.UserName),
            new Claim(JwtRegisteredClaimNames.Jti, await _jwtOptions.JtiGenerator()),
            new Claim(JwtRegisteredClaimNames.Iat, ToUnixEpochDate(_jwtOptions.IssuedAt).ToString(), ClaimValueTypes.Integer64),
            new Claim(_options.ClaimsIdentity.UserIdClaimType, user.Id.ToString()),
            new Claim(_options.ClaimsIdentity.UserNameClaimType, user.UserName)
        };
    var userClaims = await _userManager.GetClaimsAsync(user);
    var userRoles = await _userManager.GetRolesAsync(user);
    claims.AddRange(userClaims);
    foreach (var userRole in userRoles)
    {
        claims.Add(new Claim(ClaimTypes.Role, userRole));
        var role = await _roleManager.FindByNameAsync(userRole);
        if(role != null)
        {
            var roleClaims = await _roleManager.GetClaimsAsync(role);
            foreach(Claim roleClaim in roleClaims)
            {
                claims.Add(roleClaim);
            }
        }
    }
    return claims;
}

Startup.cs中,请在授权中添加所需的策略:

代码语言:javascript
代码运行次数:0
复制
void ConfigureServices(IServiceCollection service) {
   services.AddAuthorization(options =>
    {
        // Here I stored necessary permissions/roles in a constant
        foreach (var prop in typeof(ClaimPermission).GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy))
        {
            options.AddPolicy(prop.GetValue(null).ToString(), policy => policy.RequireClaim(ClaimType.Permission, prop.GetValue(null).ToString()));
        }
    });
}

ClaimPermission:

代码语言:javascript
代码运行次数:0
复制
public static class ClaimPermission
{
    public const string
        CanAddNewService = "Tự thêm dịch vụ",
        CanCancelCustomerServices = "Hủy dịch vụ khách gọi",
        CanPrintReceiptAgain = "In lại hóa đơn",
        CanImportGoods = "Quản lý tồn kho",
        CanManageComputers = "Quản lý máy tính",
        CanManageCoffees = "Quản lý bàn cà phê",
        CanManageBillards = "Quản lý bàn billard";
}

使用类似的代码段获取所有预定义的权限,并将其插入到asp.net权限声明表中:

代码语言:javascript
代码运行次数:0
复制
var staffRole = await roleManager.CreateRoleIfNotExists(UserType.Staff);

foreach (var prop in typeof(ClaimPermission).GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy))
{
    await roleManager.AddClaimIfNotExists(staffRole, prop.GetValue(null).ToString());
}

我是ASP.NET的初学者,所以如果你有更好的解决方案,请让我知道。

而且,当我将所有声明/权限都放入JWT中时,我不知道有多糟糕。太久了?性能?我是否应该将生成的JWT存储在数据库中,并在以后检查它以获取有效用户的角色/声明?

票数 55
EN

Stack Overflow用户

发布于 2018-05-25 07:25:29

这是我的工作代码!ASP.NET核心2.0 + JWT。正在向JWT令牌添加角色。

appsettings.json

代码语言:javascript
代码运行次数:0
复制
"JwtIssuerOptions": {
   "JwtKey": "4gSd0AsIoPvyD3PsXYNrP2XnVpIYCLLL",
   "JwtIssuer": "http://yourdomain.com",
   "JwtExpireDays": 30
}

Startup.cs

代码语言:javascript
代码运行次数:0
复制
// ===== Add Jwt Authentication ========
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); // => remove default claims
// jwt
// get options
var jwtAppSettingOptions = Configuration.GetSection("JwtIssuerOptions");
services
    .AddAuthentication(options =>
    {
        options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
        options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
        options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
    })
    .AddJwtBearer(cfg =>
    {
        cfg.RequireHttpsMetadata = false;
        cfg.SaveToken = true;
        cfg.TokenValidationParameters = new TokenValidationParameters
        {
            ValidIssuer = jwtAppSettingOptions["JwtIssuer"],
            ValidAudience = jwtAppSettingOptions["JwtIssuer"],
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtAppSettingOptions["JwtKey"])),
            ClockSkew = TimeSpan.Zero // remove delay of token when expire
        };
    });

AccountController.cs

代码语言:javascript
代码运行次数:0
复制
[HttpPost]
[AllowAnonymous]
[Produces("application/json")]
public async Task<object> GetToken([FromBody] LoginViewModel model)
{
    var result = await _signInManager.PasswordSignInAsync(model.Email, model.Password, false, false);

    if (result.Succeeded)
    {
        var appUser = _userManager.Users.SingleOrDefault(r => r.Email == model.Email);
        return await GenerateJwtTokenAsync(model.Email, appUser);
    }

    throw new ApplicationException("INVALID_LOGIN_ATTEMPT");
}

// create token
private async Task<object> GenerateJwtTokenAsync(string email, ApplicationUser user)
{
    var claims = new List<Claim>
    {
        new Claim(JwtRegisteredClaimNames.Sub, email),
        new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
        new Claim(ClaimTypes.NameIdentifier, user.Id)
    };

    var roles = await _userManager.GetRolesAsync(user);

    claims.AddRange(roles.Select(role => new Claim(ClaimsIdentity.DefaultRoleClaimType, role)));

    // get options
    var jwtAppSettingOptions = _configuration.GetSection("JwtIssuerOptions");

    var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtAppSettingOptions["JwtKey"]));
    var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
    var expires = DateTime.Now.AddDays(Convert.ToDouble(jwtAppSettingOptions["JwtExpireDays"]));

    var token = new JwtSecurityToken(
        jwtAppSettingOptions["JwtIssuer"],
        jwtAppSettingOptions["JwtIssuer"],
        claims,
        expires: expires,
        signingCredentials: creds
    );

    return new JwtSecurityTokenHandler().WriteToken(token);
}

小提琴手测试GetToken方法。请求:

代码语言:javascript
代码运行次数:0
复制
POST https://localhost:44355/Account/GetToken HTTP/1.1
content-type: application/json
Host: localhost:44355
Content-Length: 81

{
    "Email":"admin@admin.site.com",
    "Password":"ukj90ee",
    "RememberMe":"false"
}

调试响应令牌https://jwt.io/#debugger-io

负载数据:

代码语言:javascript
代码运行次数:0
复制
{
  "sub": "admin@admin.site.com",
  "jti": "520bc1de-5265-4114-aec2-b85d8c152c51",
  "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier": "8df2c15f-7142-4011-9504-e73b4681fb6a",
  "http://schemas.microsoft.com/ws/2008/06/identity/claims/role": "Admin",
  "exp": 1529823778,
  "iss": "http://yourdomain.com",
  "aud": "http://yourdomain.com"
}

角色管理员已完成!

票数 18
EN

Stack Overflow用户

发布于 2018-03-09 09:27:34

为了生成JWT令牌,我们需要AuthJwtTokenOptions帮助器类

代码语言:javascript
代码运行次数:0
复制
public static class AuthJwtTokenOptions
{
    public const string Issuer = "SomeIssuesName";

    public const string Audience = "https://awesome-website.com/";

    private const string Key = "supersecret_secretkey!12345";

    public static SecurityKey GetSecurityKey() =>
        new SymmetricSecurityKey(Encoding.ASCII.GetBytes(Key));
}

账号控制人编码:

代码语言:javascript
代码运行次数:0
复制
[HttpPost]
public async Task<IActionResult> GetToken([FromBody]Credentials credentials)
{
    // TODO: Add here some input values validations

    User user = await _userRepository.GetUser(credentials.Email, credentials.Password);
    if (user == null)
        return BadRequest();

    ClaimsIdentity identity = GetClaimsIdentity(user);

    return Ok(new AuthenticatedUserInfoJsonModel
    {
        UserId = user.Id,
        Email = user.Email,
        FullName = user.FullName,
        Token = GetJwtToken(identity)
    });
}

private ClaimsIdentity GetClaimsIdentity(User user)
{
    // Here we can save some values to token.
    // For example we are storing here user id and email
    Claim[] claims = new[]
    {
        new Claim(ClaimTypes.Name, user.Id.ToString()),
        new Claim(ClaimTypes.Email, user.Email)
    };
    ClaimsIdentity claimsIdentity = new ClaimsIdentity(claims, "Token");

    // Adding roles code
    // Roles property is string collection but you can modify Select code if it it's not
    claimsIdentity.AddClaims(user.Roles.Select(role => new Claim(ClaimTypes.Role, role)));
    return claimsIdentity;
}

private string GetJwtToken(ClaimsIdentity identity)
{
    JwtSecurityToken jwtSecurityToken = new JwtSecurityToken(
        issuer: AuthJwtTokenOptions.Issuer,
        audience: AuthJwtTokenOptions.Audience,
        notBefore: DateTime.UtcNow,
        claims: identity.Claims,
        // our token will live 1 hour, but you can change you token lifetime here
        expires: DateTime.UtcNow.Add(TimeSpan.FromHours(1)),
        signingCredentials: new SigningCredentials(AuthJwtTokenOptions.GetSecurityKey(), SecurityAlgorithms.HmacSha256));
    return new JwtSecurityTokenHandler().WriteToken(jwtSecurityToken);
}

Startup.cs中,在services.AddMvc调用之前将以下代码添加到ConfigureServices(IServiceCollection services)方法:

代码语言:javascript
代码运行次数:0
复制
public void ConfigureServices(IServiceCollection services)
{
    // Other code here…

    services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
        .AddJwtBearer(options =>
        {
            options.TokenValidationParameters = new TokenValidationParameters
            {
                ValidateIssuer = true,
                ValidIssuer = AuthJwtTokenOptions.Issuer,

                ValidateAudience = true,
                ValidAudience = AuthJwtTokenOptions.Audience,
                ValidateLifetime = true,

                IssuerSigningKey = AuthJwtTokenOptions.GetSecurityKey(),
                ValidateIssuerSigningKey = true
            };
        });

    // Other code here…

    services.AddMvc();
}

另外,在app.UseMvc调用之前,将app.UseAuthentication()调用添加到Startup.csConfigureMethod中。

代码语言:javascript
代码运行次数:0
复制
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    // Other code here…

    app.UseAuthentication();
    app.UseMvc();
}

现在您可以使用[Authorize(Roles = "Some_role")]属性了。

要在任何控制器中获取用户id和电子邮件,您应该这样做

代码语言:javascript
代码运行次数:0
复制
int userId = int.Parse(HttpContext.User.Claims.First(c => c.Type == ClaimTypes.Name).Value);

string email = HttpContext.User.Claims.First(c => c.Type == ClaimTypes.Email).Value;

也可以通过这种方式检索userId (这是由于声明类型名称ClaimTypes.Name所致)

代码语言:javascript
代码运行次数:0
复制
int userId = int.Parse(HttpContext.User.Identity.Name);

最好将这样的代码转移到一些控制器扩展助手中:

代码语言:javascript
代码运行次数:0
复制
public static class ControllerExtensions
{
    public static int GetUserId(this Controller controller) =>
        int.Parse(controller.HttpContext.User.Claims.First(c => c.Type == ClaimTypes.Name).Value);

    public static string GetCurrentUserEmail(this Controller controller) =>
        controller.HttpContext.User.Claims.First(c => c.Type == ClaimTypes.Email).Value;
}

对于您添加的任何其他Claim也是如此。您应该只指定有效的密钥。

票数 15
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/42036810

复制
相关文章
聊天机器人中的深度学习技术(引言)
原文链接:DEEP LEARNING FOR CHATBOTS, PART 1 – INTRODUCTION 译者:刘翔宇 审核:赵屹华 责编:周建丁(zhoujd@csdn.net) 聊天机器人,又被称为会话代理或者对话系统,它是一个热门的话题。微软在聊天机器人上下了 很大的成本 ,Facebook(M),苹果公司(Siri),谷歌,微信,和Slack也是如此。聊天机器人在初创公司中掀起了一种新浪潮,他们试图通过建立类似于 Operator 或x.ai 这样的应用程序,类似于 Chatfuel 这样的
用户1737318
2018/06/06
8120
谷歌聊天机器人bard和chatgpt比较
总的来说,Bard和ChatGPT都是强大的语言模型,都有自己的优点和缺点。Bard更擅长回答问题,而ChatGPT更擅长生成创意文本格式。Bard目前处于目前处于部分地区测试阶段,而ChatGPT则向公众开放。
程序那些事儿
2023/06/05
5560
谷歌聊天机器人bard和chatgpt比较
【每周NLP论文推荐】 聊天机器人中FAQ相关的论文推荐
今天推荐FAQ相关的论文,FAQ是聊天机器人的一种,它主要是基于用户的question,匹配相应的答案,返回给用户answer,没有太多的多轮交互。FAQ有较多的应用,如天猫精灵,小度等。
用户1508658
2019/09/03
1.3K0
【每周NLP论文推荐】 聊天机器人中FAQ相关的论文推荐
图灵聊天机器人
01 目录 确定目标 分析目标 代码实操 02 确定目标 本次通过图灵机器人平台的API接口制作一个人工智障聊天机器人 图灵机器人的网址为:'http://www.turingapi.com/ '
佛系编程人
2019/08/14
1.7K0
图灵聊天机器人
聊天机器人总结
里面提到一个名词叫槽位。其实可以简单的理解为填表格。DPO发起询问,NLG生成回复,为了使得回复自然,这里采用推荐时的回复。
故事尾音
2019/12/18
1.1K0
聊天机器人总结
Facebook 号称击败谷歌,推出最强聊天机器人
场景描述:Facebook 近日开源了新的聊天机器人 Blender,表现优于现有对话机器人,更具个性化。
黄博的机器学习圈子
2020/05/26
6760
Facebook 号称击败谷歌,推出最强聊天机器人
TensorFlow 聊天机器人
该文章介绍了如何利用基于Seq2Seq模型进行机器翻译,包括编码器、解码器和注意力机制等方面的实现。同时,文章还提供了训练、预测和评估的代码示例。
MachineLP
2018/01/09
1.1K0
TensorFlow 聊天机器人
聊天机器人一
? ? ? ? ? ? ---- ?
拾点阳光
2019/03/13
1.1K0
聊天机器人一
谷歌聊天机器人LaMDA被爆突然现身!只会聊狗子,被ChatGPT秒成渣
---- 新智元报道   编辑:编辑部 【新智元导读】这几日,被ChatGPT搞破防人的有点多。谷歌之前传说中「意识觉醒」的LaMDA又搬出来测试了。网友实测:只会聊「狗子」,被ChatGPT秒成渣。 谷歌急了! 眼看ChatGPT如日中天,谷歌也学起微软,狂掷4亿美元给「叛徒」公司Anthropic,想扶起一个自己的OpenAI。 最近,劈柴又放话说,谷歌的AI语言模型可能几周内就能用了。 哎,巧了不是,就在同一时间,有网友爆料:「LaMDA能用了!我刚和它对话了几句。」 这里的每个演示都是用La
新智元
2023/02/24
4740
谷歌聊天机器人LaMDA被爆突然现身!只会聊狗子,被ChatGPT秒成渣
开启聊天机器人模式
聊天机器人系统框架图 今天看到了一篇关于聊天机器人的一个不错的资源汇总: https://www.52ml.net/20510.html 进去看看先大概了解了一下都有哪些主要的概念: 原文:巨头们都很重视的聊天机器人,你不进来看看吗? 理想的 chatbot 什么样 现在的 bot 什么样 |处理任务 |聊天-搞笑
杨熹
2018/04/03
9230
开启聊天机器人模式
聊天机器人API Demo
昨天车上无聊,又写起了代码…… 居然真写出了个聊天机器人的API Demo……(没啥用) 这个API是调用图灵机器人的官方API,只是简单的解析json,直接把文字信息输出 话不多说,上代码 <?ph
FHYC
2018/06/22
4.4K0
聊天机器人的挑战
大家好,这里是NewBeeNLP。今天来送基本书,《自己动手做聊天机器人》,感兴趣的同学文末参加噢!
NewBeeNLP
2022/11/11
7960
聊天机器人的挑战
Transformer聊天机器人教程
在这篇文章中,我们将演示如何构建Transformer聊天机器人。 本文聚焦于:使用TensorFlow Dataset并使用tf.data创建输入管道来使用Cornell Movie-Dialogs Corpus,使用Model子类化实现MultiHeadAttention,使用Functional API实现Transformer。
昱良
2019/05/29
2.4K0
Transformer聊天机器人教程
聊天机器人模型 - ChatGPT
还有一个问题,正常情况:注册成功openai会赠送18美金,可以用于api调用。但如果你用的这个手机号被其他人用过了,这18美金就没有了。我第一次就遇到这个问题,只能换个E-mail重新注册
崔哥
2023/02/26
1.1K0
开启聊天机器人模式
本文介绍了聊天机器人技术的研究进展,从系统框架、自然语言理解、对话管理、自然语言生成等方面进行了详细阐述,并对当前面临的挑战和未来的展望进行了分析。
MachineLP
2018/01/09
1K0
开启聊天机器人模式
使用Python操作机器人聊天
萌新偶然发现一个好玩的东西 使用Python和机器人聊天 效果 工具 图灵机器人API itchat(提前安装) 准备 自己需要去图灵机器人注册账号,拿到APIkey,大家也可以直接使用我这个 ca0
小歪
2018/04/04
1.5K0
使用Python操作机器人聊天
Python实现聊天机器人
AIML全名为Artificial Intelligence Markup Language(人工智能标记语言),是一种创建自然语言软件代理的XML语言,是由RichardS. Wallace 博士和Alicebot开源软件组织于1995-2000年间发明创造的。AIML是一种为了匹配模式和确定响应而进行规则定义的 XML 格式。
海天一树
2018/07/25
1.2K0
Python实现聊天机器人
不再鹦鹉学舌:26亿参数量,谷歌开放领域聊天机器人近似人类水平
现在的对话智能体(即聊天机器人)都是非常专业化的,如果用户不偏离场景太远的话,这些机器人的表现还是很不错的。但是,要想让聊天机器人能够完成更广泛话题下的对话任务,发展开放领域聊天机器人就显得很重要了。
机器之心
2020/02/24
5490
不再鹦鹉学舌:26亿参数量,谷歌开放领域聊天机器人近似人类水平
谷歌加入聊天机器人大战,微软Facebook将如何应对?
GAIR 今年夏天,雷锋网将在深圳举办一场盛况空前的“全球人工智能与机器人创新大会”(简称GAIR)。大会现场,雷锋网将发布“人工智能&机器人Top25创新企业榜”榜单。目前,我们正在四处拜访人工智能、机器人领域的相关公司,从而筛选最终入选榜单的公司名单。如果你的公司也想加入我们的榜单之中,请联系:2020@leiphone.com 谷歌正计划加入聊天机器人开发业务,并可能在其明天的年度开发者大会上公布一些特点。在微软的基础上,谷歌找到了一种方式,使应用开发人员能够创建在现有的消息应用程序内部运行的程序,
AI科技评论
2018/03/07
1.1K0
谷歌加入聊天机器人大战,微软Facebook将如何应对?
用 ChatGPT 考一考谷歌 AI 聊天机器人 - Bard
面对微软在 AIGC 领域一拳又一拳的出击,谷歌也真的是坐不住了,最新发布对标 ChatGPT 的聊天机器人:Bard,中文意思是:吟游诗人(听这个产品的命名还有点浪漫主义感觉~)
掘金安东尼
2023/04/22
4100
用 ChatGPT 考一考谷歌 AI 聊天机器人 - Bard

相似问题

如何在聊天机器人中返回多个响应?

10

简单聊天机器人中的错误

11

聊天机器人中的结束会话

11

聊天机器人中的空响应和交叉会话问题

13

在lex聊天机器人中呈现html

220
添加站长 进交流群

领取专属 10元无门槛券

AI混元助手 在线答疑

扫码加入开发者社群
关注 腾讯云开发者公众号

洞察 腾讯核心技术

剖析业界实践案例

扫码关注腾讯云开发者公众号
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档