[camp]整理5週間(4.11~15)
4.11
暗号化
暗号化は、情報を秘密保持コードに変換する技術プロセスであり、伝送、受信、記憶された情報の復号化を防止する.
暗号化されていないファイルに含まれる変換されていないメールは「コメント」、暗号化された形式のメールは「パスワード」と呼ばれます.
一方向暗号化と双方向暗号化の比較
JWT
JWT(Json Web Token)は、Json形式でユーザ属性を格納するClaimベースのWeb Tokenである.主に会員認証または情報伝達に使用されるJWTは、以下のように処理される.
生成されたトークンがHTTP通信を行う場合
使用方法は
{ "Authorization": "Bearer {생성된 토큰 값}", }
と同じです.Nest.js Guard
誰もがAPIの最後の防御膜Beasを実行することを阻止するのは無理です...
利用
@nestjs/passport
@UseGuards(AuthGuard)
と一緒に使用します.import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';
export class JWtAccessStrategy extends PassportStrategy(Strategy, 'aaa') {
constructor() {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
secretOrKey: 'myAccessKey',
}); // 검증부
}
// 검증이 완료되면 해당부분이 실행된다.
validate(payload) {
console.log(payload);
}
}
4.12
RefreshToken
ブラウザがバックエンドサーバにログイン要求を送信すると、2つのJWTトークンが送信されます.
クッキーのRefreshTokenを使用して、対応するトークンを復元します.
Cookie & Session (Storage)
Cookie
これは、サーバの代わりにWebブラウザに保存し、要求時にサーバに送信してユーザーをパーティション化できることを意味します.
サーバはユーザのWebブラウザ(Local)に格納され,異なるブラウザは異なるCookieを格納する.
Session
CookieはWebブラウザに露出しているため、セキュリティの問題があり、これらのセキュリティの問題を解決するために、認証情報に対応するコンテンツがセッションに格納される
会話の動作原理
1. Client -> Request(Server)
2.サーバー->クライアントにCookie値をチェックし、セッションがない場合は応答値を再生成して送信
3.Client->各リクエストのセッション値をCookieに入れてリクエストを送信
4.サーバ->セッションによるユーザーの特定
5.ログイン要求時にセッションをリフレッシュし、新しいセッションに応答する
6.クライアント->終了(ブラウザ終了)時にセッションを削除し、サーバ上でもセッションを削除
ソーシャルログインとは?
OAuthは
ネイバーにログイン...KaKaoでログインするなど...他の外部サービスが認証要求を提供できるようにする
ソーシャル・ログインの順序
Clinetからサーバへのログイン要求の送信
サーバは、対応するソーシャルサービスにリクエストを送信し、応答としてトークンを受信します.
サーバが受信したトークン情報を使用してアクセス権を付与し、ログインとユーザー情報を保存します.
ログインが完了したら、別の場所に移動させます.
4.13
ソーシャル・ログインの実施(主な項目)
カイドウ羽
Controller
import { User } from '../users/entities/user.entity';
import { AuthService } from './auth.service';
import { Controller, Get, Req, Res, UseGuards } from '@nestjs/common';
import { Request, Response } from 'express';
import { AuthGuard } from '@nestjs/passport';
interface IOAuthUser {
user: Pick<User, 'email' | 'password' | 'name' | 'phone'>;
}
@Controller()
export class AuthController {
constructor(private readonly authService: AuthService) {}
@Get('/login/google')
@UseGuards(AuthGuard('google'))
async loginGoogle(
@Req() req: Request & IOAuthUser, //
@Res() res: Response,
) {
this.authService.socialLogin(req, res);
}
@Get('/login/naver')
@UseGuards(AuthGuard('naver'))
async loginNaver(
@Req() req: Request & IOAuthUser, //
@Res() res: Response,
) {
this.authService.socialLogin(req, res);
}
@Get('/login/kakao')
@UseGuards(AuthGuard('kakao'))
async loginKakao(
@Req() req: Request & IOAuthUser, //
@Res() res: Response,
) {
this.authService.socialLogin(req, res);
}
}
リクエストがログインエンドポイントである場合、サービス団はリクエストを処理し、ソーシャルログインに合致するapiにリクエストを送信する.Service
async socialLogin(req, res) {
let user = await this.userService.findOne({ email: req.user.email });
if (!user) {
const createUser = new CreateUserInput();
createUser.email = req.user.email;
createUser.name = req.user.name;
createUser.password = req.user.password;
createUser.phone = req.user.phone;
// 1:1관계의 결제테이블
// id값을 직접생성해서 넣어준다
const paymentId = v4();
createUser.payment = { id: paymentId, name: 'none' };
user = await this.userService.create({ createUserInput: createUser });
}
// 로그인
this.setRefreshToken({ user, res });
res.redirect(
302,
'http://localhost:5500/main-project/frontend/login/index.html',
);
}
ソーシャルログインリクエストを受け取ると、1.現在そのプレイヤーが存在する場合は、リフレッシュトークンを設定し、ログイン後にリフレッシュを行う
4.14
支払の種類と実施
上の図に示すように、ブランチと機能を実現する必要があるため、直接支払APIの作成は困難である
したがって、これらの支払いプロセスをプロジェクトに直接適用することは非常に困難であり、外部APIを使用すれば簡単に実施することができる.
IamPort API
入力APIストリーム
お会計ボタンをクリックして、フロントからimport社のAPIにお会計をお願いします
IMPORT APIはPG(決済代理会社)に支払いを要求する
PGクレジットカード会社に支払要求
決済が完了すると、フロントからバックグラウンドサーバに決済イベントへの応答が送信されます
バックエンドはDBの支払い情報に基づいてデバイスに格納される.
自動ログイン
Resolver
@Mutation(() => Order)
@UseGuards(GqlAuthAccessGuard)
async createOrder(
@Args('impUid') impUid: string,
@Args('merchantUid') merchantUid: string,
@Args('subscribeId')
subscribeId: string,
@CurrentUser() currentUser: ICurrentUser,
) {
//
return await this.orderService.create({
impUid,
merchantUid,
subscribeId,
currentUser,
});
}
Service async create({ impUid, merchantUid, subscribeId, currentUser }) {
// 토큰가져오기 + 결제정보가져오기도 서비스로 나눈다
// 1. 아임포트 토큰을 가져온다.
const token = await this.iamportService.createIamPortToken();
// 2. 결제정보를 찾는다
const iamPortResult = await this.iamportService.searchIamPort({ impUid });
const amount = iamPortResult.data.response.amount;
// 3. 프론트에서 넘겨준 제품과 검증이 완료된 유저 정보로 해당 리포지토리로 검색을한다.
const user = await this.userRepository.findOne({
id: currentUser.id,
});
const subscribe = await this.subscribeRepository.findOne({
id: subscribeId,
});
// 4. 결제정보 (amonut)와 subscribe()의 가격이 맞는지 확인
if (!(amount == subscribe.price)) {
throw { status: 'forgery', message: '위조된 결제시도입니다!!!' };
} // FIXME 결제취소 완성되면 추가해야한다
// 결제정보가 맞다면 존재하는지 검증한다.
const checkimpUid = await this.orderRepository.findOne({
where: { impUid: impUid },
});
// 존재하면에러
if (checkimpUid) {
throw new ConflictException('이미 등록된 결제입니다.');
}
const userSubs = await this.userSubscribeRepository.save({
subscribe: subscribe,
user: user,
});
console.log(userSubs);
return await this.orderRepository.save({
impUid: impUid,
merchantUid: merchantUid,
userSubscribe: userSubs,
});
}
これに応じて、認証および支払イベントに基づく情報がDBに格納される4.15
支払の検証と取り消し
学習決済の検証とキャンセルを基にimport APIで直接実現する.
実施の過程で取り消しを参考にしたところ、いい文章があった.
このような場合、IMPORTに問い合わせたところ、私たちの状況によって異なる回答が得られました.
もし相手が故意に金額を変更して私たちに損失をもたらしたので、出荷しないし、返金もしないなら
もちろん払い戻しのため、カスタマーサービスに連絡します.そうでなければ金額が異なる場合はコード自動返金処理を行い、
いいと言います.
上記の対応は、決済時に相手が偽改ざんをしたので決済は成功したが、DB(偽改ざんの場合)に保存できなければ、どのように決済しても完了するが、サーバ側がどのように対応するかによって方法が異なる.
もし私が支払い時に上記の変更をしたら、自動返金のためにFIX
// 허위로 결제를 만들거나 유저가 취소를 요청했을경우의 아임포트서비스 취소
async cancelIamPort({ impUid, reason = '', currentUser }) {
// 1. 아임포트 토큰을 가져온다.
const token = await this.iamportService.createIamPortToken();
// 2. 결제정보를 찾는다
const iamPortResult = await this.iamportService.searchIamPort({ impUid });
if (iamPortResult.data.response.status === 'canclled') {
throw new UnprocessableEntityException('이미 취소가된 결제입니다..');
}
const currentOrder = await this.orderRepository.findOne({
relations: [
'userSubscribe',
'userSubscribe.user',
'userSubscribe.subscribe',
],
where: { impUid: impUid },
});
const checkUser = await this.userRepository.findOne({
where: { id: currentOrder.userSubscribe.user.id },
});
// 현재유저의 아이디와 현재주문정보의 유저 아이디를 체크한다.
if (currentUser.id != checkUser.id) {
throw new UnprocessableEntityException(
'취소하려는 결제의 유저정보와 현재 유저가 맞지 않습니다.',
);
}
const merchant_uid = currentOrder.merchantUid;
const checksum = currentOrder.userSubscribe.subscribe.price;
const cancelResult = await this.iamportService.cancelIamPort({
reason,
impUid,
merchant_uid,
checksum,
});
return cancelResult.data.message;
}
このように注文をキャンセルするかどうかを確認し、注文情報が正しいかどうかを確認した後、最終的に注文をキャンセルします.Reference
この問題について([camp]整理5週間(4.11~15)), 我々は、より多くの情報をここで見つけました https://velog.io/@sjy0917/camp-5주차-정리テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol