# [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> ```