---
# System prepended metadata

title: 12th 鐵人賽 Day_10 問題中場休息-部落格的管理員系統
tags: [12th 鐵人賽]

---

---
title: 12th 鐵人賽 Day_10 問題中場休息-部落格的管理員系統 
tags: 12th 鐵人賽
--- 
# 12th 鐵人賽 Day_10 問題中場休息-部落格的管理員系統

嗨！各位朋友大家好，打給後，歹嘎吼，胎尬喉，我是阿圓，一樣有請今天的one piece：
![](https://i.imgur.com/TxyQvDg.jpg)

居然已經邁入第十天，我的挑戰也已經過了三分之一了，但我居然一行code都沒有放上來！！！這真的太母湯了！趕快來補救一下！那由於今天會放上我的部落格的程式碼，所以字數有點過長，問題就讓我放到明天來寫吧(所以明天會有兩個問題！)

## 定義路徑
```ruby=
# /routes.rb
Rails.application.routes.draw do
  # 這邊為了測試，放了一個index給他，之後想要改成文章的首頁
  root 'admins#index'
  # 我不想要有預設的路徑，直接用最簡單的方式寫給他
  get 'admins/sign_in', to: 'admins#sign_in'
  post 'admins/sign_in', to: 'admins#login'
  delete 'admins/sign_out', to: 'admins#sign_out'
end
```
## 建立views
```htmlmixed=
<!-- admins/views/index.html.erb -->
<h1>臨時首頁</h1>

<%= link_to '登入', admins_sign_in_path%>
<%= link_to '登出', admins_sign_out_path, method: "delete"%>
```
```htmlmixed=
<!-- admins/views/sign_in.html.erb -->
<h1>管理者登入</h1>
  <%= form_for(@admin, url: admins_sign_in_path) do |f| %>
    <div class="fields">
      <%= f.label :account %>
      <%= f.text_field :account, placeholder:'請輸入帳號' %>
    </div>
    <div class="fields">
      <%= f.label :password, placeholder:'請輸入密碼' %>
      <%= f.password_field :password %>
    </div>
    <%= f.submit "登入"%>
  <% end %>

```

## 建立controller
`$ rails g controller admins`
接著定義action，將routes上的action寫上去，至於預設的路徑，就先不處理，之後有需要再來改。
```ruby=
def sign_in 
  #do something
end

def login
  #do something
end

def sign_out
  #do something  
end
```

## 建立model
先建立model
`$ rails g model Admin account password:password`
Admin有兩個欄位，account 跟 password
```ruby=
# /admin.rb
class Admin < ApplicationRecord
  # account password 必填
  validates :account, presence: true, uniqueness: true
  validates :password, presence: true
  
  # 在create之前，將密碼加密
  before_create :encrypt_password
end
```
自定義自己的密碼加密方式，要在Admin這邊的一個物件create之前將密碼加密，在下面 private 的寫下：
```ruby=
private

def encrypt_password
  self.password = popper(password)
end

def popper(string)
  string = 'cu' + string + 'te'
  Digest::SHA1.hexdigest(string)
end
```
所謂 `popper` ，就是在密碼前後加些綴字，讓密碼更難破解(不過我都放上來了XD，就相當於無效了哈哈)。

## 更改controller裡的action
### sign_in
```ruby=
# /admins_controller.rb
# 在Admin的類別中，新增一個@admin的實體變數
  def sign_in
    @admin = Admin.new
  end
```
現在就有畫面了：
![](https://i.imgur.com/ctsshJt.png)

### login
接著來定義，按下登入按鈕後，要進行的事情：
```ruby=
# /admins_controller.rb
  def login
    admin = Admin.login(admin_params) 
    # 確認資料庫裡是否有使用相同account和password的管理員
    if admin
      sign_in_admin(admin) #定義在下方，發session
      redirect_to root_path, notice: '成功登入！'
    else
      redirect_to sign_in_admins_path, notice: '請輸入正確帳號密碼'
    end
  end
```
其中，第2行的 `login` 是為了要去比對資料庫裡是否有相同帳號密碼的使用者，所以將方法定義在 admin 的 model 裡面(所有的 admin 都有 login 的方法)，如下：
```ruby=
# /admin.rb
  def self.login(options)
    if options[:account] && options[:password]
      find_by(account: options[:account],
              password: Digest::SHA1.hexdigest('cu' + options[:password] + 'te'))
    end
  end
```
上面做了什麼呢？有請圖解！
![](https://i.imgur.com/1vT6j6w.png)


搞定了model的login方法，接著來繼續回來controller，在下面加上：
```ruby=
# /admins_controller.rb
 private
  def sign_in_admin(a)
    session[:admin_token] = SecureRandom.uuid
    # 這裡使用了SecureRandom的方法，他是一個在rails 裡寫好的module，所以要去最上面加上 require 'securerandom'
  end
  
  def admin_params
    params.require(:admin).permit(:account, :password)
  end
```
簡單解釋一下 `admin_params` 做的事情，是將是將登入頁面打過來的資訊，清洗後再傳給 server，若沒有經過 `permit`，在 `create` 資料的時候，就會得到下列畫面：
![](https://i.imgur.com/4pZWyvk.png)

但因為我沒有放東西進去，`login` 這個 `action` 沒有建立新的資料，所以沒有寫，網頁不會噴錯(我把方法改成 `create，`才得到這個畫面！)
這就是所謂的 `Strong parameter`，若想了解其中運作的原理，請參閱[為你自己學 Ruby on Rails](https://railsbook.tw/chapters/13-crud-part-1.html#step06-create-candidate) 。


延續 `login` 的 `action` ：若登入成功，就將使用者轉址到根目錄，若失敗重新導入 `sign_in` 的頁面。
### sign_out
直接把session清掉！
```ruby=
# /admins_controller.rb
def sign_out
  sign_out_admin
  redirect_to root_path, notice: '會員登出成功'
end

private

def sign_out_admin
  session[:admin_token] = nil
end
```
目前這就是我實作管理員系統的流程，感謝各位看到這邊，若有任何建議，==我非常需要各位的指教==！謝謝大家！我們明天見！
![](https://i.imgur.com/PPbpd2b.gif)