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

NestJS中如何通过AccessToken获取关联Profile的当前登录用户数据

问题描述

我想要获取当前登录用户的详细数据,包含User表和关联的Profile表的数据,但使用access token识别当前登录用户时,无法获取到与User表关联的Profile数据。

User表实体定义

@PrimaryGeneratedColumn('increment')
id: number;

@Column()
userName: string;

@Column({ unique: true, nullable: true })
email: string;

@Column({ unique: true })
phoneNumber: string;

@CreateDateColumn()
createdAt: Date;

@UpdateDateColumn()
updatedAt: Date;

@OneToOne(() => Profile, (profile) => profile.user)
profile: Profile;

refreshTokens: UserRefreshToken[];

Profile表实体定义

@Entity()
export class Profile extends BaseEntity {
    @PrimaryGeneratedColumn('increment')
    id: number;

    @Column({ nullable: true })
    name: string;

    @Column({ nullable: true })
    gender: string;

    @Column({ nullable: true })
    phoneNumber: string;

    @Column({ nullable: true })
    email: string;

    @Column({ nullable: true })
    nrm: string;

    @Column({ nullable: true })
    birthDate: Date;

    @CreateDateColumn({ nullable: true })
    createdAt: Date;

    @UpdateDateColumn({ nullable: true })
    updatedAt: Date;

    @OneToOne(() => Users)
    @JoinColumn()
    user: Users;
}

获取当前登录用户的控制器代码

import { Controller, Get, UseGuards } from '@nestjs/common';
import { ProfileService } from './profile.service';
import { accessTokenGuard } from 'src/guards/access-token.guard';
import { GetUser } from 'src/modules/auth/get-user-decorator';
import { Users } from '../users/entity/users.entity';

@Controller('profile')
export class ProfileController {
    constructor(private service: ProfileService) {}

    @UseGuards(accessTokenGuard)
    @Get()
    async getData(@GetUser() user: Users) {
        return user; // 示例:获取当前登录用户
    }
}

自定义GetUser装饰器代码

import { createParamDecorator, ExecutionContext } from '@nestjs/common';

export const GetUser = createParamDecorator(
    (_data, ctx: ExecutionContext): any => {
        const req = ctx.switchToHttp().getRequest();
        console.log(req.user);
        return req.user;
    },
);

当前得到的响应

{
    "id": 41,
    "userName": "73748484",
    "email": null,
    "phoneNumber": "74747474",
    "createdAt": "2023-06-25T07:35:57.467Z",
    "updatedAt": "2023-06-25T07:35:57.461Z"
}

期望的响应(无需再次查询数据库,仅通过access token获取)

{
      "id": 41,
        "userName": "1288383838",
        "email": null,
        "phoneNumber": "737373838",
        "createdAt": "2023-06-25T07:35:57.467Z",
        "updatedAt": "2023-06-25T07:35:57.461Z",
        "profile": {
            "id": 36,
            "name": "Profile Name",
            "gender": "Male",
            "phoneNumber": "3737737473747",
            "email": "",
            "nrm": "99.jejejdj.05",
            "birthDate": "1999-09-07T08:00:00.000Z",
            "createdAt": "2023-06-25T07:36:32.586Z",
            "updatedAt": "2023-06-25T07:36:32.586Z"
       }
}

解决方案

要实现通过access token直接返回包含Profile的用户数据,核心是生成access token时就把关联的Profile数据存入token payload,验证token后就能直接从req.user中拿到完整数据,无需二次查询数据库。

1. 修改JWT生成逻辑,加入Profile数据

在生成access token的代码(比如AuthService的登录方法)中,查询用户时关联加载Profile,再将用户和Profile数据一起存入JWT payload:

// AuthService示例代码
async login(loginDto: LoginDto) {
  // 查询用户时关联加载profile
  const user = await this.usersRepository.findOne({
    where: { phoneNumber: loginDto.phoneNumber },
    relations: ['profile'], // 关键:加载关联的Profile
  });

  // 此处省略用户验证逻辑...

  // 构造payload,包含用户和profile数据
  const payload = {
    sub: user.id,
    userName: user.userName,
    email: user.email,
    phoneNumber: user.phoneNumber,
    profile: user.profile, // 将profile加入payload
  };

  // 生成access token
  const accessToken = this.jwtService.sign(payload);

  return { accessToken };
}

2. 确保Guard验证后完整解析payload

如果使用自定义的accessTokenGuard,要确保验证token后将完整payload挂载到req.user上:

// 自定义accessTokenGuard示例
async canActivate(context: ExecutionContext): Promise<boolean> {
  const request = context.switchToHttp().getRequest();
  const token = this.extractTokenFromHeader(request);
  
  if (!token) {
    throw new UnauthorizedException();
  }
  
  try {
    // 验证token并解析完整payload
    const payload = await this.jwtService.verifyAsync(token, { secret: process.env.JWT_SECRET });
    // 将完整payload赋值给req.user
    request.user = payload;
  } catch {
    throw new UnauthorizedException();
  }
  
  return true;
}

如果使用NestJS内置的AuthGuard('jwt'),默认会自动将完整payload解析到req.user,无需额外修改。

3. 实体关联优化(可选)

如果后续需要从数据库查询时自动加载Profile,可以在User实体的@OneToOne装饰器中添加{ eager: true },但这仅影响数据库查询,不改变JWT payload内容:

// User实体中修改关联配置
@OneToOne(() => Profile, (profile) => profile.user, { eager: true })
profile: Profile;

调整完成后,通过@GetUser()拿到的user对象就会包含完整的Profile数据,和期望的响应一致,且无需二次查询数据库。

内容的提问来源于stack exchange,提问作者Robby Izhar Ramadhana

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 17:54:57