TensorFlow Lite,ML Kit 和 Flutter 移动深度学习:6~11(1)

简介: TensorFlow Lite,ML Kit 和 Flutter 移动深度学习:6~11(1)


六、构建人工智能认证系统

认证是任何应用中最突出的功能之一,无论它是本机移动软件还是网站,并且自从保护数据的需求以及与机密有关的隐私需求开始以来,认证一直是一个活跃的领域。 在互联网上共享的数据。 在本章中,我们将从基于 Firebase 的简单登录到应用开始,然后逐步改进以包括基于人工智能(AI)的认证置信度指标和 Google 的 ReCaptcha。 所有这些认证方法均以深度学习为核心,并提供了一种在移动应用中实现安全性的最新方法。

在本章中,我们将介绍以下主题:

  • 一个简单的登录应用
  • 添加 Firebase 认证
  • 了解用于认证的异常检测
  • 用于认证用户的自定义模型
  • 实现 ReCaptcha 来避免垃圾邮件
  • 在 Flutter 中部署模型

技术要求

对于移动应用,需要具有 Flutter 的 Visual Studio Code 和 Dart 插件以及 Firebase Console

GitHub 网址

一个简单的登录应用

我们将首先创建一个简单的认证应用,该应用使用 Firebase 认证对用户进行认证,然后再允许他们进入主屏幕。 该应用将允许用户输入其电子邮件和密码来创建一个帐户,然后使他们随后可以使用此电子邮件和密码登录。

以下屏幕快照显示了应用的完整流程:

该应用的小部件树如下:

现在让我们详细讨论每个小部件的实现。

创建 UI

让我们从创建应用的登录屏幕开始。 用户界面UI)将包含两个TextFormField来获取用户的电子邮件 ID 和密码,RaisedButton进行注册/登录,以及FlatButton进行注册和登录操作之间的切换。

以下屏幕快照标记了将用于应用的第一个屏幕的小部件:

现在让我们创建应用的 UI,如下所示:

  1. 我们首先创建一个名为signup_signin_screen.dart的新 dart 文件。 该文件包含一个有状态的小部件– SignupSigninScreen
  2. 第一个屏幕中最上面的窗口小部件是TextField,用于获取用户的邮件 ID。 _createUserMailInput()方法可帮助我们构建窗口小部件:
Widget _createUserMailInput() {
  return Padding(
     padding: const EdgeInsets.fromLTRB(0.0, 100.0, 0.0, 0.0),
     child: new TextFormField(
       maxLines: 1,
       keyboardType: TextInputType.emailAddress,
       autofocus: false,
       decoration: new InputDecoration(
           hintText: 'Email',
           icon: new Icon(
             Icons.mail,
             color: Colors.grey,
           )),
       validator: (value) => value.isEmpty ? 'Email can\'t be empty' : null,
       onSaved: (value) => _usermail = value.trim(),
     ),
   );
 }

首先,我们使用EdgeInsets.fromLTRB()为小部件提供了填充。 这有助于我们在四个基本方向的每个方向(即左,上,右和下)上创建具有不同值的偏移量。 接下来,我们使用maxLines(输入的最大行数)创建了TextFormField,其值为1作为子级,它接收用户的电子邮件地址。 另外,根据输入类型TextInputType.emailAddress,我们指定了将在属性keyboardType中使用的键盘类型。 然后,将autoFocus设置为false。 然后,我们在装饰属性中使用InputDecoration提供hintText "Email"和图标Icons.mail。 为了确保用户在没有输入电子邮件地址或密码的情况下不要尝试登录,我们添加了一个验证器。 当尝试使用空字段登录时,将显示警告“电子邮件不能为空”。 最后,我们通过使用trim()删除所有尾随空格来修剪输入的值,然后将输入的值存储在_usermail字符串变量中。

  1. 与“步骤 2”中的TextField相似,我们定义了下一个方法_createPasswordInput(),以创建用于输入密码的TextFormField()
Widget _createPasswordInput() {
   return Padding(
     padding: const EdgeInsets.fromLTRB(0.0, 15.0, 0.0, 0.0),
     child: new TextFormField(
       maxLines: 1,
       obscureText: true,
       autofocus: false,
       decoration: new InputDecoration(
           hintText: 'Password',
           icon: new Icon(
             Icons.lock,
             color: Colors.grey,
           )),
       validator: (value) => value.isEmpty ? 'Password can\'t be empty' : null,
       onSaved: (value) => _userpassword = value.trim(),
     ),
   );
 }

我们首先使用EdgeInsets.fromLTRB()在所有四个基本方向上提供填充,以在顶部提供15.0的偏移量。 接下来,我们创建一个TextFormField,其中maxLines1,并将obscureText设置为true,将autofocus设置为falseobscureText用于隐藏正在键入的文本。 我们使用InputDecoration提供hintText密码和一个灰色图标Icons.lock。 为确保文本字段不为空,使用了一个验证器,当传递空值时,该警告器会发出警告Password can't be empty,即用户尝试在不输入密码的情况下登录/注册。 最后,trim()用于删除所有尾随空格,并将密码存储在_userpassword字符串变量中。

  1. 接下来,我们在_SignupSigninScreenState外部声明FormMode枚举,该枚举在两种模式SIGNINSIGNUP之间运行,如以下代码片段所示:
enum FormMode { SIGNIN, SIGNUP }

我们将对该按钮使用此枚举,该按钮将使用户既可以登录又可以注册。 这将帮助我们轻松地在两种模式之间切换。 枚举是一组用于表示常量值的标识符。

使用enum关键字声明枚举类型。 在enum内部声明的每个标识符都代表一个整数值; 例如,第一标识符具有值0,第二标识符具有值1。 默认情况下,第一个标识符的值为0

  1. 让我们定义一个_createSigninButton()方法,该方法返回按钮小部件以使用户注册并登录:
Widget _createSigninButton() {
   return new Padding(
       padding: EdgeInsets.fromLTRB(0.0, 45.0, 0.0, 0.0),
       child: SizedBox(
         height: 40.0,
         child: new RaisedButton(
           elevation: 5.0,
           shape: new RoundedRectangleBorder(borderRadius: new BorderRadius.circular(30.0)),
           color: Colors.blue,
           child: _formMode == FormMode.SIGNIN
               ? new Text('SignIn',
                   style: new TextStyle(fontSize: 20.0, color: Colors.white))
               : new Text('Create account',
                   style: new TextStyle(fontSize: 20.0, color: Colors.white)),
           onPressed: _signinSignup,
         ),
       ));
 }

我们从Padding开始,将45.0的按钮offset置于顶部,然后将SizedBox40.0height作为子项,并将RaisedButton作为其子项。 使用RoundedRectangleBorder()为凸起的按钮赋予圆角矩形形状,其边框半径为30.0,颜色为blue。 作为子项添加的按钮的文本取决于_formMode的当前值。 如果_formMode的值(FormMode枚举的一个实例)为FormMode.SIGNIN,则按钮显示SignIn,否则创建帐户。 按下按钮时将调用_signinSignup方法,该方法将在后面的部分中介绍。

  1. 现在,我们将第四个按钮添加到屏幕上,以使用户在SIGNINSIGNUP表单模式之间切换。 我们定义返回FlatButton_createSigninSwitchButton()方法,如下所示:
Widget _createSigninSwitchButton() {
   return new FlatButton(
     child: _formMode == FormMode.SIGNIN
         ? new Text('Create an account',
             style: new TextStyle(fontSize: 18.0, fontWeight: FontWeight.w300))
         : new Text('Have an account? Sign in',
             style:
                 new TextStyle(fontSize: 18.0, fontWeight: FontWeight.w300)),
     onPressed: _formMode == FormMode.SIGNIN
         ? _switchFormToSignUp
         : _switchFormToSignin,
   );
 }

如果_formMode的当前值为SIGNIN并按下按钮,则应更改为SIGNUP并显示Create an account。 否则,如果_formModeSIGNUP作为其当前值,并且按下按钮,则该值应切换为由文本Have an account? Sign in表示的SIGNIN。 使用三元运算符创建RaisedButtonText子级时,添加了在文本之间切换的逻辑。 onPressed属性使用非常相似的逻辑,该逻辑再次检查_formMode的值以在模式之间切换并使用_switchFormToSignUp_switchFormToSignin方法更新_formMode的值。 我们将在“步骤 7”和 8 中定义_switchFormToSignUp_switchFormToSignin方法。

  1. 现在,我们定义_switchFormToSignUp()如下:
void _switchFormToSignUp() {
   _formKey.currentState.reset();
   setState(() {
     _formMode = FormMode.SIGNUP;
   });
 }

此方法重置_formMode的值并将其更新为FormMode.SIGNUP。 更改setState()内部的值有助于通知框架该对象的内部状态已更改,并且 UI 可能需要更新。

  1. 我们以与_switchFormToSignUp()非常相似的方式定义_switchFormToSignin()
void _switchFormToSignin() {
   _formKey.currentState.reset();
   setState(() {
     _formMode = FormMode.SIGNIN;
   });
 }

此方法重置_formMode的值并将其更新为FormMode.SIGNIN。 更改setState()内部的值有助于通知框架该对象的内部状态已更改,并且 UI 可能需要更新。

  1. 现在,让我们将所有屏幕小部件Email TextFieldPassword TextFiedSignIn ButtonFlatButton切换为在单个容器中进行注册和登录。 为此,我们定义了一种方法createBody(),如下所示:
Widget _createBody(){
   return new Container(
       padding: EdgeInsets.all(16.0),
       child: new Form(
         key: _formKey,
         child: new ListView(
           shrinkWrap: true,
           children: <Widget>[
             _createUserMailInput(),
             _createPasswordInput(),
             _createSigninButton(),
             _createSigninSwitchButton(),
             _createErrorMessage(),
           ],
         ),
       )
    );
 }

此方法返回一个以Form作为子元素的新Container并为其填充16.0。 表单使用_formKey作为其键,并添加ListView作为其子级。 ListView的元素是我们在前述方法中创建的用于添加TextFormFieldsButtons的小部件。 shrinkWrap设置为true,以确保ListView仅占用必要的空间,并且不会尝试扩展和填充整个屏幕

Form类用于将多个FormFields一起分组和验证。 在这里,我们使用Form将两个TextFormFields,一个RaisedButton和一个FlatButton包装在一起。

  1. 这里要注意的一件事是,由于进行认证,因此用户最终将成为网络操作,因此可能需要一些时间来发出网络请求。 在此处添加进度条可防止在进行网络操作时 UI 的死锁。 我们声明boolean标志_loading,当网络操作开始时将其设置为true。 现在,我们定义一种_createCircularProgress()方法,如下所示:
Widget _createCircularProgress(){
   if (_loading) {
     return Center(child: CircularProgressIndicator());
   } return Container(height: 0.0, width: 0.0,);
 }

仅当_loadingtrue并且正在进行网络操作时,该方法才返回CircularProgressIndicator()

  1. 最后,让我们在build()方法内添加所有小部件:
@override
 Widget build(BuildContext context) {
   return new Scaffold(
       appBar: new AppBar(
         title: new Text('Firebase Authentication'),
       ),
       body: Stack(
         children: <Widget>[
           _createBody(),
           _createCircularProgress(),
         ],
       ));
 }

build()内部,添加包含应用标题的AppBar变量后,我们返回一个支架。 支架的主体包含一个带有子项的栈,这些子项是_createBody()_createCircularProgress() 函数调用返回的小部件。

现在,我们已经准备好应用的主要 UI 结构。

可以在这个页面中找到SignupSigninScreen的完整代码。

在下一部分中,我们将介绍将 Firebase 认证添加到应用中涉及的步骤。

添加 Firebase 认证

如前所述,在“简单登录应用”部分中,我们将使用用户的电子邮件和密码通过 Firebase 集成认证。

要在 Firebase 控制台上创建和配置 Firebase 项目,请参考“附录”。

以下步骤详细讨论了如何在 Firebase Console 上设置项目:

  1. 我们首先在 Firebase 控制台上选择项目:

  1. 接下来,我们将在Develop菜单中单击Authentication选项:

这将带我们进入认证屏幕。

  1. 迁移到登录标签并启用登录提供者下的“电子邮件/密码”选项:

这是设置 Firebase 控制台所需的全部。

接下来,我们将 Firebase 集成到代码中。 这样做如下:

  1. 迁移到 Flutter SDK 中的项目,然后将firebase-auth添加到应用级别build.gradle文件中:
implementation 'com.google.firebase:firebase-auth:18.1.0'
  1. 为了使FirebaseAuthentication在应用中正常工作,我们将在此处使用firebase_auth插件。 在pubspec.yaml文件的依赖项中添加插件依赖项:
firebase_auth: 0.14.0+4

确保运行flutter pub get以安装依赖项。

现在,让我们编写一些代码以在应用内部提供 Firebase 认证功能。

创建auth.dart

现在,我们将创建一个 Dart 文件auth.dart。 该文件将作为访问firebase_auth插件提供的认证方法的集中点:

  1. 首先,导入firebase_auth插件:
import 'package:firebase_auth/firebase_auth.dart';
  1. 现在,创建一个抽象类BaseAuth,该类列出了所有认证方法,并充当 UI 组件和认证方法之间的中间层:
abstract class BaseAuth {
 Future<String> signIn(String email, String password);
 Future<String> signUp(String email, String password);
 Future<String> getCurrentUser();
 Future<void> signOut();
}

顾名思义,这些方法将使用认证的四个主要函数:

  • signIn():使用电子邮件和密码登录已经存在的用户
  • signUp():使用电子邮件和密码为新用户创建帐户
  • getCurrentUser():获取当前登录的用户
  • signOut():注销已登录的用户

这里要注意的重要一件事是,由于这是网络操作,因此所有方法都异步操作,并在执行完成后返回Future值。

  1. 创建一个实现BaseAuthAuth类:
class Auth implements BaseAuth {
    //. . . . . 
}

在接下来的步骤中,我们将定义BaseAuth中声明的所有方法。

  1. 创建FirebaseAuth的实例:
final FirebaseAuth _firebaseAuth = FirebaseAuth.instance;
  1. signIn()方法实现如下:
Future<String> signIn(String email, String password) async {
     AuthResult result = await _firebaseAuth.signInWithEmailAndPassword(email: email, password: password);
    FirebaseUser user = result.user;
    return user.uid;
}

此方法接收用户的电子邮件和密码,然后调用signInWithEmailAndPassword(),并传递电子邮件和密码以登录已经存在的用户。 登录操作完成后,将返回AuthResult实例。 我们将其存储在result中,还使用result.user,它返回FirebaseUser.。它可用于获取与用户有关的信息,例如他们的uidphoneNumberphotoUrl。 在这里,我们返回user.uid,它是每个现有用户的唯一标识。 如前所述,由于这是网络操作,因此它异步运行,并在执行完成后返回Future

  1. 接下来,我们将定义signUp()方法以添加新用户:
Future<String> signUp(String email, String password) async {
    AuthResult result = await _firebaseAuth.createUserWithEmailAndPassword(email: email, password: password);
    FirebaseUser user = result.user;
    return user.uid;
 }

前面的方法接收在注册过程中使用的电子邮件和密码,并将其值传递给createUserWithEmailAndPassword。 类似于上一步中定义的对象,此调用还返回AuthResult对象,该对象还用于提取FirebaseUser。 最后,signUp方法返回新创建的用户的uid

  1. 现在,我们将定义getCurrentUser()
Future<String> getCurrentUser() async {
   FirebaseUser user = await _firebaseAuth.currentUser();
   return user.uid;
 }

在先前定义的函数中,我们使用_firebaseAuth.currentUser()提取当前登录用户的信息。 此方法返回包装在FirebaseUser对象中的完整信息。 我们将其存储在user变量中。 最后,我们使用user.uid返回用户的uid

  1. 接下来,我们执行signOut()
Future<void> signOut() async {
   return _firebaseAuth.signOut();
 }

此函数仅在当前FirebaseAuth实例上调用signOut()并注销已登录的用户。

至此,我们已经完成了用于实现 Firebase 认证的所有基本编码。

可以在这个页面中查看auth.dart中的整个代码。

现在让我们看看如何在应用内部使认证生效。

SignupSigninScreen中添加认证

在本节中,我们将在SignupSigninScreen中添加 Firebase 认证。

我们在signup_signin_screen.dart文件中定义了_signinSignup()方法。 当按下登录按钮时,将调用该方法。 该方法的主体如下所示:

void _signinSignup() async {
   setState(() {
     _loading = true;
   });
     String userId = "";     
       if (_formMode == FormMode.SIGNIN) {
         userId = await widget.auth.signIn(_usermail, _userpassword);
       } else {
         userId = await widget.auth.signUp(_usermail, _userpassword);
       }
       setState(() {
         _loading = false;
       });
       if (userId.length > 0 && userId != null && _formMode == FormMode.SIGNIN) {
         widget.onSignedIn();
       }
}

在上述方法中,我们首先将_loading的值设置为true,以便进度条显示在屏幕上,直到登录过程完成。 接下来,我们创建一个userId字符串,一旦登录/登录操作完成,该字符串将存储userId的值。 现在,我们检查_formMode的当前值。 如果等于FormMode.SIGNIN,则用户希望登录到现有帐户。 因此,我们使用传递到SignupSigninScreen构造器中的实例来调用Auth类内部定义的signIn()方法。

这将在后面的部分中详细讨论。 否则,如果_formMode的值等于FormMode.SIGNUP,则将调用Auth类的signUp()方法,并传递用户的邮件和密码以创建新帐户。 一旦成功完成登录/注册,userId变量将用于存储用户的 ID。 整个过程完成后,将_loading设置为false,以从屏幕上删除循环进度指示器。 另外,如果在用户登录到现有帐户时userId具有有效值,则将调用onSignedIn(),这会将用户定向到应用的主屏幕。

此方法也传递给SignupSigninScreen的构造器,并将在后面的部分中进行讨论。 最后,我们将整个主体包裹在try-catch块中,以便在登录过程中发生的任何异常都可以捕获而不会导致应用崩溃,并可以在屏幕上显示。

创建主屏幕

我们还需要确定认证状态,即用户在启动应用时是否已登录,如果已经登录,则将其定向到主屏幕。如果尚未登录,则应显示SignInSignupScreen 首先,在完成该过程之后,将启动主屏幕。 为了实现这一点,我们在新的 dart 文件main_screen.dart中创建一个有状态的小部件MainScreen,然后执行以下步骤:

  1. 我们将从定义枚举AuthStatus开始,该枚举表示用户的当前认证状态,可以登录或不登录:
enum AuthStatus {
 NOT_SIGNED_IN,
 SIGNED_IN,
}
  1. 现在,我们创建enum类型的变量来存储当前认证状态,其初始值设置为NOT_SIGNED_IN
AuthStatus authStatus = AuthStatus.NOT_SIGNED_IN;
  1. 初始化小部件后,我们将通过覆盖initState()方法来确定用户是否已登录:
@override
 void initState() {
   super.initState();
   widget.auth.getCurrentUser().then((user) {
     setState(() {
       if (user != null) {
         _userId = user;
       }
       authStatus =
           user == null ? AuthStatus.NOT_SIGNED_IN : AuthStatus.SIGNED_IN;
     });
   });
 }

使用在构造器中传递的类的实例调用Auth类的getCurrentUser()。 如果该方法返回的值不为null,则意味着用户已经登录。因此,_userId字符串变量的值设置为返回的值。 另外,将authStatus设置为AuthStatus.SIGNED_IN.,否则,如果返回的值为null,则意味着没有用户登录,因此authStatus的值设置为AuthStatus.NOT_SIGNED_IN

  1. 现在,我们将定义另外两个方法onSignIn()onSignOut(),以确保将认证状态正确存储在变量中,并相应地更新用户界面:
void _onSignedIn() {
   widget.auth.getCurrentUser().then((user){
     setState(() {
       _userId = user;
     });
   });
   setState(() {
     authStatus = AuthStatus.SIGNED_IN;
   });
 }
 void _onSignedOut() {
   setState(() {
     authStatus = AuthStatus.NOT_SIGNED_IN;
     _userId = "";
   });
 }

_onSignedIn()方法检查用户是否已经登录,并将authStatus设置为AuthStatus.SIGNED_IN._onSignedOut()方法检查用户是否已注销,并将authStatus设置为AuthStatus.SIGNED_OUT

  1. 最后,我们重写build方法将用户定向到正确的屏幕:
@override
 Widget build(BuildContext context) {
   if(authStatus == AuthStatus.SIGNED_OUT) {
     return new SignupSigninScreen(
       auth: widget.auth,
       onSignedIn: _onSignedIn,
     );
   } else {
     return new HomeScreen(
       userId: _userId,
       auth: widget.auth,
       onSignedOut: _onSignedOut,
       );
   }
 }

如果authStatusAuthStatus.SIGNED_OUT,则返回SignupSigninScreen,并传递auth实例和_onSignedIn()方法。 否则,将直接返回HomeScreen,并传递已登录用户的userIdAuth实例类和_onSignedOut()方法。

可以在此处查看main_screen.dart的完整代码

在下一部分中,我们将为应用添加一个非常简单的主屏幕。

创建主屏幕

由于我们对认证部分更感兴趣,因此主屏幕(即成功登录后指向用户的屏幕)应该非常简单。 它仅包含一些文本和一个注销选项。 正如我们对所有先前的屏幕和小部件所做的一样,我们首先创建一个home_screen.dart文件和一个有状态的HomeScreen小部件。

主屏幕将显示如下:

此处的完整代码位于重写的build()方法内部:

@override
 Widget build(BuildContext context) {
     return new Scaffold(
         appBar: new AppBar(
             title: new Text('Firebase Authentication'),
             actions: <Widget>[
                 new FlatButton(
                     child: new Text('Logout',
                     style: new TextStyle(fontSize: 16.0, color: Colors.white)),
                     onPressed: _signOut
                 )
             ],
         ),
         body: Center(child: new Text('Hello User', 
         style: new TextStyle(fontSize: 32.0))
         ),
     );
 }

我们在此处返回Scaffold,其中包含标题为Text Firebase AuthenticationAppBaractions属性的小部件列表。 actions用于在应用标题旁边添加小部件列表到应用栏中。 在这里,它仅包含FlatButtonLogout,在按下时将调用_signOut

_signOut()方法显示如下:

_signOut() async {
   try {
     await widget.auth.signOut();
     widget.onSignedOut();
   } catch (e) {
     print(e);
   }
 }

该方法主要是调用Auth类中定义的signOut()方法,以将用户从应用中注销。 回忆传入HomeScreenMainScreen_onSignedOut()方法。 当用户退出时,该方法在此处用作widget.onSignedOut()来将authStatus更改为SIGNED_OUT。 同样,它包装在try-catch块中,以捕获并打印此处可能发生的任何异常。

可以在此处查看home_screen.dart的整个代码

至此,应用的主要组件已经准备就绪,现在让我们创建最终的材质应用。

创建main.dart

main.dart内部,我们创建Stateless WidgetApp,并覆盖build()方法,如下所示:

@override
 Widget build(BuildContext context) {
   return new MaterialApp(
       title: 'Firebase Authentication',
       debugShowCheckedModeBanner: false,
       theme: new ThemeData(
         primarySwatch: Colors.blue,
       ),
       home: new MainScreen(auth: new Auth()));
 }

该方法从主屏幕返回MaterialApp,以提供标题,主题。

可以在此处查看main.dart文件

了解用于认证的异常检测

异常检测是机器学习的一个备受关注的分支。 该术语含义简单。 基本上,它是用于检测异常的方法的集合。 想象一袋苹果。 识别并挑选坏苹果将是异常检测的行为。

异常检测以几种方式执行:

  • 通过使用列的最小最大范围来识别数据集中与其余样本非常不同的数据样本
  • 通过将数据绘制为线形图并识别图中的突然尖峰
  • 通过围绕高斯曲线绘制数据并将最末端的点标记为离群值(异常)

一些常用的方法是支持向量机,贝叶斯网络和 K 最近邻。 在本节中,我们将重点介绍与安全性相关的异常检测。

假设您通常在家中登录应用上的帐户。 如果您突然从数千英里外的位置登录帐户,或者在另一种情况下,您以前从未使用过公共计算机登录帐户,那将是非常可疑的,但是突然有一天您这样做。 另一个可疑的情况可能是您尝试 10-20 次密码,每次在成功成功登录之前每次都输入错误密码。 当您的帐户遭到盗用时,所有这些情况都是可能的行为。 因此,重要的是要合并一个能够确定您的常规行为并对异常行为进行分类的系统。 换句话说,即使黑客使用了正确的密码,企图破坏您的帐户的尝试也应标记为异常。

这带给我们一个有趣的观点,即确定用户的常规行为。 我们如何做到这一点? 什么是正常行为? 它是针对每个用户的还是一个通用概念? 问题的答案是它是非常特定于用户的。 但是,行为的某些方面对于所有用户而言都可以相同。 一个应用可能会在多个屏幕上启动登录。 单个用户可能更喜欢其中一种或两种方法。 这将导致特定于该用户的特定于用户的行为。 但是,如果尝试从未由开发人员标记为登录屏幕的屏幕进行登录,则无论是哪个用户尝试登录,都肯定是异常的。

在我们的应用中,我们将集成一个这样的系统。 为此,我们将记录一段时间内我们应用的许多用户进行的所有登录尝试。 我们将特别注意他们尝试登录的屏幕以及它们传递给系统的数据类型。 一旦收集了很多这些样本,就可以根据用户执行的任何操作来确定系统对认证的信心。 如果系统在任何时候认为用户表现出的行为与他们的惯常行为相差很大,则该用户将未经认证并被要求验证其帐户详细信息。

让我们从创建预测模型开始,以确定用户认证是常规的还是异常的。

用于认证用户的自定义模型

我们将本节分为两个主要子节:

  • 构建用于认证有效性检查的模型
  • 托管自定义认证验证模型

让我们从第一部分开始。

构建用于认证有效性检查的模型

在本部分中,我们将构建模型来确定是否有任何用户正在执行常规登录或异常登录:

  1. 我们首先导入必要的模块,如下所示:
import sys
import os
import json
import pandas
import numpy
from keras.models import Sequential
from keras.layers import LSTM, Dense, Dropout
from keras.layers.embeddings import Embedding
from keras.preprocessing import sequence
from keras.preprocessing.text import Tokenizer
from collections import OrderedDict
  1. 现在,我们将数据集导入到项目中。 可以在这里中找到该数据集:
csv_file = 'data.csv'
dataframe = pandas.read_csv(csv_file, engine='python', quotechar='|', header=None)
count_frame = dataframe.groupby([1]).count()
print(count_frame)
total_req = count_frame[0][0] + count_frame[0][1]
num_malicious = count_frame[0][1]
print("Malicious request logs in dataset: {:0.2f}%".format(float(num_malicious) / total_req * 100))

前面的代码块将 CSV 数据集加载到项目中。 它还会打印一些与数据有关的统计信息,如下所示:

  1. 我们在上一步中加载的数据目前尚无法使用,无法进行深度学习。 在此步骤中,我们将其分为特征列和标签列,如下所示:
X = dataset[:,0]
Y = dataset[:,1]
  1. 接下来,我们将删除数据集中包含的某些列,因为我们不需要所有这些列来构建简单的模型:
for index, item in enumerate(X):
    reqJson = json.loads(item, object_pairs_hook=OrderedDict)
    del reqJson['timestamp']
    del reqJson['headers']
    del reqJson['source']
    del reqJson['route']
    del reqJson['responsePayload']
    X[index] = json.dumps(reqJson, separators=(',', ':'))
  1. 接下来,我们将在剩余的请求正文上执行分词。 分词是一种用于将大文本块分解为较小文本的方法,例如将段落分成句子,将句子分成单词。 我们这样做如下:
tokenizer = Tokenizer(filters='\t\n', char_level=True)
tokenizer.fit_on_texts(X)
  1. 分词之后,我们将请求正文中的文本转换为单词向量,如下一步所示。 我们将数据集和DataFrame标签分为两部分,即 75%-25%,以进行训练和测试:
num_words = len(tokenizer.word_index)+1
X = tokenizer.texts_to_sequences(X)
max_log_length = 1024
train_size = int(len(dataset) * .75)
X_processed = sequence.pad_sequences(X, maxlen=max_log_length)
X_train, X_test = X_processed[0:train_size], X_processed[train_size:len(X_processed)]
Y_train, Y_test = Y[0:train_size], Y[train_size:len(Y)]
  1. 接下来,我们基于长短期记忆LSTM)创建基于循环神经网络RNN)的学习方法,来识别常规用户行为。 将单词嵌入添加到层中,以帮助维持单词向量和单词之间的关系:
model = Sequential()
model.add(Embedding(num_words, 32, input_length=max_log_length))
model.add(Dropout(0.5))
model.add(LSTM(64, recurrent_dropout=0.5))
model.add(Dropout(0.5))
model.add(Dense(1, activation='sigmoid'))

我们的输出是单个神经元,在正常登录的情况下,该神经元保存0;在登录异常的情况下,则保存1

  1. 现在,我们以精度作为度量标准编译模型,而损失则作为二进制交叉熵来计算:
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
print(model.summary())
  1. 现在,我们准备进行模型的训练:
model.fit(X_train, Y_train, validation_split=0.25, epochs=3, batch_size=128)
  1. 我们将快速检查模型所达到的准确率。 当前模型的准确率超过 96%:
score, acc = model.evaluate(X_test, Y_test, verbose=1, batch_size=128)
print("Model Accuracy: {:0.2f}%".format(acc * 100))

下面的屏幕快照显示了前面代码块的输出:

  1. 现在,我们保存模型权重和模型定义。 我们稍后将它们加载到 API 脚本中,以验证用户的认证:
model.save_weights('lstm-weights.h5')
model.save('lstm-model.h5')

现在,我们可以将认证模型作为 API 进行托管,我们将在下一部分中进行演示。

托管自定义认证验证模型

在本节中,我们将创建一个 API,用于在用户向模型提交其登录请求时对其进行认证。 请求标头将被解析为字符串,并且模型将使用它来预测登录是否有效:

  1. 我们首先导入创建 API 服务器所需的模块:
from sklearn.externals import joblib
from flask import Flask, request, jsonify
from string import digits
import sys
import os
import json
import pandas
import numpy
import optparse
from keras.models import Sequential, load_model
from keras.preprocessing import sequence
from keras.preprocessing.text import Tokenizer
from collections import OrderedDict
  1. 现在,我们实例化一个Flask应用对象。 我们还将从上一节“构建用于认证有效性检查的模型”中加载保存的模型定义和模型权重。然后,我们重新编译模型,并使用_make_predict_function( )方法创建其预测方法,如以下步骤所示:
app = Flask(__name__)
model = load_model('lstm-model.h5')
model.load_weights('lstm-weights.h5')
model.compile(loss = 'binary_crossentropy', optimizer = 'adam', metrics = ['accuracy'])
model._make_predict_function()
  1. 然后,我们创建一个remove_digits()函数,该函数用于从提供给它的输入中去除所有数字。 这将用于在将请求正文文本放入模型之前清除它:
def remove_digits(s: str) -> str:
    remove_digits = str.maketrans('', '', digits)
    res = s.translate(remove_digits)
    return res
  1. 接下来,我们将在 API 服务器中创建/login路由。 该路由由login()方法处理,并响应GETPOST请求方法。 正如我们对训练输入所做的那样,我们删除了请求标头中的非必要部分。 这可以确保模型将对数据进行预测,类似于对其进行训练的数据:
@app.route('/login', methods=['GET, POST'])
def login():
    req = dict(request.headers)
    item = {}
    item["method"] = str(request.method)
    item["query"] = str(request.query_string)
    item["path"] = str(request.path)
    item["statusCode"] = 200
    item["requestPayload"] = []
    ## MORE CODE BELOW THIS LINE
    ## MORE CODE ABOVE THIS LINE
    response = {'result': float(prediction[0][0])}
    return jsonify(response)
  1. 现在,我们将代码添加到login()方法中,该方法将标记请求正文并将其传递给模型以执行有关登录请求有效性的预测,如下所示:
@app.route('/login', methods=['GET, POST'])
def login():
    ...
    ## MORE CODE BELOW THIS LINE
    X = numpy.array([json.dumps(item)])
    log_entry = "store"
    tokenizer = Tokenizer(filters='\t\n', char_level=True)
    tokenizer.fit_on_texts(X)
    seq = tokenizer.texts_to_sequences([log_entry])
    max_log_length = 1024
    log_entry_processed = sequence.pad_sequences(seq, maxlen=max_log_length)
    prediction = model.predict(log_entry_processed)
    ## MORE CODE ABOVE THIS LINE
    ...

最后,应用以 JSON 字符串的形式返回其对用户进行认证的信心。

  1. 最后,我们使用apprun()方法启动服务器脚本:
if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8000)
  1. 将此文件另存为main.py。 要开始执行服务器,请打开一个新终端并使用以下命令:
python main.py

服务器监听其运行系统的所有传入 IP。 通过在0.0.0.0 IP 上运行它,可以实现这一点。 如果我们希望稍后在基于云的服务器上部署脚本,则需要这样做。 如果不指定0.0.0.0主机,则默认情况下会使它监听127.0.0.1,这不适合在公共服务器上进行部署。 您可以在此处详细了解这些地址之间的区别

在下一节中,我们将看到如何将 ReCaptcha 集成到迄今为止在该项目中构建的应用中。 之后,我们将把本节中构建的 API 集成到应用中。

实现 ReCaptcha 来保护垃圾邮件

为了为 Firebase 认证增加另一层安全性,我们将使用 ReCaptcha。 这是 Google 所支持的一项测试,可帮助我们保护数据免受垃圾邮件和滥用行为的自动 bot 攻击。 该测试很简单,很容易被人类解决,但是却阻碍了漫游器和恶意用户的使用。

要了解有关 ReCaptcha 及其用途的更多信息,请访问这里

ReCAPTCHA v2

在本节中,我们将把 ReCaptcha 版本 2 集成到我们的应用中。 在此版本中,向用户显示一个简单的复选框。 如果刻度变为绿色,则表明用户已通过验证。

另外,还可以向用户提出挑战,以区分人和机器人。 这个挑战很容易被人类解决。 他们要做的就是根据说明选择一堆图像。 使用 ReCaptcha 进行认证的传统流程如下所示:

一旦用户能够验证其身份,他们就可以成功登录。

获取 API 密钥

要在我们的应用内部使用 ReCaptcha,我们需要在reCAPTCHA管理控制台中注册该应用,并获取站点密钥和秘密密钥。 为此,请访问这里并注册该应用。 您将需要导航到“注册新站点”部分,如以下屏幕截图所示:

我们可以通过以下两个简单步骤来获取 API 密钥:

  1. 首先提供一个域名。 在这里,我们将在 reCAPTCHA v2 下选择 reCAPTCHA Android。
  2. 选择 Android 版本后,添加项目的包名称。 正确填写所有信息后,单击“注册”。

这将引导您到显示站点密钥和秘密密钥的屏幕,如以下屏幕快照所示:

站点密钥秘密密钥复制并保存到安全位置。 我们将在编码应用时使用它们。

代码整合

为了在我们的应用中包含 ReCaptcha v2,我们将使用 Flutter 包flutter_recaptcha_v2。 将flutter_recaptcha_v2:0.1.0依赖项添加到pubspec.yaml文件中,然后在终端中运行flutter packages get以获取所需的依赖项。 以下步骤详细讨论了集成:

  1. 我们将代码添加到signup_signin_screen.dart。 首先导入依赖项:
import 'package:flutter_recaptcha_v2/flutter_recaptcha_v2.dart';
  1. 接下来,创建一个RecaptchaV2Controller实例:
RecaptchaV2Controller recaptchaV2Controller = RecaptchaV2Controller();
  1. reCAPTCHA 复选框将添加为小部件。 首先,让我们定义一个返回小部件的_createRecaptcha()方法:
Widget _createRecaptcha() {
   return RecaptchaV2(
     apiKey: "Your Site Key here", 
     apiSecret: "Your API Key here", 
     controller: recaptchaV2Controller,
     onVerifiedError: (err){
       print(err);
     },
     onVerifiedSuccessfully: (success) {
       setState(() {
       if (success) {
         _signinSignup();
       } else {
         print('Failed to verify');
       }
       });
     },
   );
 }

在上述方法中,我们仅使用RecaptchaV2()构造器,即可为特定属性指定值。 添加您先前在apiKeyapiSecret属性中注册时保存的站点密钥和秘密密钥。 我们使用先前为属性控制器创建的recaptcha控制器recaptchaV2Controller的实例。 如果成功验证了用户,则将调用_signinSignup()方法以使用户登录。如果在验证期间发生错误,我们将打印错误。

  1. 现在,由于在用户尝试登录时应显示reCaptcha,因此我们将createSigninButton()中的登录凸起按钮的onPressed属性修改为recaptchaV2Controller
Widget _createSigninButton() {
    . . . . . . .
    return new Padding(
        . . . . . . .
        child: new RaisedButton(
            . . . . . . 
            //Modify the onPressed property
            onPressed: recaptchaV2Controller.show
        )
    )
}
  1. 最后,我们将_createRecaptcha()添加到build()内部的主体栈中:
@override
 Widget build(BuildContext context) {
    . . . . . . .
    return new Scaffold(
        . . . . . . .
        body: Stack(
            children: <Widget>[
                _createBody(),
                _createCircularProgress(),
                //Add reCAPTCHA Widget
                _createRecaptcha()
                 ],
       ));
 }

这就是一切! 现在,我们具有比 Firebase 认证更高的安全级别,可以保护应用的数据免受自动机器人的攻击。 现在让我们看一下如何集成定制模型以检测恶意用户。

在 Flutter 中部署模型

至此,我们的 Firebase 认证应用与 ReCaptcha 保护一起运行。 现在,让我们添加最后的安全层,该层将不允许任何恶意用户进入应用。

我们已经知道该模型位于以下端点。 我们只需从应用内部进行 API 调用,传入用户提供的电子邮件和密码,并从模型中获取结果值。 该值将通过使用阈值结果值来帮助我们判断登录是否是恶意的。

如果该值小于 0.20,则认为该登录名是恶意的,并且屏幕上将显示以下消息:

TensorFlow Lite,ML Kit 和 Flutter 移动深度学习:6~11(2)https://developer.aliyun.com/article/1427021

相关文章
|
7天前
|
机器学习/深度学习 供应链 TensorFlow
深度学习实战营:TensorFlow+Python,打造你的数据驱动决策引擎
【9月更文挑战第13天】在数据爆炸时代,企业日益依赖精准分析进行决策。深度学习凭借其卓越的特征提取与模式识别能力,成为构建数据驱动决策引擎的关键技术。本项目通过TensorFlow和Python,利用LSTM构建零售业销量预测模型,优化库存管理和营销策略。首先确保安装TensorFlow,然后使用Keras API搭建模型,并通过训练、评估和部署流程,展示深度学习在数据驱动决策中的强大应用潜力,助力企业提升经营效率。
18 3
|
1天前
|
机器学习/深度学习 数据挖掘 TensorFlow
解锁Python数据分析新技能,TensorFlow&PyTorch双引擎驱动深度学习实战盛宴
在数据驱动时代,Python凭借简洁的语法和强大的库支持,成为数据分析与机器学习的首选语言。Pandas和NumPy是Python数据分析的基础,前者提供高效的数据处理工具,后者则支持科学计算。TensorFlow与PyTorch作为深度学习领域的两大框架,助力数据科学家构建复杂神经网络,挖掘数据深层价值。通过Python打下的坚实基础,结合TensorFlow和PyTorch的强大功能,我们能在数据科学领域探索无限可能,解决复杂问题并推动科研进步。
11 0
|
10天前
|
机器学习/深度学习 数据挖掘 TensorFlow
从数据小白到AI专家:Python数据分析与TensorFlow/PyTorch深度学习的蜕变之路
【9月更文挑战第10天】从数据新手成长为AI专家,需先掌握Python基础语法,并学会使用NumPy和Pandas进行数据分析。接着,通过Matplotlib和Seaborn实现数据可视化,最后利用TensorFlow或PyTorch探索深度学习。这一过程涉及从数据清洗、可视化到构建神经网络的多个步骤,每一步都需不断实践与学习。借助Python的强大功能及各类库的支持,你能逐步解锁数据的深层价值。
23 0
|
20天前
|
持续交付 测试技术 jenkins
JSF 邂逅持续集成,紧跟技术热点潮流,开启高效开发之旅,引发开发者强烈情感共鸣
【8月更文挑战第31天】在快速发展的软件开发领域,JavaServer Faces(JSF)这一强大的Java Web应用框架与持续集成(CI)结合,可显著提升开发效率及软件质量。持续集成通过频繁的代码集成及自动化构建测试,实现快速反馈、高质量代码、加强团队协作及简化部署流程。以Jenkins为例,配合Maven或Gradle,可轻松搭建JSF项目的CI环境,通过JUnit和Selenium编写自动化测试,确保每次构建的稳定性和正确性。
42 0
|
20天前
|
测试技术 数据库
探索JSF单元测试秘籍!如何让您的应用更稳固、更高效?揭秘成功背后的测试之道!
【8月更文挑战第31天】在 JavaServer Faces(JSF)应用开发中,确保代码质量和可维护性至关重要。本文详细介绍了如何通过单元测试实现这一目标。首先,阐述了单元测试的重要性及其对应用稳定性的影响;其次,提出了提高 JSF 应用可测试性的设计建议,如避免直接访问外部资源和使用依赖注入;最后,通过一个具体的 `UserBean` 示例,展示了如何利用 JUnit 和 Mockito 框架编写有效的单元测试。通过这些方法,不仅能够确保代码质量,还能提高开发效率和降低维护成本。
34 0
|
20天前
|
UED 开发者
哇塞!Uno Platform 数据绑定超全技巧大揭秘!从基础绑定到高级转换,优化性能让你的开发如虎添翼
【8月更文挑战第31天】在开发过程中,数据绑定是连接数据模型与用户界面的关键环节,可实现数据自动更新。Uno Platform 提供了简洁高效的数据绑定方式,使属性变化时 UI 自动同步更新。通过示例展示了基本绑定方法及使用 `Converter` 转换数据的高级技巧,如将年龄转换为格式化字符串。此外,还可利用 `BindingMode.OneTime` 提升性能。掌握这些技巧能显著提高开发效率并优化用户体验。
39 0
|
20天前
|
开发者 算法 虚拟化
惊爆!Uno Platform 调试与性能分析终极攻略,从工具运用到代码优化,带你攻克开发难题成就完美应用
【8月更文挑战第31天】在 Uno Platform 中,调试可通过 Visual Studio 设置断点和逐步执行代码实现,同时浏览器开发者工具有助于 Web 版本调试。性能分析则利用 Visual Studio 的性能分析器检查 CPU 和内存使用情况,还可通过记录时间戳进行简单分析。优化性能涉及代码逻辑优化、资源管理和用户界面简化,综合利用平台提供的工具和技术,确保应用高效稳定运行。
31 0
|
20天前
|
Apache 开发者 Java
Apache Wicket揭秘:如何巧妙利用模型与表单机制,实现Web应用高效开发?
【8月更文挑战第31天】本文深入探讨了Apache Wicket的模型与表单处理机制。Wicket作为一个组件化的Java Web框架,提供了多种模型实现,如CompoundPropertyModel等,充当组件与数据间的桥梁。文章通过示例介绍了模型创建及使用方法,并详细讲解了表单组件、提交处理及验证机制,帮助开发者更好地理解如何利用Wicket构建高效、易维护的Web应用程序。
14 0
|
20天前
|
机器学习/深度学习 人工智能 TensorFlow
深度学习入门:使用Python和TensorFlow构建你的第一个神经网络
【8月更文挑战第31天】 本文是一篇面向初学者的深度学习指南,旨在通过简洁明了的语言引导读者了解并实现他们的第一个神经网络。我们将一起探索深度学习的基本概念,并逐步构建一个能够识别手写数字的简单模型。文章将展示如何使用Python语言和TensorFlow框架来训练我们的网络,并通过直观的例子使抽象的概念具体化。无论你是编程新手还是深度学习领域的新兵,这篇文章都将成为你探索这个激动人心领域的垫脚石。
|
2天前
|
机器学习/深度学习 人工智能 算法
深度学习在图像识别中的突破与应用
本文深入探讨了深度学习技术在图像识别领域的前沿进展、核心原理、广泛应用以及面临的伦理挑战和未来发展趋势。首先,概述了深度学习如何利用人工神经网络模拟人脑处理信息的方式,实现对图像数据的高效识别和分类。随后,详细介绍了卷积神经网络(CNN)等关键技术在提升图像识别准确性中的作用,并通过具体实例展示了深度学习在医疗影像分析、自动驾驶、面部识别等多个领域的成功应用。此外,文章还讨论了数据隐私、算法偏见等伦理问题,并展望了量子计算与深度学习融合等未来发展方向,强调了技术创新与社会责任并重的重要性。

热门文章

最新文章