JWT认证概念

时间:2014-08-19 20:38:46

标签: authentication jwt

我目前正在开发Angular JS app和Node.js Server(作为API)之间的交互,并使用基于JSON Web Token的身份验证。

但是我有一个问题我无法自己回答:当您对JWT服务器端进行编码时,将用户作为有效负载,如何继续检索客户端的用户信息? 这是一个了解我的问题的小例子:

我是基本用户,我将凭据发送到API进行身份验证。作为交换,我收到一个JWT令牌,但我没有关于用户的任何信息,因为只有服务器具有能够解码JWT令牌的密钥。那么服务器是否需要向我发送例如用户的id,以便我可以调用我的api用户/ id来检索有关用户身份验证的信息?

4 个答案:

答案 0 :(得分:6)

通过解码每个请求上的令牌来检索用户的信息。因此,在将令牌返回到客户端之后的示例中,客户端向服务器发出请求,使用存储在编码令牌中的数据来获取用户的名字和姓氏,该数据与请求一起发送回服务器。发出此GET请求时,您可以将令牌作为参数发送。我将使用非cookie存储示例。这是它如何下降:

  1. 用户使用密码和用户名登录
  2. 服务器对json Web令牌有效负载进行编码,该有效负载包含使用secret_key登录的用户的唯一标识符(即user_id)。示例函数调用可能看起来像这样。
  3. payload = {user_id:35} user_token = JWT.encode(payload,“your_secret_key”);

    1. 将user_token返回给客户端,并将所述令牌存储在隐藏的html标记或localStorage变量中。使用Angular,我将它存储在localStorage中。

    2. 既然用户是signed_in并且令牌是客户端,那么您可以提交包含user_token作为参数的GET请求。请记住,此user_token有效内容包含user_id。

    3. 服务器获取参数并解码user_token以从有效负载中获取user_id。

    4. 使用user_id查询数据库并将数据(名字和姓氏)作为普通json返回,而不是ENCODED。

    5. 重要的是要记住在您的示例中唯一要编码的是唯一标识符(user_id)。在每个请求中,您都会解码令牌,而令牌本身就是身份验证机制。

答案 1 :(得分:3)

The strategy in the accepted answer works, but it misses the fact that the client can see the payload of a JWT. It is explained nicely in The Anatomy of a JSON Web Token

JWT有3个部分。前两个headerpayloadbase64编码的。客户端可以轻松解码它们。有效负载具有关于用户的声明,客户端可以使用此数据(用户ID,名称,角色,令牌到期),而不必向服务器发出另一个请求。

JWT的第三部分是signature。它是header的{​​{3}},payload,以及只有服务器知道的秘密。服务器将在每个请求上验证令牌和用户的权限。

客户端永远不会知道这个秘密,它只有一个声称是给定用户的令牌。

答案 2 :(得分:1)

您在客户端上有有效负载,如果您需要的数据在有效负载中,您可以轻松地在有效负载上执行Base64 Decode来查找它!

要理解这一步,请执行以下步骤:

  1. 客户端发送用户名:user,密码:传递给服务器。
  2. 服务器启动身份验证业务,发现用户名和密码有效。

  3. 服务器必须将这些信息返回给客户端。这是JWT有一些规则的地方。服务器必须将token返回给客户端。令牌有三个部分Header.PayLoad.Signature。暂时忘记签名,这是造成一些混乱的部分。

  4. 第一部分是Header。有点像:

    {"typ":"JWT","alg":"HS256"}
    

    eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9之后的Base64 Decode。请考虑这只是一个解码,根本没有加密!要查看此内容,您可以转到https://www.base64decode.org/并进行测试。

    header之后,服务器需要向用户发送有效负载。服务器可能决定在json下面发送(我说决定,因为这里没有标准要求,你可以发送更多或更少的数据作为有效载荷,例如,你也可以设置用户权限,例如admin:true或用户名字和姓氏,但请记住,JWT大小必须很小,因为它会在每次请求时发送到服务器)

    {"username":"user","id":3,"iat":1465032622,"exp":1465050622}
    

    再次根据JWT,服务器在这里需要Base64 Decode(并且根本不需要加密)。上面的json将是eyJ1c2VybmFtZSI6IjEiLCJpZCI6MywiaWF0IjoxNDY1MDMyNjIyLCJleHAiOjE0NjUwNTA2MjJ9

    到目前为止,服务器已创建HeaderPayload。现在是时候签名!这很容易:

    var encodedString=base64UrlEncode(header) + "." + base64UrlEncode(payload);
    //As our example base64UrlEncode(header) is eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
    //and the base64UrlEncode(payload) is eyJ1c2VybmFtZSI6IjEiLCJpZCI6MywiaWF0IjoxNDY1MDMyNjIyLCJleHAiOjE0NjUwNTA2MjJ9
    
     var signature=HMACSHA256(encodedString, 'a secret string which is kept at server');
    

    签名是加密字符串!!你不能在客户端解密它(因为你没有秘密字符串)。你也不需要这样做。所有令牌数据都在有效载荷中,可以通过解码访问(同样没有解密!)。

    总结一下下面的标记

    //Header
    eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.
    //PayLoad
    eyJ1c2VybmFtZSI6IjEiLCJpZCI6MywiaWF0IjoxNDY1MDMyNjIyLCJleHAiOjE0NjUwNTA2MjJ9.
    //Signature
    0K8TL1YS0XKnEIfI3lYs-bu2vbWHSNZsVJkN1mXtgWg
    

    标头和有效负载为Base64 Decoded,您可以在客户端对其进行编码。但是你不能用签名做任何事情。

    签名仅供服务器使用。客户端使用其令牌发送每个请求,服务器必须确保客户端没有更改令牌有效负载的任何部分(例如更改用户ID)。这是显示签名字符串重要性的地方,服务器使用它的每个请求的密钥重新检查签名!

答案 3 :(得分:0)

JWT(JSON 网络令牌)在网络开发中变得越来越流行。这是 一种开放标准,允许以安全和紧凑的方式将数据作为 JSON 对象在各方之间传输。各方之间使用 JWT 传输的数据都经过数字签名,以便于验证和信任。

ASP.NET Core 中的 JWT

第一步是在我们的项目中配置基于 JWT 的身份验证。我们可以添加在每个授权请求中触发的自定义 jwt auth 中间件。

Startup.cs

    services.AddMvc(options => options.EnableEndpointRouting = false);
    var tokenValidationParams = new TokenValidationParameters
            {
                ValidateIssuerSigningKey = true,
                IssuerSigningKey = new SymmetricSecurityKey("Jwt_Key"),
                ValidateIssuer = true,
                ValidateAudience = true,
                ValidateLifetime = true,
                RequireExpirationTime = false,
                ValidIssuer = "Jwt_Issuer",
                ValidAudience = "Jwt_Audience",
                ClockSkew = TimeSpan.Zero
            };

            services.AddSingleton(tokenValidationParams);

            services.AddAuthentication(options => {
                options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
                options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            })
            .AddJwtBearer(jwt => {
                jwt.SaveToken = true;
                jwt.TokenValidationParameters = tokenValidationParams;
            });

            services.AddMvc();
            
            
            
            // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
                    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
                    {
                        if (env.IsDevelopment())
                        {
                            app.UseDeveloperExceptionPage();
                        }
                        // custom jwt auth middleware
                        **app.UseMiddleware<JwtMiddleware>();**
                        app.UseAuthentication();
                        app.UseMvc();
                        app.Run(async (context) =>
                        {
                            await context.Response.WriteAsync("Welcome to DATA API");
                        });
            
                    }

        

Generete JWT

GenerateJSONWebToken(User userInfo)
                {
                    var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("Jwt_Key"));
                    var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
    
                    var claims = new[] {
                        new Claim(JwtRegisteredClaimNames.Sub, userInfo.UserID),
                        new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
                    };
        
                    var token = new JwtSecurityToken("Jwt_Issuer","Jwt:Audience",
                        claims,
                        expires: DateTime.Now.AddHours(24),
                        signingCredentials: credentials);
        
                    return new JwtSecurityTokenHandler().WriteToken(token);
                }
    

此方法返回 JWT Totken 之类的

令牌:“eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJKa WduZXNoIFRyaXZlZGkiLCJlbWFpbCI6InRlc3QuYnRlc3RAZ21haWwuY29tIiwiRG F0ZU9mSm9pbmciOiIwMDAxLTAxLTAxIiwianRpIjoiYzJkNTZjNzQtZTc3Yy00ZmU xLTgyYzAtMzlhYjhmNzFmYzUzIiwiZXhwIjoxNTMyMzU2NjY5LCJpc3MiOiJUZXN0 LmNvbSIsImF1ZCI6IlRlc3QuY29tIn0.8hwQ3H9V8mdNYrFZSjbCpWSyR1CNyDYHc Gf6GqqCGnY"

调用授权方法

 [Authorize]
   public ActionResult<IEnumerable<string>> Get()
   {
    return new string[] { "value1", "value2", "value3", "value4",
    "value5" };
   }
    

在 Jwt 中间件类中验证令牌

JwtMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly TokenValidationParameters _tokenValidationParams;
        public JwtMiddleware(RequestDelegate next, TokenValidationParameters 
        tokenValidationParams)
        {
            _next = next;
            _tokenValidationParams = tokenValidationParams;
        }

        public async Task Invoke(HttpContext context)
        {
            var token = context.Request.Headers["Authorization"].FirstOrDefault()?.Split(" ").Last();

            var jwtTokenHandler = new JwtSecurityTokenHandler();
            // Validation 1 - Validation JWT token format
            var tokenInVerification = jwtTokenHandler.ValidateToken(token, _tokenValidationParams, out var validatedToken);

            if (validatedToken is JwtSecurityToken jwtSecurityToken)
            {
                var result = jwtSecurityToken.Header.Alg.Equals(SecurityAlgorithms.HmacSha256, StringComparison.InvariantCultureIgnoreCase);

                if (result == false)
                {
                    Error Invalid = new Error()
                    {
                        Success = false,
                        Errors = "Token is Invalid"
                    };

                    context.Items["Error"] = Invalid;
                }
            }

            await _next(context);
        }
    }

授权属性

public void OnAuthorization(AuthorizationFilterContext context)
 {
    var Error= (UserModel)context.HttpContext.Items["Error"];
    if (AuthResult != null)
    {
        // not logged in
        context.Result = new JsonResult(new { message = "Unauthorized Access" }) { 
     StatusCode = StatusCodes.Status401Unauthorized };
    }
 }

我希望这对你有用。