发帖  主题  评论  推荐  标签 用户 查搜   用户 密码 自动 注册  
 
面向对象 设计模式 领域驱动设计 企业架构 框架 开发教程 微服务 大数据 扩展性 并发编程 事件驱动 分布式 CQRS

使用JWT(JSON Web Tokens)实现React原生应用权限验证

  本文演示如何使用JSON Web Tokens对一个React Native应用进行权限验证,我们使用 this Auth0 sample API作为我们的应用后端。

设置和安装

首先需要准备基础环境,保证Node.js安装,如果你是OS X还需要安装 Xcode ,本文以OS X为基础环境。你需要按照React Native安装手册安装准备好基本目录,通过react-native run-ios运行你的起步项目,同时确保你的iOS模拟器已经能够运行。

下载克隆 this Auth0 sample API这个后端,部署到Node.js中,能够在本地运行。

我们也可以下载Tcomb’s Form Library以便容易加入表单到我们的app,可以通过命令npm install tcomb-form-native安装。

 

使用JWT验证我们的应用app

现在我们的后端已经下载并安装在本地,让我们浏览器访问:http://localhost:3001/api/random-quote,应该可以正常访问,返回200 OK。

下面开始布置我们的应用,现在开始我们的React Native app,下面是官方教程的一部分源码(见documentation ) ,源码是这样的:

/** 
 * Sample React Native App 
 * https://github.com/facebook/react-native 
 * @flow 
 */ 
import React, { Component } from  'react';
import {
  AppRegistry,
  StyleSheet,
  Text,
  View
} from  'react-native';
class  AwesomeProject  extends Component {
  render() {
    return (
      <View style={styles.container}>
        <Text style={styles.welcome}>
          Welcome to React Native!
        </Text>
        <Text style={styles.instructions}>
          To get started, edit index.ios.js
        </Text>
        <Text style={styles.instructions}>
          Press Cmd+R to reload,{ '\n'}
          Cmd+D or shake  for dev menu
        </Text>
      </View>
    );
  }
}
const styles = StyleSheet.create({
  container: {
、    flex:  1,
    justifyContent:  'center',
    alignItems:  'center',
    backgroundColor:  '#F5FCFF',
  },
  welcome: {
    fontSize:  20,
    textAlign:  'center',
    margin:  10,
  },
  instructions: {
    textAlign:  'center',
    color:  '#333333',
、    marginBottom:  5,
  },
});
AppRegistry.registerComponent( 'AwesomeProject', () => AwesomeProject);

 

让我们稍微修改一下这个源码,首先到前面头部,需要引入react和react-native两个,同时引入tcomb 库包:

var React = require( 'react');
var ReactNative = require( 'react-native');
var t = require( 'tcomb-form-native');
var {
    AppRegistry,
    AsyncStorage,
    StyleSheet,
    Text,
    View,
    TouchableHighlight,
    AlertIOS,
} = ReactNative;

然后把原来的样式置换出来,用下面的样式替代,注册我们自己的组件,这些代码会在应用最后面,是在所有函数和render以后:

var styles = StyleSheet.create({
    container: {
        justifyContent:  'center',
        marginTop:  50,
        padding:  20,
        backgroundColor:  '#ffffff',
    },
    title: {
        fontSize:  30,
        alignSelf:  'center',
        marginBottom:  30 
    },
    buttonText: {
        fontSize:  18,
        color:  'white',
        alignSelf:  'center' 
    },
    button: {
        height:  36,
        backgroundColor:  '#48BBEC',
        borderColor:  '#48BBEC',
        borderWidth:  1,
        borderRadius:  8,
        marginBottom:  10,
        alignSelf:  'stretch',
        justifyContent:  'center' 
    },
});
AppRegistry.registerComponent( 'AwesomeProject', () => AwesomeProject);

 

现在所有应用设置都已经完成了,现在我们需要增加用户名和密码登录表单了,用户能够选择登入或登出,一旦登入成功,就能按按钮进行上述应用的正常查询访问,如果是登出状态,访问该查询URL会得到错误结果。

 

JSON Web Tokens和AsyncStorage

我们是使用JSON Web Tokens对我们这个React native应用进行验证,当用户登入以后,后端API的响应将是一个JWT,任何访问受保护的URL都必须附上这个通过安全验证的标记Token,这样我们会在客户端需要一个保存后端发给我们的通过验证的JWT标记,以便每次发出请求时都能附上这个标记,这里我们使用AsyncStorage保存JWT这个标记。

在这个应用中,我们需要三个主要方法:

1. 一个方法名叫_userSignup,将会POST提交请求到后端端点/users,该请求提供一个用户名和密码,如果用户并不存在,它会将创建新的,一个JWT会在当前会话返回。

2.一个方法名_userLogin,会提交POST到后端/sessions/create,这个提交的请求再次带有用户名和密码,如果成功,会返回一个JWT。

3.最后,需要一个业务方法:_getProtectedQuote,查询受权限保护的资源,也就是端点api/protected/random-quote,提交的请求是GET方式,请求中包含本次会话的JWT,后端验证了这个JWT后会返回正常业务数据。

上述源码已经在 try it out 。我们解释一下,首先开始于:

var STORAGE_KEY =  'id_token';
var Form = t.form.Form;
var Person = t.struct({
    username: t. String,
    password: t. String 
});
const options = {};

 

第一行有一个STORAGE_KEY 变量,是用于隐藏加密我们的key,也就是id_token,然后设置tcombs表单库包,Person表单是由username和password组成,这是两个都需要的字段,都是字符串,我们不会增加另外选项,尽管我们能够拓展或分离登录或注册两个表单。

下面是存储JWT到应用的验证用户:

var AwesomeProject = React.createClass({
    async _onValueChange(item, selectedValue) {
        try {
            await AsyncStorage.setItem(item, selectedValue);
        }  catch (error) {
            console.log( 'AsyncStorage error: ' + error.message);
        }
    },

 

这个方法_onValueChange是在AsyncStorage中条目发生改变时被调用,它会被传入条目和值作为方法参数,当值改变或使用sets时会变化。

下面是通过JWT验证后,返回正常业务查询结果。

async  _getProtectedQuote() {
    var DEMO_TOKEN = await AsyncStorage.getItem(STORAGE_KEY);
    fetch( "http://localhost:3001/api/protected/random-quote", {
        method:  "GET",
        headers: {
            'Authorization':  'Bearer ' + DEMO_TOKEN
        }
    })
    .then((response) => response.text())
    .then((quote) => { 
        AlertIOS.alert(
        "Chuck Norris Quote:", quote)
    })
    .done();
},

这里弹出一个窗口Chuck Norris Quote表示这是一个正常业务查询结果。

_getProtectedQuote首先调用已经存储的JWT,一个id_token,如果存在会发出GET请求到后端,这是通过fetch方式实现,这里将把JWT放入GET请求的头部,头部中包含Authorization,其值是后端已经验证通过并保存在AsyncStorage中验证记号token,如果后端验证这个记号通过后,会发出给我们正常的业务响应,也就是弹出一个窗口显示查询结果。

那么关键问题来了,我们保存在AsyncStorage中的JWT是哪里来的呢?这是我们在注册用户时得到的一个JWT,代码见如下:

_userSignup() {
    var value =  this.refs.form.getValue();
    if (value) {  // if validation fails, value will be null 
        fetch( "http://localhost:3001/users", {
            method:  "POST", 
            headers: {
                'Accept':  'application/json',
、                'Content-Type':  'application/json' 
            },
            body: JSON.stringify({
                username: value.username, 
                password: value.password, 
            })
        })
        .then((response) => response.json())
        .then((responseData) => {
            this._onValueChange(STORAGE_KEY, responseData.id_token),
            AlertIOS.alert(
            "Signup Success!",
            "Click the button to get a Chuck Norris quote!" 
            )
        })
        .done();
    }
},

 

_userSignup 是在点按Signup按钮时被调用的,会从表单中收集username和passwd两个值,通过POST提交请求到后端API,后端会验证这个用户名和密码,注册一个新用户,然后返回一个当前会话的JWT,最后会调用_onValueChange方法,保存这个新的验证记号。

用户登录时也会返回一个验证的JWT,如下:

_userLogin() {
    var value =  this.refs.form.getValue();
    if (value) {  // if validation fails, value will be null 
        fetch( "http://localhost:3001/sessions/create", {
            method:  "POST", 
            headers: {
                'Accept':  'application/json',
                'Content-Type':  'application/json' 
            },
            body: JSON.stringify({
                username: value.username, 
                password: value.password, 
            })
        })
        .then((response) => response.json())
        .then((responseData) => {
            AlertIOS.alert(
            "Login Success!",
            "Click the button to get a Chuck Norris quote!" 
            ),
            this._onValueChange(STORAGE_KEY, responseData.id_token)
        })
        .done();
    }
},

 

登入用户成功后,会看到一个弹出窗口,同时保存后端验证通过返回的JWT。

如果用户登出退出,我们应该删除保存的JWT:

async  _userLogout() {
    try {
        await AsyncStorage.removeItem(STORAGE_KEY);
        AlertIOS.alert( "Logout Success!")
    }  catch (error) {
        console.log( 'AsyncStorage error: ' + error.message);
    }
},

 

好了,以上JWT的工作已经完成。

现在是输出一个登录页面让用户输入用户名和密码,页面有Signup按钮和Login按钮以及业务查询按钮:

render()
    {
       return (
        <View style={styles.container}>
            <View style={styles.row}>
                <Text style={styles.title}>Signup/Login below  for Chuck Norris Quotes!</Text
            </View>
            <View style={styles.row}>
                <Form
                    ref= "form" 
                    type={Person}
                    options={options}
                />
            </View>    
            <View style={styles.row}>
                <TouchableHighlight style={styles.button} onPress={ this._userSignup} underlayColor= '#99d9f4' > 
                    <Text style={styles.buttonText}>Signup</Text>
                </TouchableHighlight>
                <TouchableHighlight style={styles.button} onPress={ this._userLogin} underlayColor= '#99d9f4' > 
、                    <Text style={styles.buttonText}>Login</Text>
               </TouchableHighlight>
            </View>
            <View style={styles.row}>    
                <TouchableHighlight onPress={ this._getProtectedQuote} style={styles.button}>
                    <Text style={styles.buttonText}>Get a Chuck Norris Quote!</Text>
                </TouchableHighlight>
            </View>
        </View>
        );
    }
});

界面效果如下:

 

点击here 得到完整源码。

 

JSON Web Tokens(JWT)教程

React.JS专题

Javascript专题

 

 

 

 

 

解道移动版 | 关注解道 | 联系解道 | 关于解道 | 广告联系 | 网站地图 | 设为首页

沪ICP证12033263 如有意见请与我们联系 Powered by JdonFramework
返回顶部