How can i make Shiro work in Scala + Akka + Spray environment
Asked Answered
Q

2

6

I guess that i don't understand the workflow correctly. I'm writing a web service in Scala with Apache Shiro and Stormpath. My user authentication process looks like this:

1) Get user data from POST request, check it with Stormpath and if everything is fine redirect to some page:

pathPrefix("api") {
  path("login") {
    post {
      AuthToken.fromRequest { (token: AuthToken) =>
        stormpathAuth(token) { subj =>
          log.info("Subj {}", subj.getPrincipal.toString)
          redirect("/some/page", StatusCodes.Found)
        }
      }
    }
  }

In the logs it alright, Shiro return me a correct Subject with a Stormpath account. Next i want to extract subject, to use it in code:

pathPrefix("some") {
  loggedInUser { subject =>
    path("page") {
      get {
        complete {
          html.render(Page.model)
        }
      }
    } ..... other routes


loggedInUser directive should extract subject and check if it's authenticated otherwise redirect to the login form. And the problem is that it always redirects me to the login form, although in the logs SubjectUtils.getSubject.getPrincipal shows the correct account.

Updated

Actually Spray is build on top of Akka. So i think that the problem is behind getSubject implementation, which currently depends on ThreadLocal environment. I've searched on Shiro + Akka topics, but didn't find any helpful info.

Quintic answered 23/7, 2013 at 10:46 Comment(4)
Are you using the Shiro Stormpath plugin ?Double
Also, is this being run in a Servlet container or w/ Play! ?Double
@LesHazlewood Yes i'm using Stormpath plugin for user authentication and no, i have my own server and client sides build with Spray toolkit, spray-can and spray-routingQuintic
@LesHazlewood I've updated the question. I'm using Akka internally. Is there any way to make Shiro work in Akka environment?Quintic
D
7

This is definitely possible, but you will have to ensure that the Subject is available to Akka components (actors) as they are processing messages.

I am familiar with Akka's architecture (actor / messaging model), but I haven't used Akka myself, so please take the following as a best-guess answer:

In traditional Shiro-based applications and/or web-apps, something is responsible for building a Subject instance that reflects the current caller and/or request and then binding it to the currently executing Thread. This ensures that any subsequent calls during that Thread's execution to SecurityUtils.getSubject() function correctly. This is all documented in Shiro's Subject documentation (see the Subject.Builder and Thread Association sections).

In a web-app for example, the ShiroFilter does this setup/bind/unbind logic automatically per ServletRequest. I would suspect that something (some 'frameworky' code or component) in an Akka-based application would do the same setup/bind/unbind logic as well.

Now with Akka, I'm fairly certain you could use the traditional Thread-based approach as covered in the above documentation (I think Play! users have done this with success). But another interesting approach might be available with Akka immutable messages:

  • When a message is constructed, you can attach Subject-specific information to the message (e.g. a message 'header') with things like the Shiro PrincipalCollection and authentication state (is authenticated or not) and anything else (runAs state, whatever).

  • Then, when a message is received, that information would be used as input to the Subject.Builder to create a Subject instance, and that Subject instance is used during messaging processing. Shiro Subject instances are very lightweight and expected to be created and destroyed per request (or even multiple times per request if necessary), so you don't need to worry about Builder overhead.

  • When the Subject is built, you can either bind and then unbind it to the currently executing thread, or, each Actor that processes a message can go through this same logic in a 'frameworky' sort of way. This latter approach does not require thread-binding at all, since Subject state is now maintained at the message level, not at the thread level.

As a testament to this alternative (non-thread-based) approach, the upcoming Shiro plugin for ActiveMQ uses connection state to store Shiro state, not threads. Similarly, message state could be used just as easily.

Just note that with non-thread-based approaches, downstream callers can't call SecurityUtils.getSubject() to acquire the Subject instance. They'll have to get it in another 'frameworky' way.

Again, this is my best-effort analysis of how this could work in messaging environments (like Akka) without having used Akka myself. Hopefully this gives you enough information to help you solve this in a way that's relevant for your use cases!

Double answered 25/7, 2013 at 16:47 Comment(0)
H
5

Following up on this question, which was quite useful when i ran into the same problem. The main goal is to provide some extra information on Les Hazlewood's suggested implementation. Another good alternative source of information is also this topic https://groups.google.com/forum/#!topic/spray-user/wpiG4SREpl0

Shiro is currently thread based, meaning it binds the subject information to the current thread. This is an issue for Akka as it uses thread pools of dispatchers and workers.

As the threads get reused, the subject leaks from one request into the another, leading to unauthenticated request being process as authenticated as well as the opposite.

As Les awesomely suggested, a possible solution for the issue is to abandon the thread binding and store the subject on the Akka messages. For this to work it means the static methods provided on Shiro's SecurityUtils cannot be used. The operations should be done on the Subject directly. Also, this subject should be built using the Subject.Builder.

For that you can wrap your messages with the subject,

case class ActorMessage(subject:Subject, value: Any)


object MessageSender {

def ? (actorRef: ActorRef, message: Any)(implicit subject: Subject): Future[Any] = {
  val resultFuture = if (!message.isInstanceOf[ActorMessage]) {
      val actorMessage = ActorMessage(subject, message)
      actorRef ask actorMessage
  } else actorRef ask message

  for (result <- resultFuture) yield {
    if (result.isInstanceOf[ActorMessage]) {
      val actorMessageResp = result.asInstanceOf[ActorMessage]
      actorMessageResp.value
    } else result
  }
}

}

and unwrap them on the actor when it receives the message. Or initialize the subject if it was the request entry actor.

abstract class ShiroActor extends Actor {

implicit var shiroSubject: Subject = (new Subject.Builder).buildSubject

override def aroundReceive(receive: Actor.Receive, msg: Any): Unit = {
    if (msg.isInstanceOf[ActorMessage]) {
      val actorMessage = msg.asInstanceOf[ActorMessage]
      shiroSubject = actorMessage.subject
      receive.applyOrElse(actorMessage.value, unhandled)
    } else {
     shiroSubject = (new Subject.Builder).buildSubject
      receive.applyOrElse(msg, unhandled)
    }
  }

}

Now to use it the implemented actors will have to extend the ShiroActor and to exchange a message between actors you will have to use the MessageSender, instead of the ActorRef ask or tell methods.

To login the subject, checkpermissions, roles and so on you can now use the subject available on the actor. Like this,

shiroSubject.login(new AuthenticationToken(principal, credentials))

This assumes the login is done on the request entry actor and subject is just shared to be checked for permissions on the subsequent actors. But I'm sure it can be easilly adapted to update the shiroSubject on the actors bidirectionally.

Hope this can be an helpful resource as it seems to be nearly impossible to find an example of the integration between this two frameworks.

Hodges answered 1/2, 2016 at 23:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.