You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
pgfqe6ch8/app/controllers/oauth_controller.rb

231 lines
8.1 KiB

#encoding: utf-8
class OauthController < ApplicationController
include ApplicationHelper
before_filter :user_setup
before_filter :require_login, only: [:authorize]
skip_before_filter :verify_authenticity_token, only: [:token]
def index
render 'oauth/index', layout: false
end
# 客户端申请认证的URI包含以下参数
#
# response_type表示授权类型必选项此处的值固定为”code”
# client_id表示客户端的ID必选项
# redirect_uri表示重定向URI可选项
# scope表示申请的权限范围可选项
# state表示客户端的当前状态可以指定任意值最好是随机字符串认证服务器会原封不动地返回这个值可防止CSRF攻击
#
# 这个页显示授权页如果授权成功返回redirect_uri+code
#
#
# 服务器回应客户端的URI包含以下参数
#
# code表示授权码必选项。该码的有效期应该很短通常设为10分钟客户端只能使用该码一次 否则会被授权服务器拒绝。该码与客户端ID和重定向URI是一一对应关系。
# state如果客户端的请求中包含这个参数认证服务器的回应也必须一模一样包含这个参数。
def authorize
begin
#参数检查
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,
user_id: User.current.id
)
code = oauth.gen_code
redirect_to params["redirect_uri"] + "?code=#{code}&state=#{params[:state]}"
else
render 'oauth/authorize', :layout => 'forge'
end
rescue => e
logger.error e
render :text => e.message
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
begin
res = {}
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_code(params["code"], params["client_id"], params["client_secret"])
raise "认证不通过" unless oauth
## 生成 token
#
oauth.gen_token
oauth.reload
res = {
access_token: oauth.access_token,
token_type: 'bearer',
expires_in: oauth.token_expires_in,
refresh_token: oauth.refresh_token
}
end
render json: res.to_json
rescue => e
logger.error e
render text: e.message
end
end
def get_userinfo
user = Oauth.auth(params["access_token"])
user_info = {}
if user
user_info = {
token: user.id,
login: user.login,
avatar_url: "https://openi.org.cn/images/" + url_to_avatar(user),
name: user.show_name,
email: user.mail,
allow: (user.login == "guange"||user.phone=='15607313899') ? 1 : 0
}
end
render json: user_info.to_json
end
####--Start-- 获取Openi的授权码access_token以及用户信息。为在openi登录的用户创建相关的educoder用户 ####
IDENTITY_SITE = Redmine::Configuration['openi_domain']
ROOT_URL = Redmine::Configuration['educoder_domain']
DEFAULT_PASSWORD = 'a12345678'.freeze
TOKEN_CALL_BACK = '/oauth/get_token_callback'.freeze
USER_INFO = '/oauth/userinfo'.freeze
def get_code
# 从OpenI发过来的回调中获取授权码
code = params[:code]
# 利用授权码从OpenI这里获取access_token
client = get_client(IDENTITY_SITE)
redirect_uri = "#{ROOT_URL}#{TOKEN_CALL_BACK}"
access_token_hash = client.auth_code.get_token(code, redirect_uri: redirect_uri).to_hash
# 利用access_token获取OpenI的用户信息
access_token = access_token_hash[:access_token]
get_info_url = "#{IDENTITY_SITE}#{USER_INFO}?access_token=#{access_token}"
response = HTTParty.get(get_info_url)
body_json = JSON.parse response.body
openi_user_id = body_json['token']
avatar_url = body_json['avatar_url']
login = body_json['login']
name = body_json['name']
email = body_json['email']
# 根据获取的用户信息来查询数据库如已经存在对应的Educoder用户则直接访问用户要访问的实训页面否则为其创建用户后再访问实训页面
openi = Openi.find_by_login(login)
unless openi
ActiveRecord::Base.transaction do
# 如果Educoder中已存在与该OpenI用户的邮箱相同的用户则会直接跳转到登录educoder的登录页面
existing_user = User.find_by_mail(email)
break if existing_user.present?
user = User.new(lastname: name, mail: email, mail_notification: email)
user.login = custom_openi_login(login)
user.password = DEFAULT_PASSWORD
user.save!
UserExtensions.create!(user_id: user.id, school_id: School.first.id, identity: 4, gender: 0)
UserDayCertification.create!(user_id: user.id, status: 1)
openi = Openi.create!(user_id: user.id, openi_user_id: openi_user_id, avatar_url: avatar_url, login: login, name: name, email: email)
end
end
self.logged_user = openi.user if openi.present? && openi.user.present?
original_url = params[:original_url]
if current_user.logged?
redirect_to original_url
else
redirect_to signin_path
end
end
def get_token_callback
end
####--End-- 获取Openi的授权码access_token以及用户信息。为在openi登录的用户创建相关的educoder用户 ####
private
# 为了保证新创建的用户用户名不与系统中已存在的用户冲突,加上 _openi 后缀
def custom_openi_login(login)
login + '_openi'
end
def require_login
require "base64"
if !User.current.logged?
redirect_to '/login?back_url64=' + Base64.urlsafe_encode64(request.original_url)
end
end
include Trustie::Http
end