Merge branch 'dev_toschina' of https://git.trustie.net/jacknudt/trustieforge into dev_toschina

This commit is contained in:
caishi 2018-11-22 11:17:32 +08:00
commit ac0cd92c68
12 changed files with 273 additions and 34 deletions

View File

@ -451,6 +451,8 @@ class AccountController < ApplicationController
eval("code = " + "/^" + home_url.gsub(/\//,"\\\/") + "\\\/*(welcome)?\\\/*(\\\/index\\\/*.*)?\$/")
if (code=~params[:back_url] || params[:back_url].to_s.include?('lost_password')) && last_login_on != ''
redirect_to user_activities_path(user,host: Setting.host_user)
elsif params[:back_url]
redirect_to params[:back_url]
else
if last_login_on == ''
redirect_to my_account_url

View File

@ -0,0 +1,116 @@
class OauthController < ApplicationController
before_filter :user_setup
before_filter :require_login, only: [:authorize, :token]
# 客户端申请认证的URI包含以下参数
#
# response_type表示授权类型必选项此处的值固定为”code”
# client_id表示客户端的ID必选项
# redirect_uri表示重定向URI可选项
# scope表示申请的权限范围可选项
# state表示客户端的当前状态可以指定任意值最好是随机字符串认证服务器会原封不动地返回这个值可防止CSRF攻击
#
# 这个页显示授权页如果授权成功返回redirect_uri+code
#
#
# 服务器回应客户端的URI包含以下参数
#
# code表示授权码必选项。该码的有效期应该很短通常设为10分钟客户端只能使用该码一次 否则会被授权服务器拒绝。该码与客户端ID和重定向URI是一一对应关系。
# state如果客户端的请求中包含这个参数认证服务器的回应也必须一模一样包含这个参数。
def authorize
#参数检查
raise "response_type只能为code" unless params["response_type"] != "code"
raise "client_id为必传项" unless params["client_id"].present?
raise "redirect_uri为必传项" unless params["redirect_uri"].present?
config = OauthConfig.where(client_id: params["client_id"], redirect_uri: params["redirect_uri"]).first
raise "client_id或redirect_uri不正确" unless config
@data = params
if params[:gen_code]
## 检查通过生成code
oauth = Oauth.create!(client_id: config.client_id,
client_secret: config.client_secret,
redirect_uri: config.redirect_uri
)
code = oauth.gen_code
redirect_to params["redirect_uri"] + "?code=#{code}&state=#{params[:state]}"
end
end
def test_callback
# 申请 token
#
client_id = "88d893c5a345313e7b8c6fcf23d3d024ee08d5e41ce120c3448b6eea77d8de30"
client_secret = "e9240cc5fc913741db5aea93f2986a8ea0631bb67f7c00e41e491b95d9619e64"
redirect_uri = "http://localhost:3000/oauth/cb"
url = "http://127.0.0.1:3000/oauth/token?grant_type=authorization_code&code=#{params['code']}"
+"&redirect_uri=#{redirect_uri}&client_id=#{client_id}&client_secret=#{client_secret}"
render text: url
end
# 客户端向认证服务器申请令牌的HTTP请求包含以下参数
#
# grant_type表示使用的授权模式必选项此处的值固定为”authorization_code”。
# code表示上一步获得的授权码必选项。
# redirect_uri表示重定向URI必选项且必须与A步骤中的该参数值保持一致。
# client_id表示客户端ID必选项。
# client_secret: 表示客户端密钥,必选项。
#
#
# 认证服务器核对了授权码和”重定向URI”确认无误后向客户端发送访问令牌access token和更新令牌refresh token
#
# 认证服务器发送的HTTP回复包含以下内容
#
# access_token表示访问令牌必选项。
# token_type表示令牌类型该值大小写不敏感必选项可以是bearer类型或mac类型。
# expires_in表示过期时间单位为秒。如果省略该参数必须其他方式设置过期时间。
# refresh_token表示更新令牌用来获取下一次的访问令牌可选项。
# scope表示权限范围如果与客户端申请的范围一致此项可省略。
def token
if params[:grant_type] == 'authorization_code'
raise "code必传" unless params["code"]
raise "client_id必传" unless params["client_id"]
raise "client_secret必传" unless params["client_secret"]
raise "code错误或已超时" unless Oauth.code_valid?(params["code"])
oauth = Oauth.auth(params["code"], params["client_id"], params["client_secret"])
raise "认证不通过" unless oauth
## 生成 token
#
oauth.gen_token(User.current.id)
{
access_token: oauth.access_token,
token_type: 'bearer',
expires_in: oauth.token_expires_in,
refresh_token: oauth.refresh_token
}
end
end
private
def require_login
if !User.current.logged?
redirect_to '/login?back_url='+request.original_url
end
end
include Trustie::Http
end

View File

@ -1,3 +1,5 @@
#coding=utf-8
#
class OschinaController < ApplicationController
CLIENT_ID = 'e5da9855f89bc724a335d100cb63cf02a03a592bd3151bbc84acf7b2e222ddb8'
@ -48,38 +50,6 @@ class OschinaController < ApplicationController
private
def get(url)
uri = URI(url)
res = Net::HTTP.start(uri.host, uri.port, use_ssl: url.start_with?('https')) do |http|
req = Net::HTTP::Get.new(uri)
#req['Content-Type'] = 'application/json'
# The body needs to be a JSON string, use whatever you know to parse Hash to JSON
#req.body = {a: 1}.to_json
http.request(req)
end
res.body
end
def post(url)
uri = URI(url)
res = Net::HTTP.start(uri.host, uri.port, use_ssl: url.start_with?('https')) do |http|
req = Net::HTTP::Post.new(uri)
#req['Content-Type'] = 'application/json'
# The body needs to be a JSON string, use whatever you know to parse Hash to JSON
#req.body = {a: 1}.to_json
http.request(req)
end
res.body
end
def decode(s)
begin
obj = ActiveSupport::JSON.decode(s)
rescue ActiveSupport::JSON.parse_error
logger.error("Attempted to decode invalid JSON: #{s}")
end
end
include Trustie::Http
end

14
app/models/oauth.rb Normal file
View File

@ -0,0 +1,14 @@
require 'base64'
class Oauth < ActiveRecord::Base
attr_accessible :client_id, :client_secret, :redirect_uri
def gen_code
code = Base64.urlsafe_encode64 Digest::MD5.hexdigest "#{Time.now}-#{Random.new_seed}"
update_column(:code, code)
code
end
end

View File

@ -0,0 +1,3 @@
class OauthConfig < ActiveRecord::Base
attr_accessible :client_id, :client_secret, :redirect_uri, :scope
end

View File

@ -0,0 +1,19 @@
授权页
<p>当前用户: <%= current_user.login %></p>
<p>
<%= form_for oauth_authorize_path do%>
<input type="hidden" name="gen_code" value="true">
<input type="hidden" name="client_id" value="<%= @data["client_id"] %>">
<input type="hidden" name="redirect_uri" value="<%= @data["redirect_uri"] %>">
<input type="hidden" name="response_type" value="<%= @data["response_type"] %>">
<input type="hidden" name="scope" value="<%= @data["scope"] %>">
<input type="hidden" name="state" value="<%= @data["state"] %>">
<button type="submit">是否授权</button>
<% end %>
</p>

View File

@ -558,6 +558,11 @@ RedmineApp::Application.routes.draw do
match 'oschina/login', to: 'oschina#login', :via => :get
match 'oschina/login_cb', to: 'oschina#login_callback', :via => [:get, :post]
## oauth相关
match 'oauth/authorize', to: 'oauth#authorize', :via => [:get, :post]
match 'oauth/token', to: 'oauth#token', :via => :post
match 'oauth/cb', to: 'oauth#test_callback', :via => :get
# boards
match 'boards/:board_id/topics/new', :to => 'messages#new', :via => [:get, :post], :as => 'new_board_message'
match 'boards/:id/join_to_org_subfields', :to => 'boards#join_to_org_subfields'

View File

@ -0,0 +1,18 @@
class CreateOauth < ActiveRecord::Migration
def change
create_table :oauth do |t|
t.string :client_id
t.string :client_secret
t.string :code
t.string :redirect_uri
t.string :scope
t.string :access_token
t.string :refresh_token
t.integer :token_created_at
t.integer :token_expires_in #过期时间
t.timestamps
end
end
end

View File

@ -0,0 +1,22 @@
class CreateOauthConfigs < ActiveRecord::Migration
def change
create_table :oauth_configs do |t|
t.string :client_id
t.string :client_secret
t.string :redirect_uri
t.string :scope
t.timestamps
end
OauthConfig.create(
client_id: '88d893c5a345313e7b8c6fcf23d3d024ee08d5e41ce120c3448b6eea77d8de30',
client_secret: 'e9240cc5fc913741db5aea93f2986a8ea0631bb67f7c00e41e491b95d9619e64',
redirect_uri: 'http://localhost:3000/oschina/login_cb',
scope: ''
)
end
end

View File

@ -11,7 +11,7 @@
#
# It's strongly recommended to check this file into your version control system.
ActiveRecord::Schema.define(:version => 20181121032918) do
ActiveRecord::Schema.define(:version => 20181121071704) do
create_table "activities", :force => true do |t|
t.integer "act_id", :null => false
@ -1501,6 +1501,29 @@ ActiveRecord::Schema.define(:version => 20181121032918) do
t.datetime "updated_at", :null => false
end
create_table "oauth_configs", :force => true do |t|
t.string "client_id"
t.string "client_secret"
t.string "redirect_uri"
t.string "scope"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
create_table "oauths", :force => true do |t|
t.string "client_id"
t.string "client_secret"
t.string "code"
t.string "redirect_uri"
t.string "scope"
t.string "access_token"
t.string "refresh_token"
t.integer "token_created_at"
t.integer "token_expires_in"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
create_table "onclick_times", :force => true do |t|
t.integer "user_id"
t.datetime "onclick_time"

View File

@ -4,3 +4,4 @@ require 'trustie/gitlab/api'
require 'trustie/grack/grack'
require 'trustie/at/at'
require 'trustie/sms/sms'
require 'trustie/http/http'

46
lib/trustie/http/http.rb Normal file
View File

@ -0,0 +1,46 @@
#coding=utf-8
require 'net/http'
require 'uri'
module Trustie
module Http
def get(url)
uri = URI(url)
res = Net::HTTP.start(uri.host, uri.port, use_ssl: url.start_with?('https')) do |http|
req = Net::HTTP::Get.new(uri)
#req['Content-Type'] = 'application/json'
# The body needs to be a JSON string, use whatever you know to parse Hash to JSON
#req.body = {a: 1}.to_json
http.request(req)
end
res.body
end
def post(url, data=nil)
uri = URI(url)
res = Net::HTTP.start(uri.host, uri.port, use_ssl: url.start_with?('https')) do |http|
req = Net::HTTP::Post.new(uri)
#req['Content-Type'] = 'application/json'
# The body needs to be a JSON string, use whatever you know to parse Hash to JSON
req.body = data if data
http.request(req)
end
res.body
end
def decode(s)
begin
obj = ActiveSupport::JSON.decode(s)
rescue ActiveSupport::JSON.parse_error
logger.error("Attempted to decode invalid JSON: #{s}")
end
end
end
end