scala
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec
import org.apache.commons.codec.binary.Base64
class Authenticator(secretKey: String) {
private val algorithm = "HmacSHA256"
def generateToken(username: String): String = {
val key = new SecretKeySpec(secretKey.getBytes, algorithm)
val mac = Mac.getInstance(algorithm)
mac.init(key)
val signature = Base64.encodeBase64URLSafeString(mac.doFinal(username.getBytes))
s"$username:$signature"
}
def validateToken(token: String): Boolean = {
val Array(username, signature) = token.split(":")
val key = new SecretKeySpec(secretKey.getBytes, algorithm)
val mac = Mac.getInstance(algorithm)
mac.init(key)
val calculatedSignature = Base64.encodeBase64URLSafeString(mac.doFinal(username.getBytes))
calculatedSignature == signature
}
}
scala
import org.scalatra.auth.{ScentryConfig, ScentrySupport}
import org.scalatra.{ScalatraBase, ScentryStrategy}
import org.slf4j.LoggerFactory
trait AuthenticationSupport extends ScalatraBase with ScentrySupport[User] {
protected val logger = LoggerFactory.getLogger(getClass)
protected def scentryConfig = new ScentryConfig {}.asInstanceOf[ScentryConfiguration]
protected def fromSession = {
case id: String => User(id)
}
protected def toSession = {
case user: User => user.id
}
protected val authenticator = new Authenticator("secretKey")
protected def requireAuth(): Unit = {
if (!isAuthenticated) {
redirect("/login")
}
}
override def authenticate()(implicit request: HttpServletRequest, response: HttpServletResponse): Option[User] = {
val token = request.getHeader("Authorization")
if (token != null && authenticator.validateToken(token)) {
val Array(username, _) = token.split(":")
Some(User(username))
} else {
None
}
}
override protected def registerAuthStrategies: Unit = {
scentry.register("Token", app => new TokenAuthenticationStrategy(app, authenticator))
}
override protected def configureScentry() = {
scentry.unauthenticated {
scentry.strategies("Token").unauthenticated()
}
}
}
case class User(id: String)
scala
trait AuthorizationSupport extends ScalatraBase {
self: AuthenticationSupport =>
before() {
requireAuth()
}
get("/adminOnly") {
requireRole("admin")
}
def requireRole(role: String): Unit = {
if (userRoles.get(user.id).contains(role)) {
pass()
} else {
}
}
private val userRoles: Map[String, List[String]] = Map(
"admin" -> List("admin"),
"user" -> List("user")
)
}
sbt
libraryDependencies += "org.scalatra" %% "scalatra" % "2.7.1"
libraryDependencies += "org.scalatra" %% "scalatra-auth" % "2.7.1"
libraryDependencies += "org.scalatra" %% "scalatra-json" % "2.7.1"
libraryDependencies += "org.json4s" %% "json4s-jackson" % "4.0.3"
scala
import org.scalatra.json.JacksonJsonSupport
import org.scalatra.{ScalatraServlet, BaseScalatraServlet}
import org.json4s.DefaultFormats
import org.scalatra.auth.{LoginSupport, ScentrySupport}
class MyApiServlet extends ScalatraServlet with JacksonJsonSupport
with AuthenticationSupport
with AuthorizationSupport {
protected implicit val jsonFormats = DefaultFormats
post("/login") {
val username = params("username")
val password = params("password")
val user = authenticateUser(username, password)
if (user.nonEmpty) {
val token = authenticator.generateToken(user.get.id)
Ok(token)
} else {
}
}
}