集成企业SSO登录-企业端实现示例

Lisa

发布于: 2025-01-23

1. 通过接口或登录组织管理后台创建第三方集成 “企业 SSO 登陆”配置参数:

参数名

解释

是否必填

示例值

app_id

应用ID,用于标识第三方应用;由企业自行生成

必填

'80143325e07e'

app_secret

应用密钥,用于验证第三方应用;由企业自行生成

必填

'5b4d3b33104060e8c0f25ceb977238f4'

scope

范围,用于限制第三方应用的权限;由企业自行定义

必填

'all'

authorize_url

授权地址,用于获取用户授权;由企业自行定义

必填

http://localhost:4001/oauth/authorize

token_url

Token地址,用于获取token;由企业自行定义

必填

http://localhost:4001/oauth/token

profile_url

用户信息地址,用于获取用户信息;由企业自行定义

必填

http://localhost:4001/profile

profile_uid_field

用户唯一标识字段, 需要登录的用户唯一标识字段名;默认为id

可选

profile_name_field

需要登录的用户名称字段名;默认为name

可选

profile_image_field

需要登录的用户头像字段名;默认为avatar

可选

provider_ignores_state

是否忽略state参数;默认为false

可选

button_text

按钮文字,用于显示在登录按钮上;默认为“企业 SSO 登陆”

可选

note

备注,用于配置此第三方集成的备注信息;默认为空

可选

2. 账号绑定

待登录用户与‘企业 SSO’绑定后,才可通过‘企业 SSO’登录

  • 方式一: 通过登录组织管理后台绑定;在用户个人信息中,'账号绑定'页面点击已添加的第三方集成 ‘企业 SSO’,进行绑定操作。

  • 方式二: 通过接口绑定

3. 企业生成的应用ID和应用密钥,并自行记录存储。请妥善保管,不要泄露给其他人。

  # 例:
  app_id = SecureRandom.hex(6) # 80143325e07e 应用ID
  app_secret = SecureRandom.hex(16) # 5b4d3b33104060e8c0f25ceb977238f4 应用密钥

4. 企业服务内部需要实现授权地址、Token地址、用户信息地址的接口,用于企业 SSO 登陆的授权、token获取、用户信息获取。

(1)实现授权地址接口

  • 方法:GET

  • 域名:http://localhost:4001; 企业服务自己的域名

  • 地址:/oauth/authorize 自行定义路由,此仅为示例

  • 接收参数:

    参数名

    解释

    示例值

    client_id

    应用ID

    '80143325e07e'

    redirect_uri

    baklib回调地址

    http://5b85664828c1.lvh.me:3000/-/oauth/corp/callback

    response_type

    授权类型(固定值)

    code

    scope

    范围

    all

    state

    来源验证参数

    59b595c37704c0b67a95d949bd9f7d31889cc53df3a43271

  • 返回参数:重定向到回调地址,并带上code和state参数

  • 实现示例:

    class OauthController < ApplicationController
      def authorize
        # 校验client_id(自行处理)
        app = App.find_by(client_id: params[:client_id])
        unless app
          return render json: {error: "client_id参数错误"}, status: 400
        end
        # 检查redirect_uri是否合法(自行处理)
        host = URI.parse(params[:redirect_uri]).host
        unless app.redirect_uris.include?(host)
          return render json: {error: "redirect_uri参数错误"}, status: 400
        end
        # 检查response_type是否为code, scope是否为all或者app.scope,state是否合法(自行处理)
    
        # 通过cookie或者session获取企业当前登录的用户信息(自行处理)
        # 或直接跳转到企业自己的登录页面,登录成功后,再跳转回来
        user = User.find_by(cookies[:user_id])
        if user
          # 生成code,存储到redis中,用于token地址接口校验
          code = SecureRandom.hex(6)
          $redis.set("authorize_code:#{code}", user.id)
          # 设置过期时间
          $redis.expire("code:#{code}", 300)
        else
          return redirect_to '企业自己的登录页面地址'
        end
    
        # 校验通过后,重定向到回调地址,并带上code和state参数(必须)
        # 完整的回调地址为:http://5b85664828c1.lvh.me:3000/-/oauth/corp/callback?code=aaa-bbb-ccc&state=59b595c37704c0b67a95d949bd9f7d31889cc53df3a43271
        redirect_to "#{params[:redirect_uri]}?code=#{code}&state=#{params[:state]}", allow_other_host: true
      end
    end
    

(2)实现Token地址接口

  • 方法:POST

  • 域名:http://localhost:4001; 企业服务自己的域名

  • 地址:/oauth/token 自行定义路由,此仅为示例

  • 接收参数:

    参数名

    解释

    示例值

    code

    授权码,授权地址接口返回的code

    aaa-bbb-ccc

    grant_type

    授权类型(固定值)

    authorization_code

    redirect_uri

    baklib回调地址

    http://5b85664828c1.lvh.me:3000/-/oauth/corp/callback

  • 返回参数:

    参数名

    解释

    示例值

    是否必填

    access_token

    token, 自行生成用于后续获取用户信息

    2YotnFZFEjr1zCsicMWpAA

    必填

    token_type

    token类型(固定值)

    bearer

    必填

    expires_in

    token有效期,单位秒

    3600

    必填

    scope

    token权限范围

    public

    必填

  • 实现示例:

    class OauthController < ApplicationController
      skip_before_action :verify_authenticity_token, only: [:token]
      def token
        # 校验code是否合法(自行处理)
        user_id = $redis.get("authorize_code:#{params[:code]}")
        user = User.find_by(user_id)
        unless user
          return render json: {error: "code参数错误"}, status: 400
        end
    
        # 校验请求来源是否合法(自行处理)
        # 略....
    
        # 校验grant_type是否为authorization_code
        # 略....
    
        # 校验通过后,删除code(必须)
        $redis.del("authorize_code:#{params[:code]}")
    
        # 生成token,存储到redis中,用于用户信息地址接口校验(自行处理)
        # 设置过期时间
        token = SecureRandom.hex(16)
        $redis.set("token:#{token}", user.id)
        expires_in = 3600
        $redis.expire("token:#{token}", expires_in)
    
        # 校验通过后,返回json数据(必须)
        render json: {
          "access_token": token, # token
          "token_type": "bearer", # 此token的类型,固定为bearer
          "expires_in": expires_in, # 此token的有效期,单位秒
          "scope": "public" # 此token的权限范围,自行定义
        }
      end
    end
    

(3)实现用户信息地址接口

  • 方法:GET

  • 域名:http://localhost:4001; 企业服务自己的域名

  • 地址:/profile 自行定义路由,此仅为示例

  • 接收参数:token是通过HTTP头部传递,所以无需接收参数

  • 返回参数:id, name, image等字段('第三方集成'中配置的映射的字段名)

  • 实现示例:

    class ProfileController < ApplicationController
      def show
        # 校验token是否合法(自行处理)
        user_id = $redis.get("token:#{request.headers["AUTHORIZATION"].split(" ")[1]}")
        user = User.find_by(id: user_id)
        unless user
          return render json: {error: "token参数错误"}, status: 400
        end
    
        # 校验通过后,返回用户信息
        # 格式为json,字段为 ‘企业 SSO 登陆 配置’ 中映射的字段,建议全部填写并返回
        render json: {
          id: user.id, # 1
          name: user.name, # "小明"
          image: user.image # "http://xxx.com/avatar.jpg"
        }
      end
    end

提交反馈