欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

AspNetCore3.1_Secutiry源码解析_7_Authentication_其他

程序员文章站 2022-04-14 16:11:42
title: "AspNetCore3" date: 2020 03 26T13:23:27+08:00 draft: false 系列文章目录 "AspNetCore3.1_Secutiry源码解析_1_目录" "AspNetCore3.1_Secutiry源码解析_2_Authenticatio ......

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

系列文章目录

简介

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认证