GWの記事3投目です。
一連の記事
- DockerをつかってVueとRailsの開発環境をつくる
- DockerをつかってVueとRailsの開発環境にユーザー認証機能を実装するこの記事!!
- DockerをつかったVueとRailsの環境にAPIを実装する
本記事の目的
DockerをつかってVueとRailsの開発環境をつくるの続編です。
前回環境を作ったところに、以下の機能を追加する
- ユーザーの新規登録
- ログイン機能
- Tokenの発行
手順
サーバー側
CORSの設定
cors設定に必要なものとついでにhas_secure_password
を使用するためのものをgemfileに記載
# Gemfile
# Use Rack CORS for handling Cross-Origin Resource Sharing (CORS), making cross-origin AJAX possible
gem 'rack-cors'#28行目あたりのこの行のコメントアウトを外す
gem 'bcrypt', '~> 3.1.7' #追記
外したら保存してdocker-compose run rails bundle install
を実行する
Rails.application.config.middleware.insert_before 0, Rack::Cors do
allow do
origins 'http://localhost:8080'#Dockerで立ててるWebコンテナ(Vue)のアドレス
resource '*',
headers: :any,
methods: [:get, :post, :put, :patch, :delete, :options, :head]
end
end
Userモデルの作成
Userモデルを作成します
docker-compose run rails rails g model User email:string name:string password_digest:string token:string
# 終わったら
docker-compose run rails rails db:migrate
認証処理を実装する
class ApplicationController < ActionController::API
include ActionController::HttpAuthentication::Token::ControllerMethods
before_action :authenticate!
private
def authenticate!
authenticate_or_request_with_http_token do |token, options|
User.find_by(token: token).present?
end
end
def current_user
@current_user ||= User.find_by(token: request.headers['Authorization'].split[1])
end
end
before_action :authenticate!
を使うことで全コントローラーのアクションに認証を掛けられる
認証なしでもAPIを使いたい場合はskip_before_action
を使う
UsersControllerの実装
docker-compose run rails rails g controller Users create sign_in
ルーティングの調整を以下のようにする
Rails.application.routes.draw do
resources :users, only: [ :create ] do
collection do
post 'sign_in'
end
end
end
ログイン機能と新規登録機能を実装
class UsersController < ApplicationController
skip_before_action :authenticate!, only: [ :create, :sign_in ]
# 新規登録 POST/user
def create
@user = User.new(email: params[:email], password: params[:password], name: params[:name])
if @user.save
render json: @user
else
render json: { errors: @user.errors.full_messages }, status: 400
end
end
# ログイン POST/sign_in
def sign_in
@user = User.find_by(email: params[:email])
if @user && @user.authenticate(params[:password])
render json: @user
else
render json: { errors: ['ログインに失敗しました'] }, status: 401
end
end
end
これでサーバー側の準備は終了です。
フロント側
axiosの準備とElement-uiの導入
HTTP通信を行うためにAxiosを導入します
docker-compose run web npm install --save axios vue-axios element-ui
終わったら、Vueで読み込むための設定をします。
import Vue from 'vue'
import App from './App.vue'
import './registerServiceWorker'
import router from './router'
import store from './store'
import ElementUI from 'element-ui'// 追記
import 'element-ui/lib/theme-chalk/index.css'// 追記
import axios from 'axios'// 追記
import VueAxios from 'vue-axios'// 追記
Vue.use(VueAxios, axios)// 追記
Vue.use(ElementUI)// 追記
Vue.config.productionTip = false
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
Headerの作成
アプリヘッダーを作成して表示させます。
<template>
<div class="appheadder">
<router-link to="/" class="title">プロジェクト名</router-link>
<div class="menu">
<ul id="nav">
<span v-if="!$store.state.token">
<li><router-link to="/login">ログイン</router-link></li>
<li><router-link to="/signup">新規登録</router-link></li>
</span>
<span v-else>
<li><router-link to="/login">検索</router-link></li>
<li><a @click="dologout">ログアウト</a></li>
</span>
</ul>
</div>
</div>
</template>
<script>
export default {
name: 'appheadder',
methods: {
dologout () {
this.$store.dispatch('doRegistrationToken', null)
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.appheadder{
background-color: rgb(92, 182, 153);
height: 50px;
}
.menu{
text-align: right;
float: right;
position: fixed;
right: 0;
top: 0;
}
.title{
font-size: 40px;
text-align: center;
margin: auto;
text-decoration: none;
color: rgb(0, 0, 0);
font-weight: bold;
}
# nav {
padding: 0 0 0 0;
list-style: none;
overflow: hidden;
display: inline;
}
# nav li {
width: 12vw;
text-align: center;
background-color: #333;
float: left;
height: 50px;
line-height: 50px;
margin-right: 2px;
}
# nav li a {
text-decoration: none;
color: #fff;
font-weight: bold;
}
</style>
<template>
<div id="app">
<Appheadder/>
<router-view/>
</div>
</template>
<script>
import Appheadder from './components/appheadder.vue'
export default {
name: 'Home',
components: {
Appheadder
}
}
</script>
<style>
*{
margin: 0;
}
# app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
}
# nav {
padding: 30px;
}
# nav a {
font-weight: bold;
color: #2c3e50;
}
# nav a.router-link-exact-active {
color: #42b983;
}
</style>
<template>
<div class="home">
</div>
</template>
<script>
export default {
name: 'Home',
components: {
}
}
</script>
これを変更してページの状態を確認しましょう。
コンテナを立ち上げていなければ立ち上げてhttp://localhost:8080 にアクセスして、以下のようなページが表示されていればOKです。
ログインページと新規登録画面の作成
まず新規登録画面を作成します
<template>
<div class="signup">
<h1>新規登録</h1>
<div class="loginForm">
<table>
<tr>
<th>ID</th>
<td><el-input placeholder="Please input" v-model="id"></el-input></td>
</tr>
<tr>
<th>パスワード</th>
<td><el-input placeholder="Please input password" v-model="pass" show-password></el-input></td>
</tr>
</table>
<el-button type="primary" @click="dologin">新規登録</el-button>
</div>
</div>
</template>
<script>
export default {
name: 'signup',
data () {
return {
id: '',
pass: ''
}
},
components: {
},
methods: {
dologin () {
this.axios.post('http://0.0.0.0:3000/users/', {
name: this.id,
pwd: this.pass
},
{
headers: {
'Content-Type': 'application/json'
}
})
.then((response) => {
this.$store.dispatch('doRegistrationToken', response.data.token)
})
.catch((e) => {
console.log(e)
})
}
}
}
</script>
<style scoped>
.loginForm{
width: 80%;
padding: 10vh 5vw;
margin: auto;
background-size: cover;
}
.loginForm *{
margin: auto;
}
</style>
次にログイン画面も作りますがほとんど一緒です。
<template>
<div class="login">
<h1>ログイン</h1>
<div class="loginForm">
<table>
<tr>
<th>ID</th>
<td><el-input placeholder="Please input" v-model="id"></el-input></td>
</tr>
<tr>
<th>パスワード</th>
<td><el-input placeholder="Please input password" v-model="pass" show-password></el-input></td>
</tr>
</table>
<el-button type="primary" @click="dologin">ログイン</el-button>
</div>
</div>
</template>
<script>
export default {
name: 'Login',
data () {
return {
id: '',
pass: ''
}
},
components: {
},
methods: {
dologin () {
this.axios.post('http://0.0.0.0:3000/users/sign_in', {
name: this.id,
pwd: this.pass
},
{
headers: {
'Content-Type': 'application/json'
}
})
.then((response) => {
this.$store.dispatch('doRegistrationToken', response.data)
})
.catch((e) => {
console.log(e)
})
}
}
}
</script>
<style scoped>
.loginForm{
width: 80%;
padding: 10vh 5vw;
margin: auto;
background-size: cover;
}
.loginForm *{
margin: auto;
}
</style>
続いてルーティングを直していきます。
まずヘッダーを直します
<template>
<div class="appheadder">
<router-link to="/" class="title">プロジェクト名</router-link>
<div class="menu">
<ul id="nav">
<span v-if="!$store.state.token">
<li><router-link to="/login">ログイン</router-link></li><!--変更-->
<li><router-link to="/signup">新規登録</router-link></li><!--変更-->
</span>
<span v-else>
<li><router-link to="/login">検索</router-link></li><!--変更-->
<!--以下略-->
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'
import Login from '../views/login.vue'
import Signup from '../views/signup.vue'
Vue.use(VueRouter)
const routes = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/login',
name: 'login',
component: Login
},
{
path: '/signup',
name: 'signup',
component: Signup
},
{
path: '/about',
name: 'About',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
}
]
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
export default router
ストアにトークンを保存するための準備
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
token: ''
},
mutations: {
RegistrationToken (state, payload) {
state.token = payload.token
}
},
actions: {
doRegistrationToken ({ commit }, token) {
commit('RegistrationToken', { token })
}
},
modules: {
}
})
これで確認してみましょう!
ここでユーザーを登録すると右上のMENUが検索とログアウトになれば成功です。
正常にユーザーが新規登録でき、Tokenが帰ってきています。
ログアウトを押してもとに戻るのも確認してください。
その後ログインから、先程登録した情報でログインできるか確認してみてください。
あとがき
今後も続いて開発していけたらこうしてまとめていこうと思いますのでよろしくおねがいします。
あ〜、GWもあと1日か〜
参考にさせていただいた記事
めちゃくちゃおせわになりました!!
ありがとうございます!