ASP.Net Core Web及API身份认证的解决方案

一、Asp.Net Core Web项目的登录认证

    在MVC Web项目中,做用户登录授权,是必不可少的工作,不知道大家平时是怎么做的,我想,大多朋友还是使用微软提供的一套认证机制,可以省去很多功夫。从WebForm时代的Form身份认证,无非是通过客户端Cookie中存储认证票据,在请求受保护的资源时,通过Cookie中携带的身份票据,再有Asp.net的认证模块,完整对请求者的身份认证。这一过程,是很清晰简单的了。在MVC中,大多是通过中间件(MiddleWare)来完整认证授权过程。在ASP.NETMVC中,我们了解到基于声明的授权认证(Claim),这种认证方式,好处在于,我们想在用户授权时,存储多个属性信息,只需要添加多个声明即可,我们在微软的认证中间件中,看到的都是定义好的常量,当然,我们可以定义自己的ClaimTypes。然我们看看微软在.NetCore中定义的一些声明吧:

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

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

//

       // Summary:

       //     http://schemas.xmlsoap.org/ws/2009/09/identity/claims/actor.

       public const string Actor = "http://schemas.xmlsoap.org/ws/2009/09/identity/claims/actor";

       //

       // Summary:

       //     The URI for a claim that specifies the postal code of an entity, http://schemas.xmlsoap.org/ws/2005/05/identity/claims/postalcode.

       public const string PostalCode = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/postalcode";

       //

       // Summary:

       //     The URI for a claim that specifies the primary group SID of an entity, http://schemas.microsoft.com/ws/2008/06/identity/claims/primarygroupsid.

       public const string PrimaryGroupSid = "http://schemas.microsoft.com/ws/2008/06/identity/claims/primarygroupsid";

       //

       // Summary:

       //     The URI for a claim that specifies the primary SID of an entity, http://schemas.microsoft.com/ws/2008/06/identity/claims/primarysid.

       public const string PrimarySid = "http://schemas.microsoft.com/ws/2008/06/identity/claims/primarysid";

       //

       // Summary:

       //     The URI for a claim that specifies the role of an entity, http://schemas.microsoft.com/ws/2008/06/identity/claims/role.

       public const string Role = "http://schemas.microsoft.com/ws/2008/06/identity/claims/role";

       //

       // Summary:

       //     The URI for a claim that specifies an RSA key, http://schemas.xmlsoap.org/ws/2005/05/identity/claims/rsa.

       public const string Rsa = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/rsa";

       //

       // Summary:

       //     The URI for a claim that specifies a serial number, http://schemas.microsoft.com/ws/2008/06/identity/claims/serialnumber.

       public const string SerialNumber = "http://schemas.microsoft.com/ws/2008/06/identity/claims/serialnumber";

       //

       // Summary:

       //     The URI for a claim that specifies a security identifier (SID), http://schemas.xmlsoap.org/ws/2005/05/identity/claims/sid.

       public const string Sid = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/sid";

       //

       // Summary:

       //     The URI for a claim that specifies a service principal name (SPN) claim, http://schemas.xmlsoap.org/ws/2005/05/identity/claims/spn.

       public const string Spn = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/spn";

       //

       // Summary:

       //     The URI for a claim that specifies the state or province in which an entity resides,

       //     http://schemas.xmlsoap.org/ws/2005/05/identity/claims/stateorprovince.

       public const string StateOrProvince = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/stateorprovince";

       //

       // Summary:

       //     The URI for a claim that specifies the street address of an entity, http://schemas.xmlsoap.org/ws/2005/05/identity/claims/streetaddress.

       public const string StreetAddress = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/streetaddress";

       //

       // Summary:

       //     The URI for a claim that specifies the surname of an entity, http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname.

       public const string Surname = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname";

       //

       // Summary:

       //     The URI for a claim that identifies the system entity, http://schemas.xmlsoap.org/ws/2005/05/identity/claims/system.

       public const string System = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/system";

       //

       // Summary:

       //     The URI for a claim that specifies a thumbprint, http://schemas.xmlsoap.org/ws/2005/05/identity/claims/thumbprint.

       //     A thumbprint is a globally unique SHA-1 hash of an X.509 certificate.

       public const string Thumbprint = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/thumbprint";

       //

       // Summary:

       //     The URI for a claim that specifies a user principal name (UPN), http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn.

       public const string Upn = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn";

       //

       // Summary:

       //     The URI for a claim that specifies a URI, http://schemas.xmlsoap.org/ws/2005/05/identity/claims/uri.

       public const string Uri = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/uri";

       //

       // Summary:

       //     http://schemas.microsoft.com/ws/2008/06/identity/claims/userdata.

       public const string UserData = "http://schemas.microsoft.com/ws/2008/06/identity/claims/userdata";

       //

       // Summary:

       //     http://schemas.microsoft.com/ws/2008/06/identity/claims/version.

       public const string Version = "http://schemas.microsoft.com/ws/2008/06/identity/claims/version";

       //

       // Summary:

       //     The URI for a claim that specifies the webpage of an entity, http://schemas.xmlsoap.org/ws/2005/05/identity/claims/webpage.

       public const string Webpage = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/webpage";

       //

       // Summary:

       //     The URI for a claim that specifies the Windows domain account name of an entity,

       //     http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname.

       public const string WindowsAccountName = "http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname";

       //

       // Summary:

       //     http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsdeviceclaim.

       public const string WindowsDeviceClaim = "http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsdeviceclaim";

       //

       // Summary:

       //     http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsdevicegroup.

       public const string WindowsDeviceGroup = "http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsdevicegroup";

       //

       // Summary:

       //     http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsfqbnversion.

       public const string WindowsFqbnVersion = "http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsfqbnversion";

       //

       // Summary:

       //     http://schemas.microsoft.com/ws/2008/06/identity/claims/windowssubauthority.

       public const string WindowsSubAuthority = "http://schemas.microsoft.com/ws/2008/06/identity/claims/windowssubauthority";

       //

       // Summary:

       //     The URI for a claim that specifies the alternative phone number of an entity,

       //     http://schemas.xmlsoap.org/ws/2005/05/identity/claims/otherphone.

       public const string OtherPhone = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/otherphone";

       //

       // Summary:

       //     The URI for a claim that specifies the name of an entity, http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier.

       public const string NameIdentifier = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier";

       //

       // Summary:

       //     The URI for a claim that specifies the name of an entity, http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name.

       public const string Name = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name";

       //

       // Summary:

       //     The URI for a claim that specifies the mobile phone number of an entity, http://schemas.xmlsoap.org/ws/2005/05/identity/claims/mobilephone.

       public const string MobilePhone = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/mobilephone";

       //

       // Summary:

       //     The URI for a claim that specifies the anonymous user; http://schemas.xmlsoap.org/ws/2005/05/identity/claims/anonymous.

       public const string Anonymous = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/anonymous";

       //

       // Summary:

       //     The URI for a claim that specifies details about whether an identity is authenticated,

       //     http://schemas.xmlsoap.org/ws/2005/05/identity/claims/authenticated.

       public const string Authentication = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/authentication";

       //

       // Summary:

       //     The URI for a claim that specifies the instant at which an entity was authenticated;

       //     http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationinstant.

       public const string AuthenticationInstant = "http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationinstant";

       //

       // Summary:

       //     The URI for a claim that specifies the method with which an entity was authenticated;

       //     http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod.

       public const string AuthenticationMethod = "http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod";

       //

       // Summary:

       //     The URI for a claim that specifies an authorization decision on an entity; http://schemas.xmlsoap.org/ws/2005/05/identity/claims/authorizationdecision.

       public const string AuthorizationDecision = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/authorizationdecision";

       //

       // Summary:

       //     The URI for a claim that specifies the cookie path; http://schemas.microsoft.com/ws/2008/06/identity/claims/cookiepath.

       public const string CookiePath = "http://schemas.microsoft.com/ws/2008/06/identity/claims/cookiepath";

       //

       // Summary:

       //     The URI for a claim that specifies the country/region in which an entity resides,

       //     http://schemas.xmlsoap.org/ws/2005/05/identity/claims/country.

       public const string Country = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/country";

       //

       // Summary:

       //     The URI for a claim that specifies the date of birth of an entity, http://schemas.xmlsoap.org/ws/2005/05/identity/claims/dateofbirth.

       public const string DateOfBirth = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/dateofbirth";

       //

       // Summary:

       //     The URI for a claim that specifies the deny-only primary group SID on an entity;

       //     http://schemas.microsoft.com/ws/2008/06/identity/claims/denyonlyprimarygroupsid.

       //     A deny-only SID denies the specified entity to a securable object.

       public const string DenyOnlyPrimaryGroupSid = "http://schemas.microsoft.com/ws/2008/06/identity/claims/denyonlyprimarygroupsid";

       //

       // Summary:

       //     The URI for a claim that specifies the deny-only primary SID on an entity; http://schemas.microsoft.com/ws/2008/06/identity/claims/denyonlyprimarysid.

       //     A deny-only SID denies the specified entity to a securable object.

       public const string DenyOnlyPrimarySid = "http://schemas.microsoft.com/ws/2008/06/identity/claims/denyonlyprimarysid";

       //

       // Summary:

       //     The URI for a claim that specifies a deny-only security identifier (SID) for

       //     an entity, http://schemas.xmlsoap.org/ws/2005/05/identity/claims/denyonlysid.

       //     A deny-only SID denies the specified entity to a securable object.

       public const string DenyOnlySid = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/denyonlysid";

       //

       // Summary:

       //     http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsuserclaim.

       public const string WindowsUserClaim = "http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsuserclaim";

       //

       // Summary:

       //     http://schemas.microsoft.com/ws/2008/06/identity/claims/denyonlywindowsdevicegroup.

       public const string DenyOnlyWindowsDeviceGroup = "http://schemas.microsoft.com/ws/2008/06/identity/claims/denyonlywindowsdevicegroup";

       //

       // Summary:

       //     http://schemas.microsoft.com/ws/2008/06/identity/claims/dsa.

       public const string Dsa = "http://schemas.microsoft.com/ws/2008/06/identity/claims/dsa";

       //

       // Summary:

       //     The URI for a claim that specifies the email address of an entity, http://schemas.xmlsoap.org/ws/2005/05/identity/claims/email.

       public const string Email = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress";

    那么我们在Asp.Net Core项目中的认证,也是比较简单的。也是通过HttpContext的扩展方法SignInAsync,来传入声明的身份信息。要使用的微软的认证组件,我们在.Net Core Web项目中,做如下改动:

首先,在Start.cs类中,添加服务,具体代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

/// <summary>

///

/// </summary>

/// <param name="services"></param>

public void ConfigureServices(IServiceCollection services)

{

    services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)

    .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, o =>

    {

        o.Cookie.Name = "_AdminTicketCookie";

        o.LoginPath = new PathString("/Account/Login");

        o.LogoutPath = new PathString("/Account/Login");

        o.AccessDeniedPath = new PathString("/Error/Forbidden");

    });

    services.AddTransient<TiKu.Application.Interfaces.IAdminService, TiKu.Application.AdminService>();

    services.AddMvc();

}

    其次,添加认证中间件

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

/// <summary>

/// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.

/// </summary>

/// <param name="app"></param>

/// <param name="env"></param>

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)

{

 

    if (env.IsDevelopment())

    {

        app.UseDeveloperExceptionPage();

    }

    else

    {

        app.UseExceptionHandler("/Home/Error");

    }

    app.UseStaticFiles();

    app.UseAuthentication();//添加认证中间件

    app.UseMvc(routes =>

    {

        routes.MapRoute(

            name: "default",

            template: "{controller=Home}/{action=Index}/{id?}");

    });

     

}

  最后,在用户登录的地方,登录成功后,调用HttpContext的SignIn方法,将授权信息写入Cookie,示例代码如下:

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

/// <summary>

/// <![CDATA[登陆]]>

/// </summary>

/// <param name="model"></param>

/// <returns></returns>

[HttpPost]

[ValidateAntiForgeryToken]

public async Task<ActionResult> Login(Models.LoginViewModel model)

{

    try

    {

        //模型验证通过后

        if (ModelState.IsValid)

        {

            model.password = TiKu.Common.Security.MD5.Md5(model.password);//MD5加密

            TiKu.Domain.Entity.tb_Admin admin = await _AdminService.CheckAccountAndPassword(account: model.account, password: model.password);

            //验证用户名密码

            if (admin != null)

            {

                var identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme);//一定要声明AuthenticationScheme

                identity.AddClaim(new Claim(ClaimTypes.Name, admin.Account));

                identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, admin.Id.ToString()));

 

                await HttpContext.SignInAsync(identity.AuthenticationType,

                                              new ClaimsPrincipal(identity),

                                              new AuthenticationProperties

                                              {

                                                  IsPersistent = model.isPersistent,

                                                  RedirectUri = "/Home/Index",

                                                  ExpiresUtc = new System.DateTimeOffset(dateTime: DateTime.Now.AddHours(6)),

                                              });

                //更新登陆时间

                await _AdminService.UpdateLastLoginTime(id: admin.Id);

            }

            else

            {

                await HttpContext.ChallengeAsync(CookieAuthenticationDefaults.AuthenticationScheme);

                ModelState.AddModelError("""用户名或密码错误!");

            }

        }

    }

    catch (Exception ex)

    {

        ModelState.AddModelError("""用户名或密码错误!");

        _Logger.Error("用户登录时发生错误!", ex);

    }

    return View(model);

}

  这样就完成了Asp.net core web项目的登录认证工作。

二、Asp.Net Core WebApi基于JWT的认证授权

      关于JWT的工作原理,大家可以自行了解(https://jwt.io/)。JWT实现了服务端无状态,在分布式服务,会话一致性,单点登录等方面,凸显优势,不占用服务端资源。使用JWT需要注意的是,令牌过期后刷新,以及更改密码后令牌未过期的处理问题。

这里,我以JWT作为.net core webapi项目的认证方式。

首先,我再Api项目中新建了一个名为OAuthController的控制器,定义一个Action名为Token的方法,用来让客户端获取令牌之用,具体代码如下:

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

/// <summary>

/// <![CDATA[获取访问令牌]]>

/// </summary>

/// <param name="user"></param>

/// <param name="password"></param>

/// <returns></returns>

[HttpPost]

public async Task<TiKu.Domain.ValueObject.RestfulData<TiKu.Domain.ValueObject.AccessTokenObj>> Token(string user, string password)

{

    var result = new TiKu.Domain.ValueObject.RestfulData<TiKu.Domain.ValueObject.AccessTokenObj>();

    try

    {

        if (string.IsNullOrEmpty(user)) throw new ArgumentNullException("user""用户名不能为空!");

        if (string.IsNullOrEmpty(password)) throw new ArgumentNullException("password""密码不能为空!");

 

        //验证用户名和密码

        var userInfo = await _UserService.CheckUserAndPassword(mobile: user, password: password);

        var claims = new Claim[]

        {

            new Claim(ClaimTypes.Name,user),

            new Claim(ClaimTypes.NameIdentifier,userInfo.Id.ToString()),

        };

 

        var key = new SymmetricSecurityKey(System.Text.Encoding.UTF8.GetBytes(Configuration["JwtSecurityKey"]));

        var expires = DateTime.Now.AddDays(28);//

        var token = new JwtSecurityToken(

                    issuer: Configuration["issuer"],

                    audience: Configuration["audience"],

                    claims: claims,

                    notBefore: DateTime.Now,

                    expires: expires,

                    signingCredentials: new SigningCredentials(key, SecurityAlgorithms.HmacSha256));

 

        //生成Token

        string jwtToken = new JwtSecurityTokenHandler().WriteToken(token);

        result.code = 1;

        result.data = new Domain.ValueObject.AccessTokenObj() { AccessToken = jwtToken, Expires = TiKu.Common.Utility.Util.ToUnixTime(expires) };

        result.message = "授权成功!";

        return result;

    }

    catch (Exception ex)

    {

        result.message = ex.Message;

        result.code = 0;

        logger.Error("获取访问令牌时发生错误!", ex);

        return result;

    }

}

  这里,我定义了一个统一返回数据格式的模型-RestfulData,其中有不返回数据data的RestfulData和带data数据的RestfulData<T>,以及返回集合类型的RestfulArray<T>,具体代码如下:

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

/// <summary>

  ///

  /// </summary>

  public class RestfulData

  {

      /// <summary>

      /// <![CDATA[错误码]]>

      /// </summary>

      public int code { getset; }

 

      /// <summary>

      ///<![CDATA[消息]]>

      /// </summary>

      public string message { getset; }

 

      /// <summary>

      /// <![CDATA[相关的链接帮助地址]]>

      /// </summary>

      public string url { getset; }

 

  }

 

  /// <summary>

  ///

  /// </summary>

  /// <typeparam name="T"></typeparam>

  public class RestfulData<T> : RestfulData

  {

      /// <summary>

      /// <![CDATA[数据]]>

      /// </summary>

      public virtual T data { getset; }

  }

 

  /// <summary>

  /// <![CDATA[返回数组]]>

  /// </summary>

  /// <typeparam name="T"></typeparam>

  public class RestfulArray<T> : ResultData<IEnumerable<T>>

  {

 

  }

  

配置JWT认证服务,在Start.cs启动类中,配置如下:

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

/// <summary>

        ///

        /// </summary>

        /// <param name="services"></param>

        public IServiceProvider ConfigureServices(IServiceCollection services)

        {

            services.AddSingleton<IConfiguration>(Configuration);

            services.AddMemoryCache();//添加基于内存的缓存支持

            services.AddAutofac();

 

            //配置授权

            services.AddAuthentication(options =>

            {

                options.DefaultAuthenticateScheme = "JwtBearer";

                options.DefaultChallengeScheme = "JwtBearer";

 

            }).AddJwtBearer("JwtBearer",

            (jwtBearerOptions) =>

            {

                jwtBearerOptions.TokenValidationParameters = new TokenValidationParameters

                {

                    ValidateIssuerSigningKey = true,

                    IssuerSigningKey = new SymmetricSecurityKey(System.Text.Encoding.UTF8.GetBytes(Configuration["JwtSecurityKey"])),//秘钥

                    ValidateIssuer = true,

                    ValidIssuer = Configuration["issuer"],

                    ValidateAudience = true,

                    ValidAudience = Configuration["audience"],

                    ValidateLifetime = true,

                    ClockSkew = TimeSpan.FromMinutes(5)

                };

            });

 

            services.AddMvc();

 

            //IOC Autofac

            var builder = new ContainerBuilder();

            builder.Populate(services);

 

            //注册应用服务

            var assemblyApplicationService = System.Reflection.Assembly.Load("TiKu.Application");

            builder.RegisterAssemblyTypes(assemblyApplicationService).AsImplementedInterfaces();

 

            var container = builder.Build();

            Container = container;

 

            return new AutofacServiceProvider(container);

        }

  上面使用了IOC容器Autofac。

其次,配置认证中间件:

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

/// <summary>

///

/// </summary>

/// <param name="app"></param>

/// <param name="env"></param>

public void Configure(IApplicationBuilder app, IHostingEnvironment env)

{

    if (env.IsDevelopment())

    {

        app.UseDeveloperExceptionPage();

    }

    app.UseAuthentication();//配置授权

    //处理异常

    app.UseStatusCodePages(new StatusCodePagesOptions()

    {

        HandleAsync = (context) =>

        {

            if (context.HttpContext.Response.StatusCode == 401)

            {

                using (System.IO.StreamWriter sw = new System.IO.StreamWriter(context.HttpContext.Response.Body))

                {

                    sw.Write(Newtonsoft.Json.JsonConvert.SerializeObject(new

                    {

                        status = 401,

                        message = "access denied!",

                    }));

                }

            }

            return System.Threading.Tasks.Task.Delay(0);

        }

    });

    app.UseMvc(routes =>

    {

        routes.MapRoute(name: "default", template: "api/{controller=Home}/{action=Index}/{id?}");

        routes.MapRoute(name: "mvc", template: "{controller=Home}/{action=Index}/{id?}");

    });

}  

为了测试,我们给ValuesController控制器添加Authorize特性。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

/// <summary>

 ///

 /// </summary>

 [Authorize]

 [Route("api/[controller]")]

 public class ValuesController : Controller

 {

 

     // GET api/values

     [HttpGet]

     public IEnumerable<string> Get()

     {

         return new string[] { "value1""value2" };

     }

}<em id="__mceDel" style="background-color: #ffffff; font-family: "PingFang SC", "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 14px"> </em>

最后,让我们测试下API的授权,这里,我以Ajax模拟API的调用:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

script type="text/javascript">

    //获取令牌

    $.post("/oauth/token", $.param({ user: "lichaoqiang", password: "fdsfds" })).done(function (data) {

 

        if (data.code === 1) {

            localStorage.setItem("token", data.data.accessToken);

        }

    });

 

    //设置HTTP头

    $.ajaxSetup({

        beforeSend: function (xhr) {

            if (localStorage.getItem("token") !== null) {

                xhr.setRequestHeader('Authorization''Bearer ' + localStorage.getItem("token"));

            }

        }

    });

 

    $.getJSON("/api/values"function (data) { console.log(data); });//获取受保护的资源

</script>

  看下效果,直接访问/api/values,会出现如下图:

ASP.Net Core Web及API身份认证的解决方案

    当客户请求受保护的资源时,通过HTTP header携带上token。这里需要注意的是,请求头必须是Authorization,值是Bearer空格加上token。这样访问资源时,通过HTTP header携带令牌信息,服务端,通过认证中间件,完成授权认证过程。在上面的示例中,通过向全局Ajax注册事件,将token写入请求Header。、

ASP.Net Core Web及API身份认证的解决方案

至此,就完成了JWT认证授权的过程,.Net Core WebAPI配置起来也很简单。

转自:https://www.cnblogs.com/ibeisha/p/jwt-webapi.html