You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Symfony 6.4+React+JWT登录报错:key 'email'需为字符串,传入NULL

问题:JWT登录报错“The key 'email' must be a string, 'NULL' given”

使用Symfony 6.4、React、API Platform和LexikJWTAuthentication开发时,API已通过JWT正常保护,但实现登录跳转仪表盘功能时,出现上述错误。请求参数中email和密码均非空,相关配置如下:

相关配置

security.yml

security:
  enable_authenticator_manager: true
  password_hashers:
    Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto'
  providers:
    app_user_provider:
      entity:
        class: App\Entity\User
        property: email

  firewalls:
    dev:
      pattern: ^/{_{profiler|wdt}}/
      security: false

    login:
      pattern: ^/api/login
      stateless: true
      form_login:
        check_path: /api/login_check
        username_parameter: email
        password_parameter: password
        default_target_path: app_dashboard
        success_handler: lexik_jwt_authentication.handler.authentication_success
        failure_handler: lexik_jwt_authentication.handler.authentication_failure

    api:
      pattern:   ^/api/
      stateless: true
      jwt: ~

  access_control:
    - { path: ^/api/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
    - { path: ^/api,       roles: IS_AUTHENTICATED_FULLY }


when@test:
  security:
    password_hashers:
      Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface:
        algorithm: auto
        cost: 4
        time_cost: 3
        memory_cost: 10

lexik_jwt_authentication.yml

lexik_jwt_authentication:
    secret_key: '%env(resolve:JWT_SECRET_KEY)%'
    public_key: '%env(resolve:JWT_PUBLIC_KEY)%'
    pass_phrase: '%env(JWT_PASSPHRASE)%'
    token_ttl: 3600

    api_platform:
        check_path: /api/login_check
        username_path: email
        password_path: security.credentials.password

Login.jsx

const Login = () => {
    const [email, setEmail] = useState("");
    const [password, setPassword] = useState("");

    const handleEmailChange = (e) => setEmail(e.target.value);
    const handlePasswordChange = (e) => setPassword(e.target.value);

    const handleSubmit = async (e) => {
        e.preventDefault();
        try {
            const response = await axios.post(
                'https://localhost/index.php/api/login_check',
                {email, password},
                {
                    headers: {
                        'Content-Type': 'application/json',
                    },
                }
            );
            console.log('Login successful:', response.data);
        } catch (error) {
            console.error('Error during login:', error);
        }
    };
return (
    <form onSubmit={handleSubmit}>
        <Input
            type="email"
            variant="bordered"
            size="lg"
            label="Email"
            placeholder="Enter your email"
            labelPlacement="outside"
            className="pt-5 pb-5"
            value={email}
            id="email"
            name="_email"
            onChange={handleEmailChange}
        />
        <Input
            type="password"
            variant="bordered"
            size="lg"
            label="Password"
            placeholder="Enter your password"
            labelPlacement="outside"
            className="pt-5 pb-12"
            value={password}
            id="password"
            name="_password"
            onChange={handlePasswordChange}
        />
        <CustomButton label="Login" type="submit"/>
    </form>
)

请求参数示例

{"email":"admin@test.com","password":"test"}

解决思路

  • 修正LexikJWT的password_path配置
    当前password_path: security.credentials.password是针对表单提交的嵌套路径,但你使用JSON直接提交password字段,需改为:

    # lexik_jwt_authentication.yml
    api_platform:
        check_path: /api/login_check
        username_path: email
        password_path: password
    
  • 移除无状态认证中的跳转配置
    login防火墙设置了stateless: true,无状态认证不需要后端跳转,default_target_path会干扰JWT认证流程,删除security.yml中form_login下的该配置:

    # security.yml的login防火墙部分
    form_login:
      check_path: /api/login_check
      username_parameter: email
      password_parameter: password
      # 移除 default_target_path: app_dashboard
      success_handler: lexik_jwt_authentication.handler.authentication_success
      failure_handler: lexik_jwt_authentication.handler.authentication_failure
    
  • 替换form_login为json_login
    form_login默认处理表单格式(x-www-form-urlencoded),而你用JSON提交,改用json_login更适配API场景:

    # security.yml的login防火墙部分
    login:
      pattern: ^/api/login
      stateless: true
      json_login:
        check_path: /api/login_check
        username_path: email
        password_path: password
        success_handler: lexik_jwt_authentication.handler.authentication_success
        failure_handler: lexik_jwt_authentication.handler.authentication_failure
    
  • 验证User实体的标识符方法
    Symfony 6+中UserInterface用getUserIdentifier()替代getUsername(),确保User实体返回email作为标识符:

    // src/Entity/User.php
    public function getUserIdentifier(): string
    {
        return (string) $this->email;
    }
    
  • 统一表单字段name属性
    React表单Input的name为_email、_password,但提交的JSON字段是email、password,将name改为一致,避免潜在解析问题:

    <Input
        // ...其他属性
        name="email"
        onChange={handleEmailChange}
    />
    <Input
        // ...其他属性
        name="password"
        onChange={handlePasswordChange}
    />
    

内容的提问来源于stack exchange,提问作者balrcoding

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 21:00:55