• Как реализовать авторизацию пользователя на стеке React Redux Node?

    maxfarseer
    @maxfarseer
    https://maxpfrontend.ru, обучаю реакту и компании
    Пригодится jwt. (гугл)

    В общих словах: вам нужно выдать юзеру токен. С этим токеном юзер будет выполнять запросы к вашим защищенным методам api (вы можете оставить какие-то методы "без токена").

    Node.js часть
    Пример роута на логин (сделано не лучшим образом, в плане коллбэков, но что нашлось под рукой...):

    auth.js
    ...
    const v4 = require('node-uuid').v4
    const jwt = require('jsonwebtoken')
    ...
    router.post('/signin', (req, res, next) => {
    
      // валидация, например... 
    
      if (errors) {
        return res.status(400).json({ errors })
      } else {
        // поиск юзера в базе, сравнение хэша и прочие необходимые операции
        ...
       // допустим все ок, нужно ответить токеном
      // генерируем необходимые опции и сам токен
    
            const payload = {
              _id: user._id,
              iss: 'http://localhost:3000',
              permissions: 'poll',
            }
            const options = {
              expiresIn: '7d',
              jwtid: v4(),
            }
            const secret = new Buffer(process.env.AUTH0_CLIENT_SECRET, 'base64')
            jwt.sign(payload, secret, options, (err, token) => {
              // отвечаем токеном, для будущих запросов с клиента
              return res.json({ data: token })
            })
         ...
    })
    
    module.exports = router;


    Пример защищенного роута (метода, который требует токен):
    ...
    const jwt = require('jsonwebtoken')
    const Poll = require('../models/poll')
    ...
    
    const requireToken = (req,res,next) => {
      const token = req.headers['x-api-key']
      const secret = new Buffer(process.env.AUTH0_CLIENT_SECRET, 'base64')
    
      jwt.verify(token, secret, (err, decoded) => {
        if (err) {
          return res.status(401).json({ error: err.message })
        }
        req.params.userId = decoded._id
        next()
      })
    }
    ...
    
    // обратите внимание на requireToken - функция вызывается при каждом обращении к роуту
    // то есть при каждом PUT запросе по адресу (например: PUT api/v1/products/1238914)
    // будет вызываться requireToken
    
    router.put('/:id', requireToken, (req, res, next) => {
      const { productId } = req.body
      const userId = req.params.userId
    
      Poll.findById(req.params.id)
        .populate({ path: 'products' })
        .exec((err, poll) => {
            // ... необходимые действия в базе, в процессе успешного зачтения голоса...
        }
    })


    Клиентское приложение:

    Типичный ActionCreator (в качестве библиотеки для запросов можно взять bluebird, isomorphic-fetch, да хоть нативный xhr)
    actions/LoginActions.js

    export function login(data) {
      return dispatch => {
        dispatch({ type: LOGIN_REQUEST })
    
        // request в данном случае - https://github.com/KyleAMathews/superagent-bluebird-promise
        return request.post(`${API_ROOT_V1}/auth/signin`)
          .send(data)
          .then(res => {
            if (!res.ok) {
              dispatch({ type: LOGIN_FAILURE })
            } else {
              dispatch({
                type: LOGIN_SUCCESS,
                data: res.body.data,
              })
              //сохранение токена в localStorage
              localStorage.setItem('cks_token', res.body.data)
            }
          }, err => {
            dispatch({ type: LOGIN_FAILURE })
          })
      }
    }


    Пример голосования с токеном (выше, в node.js части был метод апи, а это PUT запрос с клиента)

    export function vote(productId, voteId) {
      return dispatch => {
        dispatch({ type: POLL_VOTE_REQUEST })
    
        return request.put(`${API_ROOT_V1}/api/v1/vote/${voteId}`)
          .set('X-API-Key', localStorage.getItem('cks_token')) // установка ТОКЕНА в заголовок 'X-API-Key'
          .send({ productId })
          .then(res => {
            if (!res.ok) {
              dispatch({ type: POLL_VOTE_FAILURE })
            } else {
              dispatch({
                type: POLL_VOTE_SUCCESS,
                data: normalize(res.body.data, schema.poll),
              })
            }
          }, err => {
            dispatch({ type: POLL_VOTE_FAILURE })
          })
      }
    }


    Надеюсь код формы с кнопкой "голосовать" не трубется.
    ====

    Все это можно организовать удобнее, например, выставлять токен всем запросам автоматически (речь про клиентскую часть):
    import fetch from 'isomorphic-fetch'
    ...
    const defaultHeaders = {
      Accept: 'application/json',
      'Content-Type': 'application/json',
    }
    
    function buildHeaders() {
      const authToken = localStorage.getItem('TOKEN')
      return { ...defaultHeaders, Authorization: authToken }
    }
    ...
    export function httpPost(url, data) {
      const body = JSON.stringify(data)
    
      return fetch(url, {
        method: 'post',
        headers: buildHeaders(),
        body: body,
      })
      .then(... код оработки запроса ...)
    }


    ===

    Проверить наличие токена, можно с в роутере, с помощью хука на onEnter.

    root.js
    ...
    <Provider store={store}>
          <Router history={routerHistory}>
            {configRoutes(store)} // Роуты создаются функцией, чтобы можно было использовать внутри нее store.getState()
          </Router>
        </Provider>
    ...


    routes.js
    export default function configRoutes(store) {
      function _ensureAuthenticated(nextState, replace, callback) {
        const { dispatch } = store
        const { session } = store.getState()
        const { currentUser } = session
        let nextUrl
    
        if (!currentUser && localStorage.getItem('cks.token')) {
          dispatch(getCurrentAccount())
        } else if (!localStorage.getItem('cks.token')) {
          replace('/signin')
        }
        callback()
      }
    
      return (
        <Route path='/' component={App}>
          <Route path='/signin' component={SigninContainer} />
          <Route path='/signup' component={SignupContainer} />
    
          <Route path='/locked' component={AuthenticatedContainer} onEnter={_ensureAuthenticated}>
            <Route component={LockedArea}>
              <Route path='/locked/a' component={A} />
              <Route path='/locked/b/:id' component={B} />
              <Route path='/locked/c' component={C} />
            </Route>
          </Route>
        </Route>
      )


    Остается только создать AuthenticatedContainer в котором проверять: есть ли currentUser (в рамках этого примера) и если нет - возвращать false. А если есть - this.props.children (в которых будут дальнейшие роуты..)
    ===

    Итого:
    1) у вас есть API написанное на node.js.
    2) У этого API есть защищенные методы, которые проверяют наличия токена в http-запросе. Токен выдается при запросе на логин. Само собой, операция логина (то есть POST запрос на your-api/login) не требует токена.
    3) После удачного логина, вы получаете в ответе на свой запрос токен (это уже внутри клиентского кода, и если мы говорим о redux - то внутри асинхронного вызова в actions)
    4) Сохраняете токен (например, в localStorage)
    5) Во все необходимые http-запросы устанавливаете заголовок с токеном
    Ответ написан
    15 комментариев
  • Как объективно протестировать производительность node.js и PHP в запросах к mysql БД?

    Как у нас на пхп. Пишешь строчки, выполняет по порядку. Сделал до конца одно, дальше. Как у нас с нодежс. Пишешь строчки и тоже по порядку, но не по такому порядку, как в php. К примеру у нас 2 обращения к базе. Они тоже будут идти по порядку, но почти одновременно, так как сразу после первого обращения к базе он не дожидаясь ответа обратится второй раз. Дожидаться ответа он уже будет в callback функции к обращению.

    PHP - спокойный тип, который спокойно выполняет задачи и не запутывается. Nodejs - карьерист амфетаминщик, который в любое свободное время (к примеру, пока ждет ответа от той же базы) будет выполнять другие свои обязанности.
    Ответ написан
    Комментировать
  • Как правильно организовать авторизацию в node.js?

    kostia256
    @kostia256
    Стоит понимать что принцип авторизации везде одинаков.
    1. Идентификация (проверка существование пользователя по login)
    2. Аутентификация (проверка пароля)
    3. Авторизация (проверка прав доступа)
    Сохраняете информацию о пользователе в сессии достаточную чтобы миновать этапы 1 и 2, и сразу переходить к этапу 3. Соответственно когда умирает сессия нужно повторно произвести все шаги авторизации.
    Ответ написан
    2 комментария
  • Как правильно организовать авторизацию в node.js?

    Revencu
    @Revencu
    1. Сначала сделайте простой запрос (без авторизации) для получения произвольного кода (на сервере генерируем случайный ключ и возвращаем)
    2. Полученный ключ (code) кодируем на клиенте через SHA256 (code+username+password)
    3. Делаем запрос авторизации отправляя шифрованный код
    4. На Сервере проверяем в базе комбинацию SHA256(ключ+полеUSER+полеPASSWORD). Если нашли - Вуаля! и убиваем ключ

    Таким образом будет отправляться всегда новый код для авторизации и снифферы не уловят ваши данные.
    Ответ написан
    Комментировать
  • Как с одного роутера отправить два запроса?

    @Coder321
    Делайте res.write и отлавливайте через EventSource на клиенте
    Ответ написан
    1 комментарий
  • Обьясните про пресеты бабеля, что такое stage- 0 ,1,2,3?

    0, 1, 2 я бы не стал использовать. А вот на счёт 3 можно подумать. Тут полное описание - https://tc39.github.io/process-document/
    Ответ написан
    Комментировать
  • Как получить читабельную верстку из Jade?

    Zoxon
    @Zoxon
    Веб-разработчик
    Используйте опцию pretty: true // 2 пробела '\t' табы
    jade-lang.com/api
    Можно дополнительно прогнать претифаером, по тому что миксины не правильно форматируются.
    Например gulp-prettify Как убрать лишние отступы при компиляции Jade в миксинах?
    Ответ написан
    Комментировать
  • Что указывать в package.json при глобальной установке пакетов npm?

    k12th
    @k12th
    console.log(`You're pulling my leg, right?`);
    Дык вы его установили глобально (-g), он и не записался в package.json. Надо так:
    grunt-contrib-cssmin --save-dev
    А чтобы grunt работал, надо поставить два пакета:
    npm install grunt-cli -g # обратите внимание, этот пакет ставится глобально и добавляет в систему команду grunt
    npm install grunt --save-dev # этот ставится локально и собственно выполняет работу

    (Если честно, то есть возможность и grunt-cli поставить локально, но пока этим не заморачивайтесь.)
    Ответ написан
    Комментировать
  • Как используя addEventListener передать параметр в функцию?

    @alexeyproject
    // Вариант 1. Привязываем контекст и первые по порядку параметры
    function someFunk(a, b, event) {
    	console.log(a, b, event);
    }
    element.addEventListener('click', someFunk.bind(null, 1, 2));
    
    // Вариант 2. Используем интерфейс EventListener
    function someFunk(event) {
    	console.log(this.a, this.b, event);
    }
    element.addEventListener('click', {handleEvent: someFunk, a: 1, b: 2});
    Ответ написан
    5 комментариев