摘要:我們就采用這種方式來進行權限驗證。這里我還是使用在中的下新增單頁面博客從前端到后端環境搭建單頁面博客從前端到后端基于搭建博客前后臺界面單頁面博客從前端到后端基于和的權限驗證與的設計
基于 JWT 的權限驗證
這里有一篇文章描述的已經非常詳盡,闡述了 JWT 驗證相比較傳統的持久化 session 驗證的優勢,以及基于 angular 和 express 驗證的簡單流程。
Passport 專注于用戶驗證 Nodejs 庫基于Json WebToken的權限驗證
Passport 提供了多種的驗證策略,如:
passport-http-bearer - 使用 Bearer tokens 對 HTTP 請求做權限驗證。這個最適合我們的項目不過了。
passport-local - 本地驗證,普通的登陸驗證,數據庫密碼驗證成功即可。
此外還有 passport-github , passport-weixin , passport-qq , passport-weibo … ,這些你都可以在 官網 上找到。
我們就采用這種方式來進行權限驗證。
Koa@2 基本環境首先需要注意的是使用 Koa@2,Node的版本需要 7.X的版本以上,而且啟動時需要加上 --harmony 或者 —harmony-async-await
最近 Node 8.0 已經上線,我直接采用的是 Node v8.0.0
nvm install 8.0.0
nvm alias default 8.0.0
blog/server基本的目錄結構
server ├─ bin / www # 入口文件 ├─ config # server配置文件 ├─ controller # 控制器文件夾 | └─ user.js ├─ lib | ├─ auth.js # 認證邏輯 | └─ db.js # 數據庫 連接等 ├─ models # Mongoose Models ├─ routes # Koa router ├─ utils # 工具方法 ├─ index.js └─ package.json
我們在入口文件處 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() } // 開啟服務進程 try { app.listen(port) console.log(`Server is running at http://localhost:${port}`) } catch (err) { console.error(err) } })()
server/lib/db.js 下對應的 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 } // 數據庫配置 module.exports = _config[env]
實現后端驗證邏輯由于線上和我們開發甚至是測試環境,配置都會有些許不同,我們可以用 process.env.NODE_ENV 來區分這些配置
新建 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 的認證方式 * 下面導出的路由中間件走的就是這里的邏輯 * passport-http-bearer 會自動解析出 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: "授權失敗!"}) } catch (err) { done(err) } })) /** * 默認從 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) } })) // 導出中間件 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 } } }) // 初始化用戶數據 User.seed() // Auth 認證 router.post("/auth", isLocalAuthenticated(), User.signToken) router.get("/auth", isBearerAuthenticated(), User.getUserByToken) module.exports = router.routes()
那么我們在 server/controller/user.js 下的處理邏輯久變得簡單:
// LocalStrategy 的中間件驗證通過,會把 user 儲存在 req 中 exports.signToken = async function (ctx, next) { const { user } = ctx.req // 重新請求 token 需要刪除上一次生成的 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 的中間件驗證Token有效,會把 user 儲存在 req 中 exports.getUserByToken = async function (ctx, next) { ctx.status = 200 ctx.body = { success: true, data: ctx.req.user } } // 當數據庫中user表示空的時候,創建超級管理員 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 的壽命
比如設置 7 天后過期,expires: 60 * 60 * 24 * 7
到這里我們的后端邏輯基本實現,為了和前端 webpack-dev-server 本地服務器進行數據模擬,我們可以開啟 devServer 的 proyx ,以及開啟 koa 的跨域支持
task/config :
config.devServer = { hot: true, contentBase: path.resolve(__dirname, "../dist"), publicPath: "/", proxy: { "/api/v1": "http://localhost:8082" } }
這樣,前端的任何 /api/v1 下的請求,都會被代理到 http://localhost:8082 而 8082 就是 koa 服務器的監聽端口。
// koa 跨域 const logger = require("koa-logger") const app = new koa() app.use(kcors())前端的登陸邏輯實現 實現一個 dva model
在下一篇文章中,我們會深入 dva 的框架核心實現。我們先來看看 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("授權失敗!") 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 } } } }
對于 redux-saga 的 effect 等的用法,可以參考 文檔
這里我們對 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 直接導出了 fetch
fetch 的用法很簡單,參考 github地址
這里我們把 url 的 prefix、token name 提取出來用作常量保存,以便于我們修改,最好的方法是提取出來用一個文件保存
組件與model的通信還記得我們的展示組件嗎,現在我們讓它 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 (... ) } 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) {isLogin ? : }
唯一需要注意的就是action 的 type 屬性了,如 app/doLogin 前綴 app 就是 dva.model 的 namespace
從 dva/createDva.js at master · dvajs/dva · GitHub 中可以看到,dva 會把 model.namespace 最為 reducer , effects 的 prefix 拼接
然后我們就可以在 LoginComponent 中,監聽登陸的相應事件來調用對應的方法了。
小結在寫后端的時,難免遇到很多錯誤,我們可以使用 supervisor 、pm2 來監聽文件變動來自動重啟 nodejs 。鑒于后期我們會使用 pm2 部署項目。這里我還是使用 pm2
在 server/package.json 中的 scripts 下新增:
"start": "pm2 start bin/www --watch --name blog && pm2 log blog",
【單頁面博客從前端到后端】環境搭建
【單頁面博客從前端到后端】基于 DVA+ANTD 搭建博客前后臺界面
【單頁面博客從前端到后端】基于 Passport 和 Koa@2 的權限驗證與 DVA 的 Model 設計
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/87064.html
摘要:說到底,當自己獨自開發從搭建開發環境,到前端的每一個組件,到動作交互,再到和后端的數據交互,難免遇到不少問題。單頁面博客從前端到后端基于和的權限驗證與的設計引入來實現富文本編輯器是開源的用于構建富文本編輯器的框架。 不會后端的前端,不會寫單頁面應用... 單頁面應用的概念已經被提出很長時間了,無論是基于 vue, angular 還是 react,相信大家或是耳濡目染,或是設身處地都有...
摘要:在的的配置中添加自定義主題由腳手架和官網介紹,我們已經自己配置并新建好了主題文件。單頁面博客從前端到后端環境搭建單頁面博客從前端到后端基于搭建博客前后臺界面單頁面博客從前端到后端基于和的權限驗證與的設計 在上篇文章我們已經搭建好了基礎的開發環境,接下來會介紹如何引入 DVA 和 ANTD ,以及在引入過程中需要注意的問題。這里只會詳細的書寫部分組件,其他的組件都是大同小異。你可以在 g...
摘要:的配置其中就不多說會解決更改組件的時熱更新直接刷新頁面的問題。 工欲善其事,必先利其器。單頁面應用的開發和生產環境涉及文件的編譯、壓縮、打包、合并等,目前前端最流行的莫過于 webpack 。為了深入了解 webpack 以及其相關插件,我們決定不采用腳手架,自己來搭建基于 webpack 的開發和生產環境。 基礎環境 nodejs的安裝: 移步官網 建議使用nvm來管理nodejs...
摘要:前端日報點關注,不迷路精選前端團隊工作流遷移記譯新語法私有屬性知乎專欄前端每周清單大前端技術生命周期模型發布面向生產環境的前端性能優化模塊實現入門淺析知乎專欄中文一個線下沙龍中國最大的前端技術社區單頁面博客從前端到后端基于 2017-06-13 前端日報 點關注,不迷路:-P 精選 ESLint v4.0.0 released - ESLint - Pluggable JavaScri...
摘要:基本功能提供小說操作相關的所有提供登錄注冊相關實現驗證碼定期自動更新小說爬蟲部署運行即可實現一鍵部署。如果還想更近一步的實現自動部署的話,可以試試開源免費。 項目地址 前言 作為一個優秀前端er,除了要精通前端基礎外,其他的如后臺,運維,linux等都要有所了解。這樣你才能對自己所負責的項目有一個整體的把握,不同端開發思維的碰撞,有助于你形成良好的代碼習慣,寫出高效優質的代碼。話不多說...
閱讀 1155·2021-10-15 09:39
閱讀 3053·2021-09-10 10:50
閱讀 3455·2019-08-30 15:53
閱讀 1878·2019-08-30 15:52
閱讀 2565·2019-08-29 15:31
閱讀 1978·2019-08-26 13:43
閱讀 2594·2019-08-26 13:37
閱讀 1445·2019-08-23 18:31