auth.java 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360
  1. package com.fujica.abk.api;
  2. import com.fujica.abk.common.EventBus;
  3. import com.fujica.abk.component.AuthorizationDialog;
  4. import com.fujica.abk.model.out.LoginRes;
  5. import com.fujica.abk.utils.Log;
  6. import com.fujica.abk.utils.R;
  7. import com.fujica.abk.utils.Toast;
  8. import com.fujica.abk.utils.api;
  9. import com.fujica.abk.utils.ApiOption;
  10. import com.google.gson.reflect.TypeToken;
  11. import com.huawei.hms.accountsdk.exception.ApiException;
  12. import com.huawei.hms.accountsdk.support.account.AccountAuthManager;
  13. import com.huawei.hms.accountsdk.support.account.request.AccountAuthParams;
  14. import com.huawei.hms.accountsdk.support.account.request.AccountAuthParamsHelper;
  15. import com.huawei.hms.accountsdk.support.account.result.AuthAccount;
  16. import com.huawei.hms.accountsdk.support.account.service.AccountAuthService;
  17. import com.huawei.hms.accountsdk.support.account.tasks.OnFailureListener;
  18. import com.huawei.hms.accountsdk.support.account.tasks.OnSuccessListener;
  19. import com.huawei.hms.accountsdk.support.account.tasks.Task;
  20. import ohos.app.Context;
  21. import java.util.concurrent.CompletableFuture;
  22. /**
  23. * 认证工具类
  24. */
  25. public class auth {
  26. // 登录状态标志,防止并发登录
  27. private static boolean authing = false;
  28. /**
  29. * 登录
  30. *
  31. * @param context 上下文
  32. * @return CompletableFuture<Boolean> 登录是否成功
  33. */
  34. public static CompletableFuture<Boolean> login(Context context) {
  35. CompletableFuture<Boolean> future = new CompletableFuture<>();
  36. // 检查是否正在登录
  37. if (authing) {
  38. Log.info("正在登录中,请勿重复操作");
  39. Toast.info(context, "正在登录中...");
  40. future.complete(false);
  41. return future;
  42. }
  43. if (cache.isAuth(context)) {
  44. future.complete(true);
  45. return future;
  46. }
  47. // 设置登录状态
  48. authing = true;
  49. if (config.isDebug) {
  50. proceedWithLogin(context, future);
  51. } else {
  52. // 登录之前先显示授权确认弹框
  53. // 确保在主线程中执行
  54. context.getUITaskDispatcher().asyncDispatch(() -> {
  55. AuthorizationDialog authDialog = new AuthorizationDialog(context);
  56. authDialog.setOnAuthorizationListener(new AuthorizationDialog.OnAuthorizationListener() {
  57. @Override
  58. public void onAuthorized() {
  59. // 用户确认授权后,继续登录流程
  60. proceedWithLogin(context, future);
  61. }
  62. @Override
  63. public void onCancelled() {
  64. // 用户取消授权
  65. Toast.error(context, "已取消授权");
  66. authing = false; // 重置登录状态
  67. future.complete(false);
  68. }
  69. });
  70. authDialog.show();
  71. });
  72. }
  73. return future;
  74. }
  75. /**
  76. * 执行实际的登录流程
  77. */
  78. private static void proceedWithLogin(Context context, CompletableFuture<Boolean> future) {
  79. if (config.isDebug) {
  80. HWResult hwResult = new HWResult();
  81. hwResult.setAppId(config.getAppId());
  82. //dev环境
  83. // hwResult.setOpenId("AAAjdc3utGbjvCsmhASifWUoEpmH");
  84. //dev环境
  85. hwResult.setOpenId("oq5PA6YFGAqRhTuipxFpZNK_EN04");
  86. ApiOption option = new ApiOption();
  87. option.setUrl("/member/hw/login");
  88. option.setMethod(ApiOption.POST);
  89. // 注意:HMS SDK可能不直接提供authorizationCode,这里需要根据实际情况调整
  90. // 如果HMS SDK不提供code,可能需要使用其他方式获取
  91. java.util.Map<String, String> data = new java.util.HashMap<>();
  92. data.put("appId", config.getAppId());
  93. data.put("code", hwResult.getCode()); // 需要从授权结果中获取
  94. data.put("openId", hwResult.getOpenId());
  95. data.put("unionId", hwResult.getUnionId());
  96. data.put("source", "7");
  97. option.setData(data);
  98. processCodeLogin(context, option, hwResult, future);
  99. return;
  100. }
  101. try {
  102. // 先获取华为账号信息
  103. getHuaWeiAccount(context).thenAccept(hwResult -> {
  104. if (!hwResult.isSuccess()) {
  105. authing = false; // 重置登录状态
  106. future.complete(false);
  107. return;
  108. }
  109. // 使用授权码登录
  110. ApiOption option = new ApiOption();
  111. option.setUrl("/member/hw/code/login");
  112. option.setMethod(ApiOption.POST);
  113. // 注意:HMS SDK可能不直接提供authorizationCode,这里需要根据实际情况调整
  114. // 如果HMS SDK不提供code,可能需要使用其他方式获取
  115. java.util.Map<String, String> data = new java.util.HashMap<>();
  116. data.put("appId", config.getAppId());
  117. data.put("code", hwResult.getCode()); // 需要从授权结果中获取
  118. data.put("openId", hwResult.getOpenId());
  119. data.put("unionId", hwResult.getUnionId());
  120. data.put("source", "7");
  121. option.setData(data);
  122. processCodeLogin(context, option, hwResult, future);
  123. }).exceptionally(e -> {
  124. try {
  125. Log.error(e);
  126. future.complete(false);
  127. return null;
  128. } finally {
  129. authing = false;
  130. }
  131. });
  132. } catch (Exception e) {
  133. try {
  134. Log.error(e);
  135. future.complete(false);
  136. } finally {
  137. authing = false;
  138. }
  139. }
  140. }
  141. /**
  142. * 处理授权码登录
  143. *
  144. * @param context 上下文
  145. * @param option API选项
  146. * @param hwResult 华为账号信息
  147. * @param future 异步结果
  148. */
  149. private static void processCodeLogin(Context context, ApiOption option, HWResult hwResult, CompletableFuture<Boolean> future) {
  150. api.http(context, option, new TypeToken<R<LoginRes>>() {
  151. }).thenAccept((R<LoginRes> result) -> {
  152. try {
  153. if (result != null && result.isSuccess() && result.getData() != null) {
  154. LoginRes res = result.getData();
  155. cache.setOpenId(context, res.getOpenId());
  156. cache.setToken(context, res.getToken());
  157. cache.setMobile(context, res.getMobile());
  158. // 发布登录成功事件,通知UI更新
  159. EventBus.getInstance().emit("onLoginSuccess");
  160. future.complete(true);
  161. } else {
  162. future.complete(false);
  163. }
  164. } finally {
  165. // 确保最终重置登录状态
  166. authing = false;
  167. }
  168. }).exceptionally(e -> {
  169. try {
  170. Log.error(e);
  171. future.complete(false);
  172. return null;
  173. } finally {
  174. // 确保最终重置登录状态
  175. authing = false;
  176. }
  177. });
  178. }
  179. /**
  180. * 处理手机号登录
  181. */
  182. private static void handlePhoneLogin(Context context, HWResult hwResult, CompletableFuture<Boolean> future) {
  183. // 这里需要显示手机号输入对话框
  184. // 由于PhoneDialog可能不存在,这里提供一个占位实现
  185. // 实际使用时需要根据项目中的对话框实现进行调整
  186. // 示例:假设有一个PhoneDialog.show方法
  187. // PhoneDialog.show(context, (phone, code) -> {
  188. // hwResult.setPhone(phone);
  189. // // 使用手机号和验证码登录
  190. // ApiOption option = new ApiOption();
  191. // option.setUrl("/member/hw/login");
  192. // java.util.Map<String, String> data = new java.util.HashMap<>();
  193. // data.put("phone", phone);
  194. // data.put("code", code);
  195. // data.put("openId", hwResult.getOpenId());
  196. // data.put("unionId", hwResult.getUnionId());
  197. // data.put("appId", config.appId);
  198. // data.put("source", 7);
  199. // option.setData(data);
  200. //
  201. // return api.post1(context, option).thenApply(result -> {
  202. // if (result != null && result.isSuccess() && result.getData() != null) {
  203. // LoginRes loginRes = (LoginRes) result.getData();
  204. // if (loginRes != null) {
  205. // cache.setOpenId(context, hwResult.getOpenId());
  206. // cache.setToken(context, loginRes.getToken());
  207. // cache.setMobile(context, loginRes.getMobile());
  208. // return true;
  209. // }
  210. // }
  211. // return false;
  212. // });
  213. // });
  214. // 暂时返回false,需要根据实际项目中的PhoneDialog实现进行调整
  215. authing = false; // 重置登录状态
  216. future.complete(false);
  217. }
  218. /**
  219. * 获取华为账号信息
  220. *
  221. * @param context 上下文
  222. * @return CompletableFuture<HWResult> 华为账号信息
  223. */
  224. public static CompletableFuture<HWResult> getHuaWeiAccount(Context context) {
  225. CompletableFuture<HWResult> future = new CompletableFuture<>();
  226. try {
  227. // 创建授权请求参数
  228. AccountAuthParams accountAuthParams = new AccountAuthParamsHelper(
  229. AccountAuthParams.DEFAULT_AUTH_REQUEST_PARAM)
  230. .setAuthorizationCode()
  231. .createParams();
  232. AccountAuthService accountAuthService;
  233. try {
  234. accountAuthService = AccountAuthManager.getService(accountAuthParams);
  235. } catch (ApiException e) {
  236. Log.error("Init Huawei accountAuthService FAILED: " + e.getStatusCode());
  237. Toast.error(context, "登录失败:" + e.getStatusCode());
  238. future.complete(new HWResult(false));
  239. return future;
  240. }
  241. // 先尝试静默登录
  242. Task<AuthAccount> taskSilentSignIn = accountAuthService.silentSignIn();
  243. taskSilentSignIn.addOnSuccessListener(authAccount -> {
  244. HWResult result = new HWResult(true);
  245. result.setCode(authAccount.getAuthorizationCode());
  246. result.setAppId(config.getAppId());
  247. result.setOpenId(authAccount.getOpenId());
  248. result.setUnionId(authAccount.getUnionId());
  249. // HMS SDK可能不直接提供手机号,需要单独申请权限
  250. result.setPhone("");
  251. future.complete(result);
  252. });
  253. taskSilentSignIn.addOnFailureListener(new OnFailureListener() {
  254. @Override
  255. public void onFailure(Exception e) {
  256. if (e instanceof ApiException) {
  257. ApiException apiException = (ApiException) e;
  258. int statusCode = apiException.getStatusCode();
  259. // 静默登录失败,尝试前台登录
  260. Task<AuthAccount> taskSignIn = accountAuthService.signIn();
  261. taskSignIn.addOnSuccessListener(new OnSuccessListener<AuthAccount>() {
  262. @Override
  263. public void onSuccess(AuthAccount authAccount) {
  264. HWResult result = new HWResult(true);
  265. result.setCode(authAccount.getAuthorizationCode());
  266. result.setAppId(config.getAppId());
  267. result.setOpenId(authAccount.getOpenId());
  268. result.setUnionId(authAccount.getUnionId());
  269. result.setPhone("");
  270. future.complete(result);
  271. }
  272. });
  273. taskSignIn.addOnFailureListener(new OnFailureListener() {
  274. @Override
  275. public void onFailure(Exception e) {
  276. handleAuthError(context, e, future);
  277. }
  278. });
  279. } else {
  280. handleAuthError(context, e, future);
  281. }
  282. }
  283. });
  284. } catch (Exception e) {
  285. handleAuthError(context, e, future);
  286. }
  287. return future;
  288. }
  289. /**
  290. * 处理认证错误
  291. */
  292. private static void handleAuthError(Context context, Exception error, CompletableFuture<HWResult> future) {
  293. if (error instanceof ApiException) {
  294. ApiException apiException = (ApiException) error;
  295. int code = apiException.getStatusCode();
  296. // 模拟器调试模式
  297. if (code == 12300001 && config.isDebug) {
  298. HWResult result = new HWResult(true);
  299. result.setAppId(config.getAppId());
  300. result.setOpenId("AAAjdc3utGbjvphuWASifWUoEpm1");
  301. result.setUnionId("");
  302. result.setPhone("");
  303. future.complete(result);
  304. return;
  305. }
  306. // 用户取消
  307. if (code == 1001502012) {
  308. Toast.error(context, "已取消关联");
  309. future.complete(new HWResult(false));
  310. return;
  311. }
  312. Toast.error(context, "关联失败: " + code + ", " + apiException.getMessage());
  313. Log.error("Failed to auth. Code: " + code + ", message: " + apiException.getMessage());
  314. } else {
  315. Toast.error(context, "关联失败: " + error.getMessage());
  316. Log.error(error);
  317. }
  318. future.complete(new HWResult(false));
  319. }
  320. }