从新打开的应用标签返回后丢失后端用户认证问题排查
跨应用认证后Portal后端丢失用户身份问题
我有一个名为Portal的应用,后端基于Ruby on Rails开发,前端采用Vue.js。流程如下:
- 用户完成认证后,主页面加载其有权访问的应用列表
- 用户点击应用时,通过JavaScript的
window.open()函数在新标签页打开目标应用,并自动完成该应用内的用户认证,实现类超链接的便捷访问
被打开的目标应用同样采用Ruby on Rails后端+Vue.js前端架构,且与Portal共享同一JWT认证API。注:同一用户在不同应用内拥有不同权限角色,因此必须在两个应用中分别完成认证以获取对应功能权限。
目前已实现Portal打开目标应用的功能:通过URL参数传递用户数据,目标应用前端捕获参数后完成用户认证并跳转至初始页面。但返回Portal应用时,前端仍能识别用户,但后端控制器无法再识别用户身份,尝试在调用window.open()后为Portal添加重新登录功能也未成功。预期Portal的后端不会丢失用户认证信息,恳请协助分析原因并提供解决方案。
Portal应用(应用1)的认证控制器函数
module Api module V1 class SessoesController < ActionController::API include ::CasacivilgoPermissoes::SessaoApi def autenticar resposta = login_helper render json: resposta, status: resposta[:code] end end end end
共享的login_helper函数(应用1和应用2共用)
require 'net/http' require 'casacivilgo_permissoes/usuario' module CasacivilgoPermissoes module SessaoApi extend ActiveSupport::Concern include CasacivilgoPermissoes::JwToken def login_helper if ::CasacivilgoPermissoes.configuration.sgu_url.blank? raise CasacivilgoPermissoes::Errors::ConfigConstantMissing, 'CasacivilgoPermissoes.configuration.sgu_url não está configurada' end if ::CasacivilgoPermissoes.configuration.sistema_sigla.blank? raise CasacivilgoPermissoes::Errors::ConfigConstantMissing, 'CasacivilgoPermissoes.configuration.sistema_sigla não está configurada' end login = params.dig(:login)&.strip response = Net::HTTP.post_form(URI("http://localhost:3001/acesso_externo/api/v1/autenticacao/login"), login: login, senha: params[:senha], sistema: ::CasacivilgoPermissoes.configuration.sistema_sigla) if response.code == '200' parsed_body = JSON.parse response.body usuario = ::CasacivilgoPermissoes::Usuario.new parsed_body generate_auth_cookie(usuario) { usuario: usuario.to_hash, sucesso: true, code: 200 } else format_error(response) end end end end
JWT令牌生成、编解码算法
require 'jwt' module CasacivilgoPermissoes module JwToken extend ActiveSupport::Concern include CasacivilgoPermissoes::Helpers ALGORITMO_JWT = 'HS***'.freeze ISS = 'Se******** da **** ***** do ****** de *****'.freeze def criar_token(usuario, v_key) validar_secret_key_jwt payload = { exp: expire_in(usuario).to_i, iss: ISS, iat: Time.now.to_i, usuario: usuario.to_hash, v_key: v_key } JWT.encode payload, CasacivilgoPermissoes.configuration.secret_key_jwt, ALGORITMO_JWT end def decodificar_token(private_token, public_token) validar_secret_key_jwt leeway = 30 # seconds begin token = JWT.decode(private_token, CasacivilgoPermissoes.configuration.secret_key_jwt, true, { algorithm: ALGORITMO_JWT, exp_leeway: leeway, iss: ISS, verify_iss: true, verify_iat: true }) raise CasacivilgoPermissoes::Errors::InvalidAuthToken if token.first&.dig('v_key') != public_token token rescue JWT::ExpiredSignature, JWT::InvalidIatError raise CasacivilgoPermissoes::Errors::Unauthenticated rescue JWT::ImmatureSignature, JWT::InvalidIssuerError, JWT::InvalidAudError, JWT::InvalidJtiError, JWT::InvalidSubError raise CasacivilgoPermissoes::Errors::Unauthenticated rescue JWT::DecodeError, CasacivilgoPermissoes::Errors::InvalidAuthToken raise CasacivilgoPermissoes::Errors::Unauthenticated end end def generate_auth_cookie(usuario) expiration_time = expire_in(usuario) if usuario.present? v_key = SecureRandom.hex cookies[:auth_private] = { value: criar_token(usuario, v_key), httponly: true, expires: expiration_time, domain: request&.host } cookies.signed.encrypted[:auth_public] = { value: v_key, httponly: false, expires: expiration_time, domain: request&.host } end end def validar_secret_key_jwt if CasacivilgoPermissoes.configuration.secret_key_jwt.blank? raise ::CasacivilgoPermissoes::Errors::ConfigConstantMissing, 'CasacivilgoPermissoes.configuration.secret_key_jwt não encontrada' end end end end
应用2的认证函数login_token_jwt_helper
def login_token_jwt_helper if ::CasacivilgoPermissoes.configuration.sgu_url.blank? raise CasacivilgoPermissoes::Errors::ConfigConstantMissing, 'CasacivilgoPermissoes.configuration.sgu_url não está configurada' end if ::CasacivilgoPermissoes.configuration.sistema_sigla.blank? raise CasacivilgoPermissoes::Errors::ConfigConstantMissing, 'CasacivilgoPermissoes.configuration.sistema_sigla não está configurada' end if params.dig(:params).dig(:tokenPrivado)&.strip.blank? || params.dig(:params).dig(:tokenPublico)&.strip.blank? raise CasacivilgoPermissoes::Errors::ConfigConstantMissing, 'Token de segurança não está configurado' end login = params.dig(:params).dig(:login)&.strip sistema = params.dig(:params).dig(:sistemaSigla)&.strip token_privado = params.dig(:params).dig(:tokenPrivado)&.strip token_publico = params.dig(:params).dig(:tokenPublico)&.strip token_autorizado = decodificar_token(token_privado, token_publico); if token_autorizado response = Net::HTTP.post_form(URI("http://localhost:3001/acesso_externo/api/v1/autenticacao/login_token_jwt"), login: login, token: token_autorizado, sistema: sistema) else raise CasacivilgoPermissoes::Errors::Unauthenticated end if response.code == '200' parsed_body = JSON.parse response.body novoUsuario = ::CasacivilgoPermissoes::Usuario.new parsed_body generate_auth_cookie(novoUsuario) { usuario: novoUsuario.to_hash, sucesso: true, code: 200 } else format_error(response) end end
两个应用共享部分代码,应用2的前端被Portal调用时会自动触发上述login_token_jwt_helper函数。
内容的提问来源于stack exchange,提问作者Flávio Oliveira Neto
相关产品推荐
相关产品推荐

