使用Access Token调用API时出现401未授权(.NET WebApi/React.js模板项目)
我通过Visual Studio模板创建了包含WebAPI(带独立账户认证/授权)和React.js的项目,注册、登录功能正常,但调用authService.getAccessToken()获取令牌后访问/user端点时,持续收到401 Unauthorized响应,偶尔重启项目并重新登录后可正常访问。
我扩展了IdentityUser模型添加了StravaAthleteId属性,并创建了/user控制器。该端点在使用有效令牌时可正常工作,但当前组件中,尽管authService.getAccessToken()返回了令牌,请求仍被拒绝,浏览器开发者工具显示响应头包含Www-Authenticate: Bearer error="invalid_token"。组件始终显示“You need to connect your Strava account”,但数据库中对应的WebAppUser已存在StravaAthleteId且我已完成认证,未修改过默认的授权端点或工具。
前端主组件代码
import { Link, NavLink } from "react-router-dom"; import authService from "../api-authorization/AuthorizeService"; import { WebAppUser } from "./models/webAppUser"; import { ApplicationPaths } from "../api-authorization/ApiAuthorizationConstants"; import { ToastContainer, toast } from "react-toastify"; import "react-toastify/dist/ReactToastify.css"; function SegmentSniper() { const [userName, setUsername] = useState(null); const [isAuthenticated, setIsAuthenticated] = useState(false); const loginPath = `${ApplicationPaths.Login}`; const [appUser, setAppUser] = useState<WebAppUser>(); useEffect(() => { const _subscription = authService.subscribe(() => populateState()); populateState(); return () => { authService.unsubscribe(_subscription); }; }); async function populateState() { const [isAuthenticated, user] = await Promise.all([ authService.isAuthenticated(), authService.getUser(), ]); setIsAuthenticated(isAuthenticated); setUsername(user && user.name); console.log(`user from populate state = ${JSON.stringify(user, null, 4)}`); const authToken = await authService .getAccessToken() .then((res) => console.log(`user token: ${res}`)); try { const response = await fetch("/user", { headers: { Authorization: `Bearer ${authToken}` }, }); if (!response.ok) { console.log(`There was an error fetching the user data.`); } else { response .json() .then((resolvedData) => setAppUser(resolvedData)) .catch((error) => console.log(`There was an error fetching the user data. ${error}`) ); } } catch (error) { console.log(`There was an error fetching the data. ${error}`); } } return !isAuthenticated ? ( <> <main className="container"> <div className="row justify-content-center mt-3 mb-3"> <div className="col-6"> <p>You must be logged in to access this app.</p> <p> Click <Link to={loginPath}> here </Link> to login. </p> </div> </div> </main> </> ) : isAuthenticated && !appUser?.stravaAthleteId ? ( <> <main className="container"> <div className="row justify-content-center mt-3 mb-3"> <div className="col-6"> <p>You need to connect your Strava Account.</p> <p> Click <Link to={loginPath}> {" "} <img src=".\assets\stravaImages\btn_strava_connectwith_orange\btn_strava_connectwith_orange.png" />{" "} </Link> to get started. </p> </div> </div> </main> </> ) : ( <> <main className="container"> <div className="row justify-content-center mt-3 mb-3"> <div className="main-tile"> <ul> <li style={{ cursor: "pointer" }}> <Link to="./athlete">View Athlete Details</Link> </li> <li style={{ cursor: "pointer" }}> <Link to="./activities">Segment Sniper</Link> </li> <li style={{ cursor: "pointer" }}> <Link to="./token-maintenance">Token Maintenance</Link> </li> </ul> </div> <div> <h3>webappuser: {appUser?.id}</h3> <h3>stravaAthleteId: {appUser?.stravaAthleteId}</h3> </div> </div> </main> </> ); } export default SegmentSniper;
User控制器代码
namespace StravaSegmentSniper.React.Controllers { [Authorize] [Route("[controller]")] [ApiController] public class UserController : ControllerBase { private readonly IWebAppUserService _webAppUserService; private readonly UserManager<WebAppUser> _userManager; private readonly IHttpContextAccessor _httpContextAccessor; public UserController(IWebAppUserService webAppUserService, UserManager<WebAppUser> userManager, IHttpContextAccessor httpContextAccessor) { _webAppUserService = webAppUserService; _userManager = userManager; _httpContextAccessor = httpContextAccessor; } [HttpGet] public WebAppUser GetUser() { var userId = _httpContextAccessor.HttpContext.User.FindFirstValue(ClaimTypes.NameIdentifier).ToString(); var user = _webAppUserService.GetLoggedInUserById(userId); return user; } }
Program.cs代码
using StravaSegmentSniper.React.Helpers; //var builder = WebApplication.CreateBuilder(args); var builder = WebAppBuilderConfig.ConfigureBuilder(); var app = builder.Build(); // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { app.UseMigrationsEndPoint(); } else { // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); app.UseIdentityServer(); app.UseAuthentication(); app.UseAuthorization(); app.UseSwagger(); app.UseSwaggerUI(); app.MapControllers(); app.MapRazorPages(); app.MapFallbackToFile("index.html"); ; app.Run();
WebAppBuilderConfig代码
using Authorization.Data.Data; using Authorization.Data.Models; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.EntityFrameworkCore; using Microsoft.IdentityModel.Tokens; using Microsoft.OpenApi.Models; using StravaSegmentSniper.Data; using StravaSegmentSniper.React.ActionHandlers.Activity; using StravaSegmentSniper.React.ActionHandlers.Segment; using StravaSegmentSniper.React.ActionHandlers.StravaApiToken; using StravaSegmentSniper.Services.Internal.Adapters; using StravaSegmentSniper.Services.Internal.Services; using StravaSegmentSniper.Services.StravaAPI.Activity; using StravaSegmentSniper.Services.StravaAPI.Athlete; using StravaSegmentSniper.Services.StravaAPI.Segment; using StravaSegmentSniper.Services.StravaAPI.TokenService; using System.Configuration; using System.Text; namespace StravaSegmentSniper.React.Helpers { public class WebAppBuilderConfig { public static WebApplicationBuilder ConfigureBuilder() { var builder = WebApplication.CreateBuilder(); var authConnectionString = builder.Configuration.GetConnectionString("AuthorizationData"); builder.Services.AddDbContext<AuthDbContext>(options => options.UseSqlServer(authConnectionString)); builder.Services.AddDatabaseDeveloperPageExceptionFilter(); builder.Services.AddDefaultIdentity<WebAppUser>(options => options.SignIn.RequireConfirmedAccount = true) .AddEntityFrameworkStores<AuthDbContext>(); builder.Services.AddIdentityServer() .AddApiAuthorization<WebAppUser, AuthDbContext>(); builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddIdentityServerJwt(); var appDataConnectionString = builder.Configuration.GetConnectionString("StravaSegmentSniperData"); builder.Services.AddDbContext<StravaSegmentSniperDbContext>(options => options.UseSqlServer(appDataConnectionString).UseLazyLoadingProxies()); builder.Services.AddControllersWithViews(); builder.Services.AddRazorPages(); builder.Services.AddControllers().AddNewtonsoftJson(); //builder.Services.AddRouting(ctx => ctx.LowercaseUrls = false); builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo { Title = "JWTToken_Auth_API", Version = "v1" }); c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme() { Name = "Authorization", Type = SecuritySchemeType.ApiKey, Scheme = "Bearer", BearerFormat = "JWT", In = ParameterLocation.Header, Description = "JWT Authorization header using the Bearer scheme. \r\n\r\n Enter 'Bearer' [space] and then your token in the text input below.\r\n\r\nExample: \"Bearer 1safsfsdfdfd\"", }); c.AddSecurityRequirement(new OpenApiSecurityRequirement { { new OpenApiSecurityScheme { Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "Bearer" } }, new string[] {} } }); }); builder.Services.AddAutoMapper(typeof(Program)); //builder.Services.AddScoped<IStravaSegmentSniperDbContext>(provider => provider.GetService<StravaSegmentSniperDbContext>()); //add DI services to the container builder.Services.AddScoped<IAthleteActivityService, AthleteActivityService>(); builder.Services.AddScoped<IAthleteService, AthleteService>(); builder.Services.AddScoped<IStravaTokenService, StravaTokenService>(); builder.Services.AddScoped<IWebAppUserService, WebAppUserService>(); builder.Services.AddScoped<IStravaAPIAthlete, StravaAPIAthlete>(); builder.Services.AddScoped<IStravaAPIActivity, StravaAPIActivity>(); builder.Services.AddScoped<IStravaAPISegment, StravaAPISegment>(); builder.Services.AddScoped<IStravaAPIToken, StravaAPIToken>(); builder.Services.AddScoped<IActivityAdapter, ActivityAdapter>(); builder.Services.AddScoped<ISegmentAdapter, SegmentAdapter>(); builder.Services.AddScoped<IStravaActivityActionHandler, StravaActivityActionHandler>(); builder.Services.AddScoped<IStravaApiTokenActionHandler, StravaApiTokenActionHandler>(); builder.Services.AddScoped<IStravaSegmentActionHandler, StravaSegmentActionHandler>(); return builder; } } }
内容的提问来源于stack exchange,提问作者TylerBobe
相关产品推荐
相关产品推荐

