43806


まず、以下のサイトの通りでログイン認証を実装したのちのご質問になります。  
ほぼ丸々同じです。

<http://akiomik.hatenablog.jp/entry/2013/02/07/211054>

画像アップロードの機能を備えたいと考えているのですが、ドキュメントの通りに書いてしまうと、tmpフォルダに全ての画像が入ってしまう(?)ため、アカウントID(かprimary
key)ごとにフォルダを別に分けて保存できないかと思われ、以下のように書いてみました。ですがこのファイルではhelperformからloginNameを取得していないため、当然ながら、

[code]

    value loginName is not a member of play.api.data.Form[com.github.j5ik2o.spetstore.application.controller.LoginCustomer]
    
[/code]

の一文がコンパイルエラーとして出てしまいます。(なお引数が足らないとエラーで出てしまうことと、画像をアップロードするview(profpicsupload.scala.html)にもloginNameを渡したいと思ったため、@Inject
の後に引数として val loginFormをとりあえず入れてあります)

また以前ご回答いただいた際に、クラスではなくシングルトンオブジェクトにすれば、例えば、上記の

[code]

    case class LoginCustomer ( 
         email: string
         loginName: string
         password: string
    )
    
[/code]

の情報を form ( val loginForm = Form(...) )をつかったmappingで取得し、 これを例えば以下のメソッド；

[code]

    def toprofpics(loginForm: LoginCustomer): Unit = ProfpicsUploadController.toprofpics(loginForm)
    
[/code]

で渡せばいいのかもとふと思いましたが、そもそもシングルトンオブジェクトにしてしまうとアクションメソッドを使えなくなってしまうのでどうにもならないと気づきました。これでうまくいくわけがないというのはわかってるのですが、こういう場合（アカウントごとに画像を整理して保存したい場合）どうすべきなのでしょうか。ご回答いただければ幸いです。

[code]

    package com.github.j5ik2o.spetstore.application.controller
    import javax.inject._
    import play.api._
    import play.api.mvc._
    import play.api.data._
    import play.api.data.Forms._
    import com.github.j5ik2o.spetstore.domain.model.customer._
    import com.github.j5ik2o.spetstore.application.controller.json._
    import play.api.i18n.{ I18nSupport, MessagesApi }
    import play.api.i18n.Messages.Implicits._
    import scala.util.Random
    import java.io.File
    import java.nio.file.{ Files, Path, Paths }
    import akka.stream.IOResult
    import akka.stream.scaladsl._
    import akka.util.ByteString
    import play.api.libs.streams._
    import play.api.mvc.MultipartFormData.FilePart
    import play.api.mvc._
    import play.core.parsers.Multipart.FileInfo
    import scala.concurrent.{ ExecutionContext, Future }
    import java.sql.Connection
    import scalikejdbc._
    import scalikejdbc.SQLInterpolation._
    
    case class ProfpicsUploadData(name: String)
    
    class ProfpicsUploadController @Inject() (val messagesApi: MessagesApi, val  messagesApi2: MessagesApi,
       val loginForm:
    Form[com.github.j5ik2o.spetstore.application.controller.LoginCustomer])(implicit executionContext: ExecutionContext)
         extends Controller
         with play.api.i18n.I18nSupport with Secured {
    
       private val logger = Logger(this.getClass)
    
       val profpicsUploadForm = Form(
    
         mapping(
          "name" -> text
         )(ProfpicsUploadData.apply)(ProfpicsUploadData.unapply)
    
      //標準のボディパーサを使ったupload
      def upload = Action(parse.multipartFormData) { request =>
        request.body.file("picture").map { picture =>
          import java.io.File
          val name = picture.filename
          val contentType = picture.contentType
    
          val loginName = loginForm.loginName.toString
    
          try {
    
             DB readOnly { implicit s =>
              val primarykey = sql"select pk from customer where loginName = ${loginName}"
    
              //ディレクトリ作成
              //    val dir = Paths.get("mydir")
              val dirp = Paths.get("fileupload", "customer", "profilepics", s"$primarykey")
              //    if(Files.notExists(dir)) Files.createDirectory(dir) // mkdir
              if (Files.notExists(dirp)) Files.createDirectories(dirp) // mkdir -p
    
              picture.ref.moveTo(new File(s"/fileupload/customer/profilepics/$primarykey/$name"))
            }
          } catch {
             case e: Exception => e.printStackTrace()
          }
           Ok("File uploaded")
         }.getOrElse {
    
    Redirect(com.github.j5ik2o.spetstore.application.controller.routes.ProfpicsUploadController.profpicsupload).flashing(
            "error" -> "Missing file"
           )
        }
    
      }
    
       def profpicsupload = Action { implicit request =>
        Ok(views.html.profpicsupload(messagesApi.toString, loginForm, profpicsUploadForm))
    
      }
    
      // トップページ
      def index = Action { implicit request =>
        Ok(views.html.index("Your new application is ready."))
      }
    
    }
    
    object ProfpicsUploadController {
    
      def toprofpics(loginForm: LoginCustomer): Unit = {
    
    //      val loginName = loginForm.loginName.toString
    
        ( 
          loginForm.email.toString,
          loginForm.loginName.toString,
          loginForm.password.toString
        ) match {
            case (email, loginName, password) => LoginCustomer(email, loginName, password)
           }
      }
    
    }
    
[/code]


CC BY-SA 4.0
2018-05-04T13:17:07.573
43782
2018-05-05T18:45:49.943
2018-05-05T18:09:56.513
3068
28048
question
scala playframework
Controllerに form helperを使わずにログイン情報を渡す方法


認証(ログイン)済みユーザーの情報を使用したい場合、まずは認証処理を実装してください。

* * *

通常、Webアプリケーションの認証(ログイン)処理は、Cookieやセッションなどの何かしらに認証(ログイン)情報を保存して行います。Play
Frameworkを使用したWebアプリケーションにおいても例外ではありません。

そして認証済みユーザーがサーバーにリクエストを送信した際は、保存している認証情報からユーザーの情報が取得できますので、それを利用します。

※実世界で、画像をアップロードする度に自身のログインIDやメールアドレスなどをフォームに入力するようなサービスはほぼ見かけないかと思います

* * *

さて、Play Frameworkで認証を行う方法ですが、認証処理を自分で実装することもできます。

playframework/play-scala-secure-session-example at 2.5.x  
<https://github.com/playframework/play-scala-secure-session-
example/tree/2.5.x>

ただ当たり前ですが、認証情報のようなものはハッシュ化や暗号化などが必須ですし、自分で全て実装するのは大変なので、通常はライブラリを使用します。  
(セキュリティに関する処理を自分で実装すると、大抵はバグ(セキュリティホール)の温床になるため、極力避けるべきです)

例えば、Play 2.5.xでは以下のようなライブラリが使用できますので、ドキュメントやネット上の情報を参考にして実装してください。

**play2-auth**  
<https://github.com/t2v/play2-auth>

日本語版README  
<https://github.com/t2v/play2-auth/blob/a15dbaf6d1a984bfd2fa09f877ea964df573bb3b/README.ja.md>

play2-authは、デフォルトでは Cookie を使用します。

**Silhouette**  
<https://github.com/mohiva/play-silhouette>

Play Framework 向け 認証認可ライブラリ Silhouette 3.0 の中身と利用法をお勉強して実際に実装してみた話  
<https://qiita.com/Biacco/items/2d5deacae6bffe8bd6f1>

* * *

例えばplay2-authを使用した場合、`loggedIn`メソッドでログイン時に保存したユーザー情報が取得できます。

<https://github.com/t2v/play2-auth/blob/a15dbaf6d1a984bfd2fa09f877ea964df573bb3b/README.ja.md#%E4%BD%BF%E3%81%84%E6%96%B9>

[code]

    // AuthElement は loggedIn[A](implicit RequestWithAttribute[A]): User というメソッドをもっています。
    // このメソッドから認証/認可済みのユーザを取得することができます。
    
    def main = StackAction(AuthorityKey -> NormalUser) { implicit request =>
      val user = loggedIn
      val title = "message main"
      Ok(html.message.main(title))
    }
    
[/code]

またSilhouetteを使用した場合は、`request.identity`にログインユーザー情報が格納されています。

<https://www.silhouette.rocks/docs/endpoints#section-actions>

[code]

    class Application(silhouette: Silhouette[DefaultEnv]) extends Controller {
    
      /**
       * Renders the index page.
       *
       * @returns The result to send to the client.
       */
      def index = silhouette.UserAwareAction { implicit request =>
        val userName = request.identity match {
          case Some(identity) => identity.fullName
          case None => "Guest"
        }
        Ok("Hello %s".format(userName))
      }
    }
    
[/code]

* * *

**追記**

質問に追記されたリンク先のコードを確認する限り、`IsAuthenticated()`を使うことでログインユーザーのid(メールアドレス)がAction内で参照できるようです。

ですが、そのままのコードでは任意の`BodyParser`を指定できないので、以下のようにメソッドを1つ追加して使用する必要がありそうです。

[code]

    trait Secured {
      private def email(request: RequestHeader) = request.session.get("email")
    
      // 未認証時のリダイレクト先
      private def onUnauthorized(request: RequestHeader) = Results.Redirect(routes.Application.login)
    
      // Actionに認証をかませてラップ
      def IsAuthenticated(f: => String => Request[AnyContent] => Result) = Security.Authenticated(email, onUnauthorized) { user =>
        Action(request => f(user)(request))
      }
    
      // このメソッドを新たに追加
      def IsAuthenticated[A](bodyParser: BodyParser[A])(f: => String => Request[A] => Result) = Security.Authenticated(email, onUnauthorized) { user =>
        Action(bodyParser)(request => f(user)(request))
      }
    }
    
[/code]

**controllers/ProfpicsUploadController.scala**

[code]

    class ProfpicsUploadController @Inject() (
        val messagesApi: MessagesApi,
        val messagesApi2: MessagesApi
    ) extends Controller
      with play.api.i18n.I18nSupport
      with Secured {
    
      private val logger = Logger(this.getClass)
    
      // ログイン済みの場合だけ、アップロード画面を表示する
      def profpicsupload = IsAuthenticated { email => implicit request =>
        Ok(views.html.profpicsupload())
      }
    
      //標準のボディパーサを使ったupload
      def upload = IsAuthenticated(parse.multipartFormData) { email => request =>
        request.body.file("picture").map { picture =>
    
          // email(ログインid)からユーザーidを取得する処理
    
          // ユーザーid毎のディレクトリに画像を保存する処理
    
          Ok("File uploaded")
        }.getOrElse {
          Redirect(routes.ProfpicsUploadController.profpicsupload)
            .flashing("error" -> "Missing file")
        }
      }
    }
    
[/code]

**views/profpicsupload.scala.html**

[code]

    @()(implicit messages: Messages)
    // 以下略
    
[/code]


CC BY-SA 4.0
2018-05-05T08:57:35.777
43806
2018-05-05T18:45:49.943
2018-05-05T18:45:49.943
3068
3068
43782
answer