How can I get Shiro to work in Scala + Akka + Spray

I assume that I do not understand the workflow correctly. I am writing a web service in Scala with Apache Shiro and Stormpath. My user authentication process is as follows:

1) Get user data from the POST request, check it with the Stormpath and if everything is correctly redirected to any page:

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

This is good in magazines, Syro will return me the correct item with a Stormpath account. Then I want to extract the topic, use it in the code:

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


Directive

loggedInUser should retrieve the object and check if it is authenticated, otherwise it will be redirected to the registration form. And the problem is that it always redirects me to the login form, although the correct account is displayed in the SubjectUtils.getSubject.getPrincipal logs.

Update

In fact, the spray is being built on top of Akka. Therefore, I believe that the problem is behind the implementation of getSubject , which currently depends on the ThreadLocal environment. I searched for Shiro + Akka themes but did not find any useful information.

+6
source share
2 answers

This is definitely possible, but you will need to ensure that Subject is available to Akka components (members) as they process messages.

I am familiar with the architecture of Akka (actor / message model), but I have not used Akka myself, so please take the following as the best answer:

In traditional Shiro-based and / or web-based applications, something is responsible for creating a Subject instance that reflects the current caller and / or request, and then binds it to the currently executing Thread . This ensures that any subsequent calls during the execution of this thread to SecurityUtils.getSubject() function correctly. All this is documented in extensive thematic documentation (see .Builder Theme and Thematic Association Sections).

In a web application, for example, ShiroFilter does this setting / binding / untie logic automatically for ServletRequest. I would suspect that something (some kind of "wireframe" code or component) in an Akka-based application would do the same installation / bind / unbind logic.

Now with Akka, I’m pretty sure that you can use the traditional Thread-based approach as described in the documentation above (I think Play! Users did this with success). But another interesting approach may be available with immutable Akka messages:

  • When a message is created, you can attach information about a specific subject with things such as Shiro PrincipalCollection and authentication status (authenticated or not) and anything else (runAs state, whatever) to the message (for example, the message header).

  • Then, when a message is received, this information will be used as input to Subject.Builder to create an instance of Subject , and this instance of Subject is used during message processing. Shiro Subject instances are very lightweight and are expected to be created and destroyed per request (or even several times per request, if necessary), so you don’t have to worry about Builder overhead.

  • When a Subject built, you can either bind or then untie it to the current executable thread, or each actor who processes the message can go through the same logic as a "wireframe" type. This last approach does not require thread binding at all, since the state The object is now supported at the message level, rather than at the stream level.

As evidence of this alternative (non-streaming) approach, the upcoming Shiro plugin for ActiveMQ uses connection state to store Shiro state, not streams. Similarly, message state can be used just as easily.

Just note that when using non-thread streams, callers cannot call SecurityUtils.getSubject() to get a Subject instance. They will have to get it in another "wireframe" mode.

Again, this is my most effective analysis of how this can work in a messaging environment (like Akka) without using Akka. Hopefully this will give you enough information to help you solve this problem in a way that suits your use cases!

+6
source

Following this question, it was very helpful when I came across the same problem. The main goal is to provide additional information about the proposed Les Hazlewood implementation. Another good alternative source of information is also this topic https://groups.google.com/forum/#!topic/spray-user/wpiG4SREpl0

Shiro currently uses streaming, which means linking subject information to the current stream. This is a problem for Akka, as it uses dispatcher and worker thread pools.

As threads are reused, the object flows from one request to another, which leads to the fact that an unauthorized request is processed both authenticated and vice versa.

As suggested by Les, a possible solution to the problem is to abandon the thread binding and save the object in Akka messages. For this, this means that the static methods provided by Shiro SecurityUtils cannot be used. Operations must be performed directly on the Subject. In addition, this object must be built using the Subject.Builder object.

To do this, you can wrap your messages with a 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 deploy them to the actor when he receives the message. Or initialize the topic, if it was a participant in the application.

 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, in order to use it, the performers will need to extend the ShiroActor action and exchange messages between the participants, you will have to use MessageSender, instead of requesting or telling the methods of ActorRef.

To enter the topic, checks, roles, etc., you can now use the item available to the actor. Like this,

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

This assumes that the login is made on the subject of the request input, and the subject is simply used to check permissions for subsequent participants. But I'm sure this can be easily adapted to update siroSubject for participants in two ways.

Hope this can be a useful resource, as it seems almost impossible to find an example of integration between the two structures.

+3
source

Source: https://habr.com/ru/post/950070/


All Articles