Một số câu hỏi liên quan đến Nuxtjs ( Vuejs)
Xin chào anh chị! Em có 1 số vấn đề khi làm việc Front-End với Nuxtjs. Mong được mọi người chỉ bảo. Do có nhiều câu hỏi nên em xin gói trọn lại bài này. Hiện em có 2 câu thôi, nhưng có lẽ sẽ tiếp tục có thêm nhiều câu khác do mới bắt đầu tìm hiểu về Vuejs nên trong quá trình hi vọng nhận được sự hỗ trợ từ anh chị.
Câu hỏi 1:
Do back-end em dùng Laravel. Có tích hợp sẵn Vuejs nên ban đầu thì em code front-end với Vuejs. Khi tương tác với localStorage thì cũng như bình thường là localStorage.setItem với getItem vì Vuejs Client-side rendering. Sau đó do cần SEO nên em chuyển qua Nuxtjs. Đến đây thì mắc 1 nỗi là Nuxtjs có cả Server-side rendering nên khi gọi localStorage nó nó báo lỗi ngay. Sau này em đọc được cách if (process.browser) thì thấy đúng là nó hoạt động. Browser đã lưu được storage
if (process.browser) {
localStorage.setItem("user",JSON.stringify(state.currentUser));
}
Nhưng trước đó thì em search Google hoài về vấn đề này đều đươc nhiều comment và tutorial dẫn đến cái plugin này. https://github.com/championswimmer/vuex-persist
Vậy tại sao lại như vậy? Nếu đã có thể if (process.browser) rồi dùng localStorage bình thường thì điểm khác nhau của chúng là gì? Có phải if (process.browser) không đúng trong mọi trường hợp?
Câu hỏi 2:
Khi làm việc với laravel. Mọi việc liên quan đến submit form hay dùng ajax gửi request với http method POST, PUT, DELETE đều phải có chưá csrf (gán X-CSRF-TOKEN) vào header. Nếu không sẽ bị 401 (Unauthorized). https://laravel.com/docs/5.7/csrf#csrf-x-xsrf-token
Nhưng tại sao khi chúng ta test với POSTMAN đến router API của laravel cũng với các method trên hay dùng axios trên Vuejs để request qua thì nó vẫn chạy bình thường?
CSRF ( Cross Site Request Forgery) là kĩ thuật tấn công bằng cách sử dụng quyền chứng thực của người sử dụng đối với 1 website khác. Các ứng dụng web hoạt động theo cơ chế nhận các câu lệnh HTTP từ người sử dụng, sau đó thực thi các câu lệnh này.
Hacker sử dụng phương pháp CSRF để lừa trình duyệt của người dùng gửi đi các câu lệnh http đến các ứng dụng web. Trong trường hợp phiên làm việc của người dùng chưa hết hiệu lực thì các câu lệnh trên sẽ dc thực hiện với quyền chứng thực của người sử dụng.
3 CÂU TRẢ LỜI
Về câu hỏi 1 của em:
localStorage
là một đặc tả của HTML5, chứ không phải là một phần của Javascript. Tức nó chỉ tồn tại trên một số các trình duyệt có hỗ trợ mà thôi. Khi chạy ở phần server (nodejs), đương nhiên là sẽ không có biến nào làlocalStorage
cả, nên đoạn code của em sẽ bị lỗi.- Việc dùng
if (process.browser)
là để check xem đoạn code javascript đó đang được chạy ở phía server, hay phía client (browser), nhằm đảm bảo phần code liên quan đếnlocalStorage
chỉ chạy ở phía trình duyệt thôi, và nó là cần thiết bởi nếu bê đoạn code đó lên chạy ở phần server thì sẽ có lỗi, như em đã gặp phải
Về câu hỏi thứ 2 của em:
- Bình thường Laravel tự động check CSRF token là bởi vì các controller phía backend được chỉ định là phải làm thế. Xử lý logic check CSRF token được đặt ở trong
app/Http/Middleware/VerifyCsrfToken.php
(được kế thừa từ classIlluminate\Foundation\Http\Middleware\VerifyCsrfToken
). Middleware này được mặc định add vào middleware groupweb
(em có thể check nội dung trong fileapp/Http/Kernel.php
). Nên tất cả các routes em khai báo mà có middleware group làweb
thì đều sẽ phải verify CSRF token, hay nói cách khác, để truy cập vào các route đó thì phía client phải submit CSRF token lên. - Bên cạnh middleware group là
web
, Laravel còn có middleware group khác làapi
. Mặc định củaapi
không có middleware CSRF. Cụ thể đoạn code của phần đó (trong fileKernel.php
) như sau. Em có thể tham khảo thêm tại phần document về Middleware Groups trên website của Laravel
/**
* The application's route middleware groups.
*
* @var array
*/
protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
'api' => [
'throttle:60,1',
'auth:api',
],
];
- Do đó, khi truy cập đến các route mà có middle group là
api
thì em không cần submit CSRF token lên, đó là lý do tại sao em có thể gọi trực tiếp từ POSTMAN mà không bị lỗi gì
Cám ơn anh vì câu trả lời rất đầy đủ và chi tiết Em có thêm câu hỏi ở đây. Sau khi browser đã lưu đc object User vào storage khi Login mà API trả về.
Trong index store, em có gọi nó ra để sử dụng. Thử console.log thì in ra OK.
Kết quả
Cũng là cái user có dữ liệu đã thử Log ở trên. Nhưng khi em dùng state 1 biến currentUser: user để lưu lại Object User đó. Mục đích để getter hiển thị thông tin người dùng và lấy token submit sau này.
const store = () => new Vuex.Store({
state:{
currentUser:user,
}
})
getters:{
currentUser(state){
return state.currentUser;
},
}
Nhưng có vẻ sai vì lý do đã được phân tích ở trên nên khi getter cái currentUser này thì nó luôn luôn NULL mỗi lần bị refresh chứ không có ra cái Object user đã localStorage.getItem("user") kia ( Mặc dù localstorage của trình duyệt vẫn đang lưu 1 key value Object user đó. Thành ra cũng không thể check được User đã Login. Phải Login lại.
Vậy giải pháp xử lý như thế nào trong TH này như thế nào?
Về câu 1, Nuxtjs thực chất chạy ở cả Client và Server. process.browser là một field của webpack để check xem hiện tại nuxt đang chạy trên Node hay là Broswer. Nếu field kia trả về true thì nghĩa là đang chạy trên Browser,do đó tất nhiên ta có thể sử dụng được localStorage thui.
Còn về câu 2, thì theo mình được biết CSRF hay CORS là do cơ chế của trình duyệt để bảo vệ tránh trường hợp request đến một domain khác thui. Postman là dev tool nên loại bỏ cơ chế này nên có thể request bình thường.
Cám ơn câu trả lời của anh. Câu 1 ý em không phải hỏi sao nó làm việc a @@@
Câu 2 thì từ localhost port 3000 của vuejs axios POST lên api nó vẫn chạy đc mà không bị 401. Em test chức năng login qua Api. Truyền lên chỉ có body email và password, k có thêm header gì cả. Không riêng gì POST MAN.
@huuhung96 bên port 3000 bạn đang dùng nuxtjs hay vuejs nhỉ
@quankm1097 Nuxtjs anh ạ
@huuhung96 https://github.com/nuxt-community/axios-module/issues/21 Nếu axios của nuxt thì có sẵn csrf rùi 😂
Câu hỏi 2: Với Vuejs ở file resources/js/bootstrap.js đã có config mặc cho csrf rùi nên nếu bạn không sửa file này thì mặc định axios đã có csrf trong mỗi request
let token = document.head.querySelector('meta[name="csrf-token"]');
if (token) {
window.axios.defaults.headers.common['X-CSRF-TOKEN'] = token.content;
} else {
console.error('CSRF token not found: https://laravel.com/docs/csrf#csrf-x-csrf-token');
}
Em dùng Nuxtjs. Tách biệt với laravel anh à. Laravel chỉ để làm API mà thôi.
@huuhung96 Vs Api thay vì phải gửi lên csrf-token, thì bên Api bạn có thể thêm middleware:api (laravel passport hay jwt) và header của n là một Authorization (access_token) để chứng thực ng dùng nhé bạn...