我正在尝试根据尝试登录的用户的秘密创建用户令牌。但是,我不想使用环境中的秘密,而是要使用分配给数据库内部用户对象的秘密。
import { Injectable } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { UserService } from '@src/modules/user/services';
@Injectable()
export class AuthService {
public constructor(private readonly jwtService: JwtService,
private readonly userService: UserService) {}
public async createToken(email: string): Promise<JwtReply> {
const expiresIn = 60 * 60 * 24;
const user = await this.userService.user({ where: { email } });
const accessToken = await this.jwtService.signAsync({ email: user.email },
/* user.secret ,*/
{ expiresIn });
return {
accessToken,
expiresIn,
};
}
}
我是Nestjs的新手,也许我错过了一些东西。
node-jsonwebtoken确实在sign(...)
函数中提供了必要的参数。 nestjs/jwt
缺少此参数(请参见代码)。在不使用node-jsonwebtoken
或更抽象的问题的情况下,您将如何解决此问题:在这里,我处理用户机密的方式是否有意义?谢谢。
答案 0 :(得分:1)
仅使用nest的JwtModule
还不可能,但是您可以自己轻松实现缺失的部分。
您可以通过调用以下路由来创建令牌:
user1(机密:“ 123”):https://yw7wz99zv1.sse.codesandbox.io/login/1
user2(机密:“ 456”):https://yw7wz99zv1.sse.codesandbox.io/login/2
然后用您的令牌呼叫受保护的路由'/'
,并接收您的用户:
curl -X GET https://yw7wz99zv1.sse.codesandbox.io/ \
-H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiIxIiwiaWF0IjoxNTUzNjQwMjc5fQ.E5o3djesqWVHNGe-Hi3KODp0aTiQU9X_H3Murht1R5U'
在AuthService
中,我只是使用标准的jsonwebtoken
库来创建令牌。然后,您可以从登录路线致电createToken
:
import * as jwt from 'jsonwebtoken';
export class AuthService {
constructor(private readonly userService: UserService) {}
createToken(userId: string) {
const user = this.userService.getUser(userId);
return jwt.sign({ userId: user.userId }, user.secret, { expiresIn: 3600 });
}
// ...
}
在JwtStrategy
中,您使用secretOrKeyProvider
而不是secretOrKey
,后者可以异步访问UserService
以动态获取用户密码:
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor(
private readonly authService: AuthService,
private readonly userService: UserService,
) {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
secretOrKeyProvider: (request, jwtToken, done) => {
const decodedToken: any = jwt.decode(jwtToken);
const user = this.userService.getUser(decodedToken.userId);
done(null, user.secret);
},
});
}
// ...
}
请注意,将不会使用您传递给JwtModule
之类的选项,例如expiresIn
,而是直接将您的选项传递给AuthService
。导入JwtModule
,不带任何选项:
JwtModule.register({})
我在这里处理用户秘密的方式有意义吗?
如果不知道您的确切要求,很难回答。我猜jwt有动态秘密的用例,但是随着它您将失去jwt的一个重要特性:它们是无状态的。这意味着您的AuthService
可以发出jwt令牌,而某些需要身份验证的ProductService
可以信任jwt(它知道秘密),而无需调用其他服务(即UserService
)必须查询数据库。
如果与用户相关的键不是硬性要求,请考虑频繁旋转键,而不要使用jwt的kid
属性。
答案 1 :(得分:1)
在postDelayed
版本 7.1.0 中添加了将secret
添加到JwtSignOptions的选项。
这样,示例将是:
nestjs/jwt
答案 2 :(得分:0)
我还需要使用不同的密钥对访问和刷新令牌进行签名。 如果你遵循 nestjs 文档,你会看到 JwtModule 是使用单个配置注册的,并且令牌是在没有选项的情况下签名的(使用默认配置)。要使用带有选项的 jwtService 签名函数,请使用空对象导入 JwtModule.register
import { JwtModule } from '@nestjs/jwt';
@Module({
imports: [JwtModule.register({})],
providers: [],
controllers: []
})
export class AuthModule {}
并制作具有不同标志选项的配置文件
@Injectable()
export class ApiConfigService {
constructor(private configService: ConfigService) {
}
get accessTokenConfig(): any {
return {
secret: this.configService.get('JWT_ACCESS_TOKEN_KEY'),
expiresIn: eval(this.configService.get('JWT_ACCESS_TOKEN_LIFETIME'))
}
}
get refreshTokenConfig(): any {
return {
secret: this.configService.get('JWT_REFRESH_TOKEN_KEY'),
expiresIn: eval(this.configService.get('JWT_REFRESH_TOKEN_LIFETIME'))
}
}
}
您可以使用所需的配置签署令牌
@Injectable()
export class AuthService {
constructor(private jwtService: JwtService, private apiConfigService: ApiConfigService ) {}
login(user: any) {
let payload = {username: user.username, id: user.id};
let jwt = this.jwtService.sign(payload, this.apiConfigService.accessTokenConfig);
//
return { token: jwt };
}
}