当前位置: 移动技术网 > IT编程>开发语言>.net > asp.net core 外部认证多站点模式实现

asp.net core 外部认证多站点模式实现

2018年08月22日  | 移动技术网IT编程  | 我要评论

赣州电视台,南海诸岛图,婚礼前期准备流程表

ps:之前因为需要扩展了微信和qq的认证,使得网站是可以使用qq和微信直接登录。github 传送门 。然后有小伙伴问,能否让这个配置信息(appid, appsecret)按需改变,而不是在 configureservices  里面写好。

先上 官方文档 :   

官方已经实现了 microsft,facebook,twitter,google 等这几个网站认证。代码可以认证授权库看到找到 https://github.com/aspnet/security  。

国内的qq和微信其实也是基于oauth来实现的,所以自己集成还是比较容易。

正常情况下,配置这个外部认证都是在 configureservices 里面配置好,并且使用配置或者是使用机密文件的形式来保存 appid 等信息。

回到正文,多站点模式,就是一个网站下分为多个子站点,并且不同的子站点可以配置不同的appid 。asp.net core 默认的配置模式,在这种场景下已经适应不了了。

先上代码: https://github.com/jxnkwlp/aspnetcore.authenticationqq-webchat/tree/muti-site

官方代码分析:

1,remoteauthenticationhandler  远程认证处理程序。位于 microsoft.aspnetcore.authentication  下 。 源码 (https://github.com/aspnet/security/blob/master/src/microsoft.aspnetcore.authentication/remoteauthenticationhandler.cs)

这个是泛型类,并且需要一个  toptions ,这个 toptions 必须是继承 remoteauthenticationoptions 的类。

 

2,oauthhandler 实现 oauth 认证处理程序,这个类继承 remoteauthenticationhandler 。同时必须实现一个 oauthoptions 。

正常情况下实现 qq、微信、github ,google ,facebook 等登录都是基于这个来实现的。 oauthhandler 已经实现了标准的 oauth 认证。

源码:https://github.com/aspnet/security/blob/master/src/microsoft.aspnetcore.authentication.oauth/oauthhandler.cs

在 configureservices 中,使用  addfacebook 等方法,就是将 对于的 handler 添加到 处理管道中,这些管到都是实现了 oauth,然后传递 对应的 options 来配置handler 。

 

3,回到account/externallogin ,在提交外部登录的请求中, authenticationproperties  properties = _signinmanager.configureexternalauthenticationproperties(provider, redirecturl);  //这行代码的作用是 配置当前外部登录返回url和认证的相关属性。return challenge(properties, provider);  // 将结果转到相关相关处理程序。这里返回的结果用于上面  oauthhandler 作为一个处理参数。从这开始,就进入了 oauthhandler 的处理范围了。

 

4,查看 oauthhandler 代码 。  task handlechallengeasync(authenticationproperties properties);  这个函数作为接收上一步中传递的 认证参数。   默认实现代码:

protected override async task handlechallengeasync(authenticationproperties properties) 
{

    if (string.isnullorempty(properties.redirecturi)) 
    { 
        properties.redirecturi = currenturi; 
    }

    // oauth2 10.12 csrf

    generatecorrelationid(properties);

    var authorizationendpoint = buildchallengeurl(properties, buildredirecturi(options.callbackpath));

    var redirectcontext = new redirectcontext<oauthoptions>(

        context, scheme, options,

        properties, authorizationendpoint);

    await events.redirecttoauthorizationendpoint(redirectcontext); 
}
protected virtual string buildchallengeurl(authenticationproperties properties, string redirecturi)
{
    var scopeparameter = properties.getparameter<icollection<string>>(oauthchallengeproperties.scopekey);
    var scope = scopeparameter != null ? formatscope(scopeparameter) : formatscope();

    var state = options.statedataformat.protect(properties);
    var parameters = new dictionary<string, string>
    {
        { "client_id", options.clientid },
        { "scope", scope },
        { "response_type", "code" },
        { "redirect_uri", redirecturi },
        { "state", state },
    };

    return queryhelpers.addquerystring(options.authorizationendpoint, parameters);
}

 

在这里面,构建了一个请求url, 要求的这个url 是目标站点授权的url, 比如微信的那个黑色背景中间有二维码的页面。  这个构建请求url的方法可以重写。

5,在上一步中,在需要授权的网站,授权完成后,会跳转到自己的网站并且带上授权相关数据。入口是  task<handlerequestresult> handleremoteauthenticateasync();  

改造方法:

在上面的分析中,官方的实现是 在 configureservices 中配置好参数 toptions ,然后在 handler 中 获取该参数。我们的目的是在请求中可以按需改变参数,如 client_id。

1,定义一个接口 iclientstore 和 一个实体 clientstoremodel 。

public interface iclientstore
{
    /// <summary>
    ///  由 <paramref name="provider"/> 和 <paramref name="subjectid"/> 查找 <seealso cref="clientstoremodel"/>
    /// </summary> 
    clientstoremodel findbysubjectid(string provider, string subjectid);
}

 

/// <summary>
///  表示一个 client 信息 
/// </summary>
public class clientstoremodel
{
    public string provider { get; set; }

    public string subjectid { get; set; }

    /// <summary>
    /// gets or sets the provider-assigned client id.
    /// </summary>
    public string clientid { get; set; }

    /// <summary>
    /// gets or sets the provider-assigned client secret.
    /// </summary>
    public string clientsecret { get; set; }

}

 

iclientstore 用于查找 client 的配置信息

2,在 account/externallogin 中,新增一个 参数 subjectid  ,表示在当前某个认证(provider)中是哪个请求(subjectid) 。

同时在返回的授权配置参数中将subjectid 保存起来。

 

3,定义一个 multioauthhandler ,集成 remoteauthenticationhandler  ,不继承  oauthhandler 是因为 这里需要一个新的 options.  (完整代码 请看代码仓库)  定义: class multioauthhandler<tmultioauthoptions>:remoteauthenticationhandler<tmultioauthoptions>wheretmultioauthoptions:multioauthoptions,new() 

在构造函数中添加参数 iclientstore 。

4,在默认的实现中,从外部授权网站跳转回自己的网站的时候,默认的路径是 /signin-{provider} , 比如 /signin-microsoft  。为了区分请求的 subjectid ,  这个默认路径将改为  /signin-{provider}/subject/{subjectid}  。

5,修改 handleremoteauthenticateasync  ,在开头添加2行代码,用于获取 subjectid 。

var callbackpath = options.callbackpath.add("/subject").value;

var subjectid = request.path.value.remove(0, callbackpath.length + 1);

 

6,修改 exchangecodeasync 方法

protected virtual async task<oauthtokenresponse> exchangecodeasync(string subjectid, string code, string redirecturi)
{
    var clientstore = getclientstore(subjectid);

    var tokenrequestparameters = new dictionary<string, string>()
    {
        { "client_id", clientstore.clientid },
        { "client_secret", clientstore.clientsecret },

        { "redirect_uri", redirecturi },
        { "code", code },
        { "grant_type", "authorization_code" },
    };

    var requestcontent = new formurlencodedcontent(tokenrequestparameters);

    var requestmessage = new httprequestmessage(httpmethod.post, options.tokenendpoint);
    requestmessage.headers.accept.add(new mediatypewithqualityheadervalue("application/json"));
    requestmessage.content = requestcontent;
    var response = await backchannel.sendasync(requestmessage, context.requestaborted);
    if (response.issuccessstatuscode)
    {
        var payload = jobject.parse(await response.content.readasstringasync());
        return oauthtokenresponse.success(payload);
    }
    else
    {
        var error = "oauth token endpoint failure: " + await display(response);
        return oauthtokenresponse.failed(new exception(error));
    }
}

 

7,还有一些小修改,就不一一列出来了。  到这里  multioauthhandler  相关就调整好了。

我把这个单独出来了  microsoft.aspnetcore.authentication.multioauth 

 

8,使用 。 实现 iclientstore 接口,然后在 configureservices  中添加如下代码:

services.addauthentication()
    .addmultioauthstore<mylientstore>() 
    .addmultiweixinauthentication(); // 微信

 

9, 目前github 上的demo 只对 微信  做了实现。

 

ps:如有错误,欢迎指正。

 

源地址: 

 

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网