ASP.NET Core Authentication认证实现方法

2025-05-29 0 88

追本溯源,从使用开始  

  首先看一下我们通常是如何使用微软自带的认证,一般在startup里面配置我们所需的依赖认证服务,这里通过jwt的认证方式讲解

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16
public void configureservices(iservicecollection services)

{

services.addauthentication(authopt =>

{

authopt.defaultauthenticatescheme = jwtbearerdefaults.authenticationscheme;

authopt.defaultchallengescheme = jwtbearerdefaults.authenticationscheme;

})

.addjwtbearer(o =>

{

o.tokenvalidationparameters = new tokenvalidationparameters

{

//配置自己所要验证的参数

};

});

}

  我们来看一下源码addauthentication主要做了什么

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33
public static class authenticationservicecollectionextensions

{

public static authenticationbuilder addauthentication( this iservicecollection services, action<authenticationoptions> configureoptions)

{

if (services == null)

throw new argumentnullexception(nameof (services));

if (configureoptions == null)

throw new argumentnullexception(nameof (configureoptions));

authenticationbuilder authenticationbuilder = services.addauthentication();

services.configure<authenticationoptions>(configureoptions);

return authenticationbuilder;

}

public static authenticationbuilder addauthentication( this iservicecollection services)

{

if (services == null)

throw new argumentnullexception(nameof (services));

services.addauthenticationcore();

services.adddataprotection();

services.addwebencoders();

services.tryaddsingleton<isystemclock, systemclock>();

return new authenticationbuilder(services);

}

public static authenticationbuilder addauthentication(

this iservicecollection services,

string defaultscheme)

{

return services.addauthentication((action<authenticationoptions>) (o => o.defaultscheme = defaultscheme));

}

.....

}

  configureservices方法基本都是服务的注册,基于微软的风格,这里的addauthenticationcore肯定是我们的认证服务注册方法,来看一下

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33
public static class authenticationcoreservicecollectionextensions

{

/// <summary>

/// add core authentication services needed for <see cref="t:microsoft.aspnetcore.authentication.iauthenticationservice" />.

/// </summary>

public static iservicecollection addauthenticationcore(

this iservicecollection services)

{

if (services == null)

throw new argumentnullexception(nameof (services));

services.tryaddscoped<iauthenticationservice, authenticationservice>();

services.tryaddsingleton<iclaimstransformation, noopclaimstransformation>();

services.tryaddscoped<iauthenticationhandlerprovider, authenticationhandlerprovider>();

services.tryaddsingleton<iauthenticationschemeprovider, authenticationschemeprovider>();

return services;

}

/// <summary>

/// add core authentication services needed for <see cref="t:microsoft.aspnetcore.authentication.iauthenticationservice" />.

/// </summary>

public static iservicecollection addauthenticationcore(

this iservicecollection services,

action<authenticationoptions> configureoptions)

{

if (services == null)

throw new argumentnullexception(nameof (services));

if (configureoptions == null)

throw new argumentnullexception(nameof (configureoptions));

services.addauthenticationcore();

services.configure<authenticationoptions>(configureoptions);

return services;

}

}

  我们看到这里主要注册了authenticationservice, authenticationhandlerprovider, authenticationschemeprovider这三个对象,如文章开头所说,追本溯源,从使用开始,我们先看一下这三个对象是如何在认证体系中使用的,且是如何发挥作用的。

  从使用开始

  看一下我们的认证管道构建

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17
public void configure(iapplicationbuilder app, ihostingenvironment env, iloggerfactory loggerfactory)

{

...

app.useauthentication();

...

}

 public static class authappbuilderextensions

{

public static iapplicationbuilder useauthentication( this iapplicationbuilder app)

{

if (app == null)

throw new argumentnullexception(nameof (app));

return app.usemiddleware<authenticationmiddleware>();

}

}

  这里使用了约定的注册方式usemiddleware,并且指定使用中间件authenticationmiddleware  

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43
public class authenticationmiddleware

{

private readonly requestdelegate _next;

public authenticationmiddleware(requestdelegate next, iauthenticationschemeprovider schemes)

{

if (next == null)

throw new argumentnullexception(nameof (next));

if (schemes == null)

throw new argumentnullexception(nameof (schemes));

this._next = next;

this.schemes = schemes;

}

public iauthenticationschemeprovider schemes { get; set; }

public async task invoke(httpcontext context)

{

context.features.set<iauthenticationfeature>((iauthenticationfeature) new authenticationfeature()

{

originalpath = context.request.path,

originalpathbase = context.request.pathbase

});

iauthenticationhandlerprovider handlers = context.requestservices.getrequiredservice<iauthenticationhandlerprovider>();

foreach (authenticationscheme authenticationscheme in await this.schemes.getrequesthandlerschemesasync())

{

iauthenticationrequesthandler handlerasync = await handlers.gethandlerasync(context, authenticationscheme.name) as iauthenticationrequesthandler;

bool flag = handlerasync != null;

if (flag)

flag = await handlerasync.handlerequestasync();

if (flag)

return;

}

authenticationscheme authenticateschemeasync = await this.schemes.getdefaultauthenticateschemeasync();

if (authenticateschemeasync != null)

{

authenticateresult authenticateresult = await context.authenticateasync(authenticateschemeasync.name);  //实际的认证业务

if (authenticateresult?.principal != null)

context.user = authenticateresult.principal;

}

await this._next(context);

}

}

  在继续往下之前,我们先看一下这个认证中间件的作用结果,当认证通过时,在httpcontext的user属性(claimprincipal)赋予身份标识,所以在后续的请求管道中都是基于认证结果中的身份标识做鉴权,这个我们会在后面的实际操作中会提到。

  言归正传,在这里引出了我们的两个对象authenticationhandlerprovider,authenticationschemeprovider。

  重要对象讲解

  iauthenticationschemeprovider

  从名字来看,iauthenticationschemeprovider的作用应该是提供scheme的,这也是provider在微软的风格里面起的作用(类似于工厂模式)。

  这个scheme是什么呢?很明显,在framework时代,也是有基于不同scheme验证的,比如bearer,cookie,在aspnet core中定义不同的scheme代表着不同的认证处理方式,具体体现是在每个scheme中包含对应的iauthenticationhandler类型的handler,由它来完成跟自身scheme相关的认证处理。如果没有定义会怎么样?仔细看上面这块源码,只有当authenticationscheme不为空时才会做认证,否则一旦在controller打上鉴权标签[authorize],将会直接返回401,所以我们必须指定自己的scheme。

  那么我们在哪里指定我们的scheme类似呢?我们先返回到configureservice的addjwtbearer,使用过的朋友们肯定知道,这里获取的scheme是我们在configureservice通过addxxx scheme指定的scheme类型。这里我们是使用jwt的

ASP.NET Core Authentication认证实现方法

  在这里指定了toptions 为jwtbeareroptions,而thandler为jwtbearerhandler。

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28
public virtual authenticationbuilder addscheme<toptions, thandler>(

string authenticationscheme,

string displayname,

action<toptions> configureoptions)

where toptions : authenticationschemeoptions, new()

where thandler : authenticationhandler<toptions>

{

return this.addschemehelper<toptions, thandler>(authenticationscheme, displayname, configureoptions);

}

private authenticationbuilder addschemehelper<toptions, thandler>(

string authenticationscheme,

string displayname,

action<toptions> configureoptions)

where toptions : class, new()

where thandler : class, iauthenticationhandler

{

this.services.configure<authenticationoptions>((action<authenticationoptions>) (o => o.addscheme(authenticationscheme, (action<authenticationschemebuilder>) (scheme =>

{

scheme.handlertype = typeof (thandler);

scheme.displayname = displayname;

}))));

if (configureoptions != null)

this.services.configure<toptions>(authenticationscheme, configureoptions);

this.services.addtransient<thandler>();

return this;

}

  注意这里toptions 是需要继承authenticationschemeoptions的,在这里是jwtbeareroptions,而thandler是authenticationhandler<toptions>类型的handler,在这里是jwtbearerhandler。

ASP.NET Core Authentication认证实现方法

  我们回到scheme的分析继续往下,首先看一下authenticationscheme的定义  

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29
public class authenticationscheme

{

/// <summary>constructor.</summary>

public authenticationscheme(string name, string displayname, type handlertype)

{

if (name == null)

throw new argumentnullexception(nameof (name));

if (handlertype == (type) null)

throw new argumentnullexception(nameof (handlertype));

if (!typeof (iauthenticationhandler).isassignablefrom(handlertype))

throw new argumentexception("handlertype must implement iauthenticationhandler.");

this.name = name;

this.handlertype = handlertype;

this.displayname = displayname;

}

/// <summary>the name of the authentication scheme.</summary>

public string name { get; }

/// <summary>

/// the display name for the scheme. null is valid and used for non user facing schemes.

/// </summary>

public string displayname { get; }

/// <summary>

/// the <see cref="t:microsoft.aspnetcore.authentication.iauthenticationhandler" /> type that handles this scheme.

/// </summary>

public type handlertype { get; }

}

  在这里可以看到,如果要使用aspnet core自身的认证体系,需先注册scheme,并且该scheme必须指定一个类型为iauthenticationhandler的handler,否则会抛出异常。(这个其实在addxxxscheme的时候已经指定了authenticationhandler)

  我们再看一下iauthenticationschemeprovider的getrequesthandlerschemesasync方法做了什么

?

1

2

3

4
public virtual task<ienumerable<authenticationscheme>> getrequesthandlerschemesasync()

{

return task.fromresult<ienumerable<authenticationscheme>>((ienumerable<authenticationscheme>) this._requesthandlers);

}

  这东西返回了_requesthandlers,这是什么?看代码

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49
public class authenticationschemeprovider : iauthenticationschemeprovider

{

private readonly object _lock = new object();

private readonly authenticationoptions _options;

private readonly idictionary<string, authenticationscheme> _schemes;

private readonly list<authenticationscheme> _requesthandlers;

/// <summary>

/// creates an instance of <see cref="t:microsoft.aspnetcore.authentication.authenticationschemeprovider" />

/// using the specified <paramref name="options" />,

/// </summary>

public authenticationschemeprovider(ioptions<authenticationoptions> options)

: this(options, (idictionary<string, authenticationscheme>) new dictionary<string, authenticationscheme>((iequalitycomparer<string>) stringcomparer.ordinal))

{

}

/// <summary>

/// creates an instance of <see cref="t:microsoft.aspnetcore.authentication.authenticationschemeprovider" />

/// using the specified <paramref name="options" /> and <paramref name="schemes" />.

/// </summary>

protected authenticationschemeprovider(

ioptions<authenticationoptions> options,

idictionary<string, authenticationscheme> schemes)

{

this._options = options.value;

idictionary<string, authenticationscheme> dictionary = schemes;

if (dictionary == null)

throw new argumentnullexception(nameof (schemes));

this._schemes = dictionary;

this._requesthandlers = new list<authenticationscheme>();

foreach (authenticationschemebuilder scheme in this._options.schemes)

this.addscheme(scheme.build());

}

  public virtual void addscheme(authenticationscheme scheme)

{

if (this._schemes.containskey(scheme.name))

throw new invalidoperationexception("scheme already exists: " + scheme.name);

lock (this._lock)

{

if (this._schemes.containskey(scheme.name))

throw new invalidoperationexception("scheme already exists: " + scheme.name);

if (typeof (iauthenticationrequesthandler).isassignablefrom(scheme.handlertype))

this._requesthandlers.add(scheme);

this._schemes[scheme.name] = scheme;

}

}

.....

}

  这东西就是把我们在认证注册服务中指定的scheme,通过解析出的authenticationschemeprovider 的构造函数加载来的,进而返回一系列的list<authenticationscheme>,ok拿到这些scheme之后有什么用呢?这里引出了我们的第二个对象authenticationhandlerprovider,下面我们来了解一下。  

  iauthenticationhandlerprovider

  我们看到,authenticationmiddleware中用到了iauthenticationhandlerprovider的gethandlerasync方法,那我们先看一下这个方法的作用

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32
public class authenticationhandlerprovider : iauthenticationhandlerprovider

{

private dictionary<string, iauthenticationhandler> _handlermap = new dictionary<string, iauthenticationhandler>((iequalitycomparer<string>) stringcomparer.ordinal);

/// <summary>constructor.</summary>

public authenticationhandlerprovider(iauthenticationschemeprovider schemes)

{

this.schemes = schemes;

}

/// <summary>

/// the <see cref="t:microsoft.aspnetcore.authentication.iauthenticationhandlerprovider" />.

/// </summary>

public iauthenticationschemeprovider schemes { get; }

/// <summary>returns the handler instance that will be used.</summary>

public async task<iauthenticationhandler> gethandlerasync( httpcontext context, string authenticationscheme)

{

if (this._handlermap.containskey(authenticationscheme))

return this._handlermap[authenticationscheme];

authenticationscheme schemeasync = await this.schemes.getschemeasync(authenticationscheme);

if (schemeasync == null)

return (iauthenticationhandler) null;

iauthenticationhandler handler = (context.requestservices.getservice(schemeasync.handlertype) ?? activatorutilities.createinstance(context.requestservices, schemeasync.handlertype)) as iauthenticationhandler;

if (handler != null)

{

await handler.initializeasync(schemeasync, context);

this._handlermap[authenticationscheme] = handler;

}

return handler;

}

}

  在创建handler的时候,是先从authenticationscheme中获取,如果不存在则通过activatorutilities创建。 获取到handle后,将会放在_handlermap字典里面,当下次获取handler的时候,将直接从缓存中获取。

  iauthenticationservice

  这个对象是在authenticationmiddleware中最后才用到的,而且是基于httpcontext的扩展被调用

?

1

2

3

4

5

6

7
public static class authenticationhttpcontextextensions

{

public static task<authenticateresult> authenticateasync(this httpcontext context, string scheme) =>

context.requestservices.getrequiredservice<iauthenticationservice>().authenticateasync(context, scheme);

....

}

  这里主要调用了iauthenticationservice的authenticateasync方法,看一下这个方法做了什么

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25
public class authenticationservice : iauthenticationservice

{

public iauthenticationschemeprovider schemes { get; }

public iauthenticationhandlerprovider handlers { get; }

public iclaimstransformation transform { get; }

public virtual async task<authenticateresult> authenticateasync(httpcontext context, string scheme)

{

if (scheme == null)

{

var scheme = (await this.schemes.getdefaultauthenticateschemeasync())?.name;

if (scheme == null)

throw new invalidoperationexception($"no authenticationscheme was specified, and there was no defaultauthenticatescheme found.");

}

var handler = await handlers.gethandlerasync(context, scheme);

if(handler == null)

throw await this.createmissinghandlerexception(scheme);

authenticateresult result = await handler.authenticateasync();

if (result != null && result.succeeded)

return authenticateresult.success(new authenticationticket(await transform.transformasync(result.principal), result.properties, result.ticket.authenticationscheme));

return result;

}

}

  这里其实就是我们在前面讲的根据scheme获取对应的authenticationhandler,然后调用authenticateasync()方法,这个方法调用了核心方法handleauthenticateonceasync,然后再调用handleauthenticateasync()这个核心的认证方法。

ASP.NET Core Authentication认证实现方法

  从上图看到这个handleauthenticateasync是个抽象方法,我们的子类都需要实现这个方法的动作,基于本文的例子,我们看一下jwtbearerhandler的一个实际认证。  

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126
public class jwtbearerhandler : authenticationhandler<jwtbeareroptions>

{

protected override async task<authenticateresult> handleauthenticateasync()

{

jwtbearerhandler jwtbearerhandler = this;

string token = (string) null;

object obj;

authenticationfailedcontext authenticationfailedcontext;

int num;

try

{

messagereceivedcontext messagereceivedcontext = new messagereceivedcontext(jwtbearerhandler.context, jwtbearerhandler.scheme, jwtbearerhandler.options);

await jwtbearerhandler.events.messagereceived(messagereceivedcontext);

if (messagereceivedcontext.result != null)

return messagereceivedcontext.result;

token = messagereceivedcontext.token;

if (string.isnullorempty(token))

{

string header = (string) jwtbearerhandler.request.headers["authorization"];

if (string.isnullorempty(header))

return authenticateresult.noresult();

if (header.startswith("bearer ", stringcomparison.ordinalignorecase))

token = header.substring("bearer ".length).trim();

if (string.isnullorempty(token))

return authenticateresult.noresult();

}

if (jwtbearerhandler._configuration == null && jwtbearerhandler.options.configurationmanager != null)

{

openidconnectconfiguration configurationasync = await jwtbearerhandler.options.configurationmanager.getconfigurationasync(jwtbearerhandler.context.requestaborted);

jwtbearerhandler._configuration = configurationasync;

}

tokenvalidationparameters validationparameters1 = jwtbearerhandler.options.tokenvalidationparameters.clone();

if (jwtbearerhandler._configuration != null)

{

string[] strarray = new string[1]

{

jwtbearerhandler._configuration.issuer

};

tokenvalidationparameters validationparameters2 = validationparameters1;

ienumerable<string> validissuers = validationparameters1.get_validissuers();

object obj1 = (validissuers != null ? (object) validissuers.concat<string>((ienumerable<string>) strarray) : (object) null) ?? (object) strarray;

validationparameters2.set_validissuers((ienumerable<string>) obj1);

tokenvalidationparameters validationparameters3 = validationparameters1;

ienumerable<securitykey> issuersigningkeys = validationparameters1.get_issuersigningkeys();

ienumerable<securitykey> securitykeys = (issuersigningkeys != null ? issuersigningkeys.concat<securitykey>((ienumerable<securitykey>) jwtbearerhandler._configuration.get_signingkeys()) : (ienumerable<securitykey>) null) ?? (ienumerable<securitykey>) jwtbearerhandler._configuration.get_signingkeys();

validationparameters3.set_issuersigningkeys(securitykeys);

}

list<exception> exceptionlist = (list<exception>) null;

foreach (isecuritytokenvalidator securitytokenvalidator in (ienumerable<isecuritytokenvalidator>) jwtbearerhandler.options.securitytokenvalidators)

{

if (securitytokenvalidator.canreadtoken(token))

{

securitytoken securitytoken;

claimsprincipal claimsprincipal;

try

{

claimsprincipal = securitytokenvalidator.validatetoken(token, validationparameters1, ref securitytoken);

}

catch (exception ex)

{

jwtbearerhandler.logger.tokenvalidationfailed(ex);

if (jwtbearerhandler.options.refreshonissuerkeynotfound && jwtbearerhandler.options.configurationmanager != null && ex is securitytokensignaturekeynotfoundexception)

jwtbearerhandler.options.configurationmanager.requestrefresh();

if (exceptionlist == null)

exceptionlist = new list<exception>(1);

exceptionlist.add(ex);

continue;

}

jwtbearerhandler.logger.tokenvalidationsucceeded();

tokenvalidatedcontext validatedcontext = new tokenvalidatedcontext(jwtbearerhandler.context, jwtbearerhandler.scheme, jwtbearerhandler.options);

validatedcontext.principal = claimsprincipal;

validatedcontext.securitytoken = securitytoken;

tokenvalidatedcontext tokenvalidatedcontext = validatedcontext;

await jwtbearerhandler.events.tokenvalidated(tokenvalidatedcontext);

if (tokenvalidatedcontext.result != null)

return tokenvalidatedcontext.result;

if (jwtbearerhandler.options.savetoken)

tokenvalidatedcontext.properties.storetokens((ienumerable<authenticationtoken>) new authenticationtoken[1]

{

new authenticationtoken()

{

name = "access_token",

value = token

}

});

tokenvalidatedcontext.success();

return tokenvalidatedcontext.result;

}

}

if (exceptionlist == null)

return authenticateresult.fail("no securitytokenvalidator available for token: " + token ?? "[null]");

authenticationfailedcontext = new authenticationfailedcontext(jwtbearerhandler.context, jwtbearerhandler.scheme, jwtbearerhandler.options)

{

exception = exceptionlist.count == 1 ? exceptionlist[0] : (exception) new aggregateexception((ienumerable<exception>) exceptionlist)

};

await jwtbearerhandler.events.authenticationfailed(authenticationfailedcontext);

return authenticationfailedcontext.result == null ? authenticateresult.fail(authenticationfailedcontext.exception) : authenticationfailedcontext.result;

}

catch (exception ex)

{

obj = (object) ex;

num = 1;

}

if (num == 1)

{

exception ex = (exception) obj;

jwtbearerhandler.logger.errorprocessingmessage(ex);

authenticationfailedcontext = new authenticationfailedcontext(jwtbearerhandler.context, jwtbearerhandler.scheme, jwtbearerhandler.options)

{

exception = ex

};

await jwtbearerhandler.events.authenticationfailed(authenticationfailedcontext);

if (authenticationfailedcontext.result != null)

return authenticationfailedcontext.result;

exception source = obj as exception;

if (source == null)

throw obj;

exceptiondispatchinfo.capture(source).throw();

authenticationfailedcontext = (authenticationfailedcontext) null;

}

obj = (object) null;

token = (string) null;

authenticateresult authenticateresult;

return authenticateresult;

}

}

  这个方法有点长,主要是从request.headers里面获取authorization的bearer出来解析,再在addjwtbearer中传入的委托参数jwtbeareroptions的tokenvalidationparameters属性作为依据进行对比来进行认证是否通过与否。

  总结

  本文对 asp.net core认证流程做了一个源码分析流程介绍,由于是源码分析篇,所以可能会比较枯燥和苦涩难懂。在后面的真正使用过程中,然后再结合本篇的一个总结流程,相信大家会逐渐开朗。

到此这篇关于asp.net core authentication认证实现方法的文章就介绍到这了,更多相关asp.net core authentication认证内容请搜索快网idc以前的文章或继续浏览下面的相关文章希望大家以后多多支持快网idc!

原文链接:https://www.cnblogs.com/lex-wu/p/10512424.html

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。

快网idc优惠网 建站教程 ASP.NET Core Authentication认证实现方法 https://www.kuaiidc.com/97668.html

相关文章

发表评论
暂无评论