Play framework and Authorization

Play framework and Authorization

Recently, I need to write a authorization solution for play framework. From my understanding, it is just a filter to protect the resources of all my actions in controllers.

I have done this for Spary framework. This time, we are facing play framework. It takes me sometime to figure this out.

First of all, I am using play framework 2.4.3 version.

I am using configuration file, but actually we should use Redis or other storage. But I only want to show how the filter works among the actions and controllers.
apigateway {
mapping=[
{ "key1" : "campaignId1" }
{ "key1" : "campaignId2" }
]
}

Here is how I read this configuration map.
val api_gateway_mapping: Map[String, Seq[String]]= {
val list:Iterable[ConfigObject] = config.getObjectList("apigateway.mapping").asScala

val result = (
for {
item:ConfigObject <- list
entry : Entry[String, ConfigValue] <- item.entrySet().asScala
key = entry.getKey
value = entry.getValue.unwrapped().toString
} yield (key, value)).groupBy(record => record._1 ) map { item =>
item._1 -> {
item._2.toSeq map { value =>
value._2
}
}
}
result
}

2 Important models
package com.sillycat.services.auth
import play.api.mvc.WrappedRequest
class ResourceRequest[A](val campaigns: Seq[String], val campaignID:String, request: TokenRequest[A]) extends WrappedRequest[A](request) {
def token = request.token
}

package com.sillycat.services.auth
import play.api.mvc._
class TokenRequest[A](val token: Option[String], request: Request[A]) extends WrappedRequest[A](request)

These requests are hosting more parameters, like token, campaignID, resources and etc.

Actions to Auth
what we will do in these actions, first, get the token from headers ——> fetch the mapping based on the token ——> get the resources from that request ——> match the mapping with the resources from request

#1 Get token from Headers
package com.sillycat.services.auth
import com.sillycat.utils.IncludeLogger
import play.api.mvc.{Request, ActionTransformer, ActionBuilder}
import scala.concurrent.Future

object TokenFetchAction
extends ActionBuilder[TokenRequest]
with ActionTransformer[Request, TokenRequest]
with IncludeLogger
{
def transform[A](request: Request[A]) = Future.successful {
val xToken = request.headers.get("x-api-key")
logger.trace("TokenFetchAction system gets token = " + xToken)
new TokenRequest(xToken, request)
}
}

#2 Fetch the Mapping
package com.sillycat.services.auth

object AuthorizeService
extends IncludeAuthConfig
{
/**
* no guava cache, no expire time, this is a just temperary solution
*/
val memoryCache:Map[String, Seq[String]] = api_gateway_mapping

def getResourceMappingByToken(xToken:String):Option[Seq[String]] = {
val resources = memoryCache.get(xToken)
resources
}

}

#3 Get resources from requests
package com.sillycat.services.auth
import com.sillycat.utils.IncludeLogger
import play.api.mvc.{Results, ActionRefiner}
import scala.concurrent.Future

trait IncludeAuthService
extends IncludeLogger
{

def ResourceFetchAction(campaignID:String) = new ActionRefiner[TokenRequest, ResourceRequest] {
def refine[A](request: TokenRequest[A]) = Future.successful {
val xTokenOpt = request.token
logger.trace("ResourceFetchAction get xTokenOpt = " + xTokenOpt)
xTokenOpt match {
case Some(xToken) => {
logger.trace("ResourceFetchAction get xToken = " + xToken)
val resourceOpt = AuthorizeService.getResourceMappingByToken(xToken)
resourceOpt.map{ resources =>
logger.trace("ResourceFetchAction get resources mapping = " + resources)
logger.trace("ResourceFetchAction get campaignID = " + campaignID)
new ResourceRequest(resources, campaignID, request)
}.toRight{
logger.error("ResourceFetchAction empty resources mapping data resourceOpt = " + resourceOpt)
Results.Unauthorized
}
}
case _ => {
logger.error("System does not get x-api-key, bad request.")
Left(Results.Unauthorized)
}
}
}
}

}

#4 Match the Mapping with resources
package com.sillycat.services.auth
import com.sillycat.utils.IncludeLogger
import play.api.mvc.{Results, ActionFilter}
import scala.concurrent.Future

object AuthCampaignCheckAction
extends ActionFilter[ResourceRequest]
with IncludeLogger
{
def filter[A](request: ResourceRequest[A]) = Future.successful {
val campaignID = request.campaignID
val resources = request.campaigns

logger.trace("AuthCampaignCheckAction campaignID = " + campaignID)
logger.trace("AuthCampaignCheckAction resources = " + resources)

if (resources.contains(campaignID)){
logger.trace("AuthCampaignCheckAction, You are authorized!")
None
} else{
logger.warn("AuthCampaignCheckAction, Unauthorized!")
Some(Results.Unauthorized)
}
}
}

After that, when we wants to use them in the Controller.
…snip...
with IncludeAuthService
..snip...
def addJob(
@ApiParam(name="campaignID",value="campaign ID",defaultValue=“xxxxx",required=true,allowMultiple=false)
@PathParam("campaignID")
campaignID:String
) = (TokenFetchAction andThen ResourceFetchAction(campaignID) andThen AuthCampaignCheckAction)(BodyParsers.parse.json) { implicit request =>
…snip...

References:
https://www.playframework.com/documentation/2.4.x/ScalaActionsComposition

http://stackoverflow.com/questions/19868153/authorisation-check-in-controller-scala-play
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值