React와 Node.js연동은 이전글을 참고하세요
Passport.js는 Node.js의 프레임워크인 Express를 기반으로하는 인증 모듈로,
기본 설치 및 사용방법과 Node.js + Express + MySQL + Passport-local 로그인은 아래 링크를 참고하세요.
2020/01/10 - [STUDY/Node.js] - Node.js | Passport.js (passport-local) + MySQL
1. 입력받은 ID와 비밀번호 값 상위 컴포넌트로 전달 (Login → App)
onSubmit메서드를 이용하여 input태그에 입력받은 값을 App컴포넌트로 보낸다.
class Login extends Component {
submitHandler = (event) => {
event.preventDefault();
console.log(event.target.id.value, event.target.pwd.value);
this.props.login_process({
id: event.target.id.value,
pwd: event.target.pwd.value
})
}
render () {
return(
<div className="loginWrap">
<h1>LOGIN</h1>
<form onSubmit={this.submitHandler}>
<input type="text" name="id" placeholder="ID"></input><br></br>
<input type="password" name="pwd" placeholder="PASSWORD"></input><br></br>
<input type="submit" value="LOGIN"/>
</form>
</div>
);
}
}
2. fetch를 이용하여 서버로 데이터 전달 (React → Node)
login_process에는 login이라는 함수가 선언되어 있다.
<Login login_process={this.login}></Login>
login 함수 내에서는 fetch를 이용하여 서버로 데이터를 전송한다.
이 fetch를 통해 로그인을 시도하는 것
fetch는 JQuery의 AJAX와 사용법이 비슷하기 때문에 AJAX를 사용해보았다면 fetch도 금방 사용할 수 있을 것이다.
fetch에서 사용된 url인 '/users/login'은 Node.js측의 route주소이며, 해당 route내에서 passport로그인을 시도한다.
그래서 반드시 method는 POST로 설정해주어야 함!
login = (login_info) => {
console.log("App.js login() " + JSON.stringify(login_info))
fetch('/users/login', {
method: "post",
headers: {
"Content-Type": "application/json; charset=utf-8"
},
credentials: "same-origin",
body: JSON.stringify(login_info)
})
.
.
.
}
// fetch의 기본 문법
fetch('url', options);
+) fetch 더 알아보기
3. Passport-local Starategy를 이용한 로그인 처리 및 세션 생성(Node.js)
이 부분에서 너무 어렵게 생각하는 바람에 몇 시간을 날렸는데...
Passpot는 route경로가 일치하고, POST방식이면 바로 Passport를 실행된다.
즉, fetch에서 지정한 경로로 post방식으로 데이터를 보내면 그 경로(route)안에 passport인증을 처리하는 부분이 있기만 하면 됨!
passport.authenticate('local')가 실행되면서 passport-local방식의 인증이 시도된다.
처리 후 콜백 함수가 실행되고, 로그인이 잘 실행되었다면 req.user로 접근이 가능해진다.
.then을 통해 response로 보낸 값을 React(Client Side)로 받아올 수 있게 되는 것!
router.post('/login', passport.authenticate('local', { failureRedirect: '/login' }),
function(req, res) { // 콜백함수
console.log("req.user : "+ JSON.stringify(req.user));
});
4. Passport가 생성한 user세션 전달 (Node → React)
로그인에 성공하였을 경우 passport는 user정보를 세션에 저장하며,
request.user를 통해 로그인한 user정보에 접근할 수 있다.
passport가 권장하는 기본 문법으로 사용하면 로그인에 실패했을 경우 failureRedirect가 실행되는데,
현재 상황에서는 response를 이용해 다시 fetch쪽으로 데이터를 전달해야 하기 때문에 customCallback을 사용했다.
user(req.user와 동일)를 인자값으로 받고
user정보가 있으면(로그인 성공) user를 json형식으로 변환하여 전송, user정보가 없으면(로그인 실패) 빈 json형식을 전송한다.
// user.js
router.post('/login', function(req, res, next) {
passport.authenticate('local', function(err, user, info) {
if (err) { return next(err); }
if(user){ // 로그인 성공
console.log("req.user : "+ JSON.stringify(user));
var json = JSON.parse(JSON.stringify(user));
// customCallback 사용시 req.logIn()메서드 필수
req.logIn(user, function(err) {
if (err) { return next(err); }
return res.send(json);
});
}else{ // 로그인 실패
console.log("/login fail!!!");
res.send([]);
}
})(req, res, next);
});
그리고 React에서는 .then을 이용하여 Node에서 전송한 데이터를 받는다.
login = (login_info) => {
console.log("App.js login() " + JSON.stringify(login_info))
fetch('/users/login', {
method: "post",
headers: {
"Content-Type": "application/json; charset=utf-8"
},
credentials: "same-origin",
body: JSON.stringify(login_info)
})
.then(res => res.json())
.then(data => {
console.log("App.js login .then " , data);
if(data.EMAIL === undefined){ // 로그인 실패 빈 json형식이 넘어온 경우
alert('login fail!');
}
this.setState({ users : data })
})
}
처음 users값을 세팅해주는 부분.
세션의 유효기간이 지나지 않았다면 프론트에서 users값을 계속 받을 수 있음!
(passport의 deserializeUser가 계속 세션이 유효한지 검사해주기 때문에)
// React App.js
componentDidMount() {
// 프록시로 등록한 서버주소가 생략됨
fetch('/users')
.then(res => res.json())
// json형식으로 받아온 값을 setState를 이용해 값을 재설정해줌
.then(users => this.setState({ users }));
}
// Node users.js
router.get('/', (req, res) => {
var user_info = null;
if(!req.user){
user_info = [];
}else{
user_info = JSON.parse(JSON.stringify(req.user));
}
res.json(user_info);
})
passport.serializeuser not called 발생... customCallback을 쓰면 여러가지 수정해야 할 부분들이 많다.
serializeUser가 user정보를 세션에 저장하는 부분? 정도라고 이해하고 있는데,
콘솔에 실행순서를 출력해보면
route에서 로그인 시도 → 성공 → 세션에 user정보 생성(serializeUser실행)의 순서로 이뤄진다.
user정보를 인자값으로 받고, user정보가 있으면 done을 통해 user의 고유 식별값이 EMAIL을 세션에 저장
passport.serializeUser(function(user, done) {
console.log("serializeUser ", user)
if(user){
done(null, user.EMAIL);
}
});
deserializeUser가 실행되지 않는 현상이 발생...
passport.deserializeUser never called...
req.isAuthenticated() is always return false....
deserializeUser는 클라이언트 측에서 다른 요청이 있을 때 실행되어서 세션에 담긴 유저 정보를 다시 알려주는 역할을 하는 메서드로 passport에서 가장 중요한 역할을 담당한다고 보면 된다.
그런데 이 메서드가 실행되지 않음. 심지어 로그인도 성공하고 serializeUser까지 실행되는데, 오직 deserializeUser만... 그리고 req.isAuthenticated()도 false값을 반환해준다. 정상적으로 작동하면 true값이 반환되어야 함.
우선 fetch로 서버측과의 소통을 할 때 옵션으로 반드시 credentails를 설정해줘야 한다.
원래는 same-origin으로 설정했었는데, include로 변경하니 CORS에러 발생.
세션에 요청할 때는 무조건 credentials 설정 필수
fetch('http://localhost:3002/users/login', {
method: "post",
headers: {
"Content-Type": "application/json; charset=utf-8"
},
credentials: 'include',
body: JSON.stringify(login_info)
})
우선 CORS를 설정할 때 옵션값을 넣어주자.
origin은 클라이언트 측의 주소를 넣어주고, credentials의 값을 true로 설정해준다.
const cors = require('cors');
app.use(cors({origin: "http://localhost:3000", credentials:true}));
그리고 session의 옵션값도 변경해줬음.
secure를 false로!
(다시 확인해보니 secure:fasle / httpOnly:false 설정해주지 않아도 됨!)
router.use(session({
secret: 'keyboard cat',
resave: false,
saveUninitialized: true,
cookie:{maxAge:30000,
secure:false,
httpOnly:false}
}));
실행화면
+)참고
custom Callback 작성하기
'STUDY > React' 카테고리의 다른 글
React | Node.js연동 파일 업로드 (+ multer) (2) | 2020.01.19 |
---|---|
React | LifeCycle API (생명주기 메서드) (0) | 2020.01.15 |
React | Node.js와 React 연동하기 (+ CORS 에러 해결하기) (0) | 2020.01.13 |
React | update 구현하기 / onChange (0) | 2020.01.12 |
React | onSubmit event (input값 접근하기) (1) | 2020.01.12 |