JWT, Bcrypt 및 GraphQL Nexus를 사용한 인증

프로그램의 프레임워크 인코딩을 완료했지만, 한 가지가 부족합니다. — 인증JSON Web Tokens 및 Bcrypt를 사용하여 이 옵션을 추가할 수 있습니다.본 강좌의 기초는 대부분의 모델 구축 구조와 유사해야 하지만 우리는 GraphQL Nexus을 사용할 것이다.저희도 Prisma를 저희 ORM으로 사용하지만 다른 ORM이나 데이터베이스는 모두 가능합니다.
이 강좌는 GraphQL의 돌연변이, 조회, 해석기, 상하문을 이해한다고 가정합니다. GraphQL을 이해하지 못하면 How to GraphQL 좋은 출발점입니다.
최종 응용 프로그램은 사용자가 JSON 웹 토큰을 저장하고 사용함으로써 계정을 만들고 로그인할 수 있도록 합니다.JWT는 각 방면에서 전송된 정보를 포함하는 문자열로 사용자의 신분을 검증하는 좋은 방법이다. 왜냐하면 사용자 정보를 안전하게 저장하고 디지털 서명을 제공할 수 있기 때문이다.
Google 응용 프로그램에서는 사용자가 이 JWT를 사용하여 로그인하고 등록할 수 있도록 합니다.백엔드에서 유효 부하를 만들고 JWT 기밀을 추가하며 로그인과 등록 변체를 설정하여 권한 수여 헤더를 정확하게 생성할 것입니다.앞에서, 우리는 현재 로그인한 사용자를 얻기 위해 권한 수여 영패를 머리에 전달하고, 조회를 설정할 것입니다.

백엔드


1. 도구 설치🛠


우선, Bcrypt와 JSON 웹 토큰을 설치해야 합니다!
yarn add bcrypt jsonwebtoken
이제 시작할 준비가 됐어요.✨

2. 우리 JWT의 비밀을 만들어라🗝️


JWT 기밀을 설정할 수 있습니다. config.ts 파일에 다음과 같은 내용이 추가되었습니다.
export default {  
  ...  
  jwt: {  
    JWT_SECRET: 'super-secret',  
  },  
}

3. 유효 하중 생성🚚


영패와 사용자 정보를 요청자에게 정확하게 되돌려 주기 위해서, 우리는 유효 부하를 설정해야 한다.
export const UserLoginPayload = objectType({  
  name: 'UserLoginPayload',  
  definition: t => {  
    t.field('user', {  
      type: 'User',  
    })  
    t.string('token')  
  },  
})
우리가 여기서 하는 것은 userLoginPayload라는 대상 유형을 만드는 것이다.우리는 유형을 되돌아갈 수 있는 User 필드와 사용자가 등록하거나 로그인할 때 생성된 영패로 정의합니다.

4. 로그인 및 등록 설정🚪🚶


사용자 등록과 로그인을 설정하기 위해 두 개의 새로운 변이 필드userLoginuserRegister를 만들었습니다.우리는 반환 형식을 UserLoginPayload로 설정하여 반환Usertoken로 설정할 수 있습니다. 우리의 매개 변수는 전방 표에서 수집한 사용자 이름과 비밀번호입니다.다음은 GraphQL Nexus의 돌연변이입니다.
export const userLogin = mutationField('userLogin', {  
  type: UserLoginPayload,  
  args: {  
    username: stringArg({ required: true }),  
    password: stringArg({ required: true }),  
  },  
})

export const userRegister = mutationField('userRegister', {  
  type: UserLoginPayload,  
  args: {  
    username: stringArg({ required: true }),  
    password: stringArg({ required: true }),  
  },  
})
이후 돌연변이에 해상도가 추가되었습니다.
export const userLogin = mutationField('userLogin', {  
  type: UserLoginPayload,  
  args: {  
    username: stringArg({ required: true }),  
    password: stringArg({ required: true }),  
  },  
  resolve: async (root, args, context, info) => {  
    try {  
      const { password, ...user } = await context.prisma.user({  
        where: {  
          userName: args.username,  
        },  
      })  
      var validpass = await bcrypt.compareSync(args.password, password)  
      if (validpass) {  
        const token = jwt.sign(user, config.jwt.JWT_SECRET)  
        return {  
          user: user,  
          token,  
        }  
      }  
      return null  
    } catch (e) {  
      console.log(e)  
    }  
  },  
})
우리는 이미 해석 프로그램을 추가했다.이것은 좀 감당하기 어려울 수도 있으니, 우리는 그것을 몇 부분으로 나누자.
const { password, ...user } = await context.prisma.user({  
        where: {  
          userName: args.username,  
        },  
      })
여기서, 우리는 User 데이터를 얻으려고 시도합니다.await context.prisma.users({where: {userName: args.username} 데이터베이스에서 User 정보를 얻고 password, ...user에 저장한다.우리는 암호를 분리했기 때문에 사용자 변수나 JSON 웹 영패 데이터에 포함되지 않습니다. 다음과 같습니다.
var validpass = await bcrypt.compareSync(args.password, password)  
      if (validpass) {  
        const token = jwt.sign(user, config.jwt.JWT_SECRET)  
        return {  
          user: user,  
          token,  
        }  
      }  
      return null
Bcrypt를 사용하여 암호 값이 동일한지 비교합니다.암호가 일치하면 구성 파일과 user의 JWT 기밀을 사용하여 JWT가 생성됩니다.(암호 데이터를 사전에 분리하지 않으면 사용자 데이터와 함께 되돌아와 JWT에 저장됩니다.)😱!) 하지만 이제 유효 하중user 데이터와 JWT를 돌아왔습니다!
등록 과정은 상대적으로 유사하다.
export const userRegister = mutationField('userRegister', {  
  type: UserLoginPayload,  
  args: {  
    username: stringArg({ required: true }),  
    password: stringArg({ required: true }),  
  },  
  resolve: async (root, args, context) => {  
    try {  
      const existingUser = await context.prisma.user({  
        where: {  
          userName: args.username,  
        },  
      })  
      if (existingUser) {  
        throw new Error('ERROR: Username already used.')  
      }  
      var hash = bcrypt.hashSync(args.password, 10)

      const { password, ...register } = await context.prisma.createUser({  
        userName: args.username,  
        password: hash,  
      })  
      const token = jwt.sign(register, config.jwt.JWT_SECRET)  
      return {  
        user: register,  
        token: token,  
      }  
    } catch (e) {  
      console.log(e)  
      return null  
    }  
  },  
})
우리 다시 헤어지자.
const existingUser = await context.prisma.user({  
        where: {  
          userName: args.username,  
        },  
      })  
      if (existingUser) {  
        throw new Error('ERROR: Username already used.')  
      }
이전에, 우리는 사용자 이름이 존재하는지 물었다.이것은 상대적으로 같은 것이다. 단지 지금 우리가 어떤 내용을 되돌릴 때 오류가 발생했을 뿐이다. 왜냐하면 모든 사용자 이름이 유일해야 하기 때문이다.
var hash = bcrypt.hashSync(args.password, 10)

      const { password, ...register } = await context.prisma.createUser({  
        userName: args.username,  
        password: hash,  
      })
우리는 bcrypt를 사용하여 표에 전달된 암호를 산열하고 암호를 전달하며 생성할salt 길이를 생성합니다.그 후, createUser 변이는 우리의 사용자 이름과 새로운 산열 비밀번호를 사용하여 새로운 사용자를 생성합니다.
const token = jwt.sign(register, config.jwt.JWT_SECRET)  
      return {  
        user: register,  
        token: token,  
      }
유효 부하의 생성과 귀환 방식은 사용자 로그인과 같습니다.

5. 컨텍스트에 사용자 추가🧮


우리 사용자는 지금 로그인하고 등록할 수 있습니다!이제 쿼리와 뷰어 필드를 만들어서 이 정보를 전방으로 되돌릴 수 있습니다.
현재 사용자를 컨텍스트에 추가하는 것부터 시작합니다.
export interface Context {  
  prisma: Prisma  
  currentUser: User  
}

export default async ({ req }) => {  
  const currentUser = await getUser(  
    req.get('Authorization'),  
    config.jwt,  
    prisma,  
  )  
  return {  
    prisma,  
    currentUser  
  }  
}
여기에 currentUser 형식의 변수 User 를 추가해서 Context 에서 내보낼 것입니다.우리는 getUser 함수 (다음 단계에서 이 함수를 어떻게 사용하는지 토론할 것이다. 한 마디로 하면, 이 함수는 우리의 User 유형) 을 되돌려 우리의 사용자 정보를 되돌려줄 수 있다. 방법은 영패를 req.get('Authorization') (우리의 머리에서 영패를 얻는 것), JWT 기밀, Prisma 클라이언트에게 전달하는 것이다.

6.getUser 함수 만들기👶


응용 프로그램에서 사용자 정보를 조회하려면 제목에서 사용자의 영패를 받아야 합니다.
export default async (authorization, secrets, prisma: Prisma) => {  
  const bearerLength = 'Bearer '.length  
  if (authorization && authorization.length > bearerLength) {  
    const token = authorization.slice(bearerLength)  
    const { ok, result } = await new Promise(resolve =>  
      jwt.verify(token, secrets.JWT_SECRET, (err, result) => {  
        if (err) {  
          resolve({  
            ok: false,  
            result: err,  
          })  
        } else {  
          resolve({  
            ok: true,  
            result,  
          })  
        }  
      }),  
    )  
    if (ok) {  
      const user = await prisma.user({  
        id: result.id,  
      })  
      return user  
    } else {  
      console.error(result)  
      return null  
    }  
  }  
  return null  
}
우리 한 걸음 한 걸음 이야기합시다.
const bearerLength = 'Bearer '.length  
  if (authorization && authorization.length > bearerLength) {  
    const token = authorization.slice(bearerLength)  
    ...  
  }  
  return null  
}
영패가 우리 Bearer 문자열보다 길었는지 확인하기 위한 기본적인 오류 검사가 있습니다. 만약 그렇다면, 우리는 Bearer 문자열을 잘라서 영패를 추출할 수 있습니다.
const { ok, result } = await new Promise(resolve =>  
      jwt.verify(token, secrets.JWT_SECRET, (err, result) => {  
        if (err) {  
          resolve({  
            ok: false,  
            result: err,  
          })  
        } else {  
          resolve({  
            ok: true,  
            result,  
          })  
        }  
      })  
    )
현재, 우리는 우리의 비밀로 영패를 검증하고, 전송된 영패가 유효한지, 그리고 JWT에서 온 result (이것은 우리의 user 유형) 으로 우리의 약속을 해결하고 있다.
if (ok) {  
      const user = await prisma.user({  
        id: result.id,  
      })  
      return user  
    } else {  
      console.error(result)  
      return null  
    }  
  }
마지막으로 만약에 영패가 유효하다면, 우리는 영패에서 얻은 ID를 사용하여 사용자를 조회하고 그것을 되돌려줍니다.

7. 사용자 쿼리 및 뷰어 필드 만들기🔬


프로그램에서 현재 로그인한 사용자의 정보를 조회할 수 있도록 뷰어 필드와 사용자 조회를 만들 수 있습니다.
t.string('getCurrentUser', {  
  resolve: async (root, args, context, info) => {  
    return context.prisma.user  
  },  
})
우리는 새로운 조회 getCurrentUser 를 만들 수 있습니다. 이것은 Context 함수에서 얻은 값을 되돌려줍니다. 그러면 현재 로그인한 모든 사용자를 쉽게 조회할 수 있습니다.
마지막으로, 우리는 검색에 viewer 필드를 추가해야 한다.
t.field('viewer', {  
      type: 'User',  
      nullable: true,  
      resolve: (root, args, context) => {  
        return context.currentUser  
      },  
    })
이것은 우리가 위아래 문장에 추가한 currentUser 만을 되돌려줍니다.

프런트엔드


1. 로그인 및 등록💎


현재 백엔드가 완성되었습니다. 백엔드에서 만든 해상도를 사용하여 간단한 백엔드 해결 방안을 실현할 수 있습니다.
const SIGNUP_MUTATION = gql`  
  mutation UserRegister($username: String!, $password: String!) {  
    userRegister(username: $username, password: $password) {  
      user {  
        id  
        userName  
      }  
      token  
    }  
  }  
`;
다음은 폼을 제출할 때 새 사용자를 만드는 간단한 등록 변종입니다.백엔드에서 만든 userRegister 함수를 사용하고 있습니다. 필요한 정보를 되돌려주는 동시에 사용자 이름과 비밀번호를 입력하십시오.
<Mutation  
    mutation={SIGNUP_MUTATION}  
    onCompleted={data => _confirm(data)}  
  >  
...  
</Mutation>
다음에 우리는 등록 돌연변이를 Mutation가 제공하는 react-apollo 구성 요소에 추가할 수 있다.돌연변이가 완성된 후에 우리는 함수_confirm를 호출한다.
_confirm = async data => {  
  const { token } = data.userLogin;  
  this._saveUserData(token);  
};

_saveUserData = async token => {  
  try {  
    await AsyncStorage.setItem(AUTH_TOKEN, token);  
  } catch (e) {  
    console.log("ERROR: ", e);  
  }  
};
우리의 _confirm 함수는 우리의 돌연변이에서 되돌아온 data에서 표시를 추출하여 _saveUserData에 전달하는 것이다.이 함수는 tokenAsyncStorage에 저장합니다(본 컴퓨터 개발을 사용하지 않으면 영패는 LocalStorage에 저장됩니다).
경고: 보충 설명으로 localStorage를 사용하여 JWT를 저장하는 것은 생산에서 가장 좋은 실천이 아닙니다. 더 많은 내용을 읽을 수 있습니다here.
로그인하는 과정은 매우 비슷합니다. 우리는 우리의 SIGNUP_MUTATION 와 우리의 LOGIN_MUTATION 를 교환하기만 하면 됩니다.

2. 영패를 머리에 꽂기💯


const authLink = setContext(async (_, { headers }) => {  
  const token = await AsyncStorage.getItem(AUTH_TOKEN);  
  return {  
    headers: {  
      ...headers,  
      authorization: token ? `Bearer ${token}` : ""  
    }  
  };  
});
우리는 apollo-link-contextsetContext 함수를 사용하여 프로그램의 제목을 설정합니다.우리는 AsyncStorage 에서 권한 수여 영패를 받아서 표두에 저장하고 있습니다.

3. 사용자 정보 조회🙆


우리의 부지런한 노력으로 우리는 응용 프로그램 중 어느 곳에서든 사용자 정보를 조회할 수 있다. 그래, 이렇게 간단하다.
const GET_USER = gql`  
  query getUser {  
    viewer {  
      id  
    }  
  }  
`;

결론


그것만 있으면 당신의 신분 검증을 지금 설정할 수 있습니다!현재 필요한 부하를 되돌려주고, 프로그램의 어느 위치에서든 현재 로그인한 사용자를 조회할 수 있는 해상도를 만들었습니다.이 강좌의 영감은 스빈세 칼리의 위대한 강좌GraphQL Authentication with React Native & Apollo에서 나왔다 — 본 강좌에서 소개한 내용을 더 깊이 이해하고 싶으시면 보십시오.질문이나 건의가 있으면 언제든지 댓글을 달아주시거나 our website를 통해 연락 주세요.감사합니다!

좋은 웹페이지 즐겨찾기