Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
31 |
Tags
- 카카오 클라우드 스쿨
- kakaocloud
- Java #코딩테스트
- SpringTokenizer
- node
- Spring #Java #Spring Boot #@BeforeEach #@AfterEach
- Spring #Java #Spring Boot #싱글톤
- 카카오클라우드스쿨
- 24479
- Java #오븐시계 #백준
- kakaocloudschool
- Spring #Java #Spring Boot
- 파이썬
- 9020
- Java #백준 #코딩테스트
- 11053
- 2447
- java
- 백준
- 15552
- sequelize
- Spring #Spring Boot #Java
- 알고리즘
- Spring
- 7568
- 1110
- 코딩테스트
- python
- 11054
- boj
Archives
- Today
- Total
YoungSoo
Node_Authentication 본문
- Authentication(인증)과 Authorization(인가)
- 인증 : 계정 관련, 로그인 관련
- 인가 : 권한 관련
- 인증을 구현하는 방법
- 로컬 로그인 : 회원 정보를 저장하고 있다가 인증
- 회원 정보를 저장할 때는 비밀번호는 복호화가 불가능한 방식을 사용하고 개인을 식별할 수 있는 정보를 마스킹 처리를 하거나 복호화가 가능한 방식의 암호화를 활용해야 합니다.
- OAuth(공통된 이증 방식) 로그인 : 다른 서버(카카오나 구글)에 저장된 인증 정보를 활용해서 인증을 하는 방식
- 로컬 로그인 : 회원 정보를 저장하고 있다가 인증
- Passport 모듈
- Node에서 인증 작업을 도와주는 모듈
- 세션이나 쿠키 처리를 직접하지 않고 이 모듈의 도움을 받으면 쉽게 구현이 가능합니다.
- Social 로그인 작업을 쉽게 처리할 수 있도록 해줍니다.
- https://www.passportjs.org
- 로컬 로그인 구현
- 필요한 모듈 설치 passport, passport-loacl, bcrypt(암호화를 해서 비교는 가능하지만 복호화는 안 되는 암호화 모듈)
- app.js 파일에 Passport 모듈을 사용할 수 있는 설정을 추가
- const passport = require('passport'); const passportConfig = require('./passport');//현재 프로젝트의 passport 디렉토리에 index.js 확인 passportConfig(); app.use(passport.initialize()); //세션 기능은 passport 모듈이 알아서 사용 app.use(passport.session());
- Passport 모듈 사용 설정
- passport 디렉토리 생성
- passport 디렉토리에 index.js 파일을 생성하고 작성
const passport = require('passport'); const local = require('./localStrategy'); const User = require('../models/user'); module.exports = () => { //로그인 성공했을 때 정보를 deserializeUser 함수에게 넘기는 함수 passport.serializeUser((user, done) => { done(null, user.id); }); //넘어온 id에 해당하는 데이터가 있으면 데이터베이스에서 찾아서 세션에 저장 passport.deserializeUser((id, done) => { User.findOne({where:{id}}) .then(user => done(null, user)) .catch(err => done(err)); }); local(); }
- 이렇게 하면 로그인 여부를 request 객체의 isAuthenticated() 함수로 할 수 있게 됩니다.
- 로그인 여부를 판단할 수 있는 함수를 routes 디렉토리의 middlewares.js 파일을 추가하고 작성
//로그인 여부 판단
exports.isLoggedIn = (req, res, next) => {
//로그인 여부 판단 - req.session.user 가 존재하는지 확인
if (req.isAuthenticated()) {
next();
} else {
res.status(403).send("로그인 필요");
}
};
//로그인 하지 않은 경우를 판단
exports.isNotLoggedIn = (req, res, next) => {
if (!req.isAuthenticated()) {
next();
} else {
//메시지를 생성하는 query string(parameter)로
//사용할 것이라서 encoding을 해주어야 합니다.
const message = encodeURIComponent("로그인 한 상태입니다.");
//이전 request 객체의 내용을 모두 삭제하고
//새로운 요청 흐름을 만드는 것으로 새로 고침을 하면
//결과 화면만 새로고침 됩니다.
res.redirect(`/?error=${message}`);
}
};
- routes 디렉토리의 page.js 파일을 수정
const express = require('express');
const {isLoggedIn, isNotLoggenIn} = require("./middlewares");
const router = express.Router();
//공통된 처리 - 무조건 수행
router.use((req, res, next) => {
//로그인 한 유저 정보
//유저정보를 res.locals.user에 저장
res.locals.user == req.user;
//게시글을 follow 한 개수
res.locals.followerCount = 0;
res.locals.followingCount = 0;
//게시글을 follow 하고 있는 유저들의 목록
res.locals.followerIdList = [];
next();
});
//메인 화면
router.get('/', (req, res, next) => {
const twits =[];
//템플릿 엔진을 이용한 출력
//views 디렉토리의 main.html로 출력
res.render('main', {title:"Node Authenticaiton", twits,});
});
//회원 가입 - 로그인이 되어있지 않은 경우만 수행
router.get('/join', isNotLoggenIn, (req, res, next) => {
res.render('join', {title:'회원 가입 - Node Authentication'});
});
//프로필 화면 처리
router.get('/profile', isLoggedIn, (req, res, next) => {
res.render('profile', {title:'나의 정보 - Node Authentication'});
});
module.exports = router;
- 회원 가입, 로그인 ,로그아웃 처리를 위한 내용을 routes 디렉토리에 auth.js 파일을 만들고 작성
- 이 내용은 page.js에 작성해도 됩니다.
- page.js 는 화면을 보여주는 역할을 하고 auth.js는 처리하는 역할을 하도록 분리한 것입니다.
const express = require('express'); //로그인 및 로그아웃 처리를 위해서 가져오기 const passport = require('passport'); //회원 가입을 위해서 가져오기 const bcrypt = require('bcrypt'); const { isLoggedIn, isNotLoggedIn } = require('./middlewares'); const User = require('../models/user'); const router = express.Router(); //회원 가입 처리 - /auth/join 인데 라우팅 할 때 /auth 추가 router.post('/join', isNotLoggedIn, async (req, res, next) => { //데이터 찾아오기 //req.body에서 email, nick, password를 찾아서 대입 const { email, nick, password } = req.body; try { //email 존재 여부 확인 const exUser = await User.findOne({ where: { email } }); if (exUser) { //회원 가입 페이지로 리다이렉트하는데 //error 키에 메시지를 가지고 이동 return res.redirect('/join?error=exist'); }else{ //비밀번호를 해싱 const hash = await bcrypt.hash(password, 12); //저장 await User.create({ email, nick, password: hash, }); return res.redirect('/'); } } catch (error) { console.error(error); return next(error); } }); //로그인 처리 router.post('/login', isNotLoggedIn, (req, res, next) => { //passport 모듈을 이용해서 로그인 passport.authenticate('local', (authError, user, info) => { if (authError) { console.error(authError); return next(authError); } //일치하는 User가 없을 때 if (!user) { return res.redirect(`/?loginError=${info.message}`); } return req.login(user, (loginError) => { if (loginError) { console.error(loginError); return next(loginError); } //로그인 성공하면 메인 페이지로 이동 return res.redirect('/'); }); })(req, res, next); // 미들웨어 내의 미들웨어에는 (req, res, next)를 붙입니다. }); //로그아웃 처리 router.get('/logout', isLoggedIn, (req, res) => { req.logout(function(err) { if (err) { return next(err); } //세션을 초기화 req.session.destroy(); res.redirect('/'); }); }); module.exports = router;
- passport 디렉토리에 로컬 로그인을 위한 localStrategy.js를 작성
const passport = require('passport');
const LocalStrategy = require('passport-local').Strategy;
const bcrypt = require('bcrypt');
const User = require('../models/user');
module.exports = () => {
passport.use(new LocalStrategy({
usernameField: 'email',
passwordField: 'password',
}, async (email, password, done) => {
try {
//로그인 처리를 위해서 email에 해당하는 데이터 찾기
const exUser = await User.findOne({ where: { email } });
if (exUser) {
const result = await bcrypt.compare(password, exUser.password);
if (result) {
done(null, exUser);
} else {
done(null, false, { message: '비밀번호가 일치하지 않습니다.' });
}
} else {
done(null, false, { message: '가입되지 않은 회원입니다.' });
}
} catch (error) {
console.error(error);
done(error);
}
}));
};
- app.js 파일에 로그인 관련 라우터 등록
const authRouter =require('./routes/auth');
app.use ('/auth',authRouter);
- 카카오 로그인 구현
- 사용하는 모듈 : passport-kakao
- 카카오 로그인 사용을 위한 설정
- developers.kakao.com 에 접속해서 로그인
- 애플리케이션이 없으면 추가
- Rest API 키를 복사 : *****
- 플랫폼 등록 : Web에 자신의 도메인과 포트 번호를 추가
- 로그인 활성화
- 왼쪽 메뉴에서 카카오 로그인을 클릭하고 활성화 설정 ON으로 설정하고 하단의 Redirect URI를 설정 - http://localhost/auth/kakao/callback
- 왼쪽 메뉴에서 카카오 로그인 안의 동의 항목을 클릭하고 수집할 항목을 설정
- .env 파일에 복사한 키를 저장
- passport 디렉토리의 index.js 수정
const passport = require("passport");
//로컬 로그인 구현
const local = require("./localStrategy"); //현재 디렉토리에 localStrategy 파일을 가져오기
//카카오 로그인
const kakao = require("./kakaoStrategy");
const User = require("../models/user"); //상위 디렉토리에서 model 디렉토리의 user 가져오기
module.exports = () => {
//로그인 성공했을 때 정보를 deserializeUser 함수에게 넘기는 함수
passport.serializeUser((user, done) => {
done(null, user.id);
});
//넘어온 id에 해당하는 데이터가 있으면 데이터베이스에서 찾아서
//세션에 저장
passport.deserializeUser((id, done) => {
User.findOne({ where: { id } })
.then((user) => done(null, user))
.catch((err) => done(err));
});
local();
kakao();
};
- passport 디렉토리의 kakaoStrategy.js 파일을 생성하고 작성
const passport = require("passport");
const KakaoStrategy = require("passport-kakao").Strategy;
//유저 정보
const User = require("../models/user");
//카카오 로그인
module.exports = () => {
passport.use(
new KakaoStrategy(
{
clientID: process.env.KAKAO_ID,
callbackURL: "/auth/kakao/callback",
},
async (accessToken, refreshToken, profile, done) => {
//로그인 성공했을 때 정보를 출력
console.log("kakao profile", profile);
try {
//이전에 로그인한 적이 있는지 찾기 위해서
//카카오 아이디와 provider가 kakao로 되어있는
//데이터가 있는지 조회
const exUser = await User.findOne({
where: { snsId: profile.id, provider: "kakao" },
});
//이전에 로그인 한 적이 있으면
if (exUser) {
done(null, exUser);
} else {
const newUser = await User.create({
email: profile._json.kakao_account.email,
nick: profile.displayName,
snsId: profile.id,
provider: "kakao",
});
done(null, newUser);
}
} catch (error) {
console.error(error);
done(error);
}
}
)
);
};
- routes 디렉토리의 auth.js 파일에 카카오 로그인 라우팅 처리 코드를 추가
const express = require("express");
//로그인 및 로그아웃 처리를 위해서 가져오기
const passport = require("passport");
//회원 가입을 위해서 가져오기
const bcrypt = require("bcrypt");
const { isLoggedIn, isNotLoggedIn } = require("./middlewares");
const User = require("../models/user");
const router = express.Router();
//회원 가입 처리 - /auth/join 인데 라우팅 할 때 /auth 추가
router.post("/join", isNotLoggedIn, async (req, res, next) => {
//데이터 찾아오기
//req.body에서 email, nick, password를 찾아서 대입
const { email, nick, password } = req.body;
try {
//email 존재 여부 확인
const exUser = await User.findOne({ where: { email } });
if (exUser) {
//회원 가입 페이지로 리다이렉트하는데
//error 키에 메시지를 가지고 이동
return res.redirect("/join?error=exist");
} else {
//비밀번호를 해싱
const hash = await bcrypt.hash(password, 12);
//저장
await User.create({
email,
nick,
password: hash,
});
return res.redirect("/");
}
} catch (error) {
console.error(error);
return next(error);
}
});
//로그인 처리
router.post("/login", isNotLoggedIn, (req, res, next) => {
//passport 모듈을 이용해서 로그인
passport.authenticate("local", (authError, user, info) => {
if (authError) {
console.error(authError);
return next(authError);
}
//일치하는 User가 없을 때
if (!user) {
return res.redirect(`/?loginError=${info.message}`);
}
return req.login(user, (loginError) => {
if (loginError) {
console.error(loginError);
return next(loginError);
}
//로그인 성공하면 메인 페이지로 이동
return res.redirect("/");
});
})(req, res, next); // 미들웨어 내의 미들웨어에는 (req, res, next)를 붙입니다.
});
//로그아웃 처리
router.get("/logout", isLoggedIn, (req, res) => {
req.logout(function (err) {
if (err) {
return next(err);
}
//세션을 초기화
req.session.destroy();
res.redirect("/");
});
});
//카카오 로그인을 눌렀을 때
router.get("/kakao", passport.authenticate("kakao"));
//카카오 로그인 실패했을 때
router.get(
"/kakao/callback",
passport.authenticate("kakao", {
failureRedirect: "/",
}),
(req, res) => {
res.redirect("/");
}
);
module.exports = router;
'FE > Node.js' 카테고리의 다른 글
Node_API_Server (0) | 2023.01.06 |
---|---|
Node DB (0) | 2023.01.04 |
Node (0) | 2023.01.03 |