细致想想,我们的后台系统还没有一个登录功能,太不靠谱,赶紧把防盗门安上!

SPA的鉴权方式和传统的web应用不同:因为页面的渲染不再依赖服务端,与服务端的交互都通过接口来完毕,而REASTful风格的接口提倡无状态(state less),通常不使用cookie和session来进行身份认证。

比較流行的一种方式是使用web token,所谓的token能够看作是一个标识身份的令牌。client在登录成功后能够获得服务端加密后的token,然后在兴许须要身份认证的接口请求中在header中带上这个token,服务端就能够通过推断token的有效性来验证该请求是否合法。

我们先来改造一下服务端。实现一个简单的基于token的身份认证(可直接复制代码。无需关心详细实现)。

改造服务端

先在根文件夹下运行npm i json-server -D,尽管一開始以全局的方式安装过json-server这个工具,但本次要在代码中使用json-server的api。须要将其安装为项目依赖。

然后新建/server/auth.js文件。写入下面代码:

const expireTime = 1000 * 60;

module.exports = function (req, res, next) {

res.header('Access-Control-Expose-Headers', 'access-token');

const now = Date.now();

let unauthorized = true;

const token = req.headers['access-token'];

if (token) {

const expired = now - token > expireTime;

if (!expired) {

unauthorized = false;

res.header('access-token', now);

}

}

if (unauthorized) {

res.sendStatus(401);

} else {

next();

}

};

新建/server/index.js文件。写入下面代码:

const path = require('path');

const jsonServer = require('json-server');

const server = jsonServer.create();

const router = jsonServer.router(path.join(__dirname, 'db.json'));

const middlewares = jsonServer.defaults();

server.use(jsonServer.bodyParser);

server.use(middlewares);

server.post('/login', function (req, res, next) {

res.header('Access-Control-Expose-Headers', 'access-token');

const {account, password} = req.body;

if (account === 'admin' && password === '123456') {

res.header('access-token', Date.now());

res.json(true);

} else {

res.json(false);

}

});

server.use(require('./auth'));

server.use(router);

server.listen(3000, function () {

console.log('JSON Server is running in http://localhost:3000');

});

改动/package.json文件里的scripts.server:

{

...

"scripts": {

"server": "node server/index.js",

...

},

...

}

然后使用npm run server重新启动服务器。

如今我们的服务器就拥有了身份认证的功能,訪问除了’/login’外的其他接口时,服务端会依据请求的header中access-token来推断请求是否有效,假设无效则会返回401状态码。

当client收到401的状态码时,须要跳转到登录页面进行登录,有效的管理员账号为admin。密码为123456。

以POST方法提交下面的參数到’http://localhost:3000/login‘接口。就能够完毕登录。

{

"account": "admin",

"password": "123456"}

登录成功后,接口返回true,而且在返回的headers中包括了一个有效的access-token,用于在后面的请求中使用;登录失败则返回false。

access-token的有效期为1分钟,每次有效的接口请求都会获得新的access-token;若1分钟内没有做操作,则会过期须要又一次登录。

我们的access-token仅仅是一个简单的timestamp。且没有做不论什么加密措施。

封装fetch

因为我们每一个接口的请求都须要加上一个名为access-token的header,在每次须要调用接口的时候都写一遍就很的不明智了。所以我们须要封装fetch方法。

新建/src/utils/request.js,写入下面代码:

import { hashHistory } from 'react-router';

export default function request (method, url, body) {

method = method.toUpperCase();

if (method === 'GET') {

// fetch的GET不同意有body,參数仅仅能放在url中

body = undefined;

} else {

body = body && JSON.stringify(body);

}

return fetch(url, {

method,

headers: {

'Content-Type': 'application/json',

'Accept': 'application/json',

'Access-Token': sessionStorage.getItem('access_token') || '' // 从sessionStorage中获取access token

},

body

})

.then((res) => {

if (res.status === 401) {

hashHistory.push('/login');

return Promise.reject('Unauthorized.');

} else {

const token = res.headers.get('access-token');

if (token) {

sessionStorage.setItem('access_token', token);

}

return res.json();

}

});

}

export const get = url => request('GET', url);

export const post = (url, body) => request('POST', url, body);

export const put = (url, body) => request('PUT', url, body);

export const del = (url, body) => request('DELETE', url, body);

request方法封装了加入access-token头等逻辑,然后就能够在须要调用接口的时候使用request或get、post等方法了,比方/src/components/BookEditor.js:

...

import request, {get} from '../utils/request';

class BookEditor extends React.Component {

...

handleSubmit (e) {

...

let editType = '加入';

let apiUrl = 'http://localhost:3000/book';

let method = 'post';

if (editTarget) {

...

}

request(method, apiUrl, {

name: name.value,

price: price.value,

owner_id: owner_id.value

})

.then((res) => {

if (res.id) {

...

} else {

...

}

})

.catch((err) => console.error(err));

}

getRecommendUsers (partialUserId) {

get('http://localhost:3000/user?id_like=' + partialUserId)

.then((res) => {

if (res.length === 1 && res[0].id === partialUserId) {

return;

}

...

});

}

...

}

...

其他还有/src/components/UserEditor.js、/src/pages/BookEdit.js、/src/pages/BookList.js、/src/pages/UserEdit.js和/src/pages/UserList.js文件须要进行对应的改动。

实现登录页面

如今尝试訪问一下用户列表页。发现表格里面并没有数据。因为没有登录接口訪问被拒绝了而且尝试跳转到路由’/login’。

如今来实现一个登录页面组件。在/src/pages下新建Login.js文件;

import React from 'react';

import HomeLayout from '../layouts/HomeLayout';

import FormItem from '../components/FormItem';

import { post } from '../utils/request';

import formProvider from '../utils/formProvider';

class Login extends React.Component {

constructor () {

super();

this.handleSubmit = this.handleSubmit.bind(this);

}

handleSubmit (e) {

e.preventDefault();

const {formValid, form: {account, password}} = this.props;

if (!formValid) {

alert('请输入账号或密码');

return;

}

post('http://localhost:3000/login', {

account: account.value,

password: password.value

})

.then((res) => {

if (res) {

this.context.router.push('/');

} else {

alert('登录失败,账号或密码错误');

}

})

}

render () {

const {form: {account, password}, onFormChange} = this.props;

return (

onFormChange('account', e.target.value)}/>

onFormChange('password', e.target.value)}/>

);

}

}

Login.contextTypes = {

router: React.PropTypes.object.isRequired

};

Login = formProvider({

account: {

defaultValue: '',

rules: [

{

pattern (value) {

return value.length > 0;

},

error: '请输入账号'

}

]

},

password: {

defaultValue: '',

rules: [

{

pattern (value) {

return value.length > 0;

},

error: '请输入密码'

}

]

}

})(Login);

export default Login;

登录页面组件和UserEditor或者BookEditor相似,都是一个表单。

在这里提交表单成功后跳转到首页。

最后,别忘了加上登录页面的路由。

终于效果

react 将token充入_【React全家桶入门之十】登录与身份认证相关推荐

  1. React 全家桶入门教程 01

    React 全家桶入门教程 01 前面是基础课程(难度小,略过),后面是案例 目的 巩固react基础知识,查漏补缺(熟悉的部分快进) 学习相关的库的使用 https://study.163.com/ ...

  2. Spring Cloud 全家桶 入门介绍

    Spring Cloud 全家桶 入门介绍 Spring Cloud为开发人员提供了工具,以快速构建分布式系统中的一些常见模式(例如,配置管理,服务发现,断路器,智能路由,微代理,控制总线,一次性令牌 ...

  3. Spring全家桶-Spring Security之自定义数据库表认证和鉴权

    Spring全家桶-Spring Security之自定义数据库表认证和鉴权 Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架.它提供 ...

  4. win10清理_卸载全家桶之后:用win10自带杀软,怎么清理电脑垃圾?

    日常使用电脑时,难免会产生许多缓存和下载文件,这些文件占用电脑空间,会对电脑性能产生一定的影响. 在windows10之后,其系统自带的杀毒软件已经能够满足普通用户的安全需求了,所以很多人选择只使用w ...

  5. Vue全家桶入门精细讲解

    Vue入门精细讲解 感谢coderwhy老师的精心讲解,本笔记全部内容源于coderwhy老师的课堂笔记: 一. Hello Vuejs 1.1. 认识Vuejs 为什么学习Vuejs 可能你的公司正 ...

  6. 【React全家桶入门之九】图书管理与自动完成

    图书管理 还记得搭建项目的时候在db.json文件里写的book吗? 来回顾一下book这个"数据表"的结构: {..."book": [{"id&q ...

  7. 如何将java清除干净_干掉全家桶!小编教你干干净净用Java

    现在很多移动应用软件,都是通过Java进行开发的,因此很多人的电脑上也都安装了Java.不过最近Java和国内某网络厂商进行合作,在安装Java的时候会推广一系列的软件.本文的方法,即教大家如何杜绝这 ...

  8. pandas pivot 占比_数据处理进阶pandas入门(十八)

    回顾 在数据处理进阶pandas入门(十七)中,我们介绍了pandas中对groupby()方法的分组数据处理的两个方法:transform()和apply().我们需要掌握好这两个方法,对分组数据进 ...

  9. java spring 登录验证_详解使用Spring3 实现用户登录以及权限认证

    使用Spring3 实现用户登录以及权限认证 这里我就简单介绍一下,我在实现的时候处理的一些主要的实现. 1.用户登录 用户名 密 码 © 2013 - 2014 | 以上是前台页面,后台的就是一个简 ...

最新文章

  1. Springboot 多文件上传
  2. uniapp中qrcode生成二维码后传的参数不见了_阿虚教你制作动态二维码,超详细教程!
  3. php 查看init,php 中init log
  4. Omi框架学习之旅 - 插件机制之omi-touch 及原理说明
  5. 程序的内存模型—内存四区—堆区
  6. java怎样生成32位全是整形的主键_你肯定会需要的分布式Id生成算法雪花算法(Java)...
  7. stm32 图像处理_假如STM32也有朋友圈
  8. 一图胜千言,这本交互式线代教科书让你分分钟理解复杂概念,佐治亚理工出品...
  9. 搞个这样的APP要多久? (转)
  10. Julia: 奇技淫巧......
  11. 英雄联盟轮播图手动轮播
  12. 如何在 MAC 电脑上查找 IP 地址
  13. 视频融合技术解决方案,三维全景拼接赋能平台
  14. python创建空字典什么意思_Python3基础 dict 创建字典 空字典
  15. 【HTML】人生苦短, 快卷快卷 第二课 HTML 基础
  16. swagger2报错Illegal DefaultValue null for parameter type integer
  17. 文献学习(part31)--Discovery of time-inconsecutive co-movement patterns of foreign currencies using ...
  18. 三、Linux文件管理
  19. 蓝牙模块芯片型号有哪些?国产还是进口?核心指标有哪些
  20. Fiddler抓包工具配置--IE、Chrome

热门文章

  1. 蓝屏分析_电脑突发蓝屏现象?教你如何快速修复
  2. android id 重名_android - 解决“应用自定义权限重名”
  3. canvas 在其他画好的上面继续画_详解canvas绘制多张图的排列顺序问题
  4. sqlbulkcopy能用于mysql吗_混凝土自动上料搅拌车能用于农村盖房吗?
  5. java spring源码_spring源码分析-spring中的bean
  6. java timmer模式配置_使用java配置定时任务的几种配置方式及示例
  7. Hibernate中的sql的所有的查询
  8. 基于JAVA+SpringMVC+MYSQL的网上选课系统
  9. 炒菜机器人放食材的顺序_2年要开1000家 碧桂园旗下千玺机器人餐厅开业
  10. java网页截图_Java实现的简单网页截屏功能示例