prisma orm
Over in Part 1 we setup our project to require authentication to interact with the GraphQL API. Now, we’re going to look at logging in users and generating JSON Web Tokens (JWT) for our users to lock them out from data we don’t want them to access.
在第1部分中,我们设置了项目以要求进行身份验证才能与GraphQL API交互。 现在,我们将研究登录用户并为用户生成JSON Web令牌(JWT),以将其锁定在我们不希望他们访问的数据中。
We’re going to be using bcrypt to encrypt our passwords, since storing passwords as just normal strings is very bad for security, and jsonwebtoken to generate tokens we can use to verify if a user should have access to something.
我们将使用bcrypt来加密密码,因为仅将密码存储为普通字符串对安全性非常不利,而使用jsonwebtoken生成令牌则可以用来验证用户是否应该访问某些内容。
$ npm install bcryptjs jsonwebtoken
Let’s take a moment to go over the two libraries we’ll be using. If you’re already familiar with them then you can skip this as well as the next section.
让我们花点时间浏览一下我们将要使用的两个库。 如果您已经熟悉它们,则可以跳过本节以及下一节。
bcrypt
allows us to encrypt our passwords and compare that hashed version with another string to see if they’re the same. The point of this is that you can compare the hashed and un-hashed versions but you can never decode the original from it. This way, even if someone were to query for a users password, although they would get something, it wouldn’t be usable to break into anyone’s account.
bcrypt
允许我们加密密码,并将该哈希版本与另一个字符串进行比较,以查看它们是否相同。 这样做的目的是,您可以比较散列和未散列的版本,但永远无法从中解码原始版本。 这样,即使有人要查询用户密码,尽管他们会得到一些东西,但闯入任何人的帐户将不可行。
It’s pretty simple, it just takes the string we would like to hash, and the number of salts. A salt is just a cycle that it runs the password through its hashing algorithm, the higher the number the more secure it’ll be but the longer it’ll take to generate.
这很简单,只需要我们想要哈希的字符串和盐的数量。 盐只是通过哈希算法运行密码的一个周期,数字越高,安全性越高,但生成时间就越长。
import bcrypt from 'bcryptjs';
const password = 'pleaseDontHackMe3248';
console.log('Raw Password: ', password);
bcrypt.hash(password, 8)
.then(hashed => {
console.log('Secure Password: ', hashed);
// Must take the string version then the hashed version
const doesMatch = bcrypt.compare(password, hashed);
return doesMatch;
}
).then(doesMatch => console.log('Password Matches: ', doesMatch));
jsonwebtoken
is an extremely popular library for generating unique access tokens that we can use to verify for authenticity and even have expire over some period of time.
jsonwebtoken
是一个非常流行的库,用于生成唯一的访问令牌,我们可以使用该令牌来验证其真实性,甚至可以在一段时间内过期。
jwt.sign
takes in something to link the token to a particular user and a secret that we’ll use to verify if the token is valid, since anyone could generate their own outside of our app. We can set the expiration with the expiresIn
property and a string describing the time span, such as 5000
(milliseconds), '8d'
, or '9 months'
.
jwt.sign
接受了一些将令牌链接到特定用户的东西,以及一个我们将用来验证令牌是否有效的秘密,因为任何人都可以在我们的应用程序外部生成自己的密码。 我们可以使用expiresIn
属性和描述时间跨度的字符串(例如5000
(毫秒), '8d'
或'9 months'
)来设置到期时间。
const token = jwt.sign({ name: 'Someone' }, 'anotherSecret', { expiresIn: '1 day' });
console.log(token);
Now we can use jwt.verify
to use our secret to decrypt our user’s data that was stored. You’d normally be storing this secret as an environment variable of course.
现在,我们可以使用jwt.verify
来使用我们的机密来解密存储的用户数据。 通常,您通常会将此秘密存储为环境变量。
const decrypted = jwt.verify(token, 'anotherSecret');
console.log(decrypted);
Creating a login system is a very simple process. All we need to do is give every user a token when they run the login
or createUser
mutations, which would normally be saved and sent back from the client side, but we’ll stick with GraphQL Playground’s header for now.
创建登录系统是一个非常简单的过程。 我们需要做的就是在每个用户运行login
或createUser
突变时为他们提供一个令牌,这些令牌通常会保存并从客户端发送回去,但是现在我们将继续使用GraphQL Playground的标头。
Let’s start with adding some mutations and generating a token for our user. Since a user will only sometimes need a token, we can leave it nullable.
让我们开始添加一些变异并为我们的用户生成令牌。 由于用户有时仅需要令牌,因此我们可以将其保留为空。
type Mutation {
createUser(data: CreateUserInput): User!
loginUser(email: String!, password: String!): User!
}
type User {
id: ID!
name: String!
email: String!
password: String!
token: String
}
input CreateUserInput {
name: String!
email: String!
password: String!
}
With our schema in place, let’s try to create a new user with a valid token.
使用我们的架构后,让我们尝试使用有效令牌创建一个新用户。
const expiresIn = '1 day'; // We'll be using this value repeatedly.
const Mutation = {
async createUser(parent, { data }, { prisma }, info) {
const password = await bcrypt.hash(data.password, 10);
const user = await prisma.mutation.createUser({
data: {
...data,
password
}
});
// Since id is added by prisma it is unavailable when creating a user.
const userWithToken = {
...user,
token: jwt.sign({ userId: user.id }, process.env.TOKEN_SECRET, { expiresIn })
};
return userWithToken;
}
}
Finally, just add our secret to our environment variables, which by now, should look like this.
最后,只需将我们的秘密添加到我们的环境变量中,到现在,它应该看起来像这样。
API_SECRET=SuperSecretSecret
TOKEN_SECRET=EvenSecreterSecret
The last step for now is to use the user’s email to get their account and compare their encoded password with the one they entered. If it’s correct, then we’ll generate and return a new token for them.
现在的最后一步是使用用户的电子邮件获取他们的帐户,并将他们的编码密码与他们输入的密码进行比较。 如果正确的话,我们将为其生成并返回一个新令牌。
const expiresIn = '1 day';
const Mutation = {
async createUser(parent, { data }, { prisma }, info) {...},
async loginUser(parent, { email, password }, { prisma }, info) {
const user = await prisma.query.user({ where: { email } });
if (!user) throw new Error('No User Found');
const isValid = await bcrypt.compare(password, user.password);
if (!isValid) throw new Error('Wrong Password');
const userWithToken = {
...user,
token: jwt.sign({ userId: user.id }, process.env.TOKEN_SECRET, { expiresIn })
}
return userWithToken
}
}
Stay tuned for part 3, where I’ll cover validation for queries, mutations and subscriptions!
请继续关注第3部分,其中将介绍查询,突变和订阅的验证!
翻译自: https://www.digitalocean.com/community/tutorials/graphql-prisma-authentication-tokens-login
prisma orm