Class: JPSClient::Token
- Inherits:
-
Object
- Object
- JPSClient::Token
- Defined in:
- lib/jpsclient/auth/token.rb
Overview
Token 管理类
负责登录态的本地存储、加载和清除,与 jps-cli / nbskills / 桌面端三端共享同一份加密凭证 (~/.jps_auth_info,测试服 ~/.jps_cli/test_auth_info)。
存储格式(跨端硬契约,见 OIDC 接入文档 §5.3): camelCase 紧凑 JSON → AES-128-ECB + PKCS7 → Base64 → 原子写文本文件(0600)
本地过期以 tokenExpireTimestamp(毫秒)预判,省一次后端 401 往返;最终失效以后端 20001 为权威。
Constant Summary collapse
- CAMEL_TO_ATTR =
明文 JSON 字段(camelCase)↔ 内部属性(snake)。顺序仅利于人眼比对,与 jps-cli 一致。
{ 'token' => :token, 'tokenExpireTimestamp' => :token_expire_timestamp, 'username' => :username, 'userId' => :user_id, 'permissions' => :permissions, 'larkUserId' => :lark_user_id, 'tenantManager' => :tenant_manager, 'avatar' => :avatar }.freeze
Instance Attribute Summary collapse
-
#avatar ⇒ Object
readonly
Returns the value of attribute avatar.
-
#lark_user_id ⇒ Object
readonly
Returns the value of attribute lark_user_id.
-
#permissions ⇒ Object
readonly
Returns the value of attribute permissions.
-
#tenant_manager ⇒ Object
readonly
Returns the value of attribute tenant_manager.
-
#token ⇒ Object
readonly
Returns the value of attribute token.
-
#token_expire_timestamp ⇒ Object
readonly
Returns the value of attribute token_expire_timestamp.
-
#user_id ⇒ Object
readonly
Returns the value of attribute user_id.
-
#username ⇒ Object
readonly
Returns the value of attribute username.
Instance Method Summary collapse
-
#clear ⇒ Object
清除内存与磁盘上的 token.
-
#initialize(config = nil) ⇒ Token
constructor
A new instance of Token.
-
#load ⇒ Object
加载 token;成功且未过期返回 true,否则 false.
-
#loaded? ⇒ Boolean
token 是否已加载.
-
#save(token_data) ⇒ Object
保存 token。token_data 为内部 snake 键的 Hash(见 Auth#get_token_data).
-
#to_h ⇒ Object
转换为内部 snake 键 Hash(供 Client 使用).
Constructor Details
#initialize(config = nil) ⇒ Token
Returns a new instance of Token.
38 39 40 41 42 43 44 45 46 47 48 |
# File 'lib/jpsclient/auth/token.rb', line 38 def initialize(config = nil) @config = config @aes_key = config.aes_key if config # 凭证文件路径为跨端硬契约,统一由 Endpoints 解析(忽略 config 的 token_dir/token_file_name) @token_file = Endpoints.auth_file reset_fields @verbose = JPSClient.debug? end |
Instance Attribute Details
#avatar ⇒ Object (readonly)
Returns the value of attribute avatar.
24 25 26 |
# File 'lib/jpsclient/auth/token.rb', line 24 def avatar @avatar end |
#lark_user_id ⇒ Object (readonly)
Returns the value of attribute lark_user_id.
23 24 25 |
# File 'lib/jpsclient/auth/token.rb', line 23 def lark_user_id @lark_user_id end |
#permissions ⇒ Object (readonly)
Returns the value of attribute permissions.
23 24 25 |
# File 'lib/jpsclient/auth/token.rb', line 23 def @permissions end |
#tenant_manager ⇒ Object (readonly)
Returns the value of attribute tenant_manager.
23 24 25 |
# File 'lib/jpsclient/auth/token.rb', line 23 def tenant_manager @tenant_manager end |
#token ⇒ Object (readonly)
Returns the value of attribute token.
22 23 24 |
# File 'lib/jpsclient/auth/token.rb', line 22 def token @token end |
#token_expire_timestamp ⇒ Object (readonly)
Returns the value of attribute token_expire_timestamp.
24 25 26 |
# File 'lib/jpsclient/auth/token.rb', line 24 def @token_expire_timestamp end |
#user_id ⇒ Object (readonly)
Returns the value of attribute user_id.
23 24 25 |
# File 'lib/jpsclient/auth/token.rb', line 23 def user_id @user_id end |
#username ⇒ Object (readonly)
Returns the value of attribute username.
22 23 24 |
# File 'lib/jpsclient/auth/token.rb', line 22 def username @username end |
Instance Method Details
#clear ⇒ Object
清除内存与磁盘上的 token
117 118 119 120 121 |
# File 'lib/jpsclient/auth/token.rb', line 117 def clear reset_fields FileUtils.rm_f(@token_file) if File.exist?(@token_file) puts "✓ Token 已清除" if @verbose end |
#load ⇒ Object
加载 token;成功且未过期返回 true,否则 false
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 |
# File 'lib/jpsclient/auth/token.rb', line 51 def load return false unless File.exist?(@token_file) begin content = File.read(@token_file).strip return false if content.empty? token_data = decrypt_content(content) apply_fields(token_data) # token + user_id 齐全才算有效登录。~/.jps_auth_info 为三端共享,load 失败只重置内存、 # 不删文件(删除仅保留给显式 clear / force-login),避免误删他端有效登录态 unless valid_login? puts "Token 文件缺少必要字段,需要重新登录" if @verbose reset_fields return false end # 本地过期预判:直接当未登录,省一次后端 401 往返 if token_expired? puts "Token 已过期(tokenExpireTimestamp),需要重新登录" if @verbose reset_fields return false end return true rescue => e puts "读取 token 失败: #{e.}" if @verbose reset_fields end false end |
#loaded? ⇒ Boolean
token 是否已加载
112 113 114 |
# File 'lib/jpsclient/auth/token.rb', line 112 def loaded? !@token.nil? && !@token.empty? end |
#save(token_data) ⇒ Object
保存 token。token_data 为内部 snake 键的 Hash(见 Auth#get_token_data)
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 |
# File 'lib/jpsclient/auth/token.rb', line 86 def save(token_data) return false unless token_data.is_a?(Hash) && (token_data['token'] || token_data[:token]) # ~/.jps_auth_info 为三端共享的加密凭证:缺少 AES 密钥时拒绝写入,绝不明文落盘 # (明文会泄露 SESSION,且会让 jps-cli / 桌面端把该文件判为损坏) unless @aes_key puts "✗ 缺少 AES 密钥,拒绝写入共享凭证 #{@token_file}(需配置 aes_key)" return false end apply_fields(token_data) FileUtils.mkdir_p(File.dirname(@token_file)) plaintext = JSON.generate(to_camel_hash) content = AES.new(@aes_key).encrypt(plaintext) atomic_write(@token_file, content) puts "✓ Token 已保存到 #{@token_file}" if @verbose true rescue => e puts "保存 token 失败: #{e.}" false end |
#to_h ⇒ Object
转换为内部 snake 键 Hash(供 Client 使用)
124 125 126 127 128 129 130 131 132 133 134 135 136 137 |
# File 'lib/jpsclient/auth/token.rb', line 124 def to_h return nil unless @token { 'token' => @token, 'username' => @username, 'user_id' => @user_id, 'permissions' => @permissions, 'lark_user_id' => @lark_user_id, 'tenant_manager' => @tenant_manager, 'avatar' => @avatar, 'token_expire_timestamp' => @token_expire_timestamp } end |