我目前正在开发Angular JS app和Node.js Server(作为API)之间的交互,并使用基于JSON Web Token的身份验证。
但是我有一个问题我无法自己回答:当您对JWT服务器端进行编码时,将用户作为有效负载,如何继续检索客户端的用户信息? 这是一个了解我的问题的小例子:
我是基本用户,我将凭据发送到API进行身份验证。作为交换,我收到一个JWT令牌,但我没有关于用户的任何信息,因为只有服务器具有能够解码JWT令牌的密钥。那么服务器是否需要向我发送例如用户的id,以便我可以调用我的api用户/ id来检索有关用户身份验证的信息?
答案 0 :(得分:6)
通过解码每个请求上的令牌来检索用户的信息。因此,在将令牌返回到客户端之后的示例中,客户端向服务器发出请求,使用存储在编码令牌中的数据来获取用户的名字和姓氏,该数据与请求一起发送回服务器。发出此GET请求时,您可以将令牌作为参数发送。我将使用非cookie存储示例。这是它如何下降:
payload = {user_id:35} user_token = JWT.encode(payload,“your_secret_key”);
将user_token返回给客户端,并将所述令牌存储在隐藏的html标记或localStorage变量中。使用Angular,我将它存储在localStorage中。
既然用户是signed_in并且令牌是客户端,那么您可以提交包含user_token作为参数的GET请求。请记住,此user_token有效内容包含user_id。
服务器获取参数并解码user_token以从有效负载中获取user_id。
使用user_id查询数据库并将数据(名字和姓氏)作为普通json返回,而不是ENCODED。
重要的是要记住在您的示例中唯一要编码的是唯一标识符(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个部分。前两个header
和payload
是base64编码的。客户端可以轻松解码它们。有效负载具有关于用户的声明,客户端可以使用此数据(用户ID,名称,角色,令牌到期),而不必向服务器发出另一个请求。
JWT的第三部分是signature
。它是header
的{{3}},payload
,以及只有服务器知道的秘密。服务器将在每个请求上验证令牌和用户的权限。
客户端永远不会知道这个秘密,它只有一个声称是给定用户的令牌。
答案 2 :(得分:1)
您在客户端上有有效负载,如果您需要的数据在有效负载中,您可以轻松地在有效负载上执行Base64 Decode
来查找它!
要理解这一步,请执行以下步骤:
服务器启动身份验证业务,发现用户名和密码有效。
服务器必须将这些信息返回给客户端。这是JWT
有一些规则的地方。服务器必须将token
返回给客户端。令牌有三个部分Header.PayLoad.Signature
。暂时忘记签名,这是造成一些混乱的部分。
第一部分是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
。
到目前为止,服务器已创建Header
和Payload
。现在是时候签名!这很容易:
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 };
}
}
我希望这对你有用。