# [Rails4][jwt]rails jwt簡易實作
###### tags: `Rails4`,`jwt`
參考資料:
https://jwt.io/
https://mgleon08.github.io/blog/2018/07/16/jwt/
https://github.com/jwt/ruby-jwt
https://github.com/riskimidiw/rails-jwt/tree/e6bbb582010d760f78f0260f45906374041b154c
https://medium.com/binar-academy/rails-api-jwt-authentication-a04503ea3248
>原則上是參考這篇[教學](https://medium.com/binar-academy/rails-api-jwt-authentication-a04503ea3248),
>步驟基本上差不多,所以不再詳述,
>只是有幾點不同:
>1. 未用 --api參數產生api專案,這部份其實我覺得沒啥差別
>2. token 解密跟加密的方式採用HS256
>3. 把bcrypt拿掉,使用明碼儲存,以便簡化範例
>4. 把應該是給未來上傳頭像功能用的程式碼省略
>5. 有使用plain html(放在public資料夾)實作簡單前端去測試jwt
## Routes
>這部份跟教學完全一樣,未做更動
config/routes.rb
```ruby
Rails.application.routes.draw do
resources :users,param: :_username
post '/auth/login', to: 'authentication#login'
get '/*a', to: 'application#not_found'
end
```
## Model
>user model內的程式碼全部拿掉,
>那些跟bcrypt與圖像上傳有關
## Controller
>hmac_secret變數是secret key,應該要放在environment中,
>此處僅作為示範用,所以寫在controller內
application_controller.rb
```ruby
class ApplicationController < ActionController::Base
def not_found
render json: { error: 'not_found' }
end
#jwt加解密的語法跟教學不同
#參考https://jwt.io/
def authorize_request
token = request.headers['Authorization']
hmac_secret = 'my$ecretK3y'
begin
@decoded_token = JWT.decode token, hmac_secret, true, { algorithm: 'HS256' }
data= @decoded_token[0]
@current_user=User.find_by(id: data["user_id"])
render json: {user: @current_user}
rescue ActiveRecord::RecordNotFound => e
render json: { errors: e.message }, status: :unauthorized
rescue JWT::DecodeError => e
render json: { errors: e.message }, status: :unauthorized
end
end
end
```
authentication_controller.rb
```ruby
class AuthenticationController < ApplicationController
before_action :authorize_request, except: :login
def login
@user=User.find_by(email: params[:user][:email],password: params[:user][:password])
if @user
payload={user_id: @user.id}
hmac_secret = 'my$ecretK3y'
token = JWT.encode payload, hmac_secret, 'HS256'
render json:{token: token}
else
render json:{error:'unauthorized'}
end
end
private
def login_params
params.permit(:email, :password)
end
end
```
users_controller.rb
```ruby
class UsersController < ApplicationController
before_action :authorize_request, except: :create
before_action :find_user, except: %i[create index]
# GET /users
def index
@users = User.all
render json: @users, status: :ok
end
# GET /users/{username}
def show
render json: @user, status: :ok
end
# POST /users
def create
@user = User.new(user_params)
if @user.save
render json: @user, status: :created
else
render json: { errors: @user.errors.full_messages },
status: :unprocessable_entity
end
end
# PUT /users/{username}
def update
unless @user.update(user_params)
render json: { errors: @user.errors.full_messages },
status: :unprocessable_entity
end
end
# DELETE /users/{username}
def destroy
@user.destroy
end
private
def find_user
@user = User.find_by_username!(params[:_username])
rescue ActiveRecord::RecordNotFound
render json: { errors: 'User not found' }, status: :not_found
end
def user_params
params.require(:user).permit(:name, :username, :email, :password)
end
end
```
## View(此處指放在public資料夾的html)
> 1.未使用postman,改採用plain html的方式去測試,
> 這部份在實務上應該是要用前端框架諸如Angular,React,VUe
> 去實作前端,此處省略.
> 2.請開啟瀏覽器的主控台視窗查看回傳結果
index.html
```htmlembedded=
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>index page</title>
<!-- jquery -->
<script
src="https://code.jquery.com/jquery-3.4.1.min.js"
integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo="
crossorigin="anonymous"></script>
</head>
<body>
<H2>user login</H2>
<form>
email<input type="text" id="email"><br>
password<input type="password" id="password"><br>
<button onclick="userLogin()">submit</button>
</form>
<script>
function userLogin(){
//需加上這句以避免form進行自動提交
event.preventDefault();
$.ajax({
url: "/auth/login",
type: "POST",
contentType: 'application/json',
dataType: "json",
data:JSON.stringify({
user:{
"email":$("#email").val(),
"password":$("#password").val(),
}
}),
success: function(data) {
console.log("success!!");
console.log(data);
},
error: function(err) {
console.log("ERROR!!!");
console.log(err.status);
console.log(err);
}
});
}
</script>
</body>
</html>
```
login.html
```htmlembedded=
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>index page</title>
<!-- jquery -->
<script
src="https://code.jquery.com/jquery-3.4.1.min.js"
integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo="
crossorigin="anonymous"></script>
</head>
<body>
<H2>user login</H2>
<form>
email<input type="text" id="email"><br>
password<input type="password" id="password"><br>
<button onclick="userLogin()">submit</button>
</form>
<script>
function userLogin(){
//需加上這句以避免form進行自動提交
event.preventDefault();
$.ajax({
url: "/auth/login",
type: "POST",
contentType: 'application/json',
dataType: "json",
data:JSON.stringify({
user:{
"email":$("#email").val(),
"password":$("#password").val(),
}
}),
success: function(data) {
console.log("success!!");
console.log(data);
},
error: function(err) {
console.log("ERROR!!!");
console.log(err.status);
console.log(err);
}
});
}
</script>
</body>
</html>
```
register.html
```htmlembedded=
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>index page</title>
<!-- jquery -->
<script
src="https://code.jquery.com/jquery-3.4.1.min.js"
integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo="
crossorigin="anonymous"></script>
</head>
<body>
<H2>user register</H2>
<form>
name<input type="text" id="name"><br>
username<input type="text" id="username"><br>
email<input type="text" id="email"><br>
password<input type="password" id="password"><br>
password confirm<input type="password" id="password_confirmation"><br>
<button onclick="userCreate()">submit</button>
</form>
<script>
function userCreate(){
//需加上這句以避免form進行自動提交
event.preventDefault();
$.ajax({
url: "/users",
type: "POST",
contentType: 'application/json',
dataType: "json",
data:JSON.stringify({
user:{
"name":$("#name").val(),
"username":$("#username").val(),
"email":$("#email").val(),
"password":$("#password").val(),
"password_confirmation":$("#password_confirmation").val(),
}
}),
success: function(data) {
console.log("success!!");
console.log(data);
},
error: function(err) {
console.log("ERROR!!!");
console.log(err.status);
console.log(err);
}
});
}
</script>
</body>
</html>
```
test_auth.html
>可將取得的token貼在input中並按提交,查看回傳結果
```htmlembedded=
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>index page</title>
<!-- jquery -->
<script
src="https://code.jquery.com/jquery-3.4.1.min.js"
integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo="
crossorigin="anonymous"></script>
</head>
<body>
<H2>test user token</H2>
<form>
token<input type="text" id="token"><br>
<button onclick="testToken()">submit</button>
</form>
<script>
function testToken(){
//需加上這句以避免form進行自動提交
event.preventDefault();
token=$("#token").val();
$.ajax({
beforeSend: function(request) {
request.setRequestHeader("Authorization", token);
},
url: "/users",
type: "GET",
success: function(data) {
console.log("success!!");
console.log(data);
},
error: function(err) {
console.log("ERROR!!!");
console.log(err.status);
console.log(err);
}
});
}
</script>
</body>
</html>
```