AUTHENTICATING REACTJS APP WITH DEVISE GEM
Bài đăng này đã không được cập nhật trong 8 năm
Hôm nay tôi xin giới thiệu tới các bạn sử dụng gem devise để xác thực một ứng dụng sử dụng React.
Trong bài viết này tôi sẽ xây dựng một ứng dụng Rails API để phục vụ cho ứng dụng React. ReactJS xử lý tất cả các routes không được cung cấp bởi Rails. Với thiết lập này, ReactJS luôn luôn có quyền truy cập vào đúng CSRF thẻ mà nó có thể lấy từ head của HTML document.
Trong bài viết này tôi sẽ không bàn tới việc cài đặt gem devise cơ bản và sẽ sử dụng ngay trong phần dưới đây. Các bạn có thể tham khảo cài đặt và sử dụng tại document của gem devise. Cuối cùng, tất cả các file trong các ví dụ dưới đây sẽ được liên quan đến ứng dụng rails.
Giờ chúng ta sẽ bắt đầu vào nội dung chính.
Một vài Rails Stuff cần thiết
Bước 1: Devise nhận json requests
Bằng cách thêm đoạn code sau vào file config/application.rb:
#config/application.rb
[...]
config.to_prepare do
DeviseController.respond_to :html, :json
end
[...]
Bước 2: Thực hiện một số thay đổi nhỏ trong app/controllers/application_controller.rb
Chúng ta sẽ sửa "protect_from_forgery with: :exception" thành như sau:
#app/controllers/application_controller.rb
[...]
protect_from_forgery with: :null_session
before_action :configure_permitted_parameters, if: :devise_controller?
[...]
Thêm đoạn code sau để đảm bảo rằng devise cho phép thiết lập một vài thông số như sau:
#app/controllers/application_controller.rb
[...]
private
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:sign_up) do |user_params|
user_params.permit :name, :provider, :uid
end
end
[...]
Bước 3: Tạo một controller mới tên auth_controller.rb
Trong controller này sẽ tạo một method trả về false nếu như user chưa đăng nhập và dữ liệu nếu đã đăng nhập thành công:
#app/controllers/auth_controller.rb
class AuthController < ApplicationController
def is_signed_in?
if user_signed_in?
render json: {signed_in: true, user: current_user}
else
render json: {signed_in: false}
end
end
end
Thêm một route mới:
#config/routes.rb
[...]
scope :auth do
get "is_signed_in", to: "auth#is_signed_in?"
end
[...]
Một vài React Stuff cần thiết
Chúng ta sẽ đặt ra một số mục tiêu sau đây:
- Để có một cách để xác định xem người dùng hiện tại được SignedIn từ App ReactJs.
- Để chắc chắn rằng chúng ta có thể truy cập và gửi đúng CSRF mã thông báo với mỗi request.
- Để có thể nhập từ App ReactJs.
- Để có thể Đăng xuất khỏi App ReactJs
- Để có thể đăng ký từ App ReactJs
Bước 1: Kết nối ứng dụng của bạn tới “is_signed_in” endpoint
Trong bài viết này điểm truy cập sẽ tại react/components/App.js
// app/assets/javascripts/react/components/App.js
var React = require('react');
var Router = require('react-router');
var RouteHandler = Router.RouteHandler;
var $ = require('jquery');
var App =
React.createClass({
componentWillMount: function() {
$.ajax({
method: 'GET',
url: '/auth/is_signed_in.json'
})
.done(function(data){
this.setState({signedIn: data.signed_in});
}.bind(this));
},
getInitialState: function() {
return {signedIn: null};
},
render:function(){
return <RouteHandler signedIn={this.state.signedIn}/>;
}
});
module.exports = App;
Bước 2: Hãy chắc chắn rằng chúng ta có thể truy cập và gửi đúng CSRF mã thông báo với mỗi request
Đây là một chút khó khăn, nhưng với thiết lập sau, tôi đã có thể lấy CSRF token từ phần đầu của trang bằng cách sử dụng sau đây.
// app/assets/javascripts/react/utils/Functions.js
var Functions = {
getMetaContent: function(name) {
var metas = document.getElementsByTagName('meta');
for (var i=0; i<metas.length; i++) {
if (metas[i].getAttribute('name') == name) {
return metas[i].getAttribute('content');
}
}
return '';
}
}
module.exports = Functions;
Trên đây là tất cả chúng ta cần bây giờ. Chúng ta sẽ quay trở lại mà sau này. Nó sẽ là cần thiết cho tất cả các PUT, POST và DELETE yêu cầu chúng ta làm với ajax.
Bước 3: Có thể đăng nhập từ React app
// app/assets/javascripts/react/components/auth/SignInForm.js
var React = require('react');
var Functions = require('../../utils/Functions.js');
var _ = require('lodash');
var $ = require('jquery');
var SignInForm =
React.createClass({
_handleInputChange: function(ev) {
// Get a deep clone of the component's state before the input change.
var nextState = _.cloneDeep(this.state);
//Update the state of the component
nextState[ev.target.name] = ev.target.value;
// Update the component's state with the new state
this.setState(nextState);
},
getInitialState: function() {
return {
email: '',
password: ''
};
},
_handleSignInClick: function(e) {
$.ajax({
method: "POST",
url: "/users/sign_in.json",
data: {
user: {
email: this.state.email,
password: this.state.password
},
authenticity_token: Functions.getMetaContent("csrf-token")
}
})
.done(function(data){
location.reload();
}.bind(this));
},
render:function(){
return (
<form>
<input type='email'
name='email'
placeholder='email'
value={this.state.email}
onChange={this._handleInputChange} />
<input type='password'
name='password'
placeholder='password'
value={this.state.password}
onChange={this._handleInputChange} />
<input type='submit' onClick={this._handleSignInClick} defaultValue='login' />
</form>
)
}
});
module.exports = SignInForm;
Bước 4: Có thể đăng xuất khỏi React app
// app/assets/javascripts/react/components/auth/SignOutLink.js
var React = require('react');
var $ = require('jquery');
var Functions = require('../../utils/Functions.js');
var SignOutLink =
React.createClass({
render:function(){
return (
<a href="#" onClick={this._signOut}>Sign out</a>
)
},
_signOut: function(){
$.ajax({
method: "DELETE",
url: "/users/sign_out.json",
data: {
authenticity_token: Functions.getMetaContent("csrf-token")
}
}).done(function(){
location.reload();
});
}
});
module.exports = SignOutLink;
Bước 5: Có thể đăng kí tài khoảng bằng React app
// app/assets/javascriptsreact/components/auth/SignUpForm.js
var React = require('react');
var _ = require('lodash');
var Functions = require('../../utils/Functions.js');
var SignUpForm =
React.createClass({
_handleInputChange: function(ev) {
// Get a deep clone of the component's state before the input change.
var nextState = _.cloneDeep(this.state);
//Update the state of the component
nextState[ev.target.name] = ev.target.value;
// Update the component's state with the new state
this.setState(nextState);
},
getInitialState: function() {
return {
email: '',
password: '',
password_confirmation: '',
name: ''
};
},
_handleRegistrationClick: function(e) {
$.ajax({
method: "POST",
url: "/users.json",
data: {
user: {
email: this.state.email,
uid: this.state.email,
password: this.state.password,
password_confirmation: this.state.password_confirmation,
name: this.state.name,
provider: "email"
},
authenticity_token: Functions.getMetaContent("csrf-token")
}
})
.done(function(data){
location.reload();
}.bind(this));
},
render:function(){
return (
<form>
<input type='text'
name='name'
placeholder='name'
value={this.state.name}
onChange={this._handleInputChange} />
<input type='email'
name='email'
placeholder='email'
value={this.state.email}
onChange={this._handleInputChange} />
<input type='password'
name='password'
placeholder='password'
value={this.state.password}
onChange={this._handleInputChange} />
<input type='password'
name='password_confirmation'
placeholder='re-type password'
value={this.state.password_confirmation}
onChange={this.handleInputChange} />
</div>
<input onClick={this._handleRegistrationClick} defaultValue="sign up"/>
</form>
)
}
});
module.exports = SignUpForm;
Trên đây là các bước cần thiết để có thể xác thực React app bằng gem devise. Trên đây là một hướng dẫn cơ bản để kết hợp các stuff với nhau. Cảm ơn các bạn đã theo dõi.
All rights reserved