play 认证相关的组件 silhouette

        "silhouette"的名字来源于美国的科幻小说《守望者》中的一个角色名,至于作者为何会选择这个名字,我们不得而知,总之作者可定非常喜欢这个角色吧。“silhouette”是一个基于play的认证相关的库,是目前使用比较广泛的库。它提供了OAuth1, OAuth2, OpenID, Credentials, Basic Authentication等多种认证方式。它可以整合成一个特定的Action,只要把传统的play的Action替换成整合成的Action,就可以在运行相应Action之前首先校验。这种校验在和其他代码的耦合性很低,非常的赞。具体信息可以看(http://silhouette.mohiva.com/docs/features)或者关注gitHub:(https://github.com/mohiva/play-silhouette)。

  “silhouette”有几个非常好的例子可以供大家参考:play-silhouette-seed、play-silhouette-slick-seed和play-silhouette-angular-seed等,大家可以从Activator UI上找到相应的项目clone下来。作为入手,一个完整的例子是再好不过的了。再次佩服Typesafe的工作者。

   本文就以play-silhouette-slick-seed这个例子展开,这个例子是结合了slick和silhouette的play工程,也是用的比较多的一个工程模式。

  从Activator UI clone下来工程以后,运行程序,发现它其实虽然简单,但是完整的做了一个认证的程序。

1、判断没登陆过,跳转到登陆界面。
  

2、没用户,注册用户:

3、利用注册信息登陆后,显示index界面。而且但凡是登陆过,下次不用重复输入,还会跳转到此页面。

4、点击sign out 按钮后,下次输入index网址需要登陆

这就是这个sample所有的功能,虽然很简单,但是却是一个很完整的实现了认证的一个例子。

下面我们就开始学一下这个例子:

1、首先想要引用silhouette这个框架,需要在bulid.sbt中引入它的包(目前是3.0.4版本):

 

resolvers += "Atlassian Releases" at "https://maven.atlassian.com/public/"

libraryDependencies ++= Seq(
  "com.mohiva" %% "play-silhouette" % "3.0.4",
  "com.mohiva" %% "play-silhouette-testkit" % "3.0.4" % "test"
) += "Atlassian Releases" at "https://maven.atlassian.com/public/"

libraryDependencies ++= Seq(
  "com.mohiva" %% "play-silhouette" % "3.0.4",
  "com.mohiva" %% "play-silhouette-testkit" % "3.0.4" % "test"
)

这样,silhouette就可以使用了。

 

我们先看一下认证的入口:

 

POST        /authenticate/credentials        @controllers.CredentialsAuthController.authenticate

找到对应的方法:CredentialsAuthController下的authenticate方法,发现这个class就实现了这么一个方法,而且用Guice通过构造方法注入注入了很多很多东西,其中messageApi和env是有get set方法的,并有可以被访问的字段。

  Environment是silhouette重要的组成部分,它定义了一个Silhouette项目主要的“key”模块,它包含了identity接口实现、authenticator接口实现、identity service接口实现、authenticator service接口实现、请求提供接口实现和事件总线接口实现。

从名字也可以看出来,它配置了认证相关的所有“环境”。

 CredentialAuthController这个类还继承了Silhouette这个类,这个类是框架本身定义好的工具类,从而引出了“Endpoints”这个概念,在play中一个Endpoint可以是一个Action、也可以是Websocket。。它是多面的,Silhouette提供了一套机制来确保他们的访问安全。

在此,在例子中Endpoint就是一个Action,最常见的也都是Action。还有Websocket、Request Handler的例子可以参照官网:http://silhouette.mohiva.com/docs/endpoints。本例子中用到了两个Action:一个是SecureAction,一个是UserAwareAction。

这两个Action分别基于SecureRequestHandler和UserAwareRequestHandler。下面先给出这两个RequestHandler的例子:

 

class Application(env: Environment[User, CookieAuthenticator])
  extends Silhouette[User, CookieAuthenticator] {
    
  /**
   * An example for a secured request handler.
   */
  def securedRequestHandler = Action.async { implicit request =>
    SecuredRequestHandler { securedRequest =>
      Future.successful(HandlerResult(Ok, Some(securedRequest.identity)))
    }.map {
      case HandlerResult(r, Some(user)) => Ok(Json.toJson(user.loginInfo))
      case HandlerResult(r, None) => Unauthorized
    }
  }

  /**
   * An example for an user aware request handler.
   */
  def userAwareRequestHandler = Action.async { implicit request =>
    UserAwareRequestHandler { userAwareRequest =>
      Future.successful(HandlerResult(Ok, userAwareRequest.identity))
    }.map {
      case HandlerResult(r, Some(user)) => Ok(Json.toJson(user.loginInfo))
      case HandlerResult(r, None) => Unauthorized
    }
  }
}     Application(env: Environment[User, CookieAuthenticator])
  extends Silhouette[User, CookieAuthenticator] {
    
  /**
   * An example for a secured request handler.
   */
  def securedRequestHandler = Action.async { implicit request =>
    SecuredRequestHandler { securedRequest =>
      Future.successful(HandlerResult(Ok, Some(securedRequest.identity)))
    }.map {
      case HandlerResult(r, Some(user)) => Ok(Json.toJson(user.loginInfo))
      case HandlerResult(r, None) => Unauthorized
    }
  }

  /**
   * An example for an user aware request handler.
   */
  def userAwareRequestHandler = Action.async { implicit request =>
    UserAwareRequestHandler { userAwareRequest =>
      Future.successful(HandlerResult(Ok, userAwareRequest.identity))
    }.map {
      case HandlerResult(r, Some(user)) => Ok(Json.toJson(user.loginInfo))
      case HandlerResult(r, None) => Unauthorized
    }
  }
}    

 

SecuredRequestHandler能够拦截请求,并且检查是否存在认证信息,如果存在,将会按照需求继续执行。UserAwareRequestHandler是被用于需要知道当前有没有认证的用户的endpoints,但是即使没有,也会继续执行。以此类推,SecuredAction和UserAwareAction的区别也就显而易见了。一下是两个例子是实现了相同的功能的不同Action:

SecuredAction:

 

class Application(env: Environment[User, CookieAuthenticator])
  extends Silhouette[User, CookieAuthenticator] {

  /**
   * Renders the index page.
   *
   * @returns The result to send to the client.
   */
  def index = SecuredAction { implicit request =>
    Ok(views.html.index(request.identity))
  }
} Application(env: Environment[User, CookieAuthenticator])
  extends Silhouette[User, CookieAuthenticator] {

  /**
   * Renders the index page.
   *
   * @returns The result to send to the client.
   */
  def index = SecuredAction { implicit request =>
    Ok(views.html.index(request.identity))
  }
}

UserAwareAction:

 

 

class Application(env: Environment[User, CookieAuthenticator])
  extends Silhouette[User, CookieAuthenticator] {

  /**
   * Renders the index page.
   *
   * @returns The result to send to the client.
   */
  def index = UserAwareAction { implicit request =>
    val userName = request.identity match {
      case Some(identity) => identity.fullName
      case None => "Guest"
    }
    Ok("Hello %s".format(userName))
  }
} Application(env: Environment[User, CookieAuthenticator])
  extends Silhouette[User, CookieAuthenticator] {

  /**
   * Renders the index page.
   *
   * @returns The result to send to the client.
   */
  def index = UserAwareAction { implicit request =>
    val userName = request.identity match {
      case Some(identity) => identity.fullName
      case None => "Guest"
    }
    Ok("Hello %s".format(userName))
  }
}

 

我们跟进添加了注入的类中发现,只有User是自定义的,发现User继承了Identity这个trait,Identity 这个trait是由Silhouette提供的,利用Identity,我们可以定义一个 user,这个trait没有定义任何默认的值。因此,你可以通过自身需要,自由的设计。

在User类中,你还会发现有一个LoginInfo类型的字段,这个LoginInfo也是由Silhoueete提供,它扮演着Silhouette中Identity ID这样一个角色,而且他用于确定在工作流程中的用户,他包含了认证信息。要创建一个程序的User,你必须要继承Identity trait,在User类中,你可以包含你想要的任何字段。

  然后,如果你想实现认证功能,你要定义一个Identity service,并实现它的实现类。Silhouette就是依靠这些实现类来实现认证的功能的。

User 继承 Identity trait ,Identity 依赖于 IdentityService,想要完成自己的功能,需要定义一个继承于IdentityService的trait,并且实现继承trait的实现类,实现类中可能需要访问数据库的内容,于是,IdentityServiceImpl就需要注入DAO的内容,于是,DAO相关的trait和Impl也联系了进来。这些种种的依赖trait和相应trait的实现类,都是在modules文件夹中的SilhouetteModule class中实现依赖注入的。而且想要程序自动实现此依赖关系,需要在application.conf文件中加上:

 

play.modules.enabled += "modules.SilhouetteModule"

于是models文件夹下的classes关系就理清了:

 

 User ---》Identity ----》IdentityService[User]----》UserService  (UserServiceImpl) ----》 userDAO (UserDAOImpl)

DI注入依赖关系:

SilhouetteModule

  搞清了以上关系,发现理解起Silhouette-slick-seed这个工程的代码就容易多了,可以细读一下,其实很简单。这是个很有用的工程,项目中可以直接拿这个工程来修改。

 

更多精彩内容,请关注我的微信公众账号 互联网技术窝

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

菜刚

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值