簡體   English   中英

.Net Core Web Api-無法正確授權,獲得401

[英].Net Core Web Api - unable to Authorize properly, getting 401

我正在開發一個使用JWT進行身份驗證和授權的.NET Core示例應用程序。 但是在獲取AccessToken和RefereshToken之后,我無法使用[Authorize]屬性訪問方法。 我嘗試調試,但是找不到我在這里缺少的東西。

您能調查一下我的代碼並識別出什么錯誤嗎?

Startup.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Tokens;

namespace WebApiJwtExample
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            // Add cors
            services.AddCors();
            services.AddAuthorization(auth =>
            {
                auth.AddPolicy(Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerDefaults.AuthenticationScheme, new AuthorizationPolicyBuilder()
                    .AddAuthenticationSchemes(Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerDefaults.AuthenticationScheme‌​)
                    .RequireAuthenticatedUser()
                    .Build());
            });
            services.AddAuthentication(Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerDefaults.AuthenticationScheme)
                .AddJwtBearer(options =>
                {
                    options.RequireHttpsMetadata = false;
                    options.SaveToken = true;
                    options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
                    {
                        ValidIssuer = "my_api",
                        ValidAudiences = new[] { "my_spa" },
                        IssuerSigningKeys = new List<SecurityKey> {
                            new SymmetricSecurityKey(Encoding.UTF8.GetBytes( "mykeyname") )}
                        };
                    });

            services.AddMvc();
        }

        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            //Configure Cors
            app.UseCors(builder => builder
                .AllowAnyOrigin()
                .AllowAnyHeader()
                .AllowAnyMethod());

            app.UseAuthentication();

            app.UseMvcWithDefaultRoute();

            app.Run(async (context) =>
            {
                context.Response.StatusCode = 404;
                await context.Response.WriteAsync("Page not found");
            });
        }
    }
}

TokenController.cs

using System;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Security.Principal;
using System.Text;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.IdentityModel.Tokens;

namespace WebApiJwtExample
{
    [Route("/api/token")]
    public class TokenController : Controller
    {
        [HttpPost]
        public JsonWebToken Create([FromBody]TokenModel obj)
        {
            User user = obj.grant_type == "refresh_token" ? GetUserByToken(obj.refresh_token) : GetUserByCredentials(obj.username, obj.password);

            if (user == null)
                throw new UnauthorizedAccessException("No!");

            int ageInMinutes = 20;  // However long you want...

            DateTime expiry = DateTime.UtcNow.AddMinutes(ageInMinutes);

            var token = new JsonWebToken
            {
                access_token = GenerateToken(user, expiry),
                expires_in = ageInMinutes * 60
            };

            if (obj.grant_type != "refresh_token")
                token.refresh_token = GenerateRefreshToken(user);

            return token;
        }

        private User GetUserByToken(string refreshToken)
        {
            string[] Roles = { "Administrator" };
            if (refreshToken == "test")
                return new User
                {
                    UserName = "test",
                    permission = "contents",
                    Roles = Roles
                };

            return null;
        }

        private User GetUserByCredentials(string username, string password)
        {
            string[] Roles = { "Administrator" };
            if (username == "test" && password == "dev123")
                return new User
                {
                    UserName = "test",
                    permission = "contents",
                    Roles = Roles
                };

            return null;
        }

        private string GenerateRefreshToken(User user)
        {
            return "test";
        }

        public string GenerateToken(User user, DateTime expiry)
        {
            JwtSecurityTokenHandler tokenHandler = new JwtSecurityTokenHandler();

            ClaimsIdentity identity = new ClaimsIdentity(new GenericIdentity(user.UserName, "jwt"));

            // TODO: Add whatever claims the user may have...

            RsaSecurityKey _key;
            string _algorithm = SecurityAlgorithms.RsaSha256Signature;
            string _issuer = "my_api";
            string _audience = "my_spa";
            string keyName = "mykeyname";

            var parameters = new CspParameters { KeyContainerName = keyName };
            var provider = new RSACryptoServiceProvider(2048, parameters);
            _key = new RsaSecurityKey(provider);

            SecurityToken token = tokenHandler.CreateJwtSecurityToken(new SecurityTokenDescriptor
            {
                Audience = _audience,
                Issuer = _issuer,
                SigningCredentials = new SigningCredentials(_key, _algorithm),
                Expires = expiry.ToUniversalTime(),
                Subject = identity
            });

            return tokenHandler.WriteToken(token);
        }
    }

    public class User
    {
        public string UserName { get; set; }
        public string[] Roles { get; set; }
        public string permission { get; set; }
    }

    public class JsonWebToken
    {
        public string access_token { get; set; }

        public string token_type { get; set; } = "bearer";

        public int expires_in { get; set; }

        public string refresh_token { get; set; }
    }


    public class TokenModel
    {
        public string username { get; set; }

        public string password { get; set; }

        public string client_id { get; set; }

        public string grant_type { get; set; }

        public string scope { get; set; }

        public string refresh_token { get; set; }
    }
}

HomeController File.cs (此處授權返回401狀態)

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

namespace WebApiJwtExample
{
    public class HomeController : Controller
    {
        public IActionResult Index()
        {
            return View();
        }

        [Authorize]
        public IActionResult GetUserDetails(){
            return new ObjectResult(new {
                Username = User.Identity.Name
            });                
        }
    }
}

有一個GitHub庫在這里

  1. 服務器端:您的GenerateToken()方法使用RSA算法生成JWT令牌,而身份驗證使用HcmaSha256來驗證令牌。

更改TokenController的代碼,如下所示:

public string GenerateToken(User user, DateTime expiry)
{
    JwtSecurityTokenHandler tokenHandler = new JwtSecurityTokenHandler();

    ClaimsIdentity identity = new ClaimsIdentity(new GenericIdentity(user.UserName, "jwt"));

    string _issuer = "dp_portal_api";
    string _audience = "dp_portal_spa";
    string keyName = mykeyname;

    var token = new JwtSecurityToken
    (
        issuer: _issuer,
        audience: _audience,
        claims: identity.Claims,
        expires: expiry,
        notBefore: DateTime.UtcNow,
        signingCredentials: new SigningCredentials(new SymmetricSecurityKey(Encoding.UTF8.GetBytes(mykeyname)),
            SecurityAlgorithms.HmacSha256)
        );

    return tokenHandler.WriteToken(token);
}
  1. 客戶端:您要將令牌對象存儲到localStorage。 但是,您忘記先對對象進行字符串化。

刪除自己的getSavedToken()並添加三個幫助器函數:

function getSavedToken() {
    // return localStorage.getItem("token");  /// remove this line
    var token = localStorage.getItem("token");/// get the stored json string
    return JSON.parse(token);                 /// parse token 
}

function getSavedAccessToken() { 
    return getSavedToken().access_token;
}

function saveToken(tokenObj) { 
    var str = JSON.stringify(tokenObj);
    localStorage.setItem("token", str);
    return tokenObj;
}

如下更改$.ajaxSetup

$.ajaxSetup({
    beforeSend: function(xhr) {
        if (isUserLoggedIn()) {
            /// remove the line below , since we need the access_token only
            // xhr.setRequestHeader('Authorization', 'Bearer ' + getSavedToken()); 
            xhr.setRequestHeader('Authorization', 'bearer ' + getSavedAccessToken());                      
        }
    }
})

最后,將您的點擊事件功能更改為:

$('#btLogin').click(function() {
    $.ajax({
        type: "POST",
        url: "/api/token",
        data: JSON.stringify({ username: $('#username').val(), password: $('#password').val(), grant_type: "password", client_id: "dp_portal_spa" }),
        headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json'
        },
        success: function (token) {
            //localStorage.setItem("token", token);  //// remove this line
            saveToken(token);   /// add this line , since the obj cannot be stored directly
            $('#btLoginContainer').hide();
            $('#btLogoutContainer').show();
            var message = "<p>Token received and saved in local storage under the key 'token'</p>";
            message += "<p>Token Value: </p><p style='word-wrap:break-word'>" +JSON.stringify(token) + "</p>";
            $('#responseContainer').html(message);
        },
        failure: handleError
    });
});

它將按預期工作。 這是屏幕截圖:

在此處輸入圖片說明

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM