登録/ログイン/ログアウト/権限確認機能の実装
サーバの作成、dbへの接続後、クライアントが要求を受信し、会員登録、ログイン、権限確認を行う機能を実現しました.
クライアントに単独で処理させ,まず受信要求と処理のバックエンド部分のみを処理する.
開発は2つの環境で行うことができる.1つはローカルでローカル開発モードで行うことができ、もう1つはherokuまたは他のクラウドサービスを使用して導入(導入)し、本番モードで開発することができます.
二つのことは別々に考えなければならない.
アカウントとパスワードを省略しました.
クライアントに単独で処理させ,まず受信要求と処理のバックエンド部分のみを処理する.
会員登録 /api/users/registerでpostを介してサブスクリプション情報を渡します。 ユーザモデルを使用して受信したreq。新しいモデルインスタンスユーザーとしてbodyを作成します。 その後saveメソッドでuserをdbに保存します。(保存する前にpasswordはhashing暗号化を使用します。) 暗号化プロセス:bcryptのgenSaltメソッドによってsaltが生成され、ユーザーが指定したsaltRoundsによってhashメソッドでユーザーのpasswordがハッシュされます。したがって、mongodbにはハッシュパスワードが含まれています。
ログオンLogin&ログオフLogout (ログイン) /api/user/loginでpostを介してログイン情報を渡します。 ユーザーはfindOneメソッドで同じ電子メールを持つオブジェクトをインポートします(対応するオブジェクトがない場合はfalse)。 要求された電子メールがdbにある場合は、パスワードが正しいかどうかを確認します。(パスワード確認プロセス)bcryptの比較方法により/loginで発行されたパスワードと電子メールで見つけたユーザdbに存在するハッシュパスワードを比較する. パスワードが正しい場合、jwtを使用してユーザにトークンを生成し、クッキーに格納します。 トークン作成プロセス:ログイン情報に一致するdbのuserオブジェクトのidとsecretkeyを使用してtokenを生成します。以降のuserオブジェクトのtokenプロパティで、生成したtokenを挿入して保存します。 (ログアウト) dbでそのプレイヤーのidを見つけた後、プレイヤーのトークンをdbから削除し、認証を行わない。
権限認証 getは、/api/users/auth(認証を行う場所)で実行されます。 ミドルウェアauthによりクライアントのCookieからトークンを取得する. トークンを復号した後、dbでユーザを検索します。 プレイヤーが存在する場合、reqにユーザとtokenをreqに入れる。 復号プロセス:Cookie内のトークンをjwtのverifyメソッドで復号します。この場合、トークン生成時に使用するsecret keyを用いて復号することができる。復号化によりユーザのidを得ることができる。その後、復号により、求めたidがdbに存在する対応するuserのidと一致するかどうかを検証する。(トークンも検証する)
server/models/User.js(シナリオ、モデル、メソッドの作成)
const mongoose = require('mongoose');
const bcrypt = require('bcrypt');
const saltRounds = 10;
const jwt = require("jsonwebtoken")//토큰 발행
//스키마 생성
const userSchema = mongoose.Schema({
/*데이터를 mongodb에 저장하려면 먼저 구조(스키마)가 있어야 한다.
스키마는 해당 컬렉션의 문서에 어떤 종류의 값이 들어가는지를 정의한다.
mongoose 모듈을 불러와 mongoose.Schema 를 통해 스키마 객체를 생성한다.*/
name: {
type: String,
maxlength: 50
},
lastname: {
type: String,
maxlength: 50
},
email: {
type: String,
trim: true,//email에 공백이 있을 때 없애주는 역할
unique: 1//똑같은 이메일 못쓰게
},
password: {
type: String,
minlength: 5
},
role: {
type: Number,
default: 0
},
image: String,
token: {//유효성관련
type: String
},
tokenExp: {//token의 유효기간
type: Number
}
})
//pre는 mongoose 에서 가져온 메서드, save는 저장하기 전에 function을 실행
userSchema.pre('save', function(next) {//next는 바로 이 과정을 pass 함
//현재 스키마에 들어있는 post된 password를 가져온다
var user = this;
//field에서 password가 변환될때만 password를 암호화 해준다.
if (user.isModified('password')) {
//bcrypt 패키지의 salt를 이용해서 비밀번호를 암호화 시킨다.
//genSalt는 salt를 생성한다
bcrypt.genSalt(saltRounds, function(err, salt) {
if (err) return next(err)
bcrypt.hash(user.password, salt, function(err, hash) {
if (err) return next(err)
user.password = hash//password를 암호화된 hash 로 바꿔준다
next()//완료 후 돌아감
})
})
} else {//그냥 나갈 곳을 만들어준다.
next()
}
});
userSchema.methods.comparePassword = function(plainPassword, callback) {
//클라이언트가 입력한 비밀번호와 db의 비밀번호를 비교한다.
bcrypt.compare(plainPassword, this.password, function(err, isMatch) {
if (err) return callback(err)//같을때
callback(null, isMatch)//다를때
})
}
userSchema.methods.generateToken = function(callback) {
var user = this;
//jsonwebtoken을 이용해서 token 생성
var token = jwt.sign(user._id.toHexString(), 'secretToken');//임의로 두번째(secret key) 지정
//user._id + 'secretToken' = token (incode)
//->
//'secretToken' -> user._id (decode)
user.token = token;
user.save(function(err, user) {
if (err) return callback(err)
callback(null, user)
})
}
userSchema.statics.findByToken = function(token, callback) {
var user = this;
//토큰을 복호화(decode) 한다
jwt.verify(token, 'secretToken', function(err, decoded) {
user.findOne({ "_id": decoded, "token": token }, function(err, user) {
if (err) return callback(err);
callback(null, user);
})
})
}
const User = mongoose.model('User', userSchema);
module.exports = { User };
//모델 다른 곳에서도 쓸 수 있게 export 해준다.
server/middleware/auth.js(ライセンスミドルウェア)
const { User } = require('../models/User');
//decode 복호화(암호화해제) incode 암호화
let auth = (req, res, next) => {
//인증 처리를 하는 곳
//1. 클라이언트 쿠키에서 토큰을 가져온다. cookie parser 이용
let token = req.cookies.x_auth;//암호화(incode)되있는 상태
//2. 토큰을 복호화 한 후, 해당 유저를 찾는다.
User.findByToken(token, (err, user) => {
if(err) throw err;
if(!user) return res.json({isAuth: false, error: true})
//req에서 user와 token 을 사용할 수 있게 해준다.
req.token = token;
req.user = user;
next();//미들웨어를 벗어나 계속 갈 수 있게
})
}
module.exports = { auth };
server/index.js(サーバを作成しdbに接続し、リクエストを処理)
const express = require("express");
const app = express()
//client 에서 보내는 정보를 분석해서 서버에서 받을 수 있게 해준다.
//bodyParser를 사용하지 않으면 req.body가 undefinded를 default로 받는다.
const bodyParser = require("body-parser")
//모델을 가져온다.
const cookieParser = require("cookie-parser");
const config = require('./config/key');
const { auth } = require('./middleware/auth');
const { User } = require("./models/User");
//application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: true }));
//application/json
app.use(bodyParser.json());
/*bodyparser는 client 에서 오는 정보를 "분석해서" 가져올 수 있게 한다.
x-www-form-urlencoded 이렇게 된 데이터와
json 형식의 데이터를 분석할 수 있게 하기 위해 윗 문장을 적어준다.*/
app.use(cookieParser());
const mongoose = require('mongoose');
//스키마를 만들고, 해당 스키마에 맞는 모델을 만들어 공통된 조건에 맞게 조회 및 저장이 가능하다.
mongoose.connect(config.mongoURI)//서버와 데이터베이스(mongoDB)를 연결
.then(() => console.log('MongoDB Connected...'))
.catch(err => console.log("MongoDB error: ", err));
app.get('/', (req, res) => res.send('hello world'))
app.get('/api/hello', (req,res)=> {
res.send("Hello World~ ")
})
app.post('/api/users/register', (req, res) => {
//받은 정보로 모델 생성, json 형식으로 req가 들어있다.
const user = new User(req.body)
user.save((err, doc) => {
if (err) return res.json({ success: false, err })
return res.status(200).json({ success: true })
})
//결과적으로 http post 메소드로 백엔드 서버로 유저 정보를 날려주고
//백엔드 서버에서 save 메소드로 DB에 저장을 해준다
})
app.post('/api/users/login', (req, res) => {
//요청된 이메일을 데이터베이스에서 찾는다. mongoDB 메서드 이용
User.findOne({ email: req.body.email }, (err, user) => {
//요청한 email이 db정보 안에 있을 때 해당 db정보를 담은 객체 user 가 생성된다.
if (!user) {
return res.json({
loginSuccess: false,
message: "제공된 이메일에 해당하는 유저가 없습니다."
})
}
//요청된 이메일이 데이터 베이스에 있다면 비밀번호가 맞는 비밀번호 인지 확인
user.comparePassword(req.body.password, (err, isMatch) => {
if (!isMatch) //비밀번호가 틀림
return res.json({ loginSuccess: false, message: "비밀번호가 틀렸습니다" })
//비밀번호가 맞다면 그 유저를 위한 토큰 생성
user.generateToken((err, user) => {//token이 들어있는 user 객체
if (err) return res.status(400).send(err);
//토큰을 저장한다. 어디에? 쿠키, localStorage 등.. 지금은 쿠키에
res.cookie("x_auth", user.token)
.status(200)//성공했다는 표시
.json({ loginSuccess: true, userId: user._id })
})
})
})
})
//현재의 role => role 0 -> 일반유저, role 0 아니면 관리자
app.get('/api/users/auth', auth/*미들웨어*/, (req, res) => {
//여기 까지 미들웨어를 통과해 왔다는 얘기는 authentication 이 true 라는 말
res.status(200).json({
_id: req.user._id,//auth에서 user 를 req 에 넣었기 때문에 사용가능
isAdmin: req.user.role === 0 ? false : true,//변경가능
isAuth: true,
email: req.user.email,
name: req.user.name,
lastname: req.user.lastname,
role: req.user.role,
image: req.user.image
})
})
app.get('/api/users/logout', auth, (req, res)=> {
//첫번째 인자에 찾을 조건, 두번재 인자에 변경할 것
User.findOneAndUpdate({_id: req.user._id/*auth에서 req에 넣어줌*/},
{token: ""},
(err, user)=> {
if(err) return res.json({success: false, err});
return res.status(200).send({success:true})
})
})
const port = 5000
//5000번 포트에서 연결을 청취하고, 연결됬을 시 콜백함수를 실행한다.
app.listen(port, () => console.log(`Example app listening on port ${port}!`))
開発環境
開発は2つの環境で行うことができる.1つはローカルでローカル開発モードで行うことができ、もう1つはherokuまたは他のクラウドサービスを使用して導入(導入)し、本番モードで開発することができます.
二つのことは別々に考えなければならない.
server/config/key.js(環境内のmongodbに接続する方法)
if(process.env.NODE_ENV/*환경변수*/ === 'development')
//process.env.NODE_ENV 는 현재 위치한 모드를 가리킨다.
{//development 모드
module.exports = require('./prod');
} else {//production 모드(deploy, 배포한 후)))
module.exports = require('./dev');
}
server/config/dev.js(開発モード)
アカウントとパスワードを省略しました.
//development 모드에서 사용할 것
module.exports = {
mongoURI: 'mongodb+srv://--id--:[email protected]/myFirstDatabase?retryWrites=true&w=majority'
}
server/config/prod.js(本番モード)
//production 모드에서 사용할 것
module.exports = {
mongoURI: process.env.MONGO_URL
}
github - NodeReactReference
この問題について(登録/ログイン/ログアウト/権限確認機能の実装), 我々は、より多くの情報をここで見つけました https://velog.io/@hyunn/로그인-회원-가입-기능-구현テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol