如何从System.IdentityModel的JwtSecurityToken提取Bearer Token并实现API鉴权
JWT 鉴权常见问题排查
你当前代码的核心认知误区
- 你现在的
Validated方法返回的是解析后的JwtSecurityToken实体对象,根本不是实际能用的Bearer Token。你贴出来的那段序列化JSON,就是把这个C#类对象直接序列化生成的结果,哪怕里面存了原始JWT的字段,也完全不符合鉴权的格式要求。 - 真正的Bearer Token是一个纯字符串,标准结构是:三段式JWT原始字符串 + 固定的
Bearer前缀,最终形态类似:Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjYiLCJuYmYiOjE2MTg3MTQ3OTEsImV4cCI6MTYxODcyMTk5MSwiaWF0IjoxNjE4NzE0NzkxfQ.xxxxx。这个值是要放在HTTP请求的Authorization请求头里传递的,不是要后端方法序列化返回给前端的。 - 换句话说:你写的
Validated方法从一开始的定位就跑偏了,它本来的作用应该是校验传进来的字符串JWT是不是合法有效,根本不需要它“返回Bearer Token”——Bearer Token本身就是入参传进来的那个字符串,加上协议要求的前缀而已。
现有代码的可修正点
- 如果在校验通过后需要拿到原始JWT字符串,根本不需要返回整个
JwtSecurityToken对象:直接取入参的token值,或者读解析后对象的jwtToken.RawData字段就拿到了。 - 代码里的
catch (Exception e) { throw e; }写法完全是冗余的:这种写法会把原始异常的堆栈信息重置成当前方法的位置,排查问题的时候根本找不到真实报错点,直接删掉这个try-catch,或者把throw e;改成throw;就行。 - 你最开始写的
Authenticate登录方法的逻辑是对的:它返回的AuthToken.Token字段就是原始的三段式JWT字符串,前端拿到这个字符串之后,后续调用受保护接口的时候,自动给请求头加上Bearer前缀拼上这个字符串传回来就行,不需要后端在登录接口里额外拼接前缀。
全局用Bearer Token保护API的正确配置
你知道在控制器顶部加[Authorize]的方向是对的,但如果没有提前把JWT鉴权的中间件配置好,光加这个特性是完全不生效的:
- 首先在服务注册阶段把JWT鉴权的逻辑统一配置好,不用在每个校验方法里重复写
TokenValidationParameters:// .NET 6+ 示例写法 builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuerSigningKey = true, IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(_appSettings.Secret)), ValidateIssuer = false, ValidateAudience = false, ClockSkew = TimeSpan.Zero }; }); - 然后在请求管道配置阶段注册中间件,注意中间件的顺序不能乱:必须在路由匹配之后、控制器映射之前挂载鉴权相关的中间件:
app.UseAuthentication(); app.UseAuthorization(); app.MapControllers(); - 配置完成之后,
[Authorize]特性才会真正生效:如果请求没带合法的Authorization头,接口会直接返回401未授权。 - 如果不想每个控制器都手动加
[Authorize],可以直接配置全局授权过滤器,让所有接口默认要求登录校验,只有需要开放匿名访问的接口,单独加[AllowAnonymous]特性就行:builder.Services.AddControllers(options => { options.Filters.Add(new AuthorizeFilter()); });
内容的提问来源于stack exchange,提问作者Lizi
相关产品推荐
相关产品推荐

