aokabin / nuxt-rails-practice

0 stars 0 forks source link

nuxt使って、ログインしたい #3

Open aokabin opened 4 years ago

aokabin commented 4 years ago

nuxtするぞ!!!

JWTとか使って、ログイン後のデータにアクセスしたいんだ!!

aokabin commented 4 years ago
$ npx create-nuxt-app frontend

create-nuxt-app v2.12.0
✨  Generating Nuxt.js project in frontend
? Project name frontend
? Project description My excellent Nuxt.js project
? Author name aokabin
? Choose the package manager Yarn
? Choose UI framework Buefy
? Choose custom server framework None (Recommended)
? Choose Nuxt.js modules Axios, Progressive Web App (PWA) Support, DotEnv
? Choose linting tools ESLint, Prettier, Lint staged files, StyleLint
? Choose test framework Jest
? Choose rendering mode Single Page App
? Choose development tools jsconfig.json (Recommended for VS Code)

こんな感じの設定で作った

aokabin commented 4 years ago
yarn run v1.16.0
$ eslint --ext .js,.vue --ignore-path .gitignore . --fix
✨  Done in 2.65s.

🎉  Successfully created project frontend

  To get started:

    cd frontend
    yarn dev

  To build & start for production:

    cd frontend
    yarn build
    yarn start

  To test:

    cd frontend
    yarn test

とのこと

aokabin commented 4 years ago
$ yarn dev

で、3000番アクセスするも、loadingが無限に出続けるな...

? Project name test1
? Project description My marvelous Nuxt.js project
? Author name aokabin
? Choose the package manager Yarn
? Choose UI framework Buefy
? Choose custom server framework None (Recommended)
? Choose Nuxt.js modules Axios, DotEnv
? Choose linting tools (Press <space> to select, <a> to toggle all, <i> to invert selection)
? Choose test framework Jest
? Choose rendering mode Universal (SSR)
? Choose development tools (Press <space> to select, <a> to toggle all, <i> to invert selection)

これは通ったな、、、lintingか?

aokabin commented 4 years ago
create-nuxt-app v2.12.0
✨  Generating Nuxt.js project in test1
? Project name test1
? Project description My spectacular Nuxt.js project
? Author name aokabin
? Choose the package manager Yarn
? Choose UI framework Buefy
? Choose custom server framework None (Recommended)
? Choose Nuxt.js modules Axios, Progressive Web App (PWA) Support, DotEnv
? Choose linting tools (Press <space> to select, <a> to toggle all, <i> to invert selection)
? Choose test framework Jest
? Choose rendering mode Universal (SSR)
? Choose development tools jsconfig.json (Recommended for VS Code)

linting以外fullにしても通った

aokabin commented 4 years ago

ESLint, Prettierだけ入れてみた

これも通った

create-nuxt-app v2.12.0
✨  Generating Nuxt.js project in test1
? Project name test1
? Project description My premium Nuxt.js project
? Author name aokabin
? Choose the package manager Yarn
? Choose UI framework Buefy
? Choose custom server framework None (Recommended)
? Choose Nuxt.js modules Axios, Progressive Web App (PWA) Support, DotEnv
? Choose linting tools ESLint, Prettier, Lint staged files
? Choose test framework Jest
? Choose rendering mode Universal (SSR)
? Choose development tools jsconfig.json (Recommended for VS Code)

Lint staged filesも入れた

aokabin commented 4 years ago
create-nuxt-app v2.12.0
✨  Generating Nuxt.js project in test1
? Project name test1
? Project description My premium Nuxt.js project
? Author name aokabin
? Choose the package manager Yarn
? Choose UI framework Buefy
? Choose custom server framework None (Recommended)
? Choose Nuxt.js modules Axios, Progressive Web App (PWA) Support, DotEnv
? Choose linting tools ESLint, Prettier, Lint staged files
? Choose test framework Jest
? Choose rendering mode Universal (SSR)
? Choose development tools jsconfig.json (Recommended for VS Code)

ここまで通った

aokabin commented 4 years ago
✨  Generating Nuxt.js project in frontend
? Project name frontend
? Project description My majestic Nuxt.js project
? Author name aokabin
? Choose the package manager Yarn
? Choose UI framework Buefy
? Choose custom server framework None (Recommended)
? Choose Nuxt.js modules Axios, Progressive Web App (PWA) Support, DotEnv
? Choose linting tools ESLint, Prettier, Lint staged files
? Choose test framework Jest
? Choose rendering mode Universal (SSR)
? Choose development tools jsconfig.json (Recommended for VS Code)

こんな感じで、StyleLint以外を入れた

aokabin commented 4 years ago

これで起動するようになったので、色々やっていきたい気持ち

だけど、ログインセッション欲しいが、今の所そんな仕組みないもんなぁ

aokabin commented 4 years ago

一旦、railsのプロキシとしてnuxtを扱ってみる?

image

nuxtからのリクエストをノー認証で通すというパターン これはcsrfを使わないようにすればできそう

aokabin commented 4 years ago

まずは、nuxtからバックエンドに適当にリクエストを飛ばしたい こんな感じの環境変数を設定 BACKENDがrailsになる予定

BASE_URL=http://localhost:3000
BACKEND_URL=http://localhost:3001
aokabin commented 4 years ago

/login にアクセスしたら、適当なフォームを表示して、railsにアクセスするようにしてみよう

aokabin commented 4 years ago

pages/login.vue を作った

ここに何書けばいいかは思いついてないなぁ とりあえずh1書いてみるか

<template>
  <h1>Hello</h1>
</template>

<script>
export default {
  name: 'Login'
}
</script>
aokabin commented 4 years ago

あ、なんかちっちゃくhelloって表示されたw

image

じゃあなんかフォーム作って、この中にidとpwを入れて、送信押したら、railsに問い合わせるようにしてみよう

aokabin commented 4 years ago

ログイン、 @submit みたいな記述、何だろう

へー、バリデーションとかが挟めるんだ、そしてvueの機能か

んー、一旦vueが持つ範囲を知っていた方が、それぞれがどの部分を持っているのか、とかがイメージつかなそうだな

aokabin commented 4 years ago

input v-modelでいけそう

<template>
  <div class="login-form">
    <p>{{ email }}</p>
    <input v-model="email" />
  </div>
</template>

<script>
export default {
  data() {
    return {
      error: null,
      email: '',
      password: ''
    }
  }
}
</script>

お、いい感じ

image

<template>
  <div class="login-form">
    <p>{{ email }}</p>
    <input v-model="email" />
    <p>{{ password }}</p>
    <input v-model="password" />
  </div>
</template>

<script>
export default {
  data() {
    return {
      error: null,
      email: '',
      password: ''
    }
  }
}
</script>

image

aokabin commented 4 years ago

password、隠したいな

aokabin commented 4 years ago

普通にtype指定するだけだった、便利だなぁ

<template>
  <div class="login-form">
    <p>{{ email }}</p>
    <input type="email" v-model="email" />
    <p>{{ password }}</p>
    <input type="password" v-model="password" />
  </div>
</template>

<script>
export default {
  data() {
    return {
      error: null,
      email: '',
      password: ''
    }
  }
}
</script>

image

aokabin commented 4 years ago

あれー、どうやってmethodsの中でdataの中身読むんだっけ...

<template>
  <div class="login-form">
    <p>{{ email }}</p>
    <input v-model="email" type="email" />
    <p>{{ password }}</p>
    <input v-model="password" type="password" />
    <button v-on:click="login">Login</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      error: null,
      email: '',
      password: ''
    }
  },
  methods: {
    login() {
      alert(this.email)
    }
  }
}
</script>

いけたいけた

aokabin commented 4 years ago

何となく

みたいなのが良さげだったので、それを参考に...

aokabin commented 4 years ago

lib/backend-client.js を作ってみた

import axios from 'axios'
export const request = (method, url, body) => {
  method = method.toLowerCase()
  let queryUrl = url
  return axios[method](url, body)
}

export const Login = function(parameters = {}) {
  const domain = 'http://localhost:3001'
  let path = '/login'
  let body
  if (parameters['body'] !== undefined) {
    body = parameters['body']
  }
  if (parameters['body'] === undefined) {
    return Promise.reject(new Error('Missing required  parameter: body'))
  }
  return request('post', domain + path, body)
}
aokabin commented 4 years ago

storeも書いてみた

backend.js

import { Login } from '../lib/backend-client'

export const actions = {
  login: ({}, { email, password }) => {
    const loginService = Login({ email, password })
    return loginService
  }
}

login.js

export const state = () => ({
  email: '',
  password: '',
  key: ''
})

export const mutations = {
  setLoginData: (state, params) => {
    state.email = params.email
    state.password = params.password
  },
  setKey: (state, data) => {
    state.key = data.key
  }
}

export const actions = {
  login: ({ commit, state, dispatch }) => {
    return new Promise(function(resolve, reject) {
      const api = dispatch(
        'backend/login',
        {
          email: state.email,
          password: state.password
        },
        { root: true }
      )

      api.then((resp) => {
        commit('setKey', resp.data)
      })
      api.catch((error) => {
        console.log('get netstatus got error', error)
      })
    })
  }
}

export const getters = {
  accessKey: (state) => {
    return state.key
  }
}
aokabin commented 4 years ago

これができたら、あとは、loginを呼んでみる

この時点で色々できていそうなんだけど、どうやらCORS設定してね、ってなってるな

railsに書いてみようか

aokabin commented 4 years ago

railsのCSRF offにした protect_from_forgery with: :null_session できてるかわからないけど

そしてCORS、なんか便利なのあるんだなー

aokabin commented 4 years ago

CORSをこのように設定

application.rb

    config.middleware.insert_before 0, Rack::Cors do
      allow do
        origins "http://localhost:3000"
        resource "*",
          headers: :any,
          methods: [:get, :post, :put, :delete, :head, :options]
      end
    end

そしたら、画面は変わらないけど、うまくいったっぽい

Started POST "/login" for ::1 at 2019-11-26 23:51:56 +0900
Processing by SessionsController#create as HTML
  Parameters: {"email"=>"", "password"=>"[FILTERED]", "session"=>{"email"=>"", "password"=>"[FILTERED]"}}
HTTP Origin header (http://localhost:3000) didn't match request.base_url (http://localhost:3001)
  User Load (1.0ms)  SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 0], ["LIMIT", 1]]
  ↳ app/controllers/application_controller.rb:12:in `current_user'
  User Load (0.1ms)  SELECT "users".* FROM "users" WHERE "users"."email" = ? LIMIT ?  [["email", ""], ["LIMIT", 1]]
  ↳ app/controllers/sessions_controller.rb:26:in `set_user'
  Rendering sessions/new.html.erb within layouts/application
  Rendered sessions/new.html.erb within layouts/application (Duration: 0.2ms | Allocations: 54)
  CACHE User Load (0.0ms)  SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 0], ["LIMIT", 1]]
  ↳ app/controllers/application_controller.rb:12:in `current_user'
Filter chain halted as :set_user rendered or redirected
Completed 200 OK in 24ms (Views: 16.5ms | ActiveRecord: 1.2ms | Allocations: 9564)

200帰ってきてるログが出てる

aokabin commented 4 years ago

あ、よくみたらemailもpasswordも空ですね session[email] にしなきゃ

aokabin commented 4 years ago

なんかダメだな、値渡されてないわ

aokabin commented 4 years ago

あーsetLoginDataしてないわ

aokabin commented 4 years ago

直接渡す感じにしてみた

export const actions = {
  login: ({ commit, dispatch }, { email, password }) => {
    return new Promise(function(resolve, reject) {
      const api = dispatch(
        'backend/login',
        {
          email,
          password
        },
        { root: true }
      )

      api.then((resp) => {
        commit('setKey', resp.data)
      })
      api.catch((error) => {
        console.log('get netstatus got error', error)
      })
    })
  }
}
Started POST "/login" for ::1 at 2019-11-26 23:58:13 +0900
Processing by SessionsController#create as HTML
  Parameters: {"session[email]"=>"", "session[password]"=>"[FILTERED]", "session"=>{"session[email]"=>"", "session[password]"=>"[FILTERED]"}}
HTTP Origin header (http://localhost:3000) didn't match request.base_url (http://localhost:3001)
  User Load (0.2ms)  SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 0], ["LIMIT", 1]]
  ↳ app/controllers/application_controller.rb:12:in `current_user'
Unpermitted parameters: :session[email], :session[password]
  User Load (0.3ms)  SELECT "users".* FROM "users" WHERE "users"."email" IS NULL LIMIT ?  [["LIMIT", 1]]
  ↳ app/controllers/sessions_controller.rb:26:in `set_user'
  Rendering sessions/new.html.erb within layouts/application
  Rendered sessions/new.html.erb within layouts/application (Duration: 0.3ms | Allocations: 54)
  CACHE User Load (0.0ms)  SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 0], ["LIMIT", 1]]
  ↳ app/controllers/application_controller.rb:12:in `current_user'
Filter chain halted as :set_user rendered or redirected
Completed 200 OK in 18ms (Views: 13.3ms | ActiveRecord: 0.5ms | Allocations: 9742)

うーん、なんか変なパラメータで渡されてるなぁ

aokabin commented 4 years ago

ん?なんかそのまま行った方が良さそうだぞ?

import { Login } from '../lib/backend-client'

export const actions = {
  login: ({ state }, { email, password }) => {
    console.log(email)
    console.log(password)
    const param = {
      email,
      password
    }
    const loginService = Login(param)
    return loginService
  }
}
Started POST "/login" for ::1 at 2019-11-27 00:05:40 +0900
   (0.3ms)  SELECT sqlite_version(*)
Processing by SessionsController#create as HTML
  Parameters: {"session[email]"=>"kabi@mail.com", "session[password]"=>"[FILTERED]", "session"=>{"session[email]"=>"kabi@mail.com", "session[password]"=>"[FILTERED]"}}
HTTP Origin header (http://localhost:3000) didn't match request.base_url (http://localhost:3001)
  User Load (0.3ms)  SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 0], ["LIMIT", 1]]
  ↳ app/controllers/application_controller.rb:12:in `current_user'
Unpermitted parameters: :session[email], :session[password]
  User Load (0.2ms)  SELECT "users".* FROM "users" WHERE "users"."email" IS NULL LIMIT ?  [["LIMIT", 1]]
  ↳ app/controllers/sessions_controller.rb:26:in `set_user'
  Rendering sessions/new.html.erb within layouts/application
  Rendered sessions/new.html.erb within layouts/application (Duration: 1.8ms | Allocations: 53)
  CACHE User Load (0.0ms)  SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 0], ["LIMIT", 1]]
  ↳ app/controllers/application_controller.rb:12:in `current_user'
Filter chain halted as :set_user rendered or redirected
Completed 200 OK in 54ms (Views: 41.2ms | ActiveRecord: 0.5ms | Allocations: 9766)
aokabin commented 4 years ago

何でsessionのなかに自動的にemailもpasswordも入っているか不明だな...

import axios from 'axios'
export const request = (method, url, body) => {
  method = method.toLowerCase()
  return axios[method](url, body)
}

export const Login = function(parameters = {}) {
  const domain = 'http://localhost:3001'
  const path = '/login'
  const body = parameters
  body.hoge = 'fuga'
  console.log(body)
  return request('post', domain + path, body)
}

fugaを強制追加してみた所

Started POST "/login" for ::1 at 2019-11-27 00:14:31 +0900
   (0.8ms)  SELECT sqlite_version(*)
   (0.4ms)  SELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC
Processing by SessionsController#create as HTML
  Parameters: {"email"=>"kabi@mail.com", "password"=>"[FILTERED]", "hoge"=>"fuga", "session"=>{"email"=>"kabi@mail.com", "password"=>"[FILTERED]", "hoge"=>"fuga"}}
HTTP Origin header (http://localhost:3000) didn't match request.base_url (http://localhost:3001)
  User Load (0.4ms)  SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 0], ["LIMIT", 1]]
  ↳ app/controllers/application_controller.rb:12:in `current_user'
Unpermitted parameter: :hoge
  User Load (0.1ms)  SELECT "users".* FROM "users" WHERE "users"."email" = ? LIMIT ?  [["email", "kabi@mail.com"], ["LIMIT", 1]]
  ↳ app/controllers/sessions_controller.rb:26:in `set_user'
Unpermitted parameter: :hoge
Redirected to http://localhost:3001/
Completed 302 Found in 243ms (ActiveRecord: 1.1ms | Allocations: 7673)

fugaがどちらにも入っていた... これは誰の挙動なんだ...

aokabin commented 4 years ago

なんか、それっぽくできているんだけど、ちょっとdispatch周りが適当すぎるので、修正していきたい気持ち

aokabin commented 4 years ago

↑なんか色々やってたっぽいけど、新たな知見として CORS設定していれば、ブラウザはnuxtのaxiosを使ったrailsへのアクセスにセッションを使ってくれるみたい

そうなのか、、、今まで全部勘違いしてた...

aokabin commented 4 years ago

nuxtのmiddlewareで、ログインチェックしたい、どうしたらいいかな? backendのsessionがあるかどうか、か

aokabin commented 4 years ago

ちょっと整理した方がいいな?

今やりたいのは、なんとなく、nuxtからrailsにログイン、みたいな感じのイメージだけど ちょっとやり方が微妙な可能性があるな?

aokabin commented 4 years ago

ヘルスチェック的なエンドポイント作ってみた じゃあそこをチェックして、falseならログインにリダイレクトするようにしてみようか

aokabin commented 4 years ago

んー、jwtにする、やっぱり一旦jwtにして、その検証でやろう

aokabin commented 4 years ago

なるほど、jwtをDBに保存する必要あり? やるかー、、、やらなくてよさそう

ログイン自体はWebページでやれば良い、user#checkにアクセスした時に、jwtをjsonで返すようにするか

aokabin commented 4 years ago
  def check
    render :json => payload(current_user)
  end

  def payload(user)
    return {} unless user and user.id
    {
      auth_token: JsonWebToken.encode({user_id: user.id, exp: (Time.now + 2.week).to_i}),
    }
  end

こんな感じで

aokabin commented 4 years ago

nuxt使ってログインする必要はない、どちらかといえば、jwtとったので、これを使ってリクエストとか送りたいよねとはなる

$ curl http://localhost:3001/items \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJleHAiOjE1Nzc4NzY1NDB9.jd7YoKoNiQDaEf6j6lU74auwlzh7zjUWBUj6IPxNGrk"
[{"name":"バナナ","price":200},{"name":"ぶどう","price":300},{"name":"りんご","price":100}]

いけた

aokabin commented 4 years ago

これを使ってnuxtでデータ取りたいというのは別のお話...

7