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

详解ASP.NET Core和ASP.NET Framework共享身份验证

程序员文章站 2022-06-14 15:19:07
.net core 已经热了好一阵子,1.1版本发布后其可用性也越来越高,开源、组件化、跨平台、性能优秀、社区活跃等等标签再加上“微软爸爸”主推和大力支持,尽管现阶段对比....

.net core 已经热了好一阵子,1.1版本发布后其可用性也越来越高,开源、组件化、跨平台、性能优秀、社区活跃等等标签再加上“微软爸爸”主推和大力支持,尽管现阶段对比.net framework还是比较“稚嫩”,但可以想象到它光明的前景。作为.net 开发者你是否已经开始尝试将项目迁移到.net core上?这其中要解决的一个较大的问题就是如何让你的.net core和老.net framework站点实现身份验证兼容!

1、第一篇章

我们先来看看.net core中对identity的实现,在startup.cs的configure中配置cookie认证的相关属性

public void configure(iapplicationbuilder app, ihostingenvironment env)
{
  app.usecookieauthentication(new cookieauthenticationoptions
  {
    authenticationscheme = "test",
    cookiename = "mycookie"
  });
}

controller

public iactionresult index()
{
  return view();
}

public iactionresult login()
{
  return view();
}

[httppost]
public async task<iactionresult> login(string name)
{
  var identity = new claimsidentity(
          new list<claim>
          {
          new claim(claimtypes.name,name, claimvaluetypes.string)
          },
          claimtypes.authentication,
          claimtypes.name,
          claimtypes.role);
  var principal = new claimsprincipal(identity);
  var properties = new authenticationproperties { ispersistent = true };

  await httpcontext.authentication.signinasync("test", principal, properties);

  return redirecttoaction("index");
}

login 视图

<!doctype html>
<html>
<head>
  <title>登录</title>
</head>
<body>
  <form asp-controller="account" asp-action="login" method="post">
    <input type="text" name="name" /><input type="submit" value="提交" />
  </form>
</body>
</html>

index 视图

<!doctype html>
<html>
<head>
 <title>欢迎您-@user.identity.name</title>
</head>
<body>
  @if (user.identity.isauthenticated)
  {
    <p>登录成功!</p>
  }
</body>
</html>

下面是实现效果的截图:

详解ASP.NET Core和ASP.NET Framework共享身份验证

详解ASP.NET Core和ASP.NET Framework共享身份验证

ok,到此我们用.net core比较简单地实现了用户身份验证信息的保存和读取。

接着思考,如果我的.net framework项目想读取.net core项目保存的身份验证信息应该怎么做?

要让两个项目都接受同一个identity至少需要三个条件:

  • cookiename必须相同。
  • cookie的作用域名必须相同。
  • 两个项目的cookie认证必须使用同一个ticket。

首先我们对.net core的cookie认证添加domain属性和ticket属性

public void configure(iapplicationbuilder app, ihostingenvironment env)
{
  var protectionprovider = dataprotectionprovider.create(new directoryinfo(@"c:\keypath\"));
  var dataprotector = protectionprovider.createprotector("mycookieauthentication");
  var ticketformat = new ticketdataformat(dataprotector);

  app.usecookieauthentication(new cookieauthenticationoptions
  {
    authenticationscheme = "test",
    cookiename = "mycookie",
    cookiedomain = "localhost",
    ticketdataformat = ticketformat
  });
}

此时我们在.net core 项目中执行用户登录,程序会在我们指定的目录下生成key.xml

详解ASP.NET Core和ASP.NET Framework共享身份验证

我们打开文件看看程序帮我们记录了那些信息

<?xml version="1.0" encoding="utf-8"?>
<key id="eb8b1b59-dbc5-4a28-97ad-2117a2e8f106" version="1">
 <creationdate>2016-12-04t08:27:27.8435415z</creationdate>
 <activationdate>2016-12-04t08:27:27.8214603z</activationdate>
 <expirationdate>2017-03-04t08:27:27.8214603z</expirationdate>
 <descriptor deserializertype="microsoft.aspnetcore.dataprotection.authenticatedencryption.configurationmodel.authenticatedencryptordescriptordeserializer, microsoft.aspnetcore.dataprotection, version=1.1.0.0, culture=neutral, publickeytoken=adb9793829ddae60">
  <descriptor>
   <encryption algorithm="aes_256_cbc" />
   <validation algorithm="hmacsha256" />
   <masterkey p4:requiresencryption="true" xmlns:p4="http://schemas.asp.net/2015/03/dataprotection">
    <value>yhdmeylebzcwpx0brzvibcgj45/gqrwfjmfq8pj+k7zwsnmic0embgp33foq9mfkx0xe/a1plhdizbb92erqyw==</value>
   </masterkey>
  </descriptor>
 </descriptor>
</key>

ok,接下来我们开始配置.net framework项目,同样,在startup.cs中配置cookie认证的相关属性。

public partial class startup
{
  public void configuration(iappbuilder app)
  {
    var protectionprovider = dataprotectionprovider.create(new directoryinfo(@"c:\keypath\"));
    var dataprotector = protectionprovider.createprotector("mycookieauthentication");
    var ticketformat = new aspnetticketdataformat(new dataprotectorshim(dataprotector));
      
    app.usecookieauthentication(new cookieauthenticationoptions
    {
      authenticationtype = "test",
      cookiename = "mycookie",
      cookiedomain = "localhost",
      ticketdataformat = ticketformat
    });
  }
}

view

<!doctype html>
<html>
<head>
  <title>.net framewor欢迎您-@user.identity.name</title>
</head>
<body>
  @if (user.identity.isauthenticated)
  {
    <p>.net framework登录成功!</p>
  }
</body>
</html>

写法和.net core 基本上是一致的,我们来看下能否成功获取用户名:

详解ASP.NET Core和ASP.NET Framework共享身份验证

反之在.net framework中登录在.net core中获取身份验证信息的方法是一样的,这里就不重复写了。

然而,到此为止事情就圆满解决了吗?很遗憾,麻烦才刚刚开始!

--------------------------------------------------------------------------------

2、第二篇章

如果你的子项目不多,也不复杂的情况下,新增一个.net core 站点,然后适当修改以前的.net framework站点,上述实例确实能够满足需求。可是如果你的子站点足够多,或者项目太过复杂,牵扯到的业务过于庞大或重要,这种情况下我们通常是不愿意动老项目的。或者说我们没有办法将所有的项目都进行更改,然后和新增的.net core站点同时上线,如果这么做了,那么更新周期会拉的很长不说,测试和更新之后的维护阶段压力都会很大。所以我们必须要寻找到一种方案,让.net core的身份验证机制完全迎合.net framwork。

因为.net framework 的cookie是对称加密,而.net core是非对称加密,所以要在.net core中动手的话必须要对.net core 默认的加密和解密操作进行拦截,如果可行的话最好的方案应该是将.net framework的formsauthentication类移植到.net core中。但是用reflector看了下,牵扯到的代码太多,剪不断理还乱,github上也没找到其源码,瞎忙活了一阵之后终于感慨:臣妾做不到(>﹏< )。

cookie认证的相关属性

app.usecookieauthentication(new cookieauthenticationoptions
{
  authenticationscheme = "test",
  cookiename = "mycookie",
  cookiedomain = "localhost",
  ticketdataformat = new formsauthticketdataformat("")
});

formsauthticketdataformat

public class formsauthticketdataformat : isecuredataformat<authenticationticket>
{
  private string _authenticationscheme;

  public formsauthticketdataformat(string authenticationscheme)
  {
    _authenticationscheme = authenticationscheme;
  }

  public authenticationticket unprotect(string protectedtext, string purpose)
  {
    var formsauthticket = getformsauthticket(protectedtext);
    var name = formsauthticket.name;
    datetime issuedate = formsauthticket.issuedate;
    datetime expiration = formsauthticket.expiration;

    var claimsidentity = new claimsidentity(new claim[] { new claim(claimtypes.name, name) }, "basic");
    var claimsprincipal = new claimsprincipal(claimsidentity);

    var authproperties = new microsoft.aspnetcore.http.authentication.authenticationproperties
    {
      issuedutc = issuedate,
      expiresutc = expiration
    };
    var ticket = new authenticationticket(claimsprincipal, authproperties, _authenticationscheme);
    return ticket;
  }

  formsauthticket getformsauthticket(string cookie)
  {
    return decryptcookie(cookie).result;
  }

  async task<formsauthticket> decryptcookie(string cookie)
  {
    httpclient _httpclient = new httpclient();
    var response = await _httpclient.getasync("http://192.168.190.134/user/getmyticket?cookie={cookie}");
    response.ensuresuccessstatuscode();
    return await response.content.readasasync<formsauthticket>();
  }
}

formsauthticket

public class formsauthticket
{
  public datetime expiration { get; set; }
  public datetime issuedate { get; set; }
  public string name { get; set; }
}

以上实现了对cookie的解密拦截,然后通过webapi从.net framework获取ticket

[route("getmyticket")]
public ihttpactionresult getmyticket(string cookie)
{
  var formsauthticket = formsauthentication.decrypt(cookie);
  return ok(new { formsauthticket.name, formsauthticket.issuedate, formsauthticket.expiration });
}

有了webapi这条线,解密解决了,加密就更简单了,通过webapi获取加密后的cookie,.net core要做的只有一步,保存cookie就行了

[httppost]
public async task<iactionresult> login(string name)
{
  httpclient _httpclient = new httpclient();
  var response = await _httpclient.getasync($"http://192.168.190.134/user/getmycookie?name={name}");
  response.ensuresuccessstatuscode();

  string cookievalue = (await response.content.readasstringasync()).trim('\"');
  cookieoptions options = new cookieoptions();
  options.expires = datetime.maxvalue;
  httpcontext.response.cookies.append("mycookie", cookievalue, options);

  return redirecttoaction("index");
}

webapi获取cookie

[route("getmycookie")]
public string getmycookie(string name)
{
  formsauthentication.setauthcookie(name, false);
  return formsauthentication.getauthcookie(name, false).value;
}

其余代码不用做任何更改,ok,我们来测试一下

详解ASP.NET Core和ASP.NET Framework共享身份验证

ok,登录成功,至此完成.net framework和.net core身份验证的兼容,哎,如果.net core 的团队能多考虑一些这方面的兼容问题,哪怕是一个折中方案也能让开发者更有动力去做迁移。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。