From 6dbed6cf4942ddbb5ed0add81620ad7351109742 Mon Sep 17 00:00:00 2001 From: jyuesong <425698907@qq.com> Date: Wed, 19 Jan 2022 17:40:32 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=AF=E6=8C=81clientid=E7=99=BB=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/base/http/api.dart | 18 +- lib/base/http/url.dart | 82 ++++++--- lib/base/sp_const.dart | 1 + lib/base/userinfo_viewmodel.dart | 9 + lib/module/login/login_page.dart | 289 ++++++++++++++++++++++--------- pubspec.lock | 14 ++ pubspec.yaml | 1 + 7 files changed, 306 insertions(+), 108 deletions(-) diff --git a/lib/base/http/api.dart b/lib/base/http/api.dart index e299d67..74278cb 100644 --- a/lib/base/http/api.dart +++ b/lib/base/http/api.dart @@ -12,7 +12,10 @@ import 'package:qinglong_app/module/task/task_bean.dart'; import 'url.dart'; class Api { - static Future> login(String userName, String passWord) async { + static Future> login( + String userName, + String passWord, + ) async { return await Http.post( Url.login, { @@ -22,6 +25,19 @@ class Api { ); } + static Future> loginByClientId( + String id, + String secret, + ) async { + return await Http.post( + Url.loginByClientId, + { + "client_id": id, + "client_secret": secret, + }, + ); + } + static Future> user() async { return await Http.get( Url.user, diff --git a/lib/base/http/url.dart b/lib/base/http/url.dart index 14d2ebf..b36f3f7 100644 --- a/lib/base/http/url.dart +++ b/lib/base/http/url.dart @@ -1,36 +1,64 @@ +import '../../main.dart'; +import '../userinfo_viewmodel.dart'; + class Url { - static const login = "/api/user/login"; + static get login => "/api/user/login"; + static const loginByClientId = "/open/auth/token"; static const user = "/api/user"; - static const tasks = "/api/crons"; - static const runTasks = "/api/crons/run"; - static const stopTasks = "/api/crons/stop"; - static const taskDetail = "/api/crons/"; - static const addTask = "/api/crons"; - static const pinTask = "/api/crons/pin"; - static const unpinTask = "/api/crons/unpin"; - static const enableTask = "/api/crons/enable"; - static const disableTask = "/api/crons/disable"; - static const files = "/api/configs/files"; - static const configContent = "/api/configs/"; - static const saveFile = "/api/configs/save"; - static const envs = "/api/envs"; - static const addEnv = "/api/envs"; - static const delEnv = "/api/envs"; - static const disableEnvs = "/api/envs/disable"; - static const enableEnvs = "/api/envs/enable"; - static const loginLog = "/api/user/login-log"; - static const taskLog = "/api/logs"; - static const taskLogDetail = "/api/logs/"; - static const scripts = "/api/scripts/files"; - static const scriptDetail = "/api/scripts/"; - static const dependencies = "/api/dependencies"; - static const dependencyReinstall = "/api/dependencies/reinstall"; + + static get tasks => getIt().useSecretLogined ? "/open/user/login" : "/api/crons"; + + static get runTasks => getIt().useSecretLogined ? "/open/crons/run" : "/api/crons/run"; + + static get stopTasks => getIt().useSecretLogined ? "/open/crons/stop" : "/api/crons/stop"; + + static get taskDetail => getIt().useSecretLogined ? "/open/crons/" : "/api/crons/"; + + static get addTask => getIt().useSecretLogined ? "/open/crons" : "/api/crons"; + + static get pinTask => getIt().useSecretLogined ? "/open/crons/pin" : "/api/crons/pin"; + + static get unpinTask => getIt().useSecretLogined ? "/open/crons/unpin" : "/api/crons/unpin"; + + static get enableTask => getIt().useSecretLogined ? "/open/crons/enable" : "/api/crons/enable"; + + static get disableTask => getIt().useSecretLogined ? "/open/crons/disable" : "/api/crons/disable"; + + static get files => getIt().useSecretLogined ? "/open/configs/files" : "/api/configs/files"; + + static get configContent => getIt().useSecretLogined ? "/open/configs/" : "/api/configs/"; + + static get saveFile => getIt().useSecretLogined ? "/open/configs/save" : "/api/configs/save"; + + static get envs => getIt().useSecretLogined ? "/open/envs" : "/api/envs"; + + static get addEnv => getIt().useSecretLogined ? "/open/envs" : "/api/envs"; + + static get delEnv => getIt().useSecretLogined ? "/open/envs" : "/api/envs"; + + static get disableEnvs => getIt().useSecretLogined ? "/open/envs/disable" : "/api/envs/disable"; + + static get enableEnvs => getIt().useSecretLogined ? "/open/envs/enable" : "/api/envs/enable"; + + static get loginLog => getIt().useSecretLogined ? "/open/user/login-log" : "/api/user/login-log"; + + static get taskLog => getIt().useSecretLogined ? "/open/logs" : "/api/logs"; + + static get taskLogDetail => getIt().useSecretLogined ? "/open/logs/" : "/api/logs/"; + + static get scripts => getIt().useSecretLogined ? "/open/scripts/files" : "/api/scripts/files"; + + static get scriptDetail => getIt().useSecretLogined ? "/open/scripts/" : "/api/scripts/"; + + static get dependencies => getIt().useSecretLogined ? "/open/dependencies" : "/api/dependencies"; + + static get dependencyReinstall => getIt().useSecretLogined ? "/open/dependencies/reinstall" : "/api/dependencies/reinstall"; static intimeLog(String cronId) { - return "/api/crons/$cronId/log"; + return getIt().useSecretLogined ? "/open/crons/$cronId/log" : "/api/crons/$cronId/log"; } static envMove(String envId) { - return "/api/envs/$envId/move"; + return getIt().useSecretLogined ? "/open/envs/$envId/move" : "/api/envs/$envId/move"; } } diff --git a/lib/base/sp_const.dart b/lib/base/sp_const.dart index 3767e14..647ce53 100644 --- a/lib/base/sp_const.dart +++ b/lib/base/sp_const.dart @@ -3,3 +3,4 @@ String spHost = "host"; String spUserName = "username"; String spPassWord = "password"; String spTheme = "dart_mode"; +String spSecretLogined = "secret_logined"; diff --git a/lib/base/userinfo_viewmodel.dart b/lib/base/userinfo_viewmodel.dart index f4b73c3..147f344 100644 --- a/lib/base/userinfo_viewmodel.dart +++ b/lib/base/userinfo_viewmodel.dart @@ -7,11 +7,13 @@ class UserInfoViewModel { String? _host = ""; String? _userName; String? _passWord; + bool _useSecertLogined = false; UserInfoViewModel() { String userInfoJson = SpUtil.getString(spUserInfo); _userName = SpUtil.getString(spUserName); _passWord = SpUtil.getString(spPassWord); + _useSecertLogined = SpUtil.getBool(spSecretLogined, defValue: false); _host = SpUtil.getString(spHost, defValue: 'http://49.234.59.95:5700'); if (userInfoJson.isNotEmpty) { _token = userInfoJson; @@ -30,6 +32,11 @@ class UserInfoViewModel { SpUtil.putString(spPassWord, password); } + void useSecretLogin(bool use) { + _useSecertLogined = use; + SpUtil.putBool(spSecretLogined, _useSecertLogined); + } + void updateHost(String host) { _host = host; SpUtil.putString(spHost, host); @@ -43,6 +50,8 @@ class UserInfoViewModel { String? get passWord => _passWord; + bool get useSecretLogined => _useSecertLogined; + bool isLogined() { return token != null && token!.isNotEmpty; } diff --git a/lib/module/login/login_page.dart b/lib/module/login/login_page.dart index a85b313..c0ff69c 100644 --- a/lib/module/login/login_page.dart +++ b/lib/module/login/login_page.dart @@ -10,6 +10,7 @@ import 'package:qinglong_app/main.dart'; import 'package:qinglong_app/module/login/user_bean.dart'; import 'package:qinglong_app/utils/extension.dart'; import 'package:qinglong_app/utils/utils.dart'; +import 'package:flip_card/flip_card.dart'; import 'login_bean.dart'; @@ -24,6 +25,9 @@ class _LoginPageState extends ConsumerState { final TextEditingController _hostController = TextEditingController(text: getIt().host); final TextEditingController _userNameController = TextEditingController(); final TextEditingController _passwordController = TextEditingController(); + final TextEditingController _cIdController = TextEditingController(); + final TextEditingController _cSecretController = TextEditingController(); + GlobalKey cardKey = GlobalKey(); bool rememberPassword = false; @@ -32,15 +36,32 @@ class _LoginPageState extends ConsumerState { super.initState(); if (getIt().userName != null && getIt().userName!.isNotEmpty) { - _userNameController.text = getIt().userName!; + if (getIt().useSecretLogined) { + _cIdController.text = getIt().userName!; + } else { + _userNameController.text = getIt().userName!; + } rememberPassword = true; } else { rememberPassword = false; } if (getIt().passWord != null && getIt().passWord!.isNotEmpty) { - _passwordController.text = getIt().passWord!; + if (getIt().useSecretLogined) { + _cSecretController.text = getIt().passWord!; + } else { + _passwordController.text = getIt().passWord!; + } } getIt().updateToken(""); + WidgetsBinding.instance?.addPostFrameCallback((timeStamp) { + if (getIt().useSecretLogined) { + cardKey.currentState?.toggleCard(); + getIt().useSecretLogin(!(cardKey.currentState?.isFront ?? true)); + WidgetsBinding.instance?.addPostFrameCallback((timeStamp) { + setState(() {}); + }); + } + }); } @override @@ -128,51 +149,117 @@ class _LoginPageState extends ConsumerState { ), autofocus: false, ), - const SizedBox( - height: 15, - ), - const Text( - "用户名:", - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w600, + FlipCard( + key: cardKey, + fill: Fill.fillBack, + // Fill the back side of the card to make in the same size as the front. + direction: FlipDirection.HORIZONTAL, + front: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox( + height: 15, + ), + const Text( + "用户名:", + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + ), + ), + TextField( + onChanged: (_) { + setState(() {}); + }, + controller: _userNameController, + decoration: const InputDecoration( + contentPadding: EdgeInsets.fromLTRB(0, 5, 0, 5), + hintText: "请输入用户名", + ), + autofocus: false, + ), + const SizedBox( + height: 15, + ), + const Text( + "密码:", + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + ), + ), + TextField( + onChanged: (_) { + setState(() {}); + }, + controller: _passwordController, + obscureText: true, + decoration: const InputDecoration( + contentPadding: EdgeInsets.fromLTRB(0, 5, 0, 5), + hintText: "请输入密码", + ), + autofocus: false, + ), + const SizedBox( + height: 10, + ), + ], ), - ), - TextField( - onChanged: (_) { - setState(() {}); - }, - controller: _userNameController, - decoration: const InputDecoration( - contentPadding: EdgeInsets.fromLTRB(0, 5, 0, 5), - hintText: "请输入用户名", + back: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox( + height: 15, + ), + const Text( + "client_id:", + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + ), + ), + TextField( + onChanged: (_) { + setState(() {}); + }, + controller: _cIdController, + decoration: const InputDecoration( + contentPadding: EdgeInsets.fromLTRB(0, 5, 0, 5), + hintText: "请输入client_id", + ), + autofocus: false, + ), + const SizedBox( + height: 15, + ), + const Text( + "client_secret:", + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + ), + ), + TextField( + onChanged: (_) { + setState(() {}); + }, + controller: _cSecretController, + obscureText: true, + decoration: const InputDecoration( + contentPadding: EdgeInsets.fromLTRB(0, 5, 0, 5), + hintText: "请输入client_secret", + ), + autofocus: false, + ), + const SizedBox( + height: 10, + ), + ], ), - autofocus: false, - ), - const SizedBox( - height: 15, - ), - const Text( - "密码:", - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w600, - ), - ), - TextField( - onChanged: (_) { - setState(() {}); - }, - controller: _passwordController, - obscureText: true, - decoration: const InputDecoration( - contentPadding: EdgeInsets.fromLTRB(0, 5, 0, 5), - hintText: "请输入密码", - ), - autofocus: false, - ), - const SizedBox( - height: 10, ), Row( children: [ @@ -184,11 +271,26 @@ class _LoginPageState extends ConsumerState { }, ), const Text( - "记住密码", + "记住密码/client", style: TextStyle( fontSize: 14, ), ), + const Spacer(), + GestureDetector( + onTap: () { + cardKey.currentState?.toggleCard(); + WidgetsBinding.instance?.addPostFrameCallback((timeStamp) { + setState(() {}); + }); + }, + child: Text( + (cardKey.currentState?.isFront ?? true) ? "client_id登录" : "用户名密码登录", + style: const TextStyle( + fontSize: 14, + ), + ), + ), ], ), const SizedBox( @@ -199,31 +301,29 @@ class _LoginPageState extends ConsumerState { child: IgnorePointer( ignoring: _hostController.text.isEmpty || _userNameController.text.isEmpty || _passwordController.text.isEmpty || isLoading, child: CupertinoButton( - padding: const EdgeInsets.symmetric( - vertical: 5, - ), - color: - (_hostController.text.isNotEmpty && _userNameController.text.isNotEmpty && _passwordController.text.isNotEmpty && !isLoading) - ? ref.watch(themeProvider).themeColor.buttonBgColor() - : Theme.of(context).primaryColor.withOpacity(0.4), - child: isLoading - ? const CupertinoActivityIndicator() - : const Text( - "登 录", - style: TextStyle( - fontSize: 16, - ), + padding: const EdgeInsets.symmetric( + vertical: 5, + ), + color: canClickLoginBtn() ? ref.watch(themeProvider).themeColor.buttonBgColor() : Theme.of(context).primaryColor.withOpacity(0.4), + child: isLoading + ? const CupertinoActivityIndicator() + : const Text( + "登 录", + style: TextStyle( + fontSize: 16, ), - onPressed: () { - if (rememberPassword) { - getIt().updateUserName(_userNameController.text, _passwordController.text); - } - - Http.pushedLoginPage = false; - Utils.hideKeyBoard(context); - getIt().updateHost(_hostController.text); + ), + onPressed: () { + Http.pushedLoginPage = false; + Utils.hideKeyBoard(context); + getIt().updateHost(_hostController.text); + if (cardKey.currentState?.isFront ?? true) { login(_userNameController.text, _passwordController.text); - }), + } else { + login(_cIdController.text, _cSecretController.text); + } + }, + ), ), ), ], @@ -239,26 +339,44 @@ class _LoginPageState extends ConsumerState { bool isLoading = false; + bool loginByUserName() { + return !getIt().useSecretLogined; + } + Future login(String userName, String password) async { isLoading = true; setState(() {}); - HttpResponse response = await Api.login(userName, password); + HttpResponse response; + + if (loginByUserName()) { + response = await Api.login(userName, password); + } else { + response = await Api.loginByClientId(userName, password); + } if (response.success) { getIt().updateToken(response.bean?.token ?? ""); + getIt().useSecretLogin(!loginByUserName()); + if (rememberPassword) { + getIt().updateUserName(userName, password); + } - HttpResponse userResponse = await Api.user(); - if (userResponse.success) { - if (userResponse.bean != null && userResponse.bean!.twoFactorActivated != null && userResponse.bean!.twoFactorActivated!) { - ("你已开启两步验证,App暂不支持").toast(); + if (!loginByUserName()) { + Navigator.of(context).pushReplacementNamed(Routes.routeHomePage); + } else { + HttpResponse userResponse = await Api.user(); + if (userResponse.success) { + if (userResponse.bean != null && userResponse.bean!.twoFactorActivated != null && userResponse.bean!.twoFactorActivated!) { + ("你已开启两步验证,App暂不支持").toast(); + isLoading = false; + setState(() {}); + } else { + Navigator.of(context).pushReplacementNamed(Routes.routeHomePage); + } + } else { + (response.message ?? "请检查网络情况").toast(); isLoading = false; setState(() {}); - } else { - Navigator.of(context).pushReplacementNamed(Routes.routeHomePage); } - } else { - (response.message ?? "请检查网络情况").toast(); - isLoading = false; - setState(() {}); } } else { (response.message ?? "请检查网络情况").toast(); @@ -266,4 +384,15 @@ class _LoginPageState extends ConsumerState { setState(() {}); } } + + bool canClickLoginBtn() { + if (isLoading) return false; + + if (_hostController.text.isEmpty) return false; + if (!loginByUserName()) { + return _cIdController.text.isNotEmpty && _cSecretController.text.isNotEmpty; + } else { + return _userNameController.text.isNotEmpty && _passwordController.text.isNotEmpty; + } + } } diff --git a/pubspec.lock b/pubspec.lock index 4165fc2..da0ad75 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -239,6 +239,13 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "1.0.0" + flip_card: + dependency: "direct main" + description: + name: flip_card + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.6.0" flutter: dependency: "direct main" description: flutter @@ -450,6 +457,13 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "1.0.1" + move_to_background: + dependency: "direct main" + description: + name: move_to_background + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.0.2" package_config: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 280c82d..a0e0ecd 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -27,6 +27,7 @@ dependencies: drag_and_drop_lists: ^0.3.2+2 fluttertoast: ^8.0.8 move_to_background: ^1.0.2 + flip_card: ^0.6.0 dev_dependencies: flutter_native_splash: ^1.3.3