Оригинальный текст был опубликован на http://blog.ahui.me/posts/2018-03-26/permission-control-of-vuejs/
Среди многих приложений на стороне B, простые, как фон управления небольшим предприятием или крупномасштабная CMS, система CRM и управление разрешениями, являются главными приоритетами.Большинство прошлых веб-приложений использовали шаблоны на стороне сервера + сервер- в режиме боковой маршрутизации управление разрешениями естественным образом контролируется и фильтруется сервером.Однако в условиях разделения клиентской и серверной части, если принят режим разработки одностраничных приложений, клиентская часть неизбежно будет сотрудничать с сервером для управляйте разрешениями вместе. Взяв за пример разработку одностраничных приложений с vuejs, приводятся некоторые попытки, и я надеюсь дать вам некоторые идеи. Обратите внимание, что разделение интерфейса и сервера с использованием nodejs в качестве среднего уровня не входит в предмет данной статьи.
Цель
Что касается управления разрешениями, поскольку я не очень хорошо разбираюсь в сервере, я могу только обобщить свой предыдущий опыт работы с проектом, который может быть не очень точным.
Общее управление правами разделено на следующие части.
- Права на использование приложений
- Разрешения на уровне страницы
- Разрешения на уровне модуля
- Разрешения на уровне интерфейса
Далее приведенные выше части будут объяснены одна за другой. Полный код примера размещен вgithub-funkyLover/vue-permission-control-demoначальство.
Права на использование приложения — управление статусом входа и хранение
первый应用使用权
На самом деле, это просто оценка состояния входа в систему. Во многих приложениях на стороне C возможность использования дополнительных функций после входа в систему также может в определенной степени учитываться как часть управления разрешениями. В приложениях на стороне B это обычно показывает что вы не можете использовать его без входа в систему. (Конечно, вы также можете использовать такие функции, как восстановление пароля).
В прошлом состояние входа в систему обычно управлялось сеансом + файлом cookie/токеном. Пользователь приносил файл cookie/токен при открытии веб-страницы, которая оценивалась внутренней логикой и перенаправлялась. В режиме SPA переход на страницу был выполнен интерфейсной маршрутизацией. Контролируемая оценка статуса пользователя должна быть активно отправлена интерфейсной частью один раз.自动登录
запрос и переход в соответствии с возвращенным результатом.
это自动登录
Логику можно копать глубже, чтобы сделать различные реализации.Например, после успешного входа в систему информация о пользователе шифруется и распределяется между несколькими вкладками через локальное хранилище, так что при повторном открытии вкладки нет необходимости повторно -открой еще раз.自动登录
, Вот простейшая реализация для объяснения, основной процесс выглядит следующим образом:
- Пользователь запрашивает ресурсы страницы
- Проверьте, является ли локальный файл cookie / localstorage токеном
- Если токена нет, независимо от того, какой маршрут запрашивает пользователь, он всегда будет переходить на маршрут входа.
- Если токен проверен, сначала запросите
自动登录
интерфейс, в соответствии с возвращенным результатом, чтобы определить, следует ли вводить маршрут, запрошенный пользователем, или переходить к маршруту входа
Что касается суждения о статусе пользователя, как правило, оно должно основываться на进入login路由
(включая такие маршруты, как забытые пароли) и进入其他路由
Чтобы принять решение, исходя из vuejs@2.x, вы можете оценить статус пользователя и переключить маршрут на хуке маршрутизатора beforeEach.Некоторые коды приведены ниже:
const routes = [
{
path: '/',
component: Layout,
children: [
{
path: '',
name: 'Dashboard',
component: Dashboard
}, {
path: 'page1',
name: 'Page1',
component: Page1
}, {
path: 'page2',
name: 'Page2',
component: Page2
}
]
}, {
path: '/login',
name: 'Login',
component: Login
}
]
const router = new Router({
routes,
mode: 'history'
// 其他配置
})
router.beforeEach((to, from, next) => {
if (to.name === 'Login') {
// 当进入路由为login时,判断是否已经登录
if (store.getters.user.isLogin) {
// 如果已经登录,则进入功能页面
return next('/')
} else {
return next()
}
} else {
if (store.getters.user.isLogin) {
return next()
} else {
// 如果没有登录,则进入login路由
return next('/login')
}
}
})
После настройки логики перехода нам нужно проверить, есть ли токен в маршруте входа и выполнить автоматический вход.
// Login.vue
async mounted () {
var token = Cookie.get('vue-login-token')
if (token) {
var { data } = await axios.post('/api/loginByToken', {
token: token
})
if (data.ok) {
this[LOGIN]()
Cookie.set('vue-login-token', data.token)
this.$router.push('/')
} else {
// 登录失败逻辑
}
}
},
methods: {
...mapMutations([
LOGIN
]),
async login () {
var { data } = await axios.post('/api/login', {
username: this.username,
password: this.password
})
if (data.ok) {
this[LOGIN]()
Cookie.set('vue-login-token', data.token)
this.$router.push('/')
} else {
// 登录错误逻辑
}
}
}
Точно так же вы можете оставить токен пустым при выходе из системы.Обратите внимание, что приведенная здесь логика реализации является относительно грубой, и ее следует изменить в соответствии с фактическими потребностями.Например, при автоматическом входе в систему пользователь выдаются соответствующие подсказки, а логика чтения/хранения токена изменена, положить его в хранилище для единого управления, разобраться с устаревшей логикой токена и т.д.
Разрешения на уровне страницы — создание объектов маршрутизатора на основе разрешений
Здесь вы можете использоватьЭксклюзивная защита vue-router/routingДля обработки Основная идея состоит в том, чтобы установить хук-функцию beforeEnter в каждом маршруте, который должен проверять разрешения, и судить о разрешениях пользователя в нем.
const routes = [
{
path: '/',
component: Layout,
children: [
{
path: '',
name: 'Dashboard',
component: Dashboard
}, {
path: 'page1',
name: 'Page1',
component: Page1,
beforeEnter: (to, from, next) => {
// 这里检查权限并进行跳转
next()
}
}, {
path: 'page2',
name: 'Page2',
component: Page2,
beforeEnter: (to, from, next) => {
// 这里检查权限并进行跳转
next()
}
}
]
}, {
path: '/login',
name: 'Login',
component: Login
}
]
Приведенного выше кода достаточно, чтобы выполнить требования, а затем сотрудничать сvue-router/ленивая загрузка маршрутаТакже возможно реализовать, что ресурсы соответствующих компонентов страницы не будут загружены для маршрутов без разрешений, однако в приведенной выше реализации все еще есть некоторые проблемы.
- Когда права доступа к странице будут достаточно подробными, конфигурация маршрутизатора станет больше и сложнее в обслуживании.
- Всякий раз, когда правила разрешений страницы обновляются в фоновом режиме, логика оценки внешнего интерфейса также должна быть изменена, а это означает, что внешний и внутренний интерфейсы должны совместно поддерживать набор разрешений на уровне страницы.
Первую проблему все еще можно решить с помощью средств кодирования, таких как размещение логики в хуке beforeEach или абстрагирование логики проверки разрешений с помощью функций более высокого порядка, но вторая проблема неизбежна, если мы только серверная часть настраивает маршрутизацию. , а фронтенд расширяет роутер в соответствии с конфигурацией, возвращаемой бэкендом, чтобы не поддерживать набор логики во фронтенде и бэкенде.По этой идее мы переписываем предыдущую логику.
// Login.vue
async mounted () {
var token = Cookie.get('vue-login-token')
if (token) {
var { data } = await axios.post('/api/loginByToken', {
token: token
})
if (data.ok) {
this[LOGIN]()
Cookie.set('vue-login-token', data.token)
// 这里调用更新router的方法
this.updateRouter(data.routes)
}
}
},
// ...
methods: {
async updateRouter (routes) {
// routes是后台返回来的路由信息
const routers = [
{
path: '/',
component: Layout,
children: [
{
path: '',
name: 'Dashboard',
component: Dashboard
}
]
}
]
routes.forEach(r => {
routers[0].children.push({
name: r.name,
path: r.path,
component: () => routesMap[r.component]
})
})
this.$router.addRoutes(routers)
this.$router.push('/')
}
}
Таким образом, маршрут может быть динамически расширен в соответствии с возвратом бэкэнда.Конечно, меню навигации боковой или верхней панели также может быть сгенерировано в соответствии с возвратом бэкэнда, так что нет необходимости иметь дело с правами доступа к страницам во внешнем интерфейсе. В примере реализованы только самые основные функции, опущено много логики, которую можно оптимизировать.
- Каждый раз, когда вы открываете новую вкладку (маршрутизация без входа в систему)
自动登录
и перепродлить роутер - Каждый раз, когда вы открываете новую вкладку, она по-прежнему будет переходить на
/
маршрутизация, даже если вновь открытый URL-адрес/page1
Решение состоит в том, чтобы хранить информацию о входе пользователя и информацию о маршрутизации в локальном хранилище и напрямую генерировать объекты маршрутизатора с помощью информации, хранящейся в локальном хранилище, при открытии новой вкладки.store.jsа такжеvuex-shared-mutationsКласс плагинов может в некоторой степени упростить эту часть логики, и здесь он обсуждаться не будет.
Разрешения уровня модуля — разрешения компонента
Разрешения на уровне модуля легко понять. На самом деле это компоненты с оценкой разрешений. Очень просто и легко понять компоненты, которым необходимо фильтровать разрешения с помощью компонентов более высокого порядка в React. См. следующий пример.
const withAuth = (Comp, auth) => {
return class AuthComponent extends Component {
constructor(props) {
super(props);
this.checkAuth = this.checkAuth.bind(this)
}
checkAuth () {
const auths = this.props;
return auths.indexOf(auth) !== -1;
}
render () {
if (this.checkAuth()) {
<Comp { ...this.props }/>
} else {
return null
}
}
}
}
В приведенном выше примере показано, что компонент отображается при наличии разрешения, а скрытые компоненты могут быть определены в соответствии с различными требованиями фильтрации разрешений для работы с ним.
В vuejs вы можете использовать функцию рендеринга для достижения
// Auth.vue
import { mapGetters } from 'vuex'
export default {
name: 'Auth-Comp',
render (h) {
if (this.auths.indexOf(this.auth) !== -1) {
return this.$slots.default
} else {
return null
}
},
props: {
auth: String
},
computed: {
...mapGetters(['auths'])
}
}
// 使用
<Auth auth="canShowHello">
<Hello></Hello>
</Auth>
Функция рендеринга в vuejs предоставляет полные возможности программирования и даже может использовать синтаксис jsx в функции рендеринга, чтобы получить опыт разработки, близкий к React.документация vuejs/функции рендеринга и jsx.
Разрешения на уровне интерфейса
Разрешения на уровне интерфейса, как правило, не связаны с библиотекой пользовательского интерфейса. Вот краткое введение в то, как с ними работать.
- Во-первых, получите разрешение интерфейса API, к которому текущему пользователю разрешен доступ из бэкэнда.
- Настройте перехватчик внешней библиотеки запросов ajax (например, axios) в соответствии с возвращенным результатом.
- Оценка разрешений в перехватчике и подсказка пользователям в соответствии с их потребностями
axios.interceptors.request.use((config) => {
// 这里进行权限判断
if (/* 没有权限 */) {
return Promise.reject('no auth')
} else {
return config
}
}, err => {
return Promise.reject(err)
})
На самом деле, я лично считаю, что фронтенду не обязательно судить о разрешениях запрашиваемого апи, ведь интерфейс не похож на маршрут, и маршрутом теперь управляет фронтенд, а интерфейс необходимо пройти проверку сервера в конце.Его можно добавить по мере необходимости.
постскриптум
Это грязно, как работающая учетная запись, полный пример кода находится вgithub-funkyLover/vue-permission-control-demo, Если у вас есть какие-либо вопросы или комментарии, пожалуйста, оставьте комментарий, я буду смиренно преподавать.