国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

【單頁面博客從前端到后端】基于 Passport 和 Koa@2 的權(quán)限驗(yàn)證與 DVA 的 Mode

luodongseu / 1545人閱讀

摘要:我們就采用這種方式來進(jìn)行權(quán)限驗(yàn)證。這里我還是使用在中的下新增單頁面博客從前端到后端環(huán)境搭建單頁面博客從前端到后端基于搭建博客前后臺(tái)界面單頁面博客從前端到后端基于和的權(quán)限驗(yàn)證與的設(shè)計(jì)

基于 JWT 的權(quán)限驗(yàn)證

這里有一篇文章描述的已經(jīng)非常詳盡,闡述了 JWT 驗(yàn)證相比較傳統(tǒng)的持久化 session 驗(yàn)證的優(yōu)勢,以及基于 angularexpress 驗(yàn)證的簡單流程。

基于Json WebToken的權(quán)限驗(yàn)證

Passport 專注于用戶驗(yàn)證 Nodejs 庫

Passport 提供了多種的驗(yàn)證策略,如:

passport-http-bearer - 使用 Bearer tokens 對(duì) HTTP 請(qǐng)求做權(quán)限驗(yàn)證。這個(gè)最適合我們的項(xiàng)目不過了。

passport-local - 本地驗(yàn)證,普通的登陸驗(yàn)證,數(shù)據(jù)庫密碼驗(yàn)證成功即可。

此外還有 passport-github , passport-weixin , passport-qq , passport-weibo … ,這些你都可以在 官網(wǎng) 上找到。

我們就采用這種方式來進(jìn)行權(quán)限驗(yàn)證。

Koa@2 基本環(huán)境

首先需要注意的是使用 Koa@2,Node的版本需要 7.X的版本以上,而且啟動(dòng)時(shí)需要加上 --harmony 或者 —harmony-async-await
最近 Node 8.0 已經(jīng)上線,我直接采用的是 Node v8.0.0
nvm install 8.0.0
nvm alias default 8.0.0

blog/server基本的目錄結(jié)構(gòu)

server
├─ bin / www      # 入口文件
├─ config         # server配置文件
├─ controller     # 控制器文件夾
|  └─ user.js     
├─ lib            
|  ├─ auth.js     # 認(rèn)證邏輯
|  └─ db.js       # 數(shù)據(jù)庫 連接等
├─ models         # Mongoose Models
├─ routes         # Koa router
├─ utils          # 工具方法
├─ index.js       
└─ package.json  

我們?cè)谌肟谖募?server/bin/www 來連接 MongoDB

(async () => {
    // 測試連接 MongoDB
    try {
        const info = await connect(dbConfig)
        console.log(`Success to connect to MongoDB at ${info.host}:${info.port}/${info.name}`)
    } catch (err) {
        console.error(err)
        process.exit()
    }
    // 開啟服務(wù)進(jìn)程
    try {
        app.listen(port)
        console.log(`Server is running at http://localhost:${port}`)
    } catch (err) {
        console.error(err)
    }
})()

server/lib/db.js 下對(duì)應(yīng)的 connect 方法

exports.connect = function (config) {
    return new Promise((resolve, reject) => {
        mongoose.connection
            .on("error", err => reject(err))
            .on("close", () => console.log("MongoDB connection closed! "))
            .on("open", () => resolve(mongoose.connections[0]))
        mongoose.connect(`mongodb://${config.host}:${config.port}/${config.database}`, config.options)
    })
}

server/config/index.js 增加 MongoDB 的配置

const base = {
    admin: {
        username: "whistleyz",
        password: "admin123",
        email: "whistleyz@163.com",
        level: 51  // >50 超管
    }
}
const dev = Object.assign(base, {
    db: {
        host: "127.0.0.1",
        port: 27017,
        database: "fullblog",
        options: {
            user: "",
            pass: ""
        }
    }
})
const prod = Object.assign(base, {})
const env = process.env.NODE_ENV || "development"
const _config = {
    development: dev,
    production: prod
}
// 數(shù)據(jù)庫配置
module.exports = _config[env]

由于線上和我們開發(fā)甚至是測試環(huán)境,配置都會(huì)有些許不同,我們可以用 process.env.NODE_ENV 來區(qū)分這些配置

實(shí)現(xiàn)后端驗(yàn)證邏輯

新建 server/lib/auth.js

// serialize deserialize user objects into the session
passport.serializeUser((user, done) => done(null, user.username))
passport.deserializeUser(async (username, done) => {
    const user = await UserModel.findOne({username})
    done(null, user)
})
/**
 * 基于 Bearer、Local 的認(rèn)證方式 
 * 下面導(dǎo)出的路由中間件走的就是這里的邏輯 
 * passport-http-bearer 會(huì)自動(dòng)解析出 headers 中的 token
 * https://github.com/jaredhanson/passport-http-bearer/blob/master/lib/strategy.js#L89
 */
passport.use(new BearerStrategy(async (token, done) => {
    try {
        console.log(token)
        const accessToken = await AccessToken.findOne({token}).populate("user")
        accessToken ? done(null, accessToken.user) : done(null, false, {type: "error", message: "授權(quán)失敗!"})
    } catch (err) {
        done(err)
    }
}))

/**
 * 默認(rèn)從 req.body 或者 req.query 中取出 username, password 字段
 * https://github.com/jaredhanson/passport-local/blob/master/lib/strategy.js#L49
 */
passport.use(new LocalStrategy(async (username, password, done) => {
    try {
        const user = await UserModel.findOne({username})
        if (user && user.validPassword(password)) {
            done(null, user)
        } else {
            done(null, false)
        }
    } catch (err) {
        done(err)
    }
}))
// 導(dǎo)出中間件 
exports.isBearerAuthenticated = function () {
    return passport.authenticate("bearer", {session: false})
}
exports.isLocalAuthenticated = function () {
    return passport.authenticate("local", {session: false})
}
exports.passport = passport

新建 server/routes/api.js

const Router = require("koa-router")
const User = require("../controllers/user")
const { isBearerAuthenticated, isLocalAuthenticated } = require("../lib/auth")
const router = new Router()
router.use(async (ctx, next) => {
    try {
        await next()
    } catch (error) {
        console.error(error)
        ctx.status = 400
        ctx.body = {
            code: error.code,
            message: error.message || error.errmsg || error.msg || "unknown_error",
            error
        }
    }
})
// 初始化用戶數(shù)據(jù)
User.seed()
// Auth 認(rèn)證
router.post("/auth", isLocalAuthenticated(), User.signToken)
router.get("/auth", isBearerAuthenticated(), User.getUserByToken)
module.exports = router.routes()

那么我們?cè)?server/controller/user.js 下的處理邏輯久變得簡單:

// LocalStrategy 的中間件驗(yàn)證通過,會(huì)把 user 儲(chǔ)存在 req 中
exports.signToken = async function (ctx, next) {
    const { user } = ctx.req
    // 重新請(qǐng)求 token 需要?jiǎng)h除上一次生成的 token
    await TokenModel.findOneAndRemove({user: user._id})
    const result = await TokenModel.create({
        // md5加密
        token: genHash(user.username + Date.now()),
        user: user._id
    })
    ctx.status = 200
    ctx.body = {
        success: true,
        data: result
    }
}
// LocalStrategy 的中間件驗(yàn)證Token有效,會(huì)把 user 儲(chǔ)存在 req 中
exports.getUserByToken = async function (ctx, next) {
    ctx.status = 200
    ctx.body = {
        success: true,
        data: ctx.req.user
    }
}
// 當(dāng)數(shù)據(jù)庫中user表示空的時(shí)候,創(chuàng)建超級(jí)管理員
exports.seed = async function (ctx, next) {
    const users = await UserModel.find({})
    const adminInfo = config.admin
    if (users.length === 0) {
        const _admin = new UserModel(adminInfo)
        const adminUser = await _admin.save()
    }
}

我們可以借助 mongoose 還控制 Token 的壽命
比如設(shè)置 7 天后過期,expires: 60 * 60 * 24 * 7

到這里我們的后端邏輯基本實(shí)現(xiàn),為了和前端 webpack-dev-server 本地服務(wù)器進(jìn)行數(shù)據(jù)模擬,我們可以開啟 devServerproyx ,以及開啟 koa 的跨域支持

task/config

config.devServer = {
    hot: true,
    contentBase: path.resolve(__dirname, "../dist"),
    publicPath: "/",
    proxy: {
        "/api/v1": "http://localhost:8082"
    }
}

這樣,前端的任何 /api/v1 下的請(qǐng)求,都會(huì)被代理到 http://localhost:80828082 就是 koa 服務(wù)器的監(jiān)聽端口。

// koa 跨域
const logger = require("koa-logger")
const app = new koa()
app.use(kcors())
前端的登陸邏輯實(shí)現(xiàn) 實(shí)現(xiàn)一個(gè) dva model

在下一篇文章中,我們會(huì)深入 dva 的框架核心實(shí)現(xiàn)。我們先來看看 dav 的基本使用

新建 src/model/app.js

import { doLogin, getUserByToken } from "../service/app"
import { LocalStorage } from "../utils"
import { message } from "antd"
export default {
    namespace: "app",
    state: {
        isLogin: false,
        user: null
    },
    subscriptions: {},
    effects: {
        *checkToken({next}, {call, put}){
            const Token = LocalStorage.getItem("token")
            if (Token) {
                yield put({type: "loginSuccess"})
            } else {
                message.error("你還沒有登陸哦!")
            }
        },
        *doLogin({payload}, {call, put}){
            try {
                const { success, data } = yield call(doLogin, payload)
                if (success) {
                    LocalStorage.setItem("token", data.token)
                    yield put({type: "requireAuth"})
                }
            } catch (err) {
                message.error("授權(quán)失敗!")
                yield put({type: "authErr"})
            }
        },
        *getUserByToken({}, {call, put}){
            try {
                const { success, data } = yield call(getUserByToken)
                if (success) {
                    yield put({type: "authSuccess", payload: data})
                }
            } catch (err) {
                message.error(err.message)
                yield put({type: "authErr"})
            }
        }
    },
    reducers: {
        loginSuccess(state){
            return {
                ...state, isLogin: true
            }
        },
        authErr(state){
            return {
                ...state, isLogin: false, user: null
            }
        },
        authSuccess(state, {payload}){
            return {
                ...state, user: payload
            }
        }
    }
}

對(duì)于 redux-sagaeffect 等的用法,可以參考 文檔

這里我們對(duì) localStorage 做了一次封裝,看了源碼相信你就知道目的是什么了:

/**
 * src/utils/localStorage.js
 * Custom window.localStorage
 */
const STORE_PREFIX = "blog"
export function getItem (key) {
    return window.localStorage.getItem(STORE_PREFIX + "-" + key)
}
export function setItem (key, value) {
    window.localStorage.setItem(STORE_PREFIX + "-" + key, value)
}
export function removeItem (key) {
    window.localStorage.removeItem(STORE_PREFIX + "-" + key)
}

封裝 src/utils/request.js

import fetch from "dva/fetch"
import * as LocalStorage from "./localStorage"
const URL_PREFIX = "/api/v1"
const TOKEN_NAME = "token"
function checkStatus(response) {
    if (response.status >= 200 && response.status < 300) {
        return response;
    }
    const error = new Error(response.statusText);
    error.response = response;
    throw error;
}
/**
 * Requests a URL, returning a promise.
 *
 * @param  {string} url       The URL we want to request
 * @param  {object} [options] The options we want to pass to "fetch"
 * @return {object}           An object containing either "data" or "err"
 */
export default function request(url, options) {
    options = Object.assign({
        headers: new Headers({
            "Content-Type": "application/json"
        })
    }, options)
    return fetch(URL_PREFIX + url, options)
        .then(checkStatus)
        .then(res => res.json())
        .then(data => data)
}
/**
 * Request width token
 * @param  {[type]} url    
 * @param  {[type]} options
 * @return {[type]}        
 */
export function requestWidthToken (url, options) {
        const TOKEN = LocalStorage.getItem(TOKEN_NAME)
        options = Object.assign({
            headers: new Headers({
                "Content-Type": "application/json",
                "Authorization": `Bearer ${TOKEN}`
            })
        }, options)
        return request(url, options)
}

dva/fetch 直接導(dǎo)出了 fetch
fetch 的用法很簡單,參考 github地址

這里我們把 url 的 prefix、token name 提取出來用作常量保存,以便于我們修改,最好的方法是提取出來用一個(gè)文件保存

組件與model的通信

還記得我們的展示組件嗎,現(xiàn)在我們讓它 connect 到我們的 model

import { connect } from "dva"
const { Header, Content, Footer } = Layout
const { HeaderRight } = HeaderComponent
const App = ({children, routes, app, doLogin}) => {
    const { isLogin, user } = app
    return (
        
            
{isLogin ? : }
... ) } function mapStateToProps ({app}, ownProps) { return { app } } function mapDispatchToProps (dispatch) { return { doLogin({username, password}){ dispatch({type: "app/doLogin", payload: {username, password}}) } } } export default connect(mapStateToProps, mapDispatchToProps)(App)

唯一需要注意的就是actiontype 屬性了,如 app/doLogin 前綴 app 就是 dva.modelnamespace

從 dva/createDva.js at master · dvajs/dva · GitHub 中可以看到,dva 會(huì)把 model.namespace 最為 reducer , effects prefix 拼接

然后我們就可以在 LoginComponent 中,監(jiān)聽登陸的相應(yīng)事件來調(diào)用對(duì)應(yīng)的方法了。

小結(jié)

在寫后端的時(shí),難免遇到很多錯(cuò)誤,我們可以使用 supervisorpm2 來監(jiān)聽文件變動(dòng)來自動(dòng)重啟 nodejs 。鑒于后期我們會(huì)使用 pm2 部署項(xiàng)目。這里我還是使用 pm2

server/package.json 中的 scripts 下新增:
"start": "pm2 start bin/www --watch --name blog && pm2 log blog",

【單頁面博客從前端到后端】環(huán)境搭建

【單頁面博客從前端到后端】基于 DVA+ANTD 搭建博客前后臺(tái)界面

【單頁面博客從前端到后端】基于 Passport 和 Koa@2 的權(quán)限驗(yàn)證與 DVA 的 Model 設(shè)計(jì)

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/87064.html

相關(guān)文章

  • 頁面博客前端后端

    摘要:說到底,當(dāng)自己獨(dú)自開發(fā)從搭建開發(fā)環(huán)境,到前端的每一個(gè)組件,到動(dòng)作交互,再到和后端的數(shù)據(jù)交互,難免遇到不少問題。單頁面博客從前端到后端基于和的權(quán)限驗(yàn)證與的設(shè)計(jì)引入來實(shí)現(xiàn)富文本編輯器是開源的用于構(gòu)建富文本編輯器的框架。 不會(huì)后端的前端,不會(huì)寫單頁面應(yīng)用... 單頁面應(yīng)用的概念已經(jīng)被提出很長時(shí)間了,無論是基于 vue, angular 還是 react,相信大家或是耳濡目染,或是設(shè)身處地都有...

    whinc 評(píng)論0 收藏0
  • 頁面博客前端后端基于 DVA+ANTD 搭建博客前后臺(tái)界面

    摘要:在的的配置中添加自定義主題由腳手架和官網(wǎng)介紹,我們已經(jīng)自己配置并新建好了主題文件。單頁面博客從前端到后端環(huán)境搭建單頁面博客從前端到后端基于搭建博客前后臺(tái)界面單頁面博客從前端到后端基于和的權(quán)限驗(yàn)證與的設(shè)計(jì) 在上篇文章我們已經(jīng)搭建好了基礎(chǔ)的開發(fā)環(huán)境,接下來會(huì)介紹如何引入 DVA 和 ANTD ,以及在引入過程中需要注意的問題。這里只會(huì)詳細(xì)的書寫部分組件,其他的組件都是大同小異。你可以在 g...

    zqhxuyuan 評(píng)論0 收藏0
  • 頁面博客前端后端】環(huán)境搭建

    摘要:的配置其中就不多說會(huì)解決更改組件的時(shí)熱更新直接刷新頁面的問題。 工欲善其事,必先利其器。單頁面應(yīng)用的開發(fā)和生產(chǎn)環(huán)境涉及文件的編譯、壓縮、打包、合并等,目前前端最流行的莫過于 webpack 。為了深入了解 webpack 以及其相關(guān)插件,我們決定不采用腳手架,自己來搭建基于 webpack 的開發(fā)和生產(chǎn)環(huán)境。 基礎(chǔ)環(huán)境 nodejs的安裝: 移步官網(wǎng) 建議使用nvm來管理nodejs...

    wizChen 評(píng)論0 收藏0
  • 2017-06-13 前端日?qǐng)?bào)

    摘要:前端日?qǐng)?bào)點(diǎn)關(guān)注,不迷路精選前端團(tuán)隊(duì)工作流遷移記譯新語法私有屬性知乎專欄前端每周清單大前端技術(shù)生命周期模型發(fā)布面向生產(chǎn)環(huán)境的前端性能優(yōu)化模塊實(shí)現(xiàn)入門淺析知乎專欄中文一個(gè)線下沙龍中國最大的前端技術(shù)社區(qū)單頁面博客從前端到后端基于 2017-06-13 前端日?qǐng)?bào) 點(diǎn)關(guān)注,不迷路:-P 精選 ESLint v4.0.0 released - ESLint - Pluggable JavaScri...

    曹金海 評(píng)論0 收藏0
  • RN+dva+node+mongo+nginx+docker 開發(fā)到部署,全棧入坑指引!

    摘要:基本功能提供小說操作相關(guān)的所有提供登錄注冊(cè)相關(guān)實(shí)現(xiàn)驗(yàn)證碼定期自動(dòng)更新小說爬蟲部署運(yùn)行即可實(shí)現(xiàn)一鍵部署。如果還想更近一步的實(shí)現(xiàn)自動(dòng)部署的話,可以試試開源免費(fèi)。 項(xiàng)目地址 前言 作為一個(gè)優(yōu)秀前端er,除了要精通前端基礎(chǔ)外,其他的如后臺(tái),運(yùn)維,linux等都要有所了解。這樣你才能對(duì)自己所負(fù)責(zé)的項(xiàng)目有一個(gè)整體的把握,不同端開發(fā)思維的碰撞,有助于你形成良好的代碼習(xí)慣,寫出高效優(yōu)質(zhì)的代碼。話不多說...

    liaorio 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<