加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > 安全 > 正文

scala – 如何集成Play(Web框架),Deadbolt(授权)和Slick(数据库

发布时间:2020-12-16 18:49:20 所属栏目:安全 来源:网络整理
导读:简而言之:我的应用程序使用 Play web framework版本2.5.1.我想使用 the Deadbolt authorization system和 Slick来访问我的数据库中的用户授权信息.我怎样才能做到这一点? Deadbolt是专门为Play而制作的,而且Play带有Slick集成的开箱即用功能,所以它应该是
简而言之:我的应用程序使用 Play web framework版本2.5.1.我想使用 the Deadbolt authorization system和 Slick来访问我的数据库中的用户授权信息.我怎样才能做到这一点? Deadbolt是专门为Play而制作的,而且Play带有Slick集成的开箱即用功能,所以它应该是可能的,如果不是很容易的话.

根据Deadbolt文档中的“Integrating Deadbolt”,我扩展了DeadboltHandler特性.它的抽象getSubject()方法似乎是进行数据库查询的地方(所以说the documentation但没有任何例子).该方法接收AuthenticatedRequest作为参数,并返回Subject,基本上是经过身份验证的用户ID,以及角色和权限(授权).

我被卡住了,因为虽然Play附带Slick integration,但文档仅描述了如何在Play控制器中使用它. (注意我想使用依赖注入来执行此操作,因为使用全局查找已弃用且容易出错)

我在我的控制器中成功使用Deadbolt来限制对某些资源的访问,但是对于Deadbolt来说,对于授权细节进行数据库查询似乎是错误的地方(如果是,那么DeadboltHandler就没用了).控制器构造函数签名定义类似于(注意控制器访问存储Web内容的默认数据库而不是授权数据库):

class Application @Inject()(
  dbConfigProvider: DatabaseConfigProvider,playConfig: play.api.Configuration,deadbolt: DeadboltActions
) extends Controller {

这样可行.但是,类似地使用@Inject注释DeadboltHandler扩展无法提供对数据库的Slick访问:

class AuthHandler @Inject()(@play.db.NamedDatabase("auth") dbConfigProvider: DatabaseConfigProvider)
  extends DeadboltHandler {

结果是

not enough arguments for constructor AuthHandler: (dbConfigProvider: play.api.db.slick.DatabaseConfigProvider)services.AuthHandler.
Unspecified value parameter dbConfigProvider.

显然,Play会为控制器执行一些特殊操作,以便@Inject注释工作,这是我缺乏理解的.我认为它是使用注入器而不是new关键字构造控制器的本质,但我通过Play源代码搜索未能告诉我究竟发生了什么.如果我能找到它,也许我可以模仿该技术来构造DeadboltHandler.

我看到这个游戏附带了诸如GuiceInjector和GuiceInjectorBuilder这样的类,听起来好像它们可能是解决方案的一部分,但我的实验还没有告诉我如何,以及是否有任何关于如何在特定中使用它们的文档DeadboldHandler扩展的上下文,我想念它.

我发现了上一个问题:Scala (Play 2.4.x) How to call a class with @inject() annotation,这似乎非常重要.不幸的是,尽管原始海报发表了六条后续评论,但尚未得到答复.我觉得如果我得到了这个问题的答案,我会得到这个问题的答案,尽管我的问题非常具体:如何使用Play和Deadbolt以及Slick(在Scala中).

最令我困惑的是,这似乎应该是足够普遍的东西,它可以在文档中提到,或者已经被问到已经在SO上.我没有找到任何这样的引用通常意味着我正在做一些如此独特的错误,没有其他人有机会谈论它.它似乎应该足够简单,我乐观地希望我错过了一些非常基本的东西,我期待有一些善意的灵魂告诉我这些知识.

解决方法

正如您在问题中所述,检索用户的位置在DeadboltHandler.getSubject中.实际上,您可以将特定于数据库的代码移动到自己的类中,因此在此示例中,这就是我所做的.

这是DeadboltHandler的通用实现;你应该能够将它放入你的代码并按原样使用它,因为稍后将处理持久性细节.

import javax.inject.{Inject,Singleton}

import be.objectify.deadbolt.scala.models.Subject
import be.objectify.deadbolt.scala.{AuthenticatedRequest,DeadboltHandler,DynamicResourceHandler}
import models.{LogInForm,User}
import play.api.mvc.{Request,Result,Results}
import play.twirl.api.HtmlFormat
import views.html.security.denied

import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future

@Singleton
class MyDeadboltHandler @Inject() (authSupport: AuthSupport) extends DeadboltHandler {

  override def beforeAuthCheck[A](request: Request[A]): Future[Option[Result]] = Future {None}

  override def getDynamicResourceHandler[A](request: Request[A]): Future[Option[DynamicResourceHandler]] = Future {None}

  /**
    * Get the current user.
    *
    * @param request the HTTP request
    * @return a future for an option maybe containing the subject
    */
  override def getSubject[A](request: AuthenticatedRequest[A]): Future[Option[Subject]] = 
    Future {
      request.subject.orElse {
        // replace request.session.get("userId") with how you identify the user
        request.session.get("userId") match {
          case Some(userId) => authSupport.getUser(userId)
          case _ => None
        }
      }}

  /**
    * Handle instances of authorization failure.
    *
    * @param request the HTTP request
    * @return either a 401 or 403 response,depending on the situation
    */
  override def onAuthFailure[A](request: AuthenticatedRequest[A]): Future[Result] = {
    def toContent(maybeSubject: Option[Subject]): (Boolean,HtmlFormat.Appendable) =
      maybeSubject.map(subject => subject.asInstanceOf[User])
      .map(user => (true,denied(Some(user))))
      .getOrElse {(false,views.html.security.logIn(LogInForm.logInForm))}

    getSubject(request).map(maybeSubject => toContent(maybeSubject))
    .map(subjectPresentAndContent =>
      if (subjectPresentAndContent._1) Results.Forbidden(subjectPresentAndContent._2)
      else Results.Unauthorized(subjectPresentAndContent._2))
  }
}

现在,转到数据库的需要减少到尚未将主题放入请求的情况.请注意有关替换request.session.get(“userId”)的注释,但要标识用户.

然后,AuthSupport类提供对主题持久性的访问.这将DB访问与DeadboltHandler隔离开来.它非常简单,主要是因为您将使用Slick查询填充此内容.

@Singleton
class AuthSupport @Inject()(dbConfigProvider: DatabaseConfigProvider) {
    // set up your usual Slick support

    // use Slick to get the subject from the database
    def getUser(userId: String): Option[User] = ???
}

要公开它,您需要创建一个模块并在application.conf中注册它.

import be.objectify.deadbolt.scala.DeadboltHandler
import be.objectify.deadbolt.scala.cache.HandlerCache
import security.{AuthSupport,MyDeadboltHandler,MyHandlerCache}
import play.api.inject.{Binding,Module}
import play.api.{Configuration,Environment}

class CustomBindings extends Module  {
  override def bindings(environment: Environment,configuration: Configuration): Seq[Binding[_]] =
    Seq(
         bind[DeadboltHandler].to[MyDeadboltHandler],bind[AuthSupport].toSelf,// other bindings,such as HandlerCache
       )
}

在application.conf中声明它是使用play.modules.enabled的常见问题:

play {
  modules {
    enabled += be.objectify.deadbolt.scala.DeadboltModule
    enabled += modules.CustomBindings
  }
}

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读