title: “aspnetcore3”
date: 2020-03-26t13:23:27+08:00
draft: false

系列文章目录

  • aspnetcore3.1_secutiry源码解析_1_目录
  • aspnetcore3.1_secutiry源码解析_2_authentication_核心流程
  • aspnetcore3.1_secutiry源码解析_3_authentication_cookies
  • aspnetcore3.1_secutiry源码解析_4_authentication_jwtbear
  • aspnetcore3.1_secutiry源码解析_5_authentication_oauth
  • aspnetcore3.1_secutiry源码解析_6_authentication_openidconnect
  • aspnetcore3.1_secutiry源码解析_7_authentication_其他
  • aspnetcore3.1_secutiry源码解析_8_authorization_核心项目
  • aspnetcore3.1_secutiry源码解析_9_authorization_policy

简介

secutiry的认证目录还有这些项目,基本都是具体的oauth2.0服务商或者其他用的比较少的认证架构,简单看一下,了解一下。

  • microsoft.aspnetcore.authentication.certificate
  • microsoft.aspnetcore.authentication.facebook
  • microsoft.aspnetcore.authentication.google
  • microsoft.aspnetcore.authentication.microsoftaccount
  • microsoft.aspnetcore.authentication.negotiate
  • microsoft.aspnetcore.authentication.twitter
  • microsoft.aspnetcore.authentication.wsfederation

oauth2.0服务商

facebook, google,microsoftaccount这几个都可以归为一类,都是oauth2.0的服务商。国内用的比较多的是qq,weixin。我们看一下facebook的代码,其他的原理都是大同小异的,根据不同厂商的差异稍作调整就可以了。

twitter似乎是用的oauth1.0协议。

依赖注入

配置类: facebookoptions,处理器类:facebookhandler

public static class facebookauthenticationoptionsextensions
{
    public static authenticationbuilder addfacebook(this authenticationbuilder builder)
        => builder.addfacebook(facebookdefaults.authenticationscheme, _ => { });

    public static authenticationbuilder addfacebook(this authenticationbuilder builder, action<facebookoptions> configureoptions)
        => builder.addfacebook(facebookdefaults.authenticationscheme, configureoptions);

    public static authenticationbuilder addfacebook(this authenticationbuilder builder, string authenticationscheme, action<facebookoptions> configureoptions)
        => builder.addfacebook(authenticationscheme, facebookdefaults.displayname, configureoptions);

    public static authenticationbuilder addfacebook(this authenticationbuilder builder, string authenticationscheme, string displayname, action<facebookoptions> configureoptions)
        => builder.addoauth<facebookoptions, facebookhandler>(authenticationscheme, displayname, configureoptions);
}

配置类 – facebookoptions

配置类继承自oauthoptions,构造函数根据facebook做了一些定制处理,如claim的映射等。

/// <summary>
/// configuration options for <see cref="facebookhandler"/>.
/// </summary>
public class facebookoptions : oauthoptions
{
    /// <summary>
    /// initializes a new <see cref="facebookoptions"/>.
    /// </summary>
    public facebookoptions()
    {
        callbackpath = new pathstring("/signin-facebook");
        sendappsecretproof = true;
        authorizationendpoint = facebookdefaults.authorizationendpoint;
        tokenendpoint = facebookdefaults.tokenendpoint;
        userinformationendpoint = facebookdefaults.userinformationendpoint;
        scope.add("email");
        fields.add("name");
        fields.add("email");
        fields.add("first_name");
        fields.add("last_name");

        claimactions.mapjsonkey(claimtypes.nameidentifier, "id");
        claimactions.mapjsonsubkey("urn:facebook:age_range_min", "age_range", "min");
        claimactions.mapjsonsubkey("urn:facebook:age_range_max", "age_range", "max");
        claimactions.mapjsonkey(claimtypes.dateofbirth, "birthday");
        claimactions.mapjsonkey(claimtypes.email, "email");
        claimactions.mapjsonkey(claimtypes.name, "name");
        claimactions.mapjsonkey(claimtypes.givenname, "first_name");
        claimactions.mapjsonkey("urn:facebook:middle_name", "middle_name");
        claimactions.mapjsonkey(claimtypes.surname, "last_name");
        claimactions.mapjsonkey(claimtypes.gender, "gender");
        claimactions.mapjsonkey("urn:facebook:link", "link");
        claimactions.mapjsonsubkey("urn:facebook:location", "location", "name");
        claimactions.mapjsonkey(claimtypes.locality, "locale");
        claimactions.mapjsonkey("urn:facebook:timezone", "timezone");
    }

    /// <summary>
    /// check that the options are valid.  should throw an exception if things are not ok.
    /// </summary>
    public override void validate()
    {
        if (string.isnullorempty(appid))
        {
            throw new argumentexception(string.format(cultureinfo.currentculture, resources.exception_optionmustbeprovided, nameof(appid)), nameof(appid));
        }

        if (string.isnullorempty(appsecret))
        {
            throw new argumentexception(string.format(cultureinfo.currentculture, resources.exception_optionmustbeprovided, nameof(appsecret)), nameof(appsecret));
        }

        base.validate();
    }

    // facebook uses a non-standard term for this field.
    /// <summary>
    /// gets or sets the facebook-assigned appid.
    /// </summary>
    public string appid
    {
        get { return clientid; }
        set { clientid = value; }
    }

    // facebook uses a non-standard term for this field.
    /// <summary>
    /// gets or sets the facebook-assigned app secret.
    /// </summary>
    public string appsecret
    {
        get { return clientsecret; }
        set { clientsecret = value; }
    }

    /// <summary>
    /// gets or sets if the appsecret_proof should be generated and sent with facebook api calls.
    /// this is enabled by default.
    /// </summary>
    public bool sendappsecretproof { get; set; }

    /// <summary>
    /// the list of fields to retrieve from the userinformationendpoint.
    /// https://developers.facebook.com/docs/graph-api/reference/user
    /// </summary>
    public icollection<string> fields { get; } = new hashset<string>();
}

处理器类

重写了oauthhanlder的创建凭据方法,其他的都是使用的父类实现。

public class facebookhandler : oauthhandler<facebookoptions>
{
    public facebookhandler(ioptionsmonitor<facebookoptions> options, iloggerfactory logger, urlencoder encoder, isystemclock clock)
        : base(options, logger, encoder, clock)
    { }

    protected override async task<authenticationticket> createticketasync(claimsidentity identity, authenticationproperties properties, oauthtokenresponse tokens)
    {
        var endpoint = queryhelpers.addquerystring(options.userinformationendpoint, "access_token", tokens.accesstoken);
        if (options.sendappsecretproof)
        {
            endpoint = queryhelpers.addquerystring(endpoint, "appsecret_proof", generateappsecretproof(tokens.accesstoken));
        }
        if (options.fields.count > 0)
        {
            endpoint = queryhelpers.addquerystring(endpoint, "fields", string.join(",", options.fields));
        }

        var response = await backchannel.getasync(endpoint, context.requestaborted);
        if (!response.issuccessstatuscode)
        {
            throw new httprequestexception($"an error occurred when retrieving facebook user information ({response.statuscode}). please check if the authentication information is correct and the corresponding facebook graph api is enabled.");
        }

        using (var payload = jsondocument.parse(await response.content.readasstringasync()))
        {
            var context = new oauthcreatingticketcontext(new claimsprincipal(identity), properties, context, scheme, options, backchannel, tokens, payload.rootelement);
            context.runclaimactions();
            await events.creatingticket(context);
            return new authenticationticket(context.principal, context.properties, scheme.name);
        }
    }

    private string generateappsecretproof(string accesstoken)
    {
        using (var algorithm = new hmacsha256(encoding.ascii.getbytes(options.appsecret)))
        {
            var hash = algorithm.computehash(encoding.ascii.getbytes(accesstoken));
            var builder = new stringbuilder();
            for (int i = 0; i < hash.length; i++)
            {
                builder.append(hash[i].tostring("x2", cultureinfo.invariantculture));
            }
            return builder.tostring();
        }
    }

    protected override string formatscope(ienumerable<string> scopes)
    {
        // facebook deviates from the oauth spec here. they require comma separated instead of space separated.
        // https://developers.facebook.com/docs/reference/dialogs/oauth
        // http://tools.ietf.org/html/rfc6749#section-3.3
        return string.join(",", scopes);
    }

    protected override string formatscope()
        => base.formatscope();
}

microsoft.aspnetcore.authentication.certificate

这个项目是3.1新加的,是做证书校验的,具体的不细说了,不太懂,有兴趣的看巨硬文档

microsoft.aspnetcore.authentication.negotiate

这个也是新增的项目,是做windows校验的,文档如下

microsoft.aspnetcore.authentication.wsfederation

windows的azure active directory认证