使用IdentityServer 4时如何在Api项目中添加其他声明

时间:2018-03-15 14:43:40

标签: asp.net-mvc asp.net-core asp.net-identity identityserver4 claims-based-identity

抱歉我的英文。

我有三个项目:IdentityServer,Ensino.Mvc,Ensino.Api。 IdentityServer项目提供主要身份信息和声明 - 声明配置文件,声明地址,声明Sid ...等,来自IdentityServer4库。 Ensino.Mvc项目在令牌中获取此信息并将其发送到API,以便MVC获得对资源的访问权限。令牌包含IdentityServer提供的所有声明。但是在API中,我需要生成API特定的其他声明,例如:声明与令牌中的声明Sid相对应的EnrollmentId。此外,我想在HttpContext中添加此声明以供将来使用。有人能告诉我如何实现这个目标吗?

我在Startup.ConfigureServices中有这段代码:

def reports_engagement_device_archive():
    selectList = []
    selectString = ""
    devicesList = [
        "aoldesktop|4|AOL Desktop",
        "aolmail|14|AOL Mail",
        "appleipad|24|Apple iPad",
        "appleiphone|34|Apple iPhone",
        "applemail|44|Apple Mail",
        "blackberry|54|BlackBerry",
        "gmail|64|Gmail",
        "googleandroid|74|Google Android",
        "icloud|84|iCloud",
        "officethreesixfive|394|Office 365",
        "other|94|Other",
        "outlook|104|Outlook",
        "outlookcom|114|Outlook.com",
        "samsungemailapp|124|Samsung Email App",
        "samsungemailapp|134|Samsung Mail",
        "thunderbird|364|Thunderbird",
        "webversion|144|Web version",
        "windowslivemail|154|Windows Live Mail",
        "windowsmail|164|Windows Mail",
        "windowsphone|174|Windows Phone",
        "yahoomail|184|Yahoo! Mail"
    ]
    for devString in devicesList:
        (tblName, devID, devName) = devString.split("|")
        dropMySQLTable("temp_" + tblName ,verbose)
        selectList.append("""(SELECT SUM(t.total) FROM temp_""" + tblName + """ AS t WHERE t.mailingID=s.mailingID) AS \"""" + devName +  """\"""")
        db.sql("""
                CREATE TABLE temp_""" + tblName + """ AS (
                    SELECT s.mailingID,SUM(l.averageReadTime) AS "total" FROM eventlog AS l
                    INNER JOIN recipients AS r ON l.recipientKey=r.id
                    INNER JOIN sends AS s ON l.mailingIDKey=s.id
                    WHERE r.commonDeviceKey=""" + devID + """ AND l.averageReadTime!="-1" AND l.averageReadTime!="(none)" AND (s.sendDateTime BETWEEN '""" + startDate + """' AND '""" + endDate + """')
                    GROUP BY s.mailingID
                );
        """, verbose)
    selectString = ",".join(selectList)
    dropMySQLTable("t1" ,verbose)
    db.sql("""
        CREATE TABLE t1 AS (
            SELECT DISTINCT
            s.mailingID AS "mailingID"
            s.TFPIDSanitised AS "TFPID Sanitised",
            (SELECT SUM(l2.averageReadTime) FROM eventlog AS l2 INNER JOIN sends as s2 ON l2.mailingIDKey=s2.id WHERE s2.TFPIDSanitised=s.TFPIDSanitised AND l2.averageReadTime!="-1" AND l2.averageReadTime!="(none)") AS "Total",
            """ + selectString + """
            FROM sends AS s
            WHERE s.TFPIDSanitised IS NOT NULL
            AND (s.sendDateTime BETWEEN '""" + startDate + """' AND '""" + endDate + """')
        );
    """, verbose)
    for devString in devicesList:
        (tblName, devID, devName) = devString.split("|")
        dropMySQLTable("temp_" + tblName ,verbose)

在其他项目中,没有API,只有mvc,我已继承// Add identity services services.AddAuthentication("Bearer") .AddIdentityServerAuthentication(options => { options.Authority = "http://localhost:5100"; options.RequireHttpsMetadata = false; options.ApiName = "beehouse.scope.ensino-api"; }); // Add mvc services services.AddMvc(); 并覆盖UserClaimsPrincipalFactory以添加其他声明。我喜欢在API项目中做这样的事情。有可能吗?

这样做的最佳方法是什么?

编辑:经过一些研究,我想做的是:IdentityServer认证,并根据声明和特定的api数据库数据在api中设置授权。

3 个答案:

答案 0 :(得分:4)

在您的API项目中,您可以将自己的事件处理程序添加到options.JwtBearerEvents.OnTokenValidated。这是ClaimsPrincipal已设置的点,您可以向标识添加声明或向主体添加新标识。

services.AddAuthentication("Bearer")
   .AddIdentityServerAuthentication(options =>
   {
       options.Authority = "http://localhost:5100";
       options.RequireHttpsMetadata = false;
       options.ApiName = "beehouse.scope.ensino-api";

       options.JwtBearerEvents.OnTokenValidated = async (context) => 
       {
           var identity = context.Principal.Identity as ClaimsIdentity;

           // load user specific data from database
           ...

           // add claims to the identity
           identity.AddClaim(new Claim("Type", "Value"));
       };
   });

请注意,这将在每次API请求时运行,因此如果您从数据库加载信息,最好缓存声明。

此外,Identity Server应该只负责识别用户,而不是他们的用户。他们所做的是特定于应用程序(角色,权限等),因此您正确认识到这一点并避免与Identity Server进行逻辑交叉。

答案 1 :(得分:1)

使用AuthenticationHandler创建自己的IdentityServerAuthenticationHandler将是最佳选择。这将允许您使用DI,拒绝身份验证,并在不需要时跳过自定义身份验证处理程序。

首先验证令牌然后添加更多声明的示例AuthenticationHandler

public class MyApiAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
    protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
    {
        // Pass authentication to IdentityServerAuthenticationHandler
        var authenticateResult = await Context.AuthenticateAsync("Bearer");

        // If token authentication fails, return immediately
        if (!authenticateResult.Succeeded)
        {
            return authenticateResult;
        }

        // Get user ID from token
        var userId = authenticateResult.Principal.Claims
            .FirstOrDefault(c => c.Type == JwtClaimTypes.Subject)?.Value;

        // Do additional checks for authentication
        // e.g. lookup user ID in database
        if (userId == null)
        {
            return AuthenticateResult.NoResult();
        }

        // Add additional claims
        var identity = authenticateResult.Principal.Identity as ClaimsIdentity;
        identity.AddClaim(new Claim("MyClaim", "MyValue"));

        return authenticateResult;
    }
}

为服务添加处理程序:

services.AddAuthentication()
    .AddIdentityServerAuthentication(options =>
    {
        // ...
    })
    .AddScheme<AuthenticationSchemeOptions, MyApiAuthenticationHandler>("MyApiScheme", null);

现在你可以使用任何一种方案:

// Authenticate token and get extra API claims
[Authorize(AuthenticationSchemes = "MyApiScheme")]

// Authenticate just the token
[Authorize(AuthenticationSchemes = "Bearer")]

请注意,IdentityServerAuthenticationHandler执行相同的操作,using the dotnet JWT handler:

public class IdentityServerAuthenticationHandler : AuthenticationHandler<IdentityServerAuthenticationOptions>
{
    protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
    {
        ...
        return await Context.AuthenticateAsync(jwtScheme);
        ...
    }
}

答案 2 :(得分:0)

一步一步确定:

  1. 您需要在Identity Server中创建一个API资源(options.ApiName,但我建议您在此处发布代码时隐藏此类信息)。它应与您的new ApiResource("beehouse.scope.ensino-api", "My test resource", new List<string>() { "claim1", "claim2" });
  2. 同名
  3. 您需要将此范围添加到MVC客户端的允许范围。
  4. 这两个步骤都描述为here,但主要的是在添加资源时可以执行以下操作:

    new Client
        {
            ClientId = "client",
            .
            .
            // scopes that client has access to
            AllowedScopes = { "beehouse.scope.ensino-api" }
            .
            .
        }
    

    然后在您的客户端配置中:

    autoCropImage(url){
        var img = new Image();
        const cropApp = this;
    
    let x = img.onload = function(){
        var canvas = document.createElement('canvas');
        var ctx = canvas.getContext('2d');
        const center_X = img.width/2, center_Y = img.height/2;
        let init_X=0, init_Y=0;
        ctx.drawImage(img, init_X, init_Y, img.width, img.height, 0, 0, img.width, img.height);
    
    let dataUrl = canvas.toDataURL("image/jpeg", 1.0);
    let dataUrl_short = dataUrl.replace("data:image/jpeg;base64,", "");
    return dataUrl;
    }();
    
    
    
    img.src=url;
    console.log(x);
    return x;
    
    }
    //log result:
    //data:,
    

    这会将与此资源关联的声明添加到令牌。 当然,您必须在Identity Server级别设置此声明,但根据您的说法,您已经知道如何执行此操作。