Adonis.js RESTFUL API解决方法

时间:2017-10-16 06:41:10

标签: javascript restful-authentication adonis.js

我最近开始在adonisjs框架上开发一个应用程序。我有一个选择使用expressjs但我更喜欢adonisjs因为我喜欢它的结构(主要是laravel风格)。

我目前正在尝试构建一个RESTFUL API但无法弄清楚基本的路由/中间件/ apiController(我的自定义控制器来处理所有api请求)场景。

这是我到目前为止所做的:

routes.js

Route.post('api/v1/login', 'ApiController.login')
Route.post('api/v1/register', 'ApiController.register')

// API Routes
Route.group('api', function() {

  Route.get('users', 'ApiController.getUsers')

}).prefix('/api/v1').middlewares(['auth:api'])

ApiController.js

'use strict'

const User = use('App/Model/User')
const Validator = use('Validator')

const FAIL = 0
const SUCCESS = 1

class ApiController {

  * login (request, response) {

    let jsonResponse = {}

    const email = request.input('email')
    const password = request.input('password')

    // validate form input
    const rules = {
      email: 'required|email',
      password: 'required'
    }

    const messages = {
      'email.required': 'Email field is required.',
      'password.required': 'Password field is required.'
    }

    const validation = yield Validator.validateAll(request.all(), rules, messages)

    if (validation.fails()) {

      jsonResponse.status = FAIL
      jsonResponse.response = {}
      jsonResponse.response.message = validation.messages()[0].message

    } else {

      try {

        yield request.auth.attempt(email, password)

        const user = yield User.findBy('email', email)

        const token = yield request.auth.generate(user)

        jsonResponse.status = SUCCESS
        jsonResponse.response = {}
        jsonResponse.response.message = "Logged In Successfully"
        jsonResponse.response.user = user
        jsonResponse.response.token = token

      } catch (e) {

        jsonResponse.status = FAIL
        jsonResponse.response = {}
        jsonResponse.response.message = e.message

      }

    }

    return response.json(jsonResponse)

  }

}

module.exports = ApiController

配置/ auth.js

'use strict'

const Config = use('Config')

module.exports = {

  /*
  |--------------------------------------------------------------------------
  | Authenticator
  |--------------------------------------------------------------------------
  |
  | Authenticator is a combination of HTTP Authentication scheme and the
  | serializer to be used for retrieving users. Below is the default
  | authenticator to be used for every request.
  |
  | Available Schemes - basic, session, jwt, api
  | Available Serializers - Lucid, Database
  |
  */
  authenticator: 'session',

  /*
  |--------------------------------------------------------------------------
  | Session Authenticator
  |--------------------------------------------------------------------------
  |
  | Session authenticator will make use of sessions to maintain the login
  | state for a given user.
  |
  */
  session: {
    serializer: 'Lucid',
    model: 'App/Model/User',
    scheme: 'session',
    uid: 'email',
    password: 'password'
  },

  /*
  |--------------------------------------------------------------------------
  | Basic Auth Authenticator
  |--------------------------------------------------------------------------
  |
  | Basic Authentication works on Http Basic auth header.
  |
  */
  basic: {
    serializer: 'Lucid',
    model: 'App/Model/User',
    scheme: 'basic',
    uid: 'email',
    password: 'password'
  },

  /*
  |--------------------------------------------------------------------------
  | JWT Authenticator
  |--------------------------------------------------------------------------
  |
  | Jwt authentication works with a payload sent with every request under
  | Http Authorization header.
  |
  */
  jwt: {
    serializer: 'Lucid',
    model: 'App/Model/User',
    scheme: 'jwt',
    uid: 'email',
    password: 'password',
    secret: Config.get('app.appKey')
  },

  /*
  |--------------------------------------------------------------------------
  | API Authenticator
  |--------------------------------------------------------------------------
  |
  | Api authenticator authenticates are requests based on Authorization
  | header.
  |
  | Make sure to define relationships on User and Token model as defined
  | in documentation
  |
  */
  api: {
    serializer: 'Lucid',
    model: 'App/Model/Token',
    scheme: 'api'
  }

}

配置/ shield.js

'use strict'

module.exports = {
  /*
  |--------------------------------------------------------------------------
  | Content Security Policy
  |--------------------------------------------------------------------------
  |
  | Content security policy filters out the origins not allowed to execute
  | and load resources like scripts, styles and fonts. There are wide
  | variety of options to choose from.
  | @examples
  | directives: {
  |   defaultSrc: ['self', '@nonce', 'cdnjs.cloudflare.com']
  | }
  */
  csp: {
    directives: {
    },
    reportOnly: false,
    setAllHeaders: false,
    disableAndroid: true
  },

  /*
  |--------------------------------------------------------------------------
  | X-XSS-Protection
  |--------------------------------------------------------------------------
  |
  | X-XSS Protection saves from applications from XSS attacks. It is adopted
  | by IE and later followed by some other browsers.
  |
  */
  xss: {
    enabled: true,
    enableOnOldIE: false
  },

  /*
  |--------------------------------------------------------------------------
  | Iframe Options
  |--------------------------------------------------------------------------
  |
  | xframe defines whether or not your website can be embedded inside an
  | iframe. Choose from one of the following options.
  | @available options
  | DENY, SAMEORIGIN, ALLOW-FROM http://example.com
  */
  xframe: 'DENY',

  /*
  |--------------------------------------------------------------------------
  | No Sniff
  |--------------------------------------------------------------------------
  |
  | Browsers have a habit of sniffing content-type of a response. Which means
  | files with .txt extension containing Javascript code will be executed as
  | Javascript. You can disable this behavior by setting nosniff to false.
  |
  */
  nosniff: true,

  /*
  |--------------------------------------------------------------------------
  | No Open
  |--------------------------------------------------------------------------
  |
  | IE users can execute webpages in the context of your website, which is
  | a serious security risk. Below options will manage this for you.
  |
  */
  noopen: true,

  /*
  |--------------------------------------------------------------------------
  | CSRF Protection
  |--------------------------------------------------------------------------
  |
  | CSRF Protection adds another layer of security by making sure, actionable
  | routes does have a valid token to execute an action.
  |
  */
  csrf: {
    enable: true,
    methods: ['POST', 'PUT', 'DELETE'],
    filterUris: ['/api/v1/login', '/api/v1/register'],
    compareHostAndOrigin: true
  }

}

现在我点击登录网络服务(使用邮递员)。它验证用户,但在const token = request.auth.generate(user)处抛出异常,并说request.auth.generate is not a function

我不知道发生了什么事。请帮忙。

由于

1 个答案:

答案 0 :(得分:2)

您需要生成一个JWT令牌(当用户调用登录api调用时)并将其发送回来,以便请求登录服务的应用程序可以存储它并使用它来进行将来的请求(使用"授权和# 34;带有值的标题"承载[JWT令牌字符串]")。当app发送另一个请求,即获取业务类别列表(在其标题中包含JWT令牌)时,我们将在api中间件中验证该请求。验证请求后,我们将提供请求并以json格式发回数据。

这是您的标题的样子:

image

这是您在代码中实际需要做的事情:

// ROUTES.JS

// API Routes
Route.post('/api/v1/register', 'ApiController.register')
Route.post('/api/v1/login', 'ApiController.login')

Route.group('api', function() {

  Route.get('/business_categories', 'ApiController.business_categories')

}).prefix('/api/v1').middlewares(['api'])

// API.JS(中间件)

'use strict'

class Api {

  * handle (request, response, next) {

    // here goes your middleware logic
    const authenticator = request.auth.authenticator('jwt')
    const isLoggedIn = yield authenticator.check()

    if (!isLoggedIn) {
      return response.json({
        status: 0,
        response: {
          message: 'API Authentication Failed.'
        }
      })
    }

    // yield next to pass the request to next middleware or controller
    yield next

  }

}

module.exports = Api

// APICONTROLLER.JS

'use strict'

// Dependencies
const Env = use('Env')
const Validator = use('Validator')
const Config = use('Config')
const Database = use('Database')
const Helpers = use('Helpers')
const RandomString = use('randomstring')
const Email = use('emailjs')
const View = use('View')

// Models
const User = use('App/Model/User')
const UserProfile = use('App/Model/UserProfile')
const DesignCenter = use('App/Model/DesignCenter')
const Settings = use('App/Model/Setting')

// Properties
const FAIL      = 0
const SUCCESS   = 1
const SITE_URL  = "http://"+Env.get('HOST')+":"+Env.get('PORT')

// Messages
const MSG_API_AUTH_FAILED             = 'Api Authentication Failed.'
const MSG_REGISTERED_SUCCESS          = 'Registered Successfully.'
const MSG_LOGGED_IN_SUCCESS           = 'Logged In Successfully.'
const MSG_LOGGED_IN_CHECK             = 'You Are Logged In.'
const MSG_LOGGED_IN_FAIL              = 'Invalid Credentials.'
const MSG_FORGOT_PASS_EMAIL_SUCCESS   = 'Your password reset email has been sent. Please check your inbox to continue.'

class ApiController {

  * register (request, response) {

    let jsonResponse = {}

    // validate form input
    const validation = yield Validator.validateAll(request.all(), Config.get('validation.api.register.rules'), Config.get('validation.api.register.messages'))

    // show error messages upon validation fail
    if (validation.fails()) {

      jsonResponse.status = FAIL
      jsonResponse.response = {}
      jsonResponse.response.message = validation.messages()[0].message

    } else {

      // handle card image
      let card_image = null

      if ( request.file('card_image') ) {

        const image = request.file('card_image', {
          allowedExtensions: ['jpg', 'png', 'jpeg']
        })

        if (image.clientSize() > 0) {
          const filename = RandomString.generate({length: 30, capitalization: 'lowercase'}) + '.' + image.extension()
          yield image.move(Helpers.publicPath(Config.get('constants.user_card_img_upload_path')), filename)

          if (!image.moved()) {
            jsonResponse.status = FAIL
            jsonResponse.response = {}
            jsonResponse.response.message = image.errors()
            return response.json(jsonResponse)
          }

          // set value for DB
          card_image = filename
        }

      }

      // create user
      const user = yield User.create({
        username: new Date().getTime(),
        email: request.input('email'),
        password: request.input('password')
      })

      // create user profile
      const user_profile = yield UserProfile.create({
        user_id: user.id,
        user_type_id: 3, // designer
        first_name: request.input('first_name'),
        last_name: request.input('last_name'),
        business_name: request.input('business_name'),
        business_category_id: request.input('business_category'),
        card_image: card_image,
        phone: request.input('mobile_no'),
        is_active: 1
      })

      jsonResponse.status = SUCCESS
      jsonResponse.response = {}
      jsonResponse.response.message = MSG_REGISTERED_SUCCESS
      jsonResponse.response.user = {
        'id': user.id,
        'first_name': user_profile.first_name,
        'last_name': user_profile.last_name,
        'business_name': user_profile.business_name,
        'business_category_id': user_profile.business_category_id,
        'card_image': user_profile.card_image == null ? "" : SITE_URL + "/" + Config.get('constants.user_card_img_upload_path') + "/" + user_profile.card_image,
        'mobile_no': user_profile.phone
      }

    }

    return response.json(jsonResponse)

  }

  * login (request, response) {

    let jsonResponse = {}

    const email = request.input('email')
    const password = request.input('password')

    // validate form input
    const validation = yield Validator.validateAll(request.all(), Config.get('validation.api.login.rules'), Config.get('validation.api.login.messages'))

    if (validation.fails()) {

      jsonResponse.status = FAIL
      jsonResponse.response = {}
      jsonResponse.response.message = validation.messages()[0].message

    } else {

      try {
        const jwt = request.auth.authenticator('jwt')
        const token = yield jwt.attempt(email, password)
        const user = yield User.findBy('email', email)
        const user_profile = yield UserProfile.findBy('user_id', user.id)

        // check if user type is designer
        if ( user_profile.user_type_id == 3 ) {

          jsonResponse.status = SUCCESS
          jsonResponse.response = {}
          jsonResponse.response.message = MSG_LOGGED_IN_SUCCESS

          let card_image = null
          if (user_profile.card_image) {
            card_image = SITE_URL + "/" + Config.get('constants.user_card_img_upload_path') + "/" + user_profile.card_image
          }

          jsonResponse.response.user = {
            'id': user.id,
            'first_name': user_profile.first_name,
            'last_name': user_profile.last_name,
            'business_name': user_profile.business_name,
            'business_category_id': user_profile.business_category_id,
            'card_image': card_image,
            'mobile_no': user_profile.phone
          }
          jsonResponse.response.token = token

        } else {

          jsonResponse.status = FAIL
          jsonResponse.response = {}
          jsonResponse.response.message = MSG_LOGGED_IN_FAIL

        }
      } catch (e) {
        jsonResponse.status = FAIL
        jsonResponse.response = {}
        jsonResponse.response.message = e.message
      }

    }

    return response.json(jsonResponse)

  }

  * business_categories (request, response) {

    let jsonResponse = {}

    try {

      jsonResponse.status = SUCCESS
      const dbRecords = yield Database.select('id', 'name').from('business_categories')
      let records = []

      dbRecords.forEach(function(row) {
        records.push({
          id: row.id,
          name: row.name
        })
      })

      jsonResponse.response = records

    } catch (e) {

      jsonResponse.status = FAIL
      jsonResponse.response = {}
      jsonResponse.response.message = e.message

    }

    response.json(jsonResponse)

}

module.exports = ApiController

// CONFIG / AUTH.JS

由于JWT令牌保持有效,除非它们已过期或删除(从应用程序,强行记录用户时)。我们还可以设置如下的到期期限:

jwt: {
    serializer: 'Lucid',
    model: 'App/Model/User',
    scheme: 'jwt',
    uid: 'email',
    password: 'password',
    secret: Config.get('app.appKey'),
    options: {
        // Options to be used while generating token
        expiresIn: Ms('3m') // 3 months
    }
}

// CONFIG / SHIELD.JS

由于大多数api服务在发送POST请求时无法发送CSRF令牌,因此您可以在此文件中排除那些不检查CSRF令牌的api路径,如下所示:

csrf: {
    enable: true,
    methods: ['POST', 'PUT', 'DELETE'],
    filterUris: [
        '/api/v1/login',
        '/api/v1/register'
    ],
    compareHostAndOrigin: true
}

希望这会有所帮助:)