【React全家桶入门之十】登录与身份认证
2017-08-18 20:05
459 查看
细致想想,我们的后台系统还没有一个登录功能,太不靠谱,赶紧把防盗门安上!
SPA的鉴权方式和传统的web应用不同:因为页面的渲染不再依赖服务端,与服务端的交互都通过接口来完毕,而REASTful风格的接口提倡无状态(state less),通常不使用cookie和session来进行身份认证。
比較流行的一种方式是使用web token,所谓的token能够看作是一个标识身份的令牌。client在登录成功后能够获得服务端加密后的token,然后在兴许须要身份认证的接口请求中在header中带上这个token,服务端就能够通过推断token的有效性来验证该请求是否合法。
我们先来改造一下服务端。实现一个简单的基于token的身份认证(可直接复制代码。无需关心详细实现)。
然后新建
新建
改动
然后使用
如今我们的服务器就拥有了身份认证的功能,訪问除了’/login’外的其他接口时,服务端会依据请求的header中access-token来推断请求是否有效,假设无效则会返回401状态码。
当client收到401的状态码时,须要跳转到登录页面进行登录,有效的管理员账号为admin。密码为123456。
以POST方法提交下面的參数到’http://localhost:3000/login‘接口。就能够完毕登录。
登录成功后,接口返回
access-token的有效期为1分钟,每次有效的接口请求都会获得新的access-token;若1分钟内没有做操作,则会过期须要又一次登录。
我们的access-token仅仅是一个简单的timestamp。且没有做不论什么加密措施。
新建
request方法封装了加入access-token头等逻辑,然后就能够在须要调用接口的时候使用request或get、post等方法了,比方
其他还有
如今来实现一个登录页面组件。在
登录页面组件和UserEditor或者BookEditor相似,都是一个表单。
在这里提交表单成功后跳转到首页。
最后,别忘了加上登录页面的路由。
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 ( <HomeLayout title="请登录"> <form onSubmit={this.handleSubmit}> <FormItem label="账号:" valid={account.valid} error={account.error}> <input type="text" value={account.value} onChange={e => onFormChange('account', e.target.value)}/> </FormItem> <FormItem label="密码:" valid={password.valid} error={password.error}> <input type="password" value={password.value} onChange={e => onFormChange('password', e.target.value)}/> </FormItem> <br/> <input type="submit" value="登录"/> </form> </HomeLayout> ); } } 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相似,都是一个表单。
在这里提交表单成功后跳转到首页。
最后,别忘了加上登录页面的路由。
终于效果
![](https://oscdn.geek-share.com/Uploads/Images/Content/201708/a6cc94b7b0e819c3c64164c28f931023.gif)
相关文章推荐
- 【React全家桶入门之十】登录与身份认证
- 【React全家桶入门之十】登录与身份认证
- 【React全家桶入门之十】登录与身份认证
- react 项目实战(九)登录与身份认证
- 【React全家桶入门之四】加入表单验证
- Nodejs进阶:express+session实现简易登录身份认证
- SQLserver2005以身份认证方式登录的一些配置
- MS SQL入门基础:用户和安全性管理--SQL Server的登录认证
- React Native入门(五)之使用输入框TextInput,按钮Button搭建登录界面
- Kerberos身份认证在域用户工作站登录中的应用 推荐
- Kerberos身份认证在域用户工作站登录中的应用
- 单点登录(SSO)的核心--kerberos身份认证协议技术参考(一)
- Asp.net2.0身份验证技术学习入门---新用户注册,登录,显示用户信息
- React学习笔记_基于Cookie的登录认证
- 通用权限管理系统组件 (GPM - General Permissions Manager) 中实现统一身份认证(Single Sign On,单点登录)附源码
- shiro,基于springboot,基于前后端分离,从登录认证到鉴权,从入门到放弃
- shiro入门实战笔记(3)--身份认证
- 【React全家桶入门之四】加入表单验证
- SQLserver2005以身份认证方式登录的一些配置
- Microsoft ADFS+Shibboleth配置联合身份认证+单点登录服务