Search code examples
akkashirospray

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


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.


Solution

  • 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!