ASP.NET Core+React JWT认证提交表单时出现userId required错误求助
问题:JWT认证下提交表单返回"userId is required" 400错误
我正在开发一个需用户登录才能提交表单的应用,采用JWT认证并将token存储在sessionStorage中(也曾尝试本地存储)。登录后控制台可看到token,进入提交表单页面后token仍有效,但提交表单时返回400错误“userId is required”,尽管控制台显示表单使用的token与登录时一致。我已尝试调整Program.cs中Identity与JWT配置的顺序、修改Controller的Authorize属性,但问题仍未解决。
用户控制器代码(C#)
public UserController(HelpDeskDbContext context, UserManager<User> userManager, SignInManager<User> signInManager, IConfiguration configuration) { _context = context; _userManager = userManager; _signInManager = signInManager; _jwtKey = configuration["Jwt:Key"]; } [Authorize(AuthenticationSchemes = "Bearer")] [HttpPost("createComplaint")] public async Task<IActionResult> CreateComplaint([FromBody] ComplaintForm complaintForm) { var userId = _userManager.GetUserId(User); complaintForm.UserId = userId; _context.ComplaintForms.Add(complaintForm); await _context.SaveChangesAsync(); return Ok(complaintForm); } [HttpPost("login")] public async Task<IActionResult> Login([FromBody] LoginViewModel model) { if (ModelState.IsValid) { var user = await _userManager.FindByEmailAsync(model.Email); if (user != null) { var passwordValid = await _userManager.CheckPasswordAsync(user, model.Password); if (passwordValid) { var token = GenerateJwtToken(user); return Ok(new { token, Role = user.Role }); } } return Unauthorized("Invalid login attempt."); } return BadRequest("Invalid login attempt."); } private string GenerateJwtToken(User user) { var tokenHandler = new JwtSecurityTokenHandler(); var key = Encoding.UTF8.GetBytes(_jwtKey); var tokenDescriptor = new SecurityTokenDescriptor { Subject = new ClaimsIdentity(new[] { new Claim(ClaimTypes.NameIdentifier, user.Id), new Claim(ClaimTypes.Name, user.UserName), // Optionally add more claims }), Expires = DateTime.UtcNow.AddDays(7), SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature), Issuer = "YourIssuer", // Ensure these match in your token validation settings Audience = "YourAudience" }; var token = tokenHandler.CreateToken(tokenDescriptor); return tokenHandler.WriteToken(token); } public class RegisterViewModel { public string? Email { get; set; } public string? Password { get; set; } } public class LoginViewModel { public string? Email { get; set; } public string? Password { get; set; } public bool RememberMe { get; set; } }
实体类代码(C#)
public class ComplaintForm { public int Id { get; set; } public string? Subject { get; set; } public string? Description { get; set; } public string? FilePath { get; set; } public FormStatus Status { get; set; } public string UserId { get; set; } public string? AssignedWorkerId { get; set; } public DateTime DueDate { get; set; } [JsonIgnore] public virtual User? User { get; set; } [JsonIgnore] public virtual User? AssignedWorker { get; set; } [JsonIgnore] public virtual ICollection<Comment>? Comments { get; set; } } public class User : IdentityUser { public string? Role { get; set; } public virtual ICollection<ComplaintForm>? ComplaintForms { get; set; } public virtual ICollection<ComplaintForm>? AssignedComplaintForms { get; set; } }
appsettings.json配置
"Jwt": { "Key": "my key", "Issuer": "YourIssuer", "Audience": "YourAudience" },
Program.cs代码(C#)
var jwtSettings = builder.Configuration.GetSection("Jwt"); var key = Encoding.UTF8.GetBytes(jwtSettings["Key"]); builder.Services.AddAuthentication(options => { options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }) .AddJwtBearer(options => { options.RequireHttpsMetadata = false; options.SaveToken = true; options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuerSigningKey = true, IssuerSigningKey = new SymmetricSecurityKey(key), ValidateIssuer = false, ValidateAudience = false, ValidIssuer = jwtSettings["Issuer"], ValidAudience = jwtSettings["Audience"] }; }); // Add services to the container. builder.Services.AddControllers(); // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); builder.Services.AddDbContext<mydbcontext>(options => options.UseSqlServer(builder.Configuration.GetConnectionString("mydbcontext"))); builder.Services.AddIdentity<User, IdentityRole>() .AddEntityFrameworkStores<HelpDeskDbContext>() .AddDefaultTokenProviders(); builder.Services.AddControllers().AddJsonOptions(options => { options.JsonSerializerOptions.MaxDepth = 64; }); builder.Services.AddCors(options => { options.AddPolicy("AllowSpecificOrigin", builder => { builder.WithOrigins("http://localhost:mylocalhost") .AllowAnyMethod() .AllowAnyHeader() .AllowCredentials(); }); }); var app = builder.Build(); // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); } app.UseHttpsRedirection(); // Apply the CORS policy app.UseCors("AllowSpecificOrigin"); app.UseAuthentication(); app.UseAuthorization(); app.MapControllers(); app.Run();
React前端代码
api.js
export const api = axios.create({ baseURL: API_BASE_URL, headers: { 'Content-Type': 'application/json' }, }); api.interceptors.request.use((config) => { const token = sessionStorage.getItem('authToken'); if (token) { config.headers['Authorization'] = `Bearer ${token}`; } return config; }, (error) => { return Promise.reject(error); }); // Authentication export const register = (data) => api.post('/user/register', data); export const login = async (data) => { try { const response = await api.post('/user/login', data); return { token: response.data.token, // Ensure this matches your API response structure role: response.data.role, // Ensure this matches your API response structure }; } catch (error) { console.error('Login failed', error); throw error; } }; // User Complaints export const createComplaint = (data) => api.post('/user/createComplaint', data); export const getUserComplaints = async () => { try { const response = await api.get('/user/complaints'); return response.data; } catch (error) { console.error('Failed to get user complaints', error); throw error; } };
auth.js、Login组件、ComplaintForm组件
export const isAuthenticated = () => { const token = sessionStorage.getItem('authToken'); return token !== null; }; import React, { useState } from 'react'; import { login } from '../api'; // Adjust the import path as needed import { useNavigate } from 'react-router-dom'; function Login() { const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); const [error, setError] = useState(''); const navigate = useNavigate(); // Initialize useNavigate hook const handleLogin = async (e) => { e.preventDefault(); try { const { token, role } = await login({ email, password }); console.log('Login successful:', token); // Store the token in sessionStorage sessionStorage.setItem('authToken', token); // Redirect based on the role if (role === 'Admin') { navigate('/admin'); } else if (role === 'Worker') { navigate('/worker'); // Redirect to worker dashboard } else { navigate('/'); // Redirect to the homepage or user dashboard } } catch (err) { setError('Login failed. Please check your credentials and try again.'); } }; return ( <div> <h2>Login</h2> <form onSubmit={handleLogin}> <div> <label>Email:</label> <input type="email" value={email} onChange={(e) => setEmail(e.target.value)} required /> </div> <div> <label>Password:</label> <input type="password" value={password} onChange={(e) => setPassword(e.target.value)} required /> </div> <button type="submit">Login</button> </form> {error && <p>{error}</p>} </div> ); } export default Login; import React, { useState } from 'react'; import { useNavigate } from 'react-router-dom'; import { createComplaint } from '../api'; const ComplaintForm = () => { const [subject, setSubject] = useState(''); const [description, setDescription] = useState(''); const [filePath, setFilePath] = useState(''); const navigate = useNavigate(); const handleSubmit = async (e) => { e.preventDefault(); const data = { subject, description, filePath }; try { await createComplaint(data); navigate('/success'); } catch (error) { console.error('Error submitting complaint', error); if (error.response && error.response.data) { console.error('Error response:', error.response.data); } else { console.error('Error message:', error.message); } alert('Failed to submit complaint'); } }; return ( <form onSubmit={handleSubmit}> <div> <label>Subject</label> <input type="text" value={subject} onChange={(e) => setSubject(e.target.value)} required /> </div> <div> <label>Description</label> <textarea value={description} onChange={(e) => setDescription(e.target.value)} required ></textarea> </div> <div> <label>File Path</label> <input type="text" value={filePath} onChange={(e) => setFilePath(e.target.value)} /> </div> <button type="submit">Submit Complaint</button> </form> ); }; export default ComplaintForm;
内容的提问来源于stack exchange,提问作者David
相关产品推荐
相关产品推荐

