2018年2月17日土曜日

GAE x Spring Boot で Spring Security が上手く動かぬ

Google App Engine Java8 Standard x Spring Boot x Spring Security

同じ現象で困ってる方の助けになれば幸いでございます!
と、ほかの解決方法探し中...!

構成
  • Google App Engine Java 8 Standard 
  • Spring Boot 1.5.9 
  • Spring Security 
  • Kotlin
  • Gradle 

 Devローカルサーバーならしっかり動いている。

 本番環境で問題が発生する。

 デプロイしてちゃんと起動はする。
 しかし、Spring Securityを使ったフォームのログインで問題。

 ログインボタンを押した後、
なにごとも無かったかのように、
ログイン画面に戻る笑

どうやらGAEのセッションハンドラを使うと上手く動かぬもよう。

GAEのセッションハンドラを使わず、
GAE内のmemcacheもしくはDatastoreを使って
自前のセッションハンドラにします。

DatastoreSessionRepository
package com.hometest.libs.appengine.session;

import com.google.appengine.api.datastore.*
import org.springframework.session.MapSession
import org.springframework.session.SessionRepository
import org.springframework.stereotype.Component
import java.io.*
import java.util.logging.Logger

@Component
class DatastoreSessionRepository(private val datastoreService: DatastoreService) : SessionRepository<MapSession> {
    private val logger = Logger.getLogger(javaClass.simpleName)
    private val maxInactiveIntervalInSeconds: Int = 3600
    private val kind = "Session"
    private val propertyName = "serialized"

    override fun createSession(): MapSession = MapSession().also { session ->
        session.maxInactiveIntervalInSeconds = maxInactiveIntervalInSeconds
        logger.info { "createSession() = ${session.id}" }
    }

    override fun save(session: MapSession) {
        logger.info { "save(${session.id}) with expiration ${session.maxInactiveIntervalInSeconds}" }

        ByteArrayOutputStream().use { byteArray ->
            ObjectOutputStream(byteArray).use { outStream ->
                outStream.writeObject(session)
            }
            datastoreService.put(Entity(kind, session.id).apply {
                // TODO: Check byteArray size. This byte array can be no bigger than 1MB.
                // https://cloud.google.com/appengine/docs/standard/java/javadoc/com/google/appengine/api/datastore/Blob
                setProperty(propertyName, Blob(byteArray.toByteArray()))
            })
        }
    }

    override fun getSession(id: String): MapSession? {
        val blob: Blob? = try {
            datastoreService.get(KeyFactory.createKey(kind, id))
                    .getProperty(propertyName) as? Blob
        } catch (_: EntityNotFoundException) {
            null
        }
        val session: MapSession? = blob?.let {
            ByteArrayInputStream(it.bytes).use { byteArray ->
                ObjectInputStream(byteArray).use { inStream ->
                    (inStream.readObject() as MapSession).also { session ->
                        session.lastAccessedTime = System.currentTimeMillis()
                    }
                }
            }
        }
        logger.info { "getSession($id) = ${session?.id}" }
        return session
    }

    override fun delete(id: String) {
        logger.info { "delete($id)" }
        datastoreService.delete(KeyFactory.createKey(kind, id))
    }
}
MemcacheSessionRepository
package com.hometest.libs.appengine.session;

import com.google.appengine.api.memcache.Expiration
import com.google.appengine.api.memcache.MemcacheService
import org.springframework.session.MapSession
import org.springframework.session.SessionRepository
import org.springframework.stereotype.Component
import java.util.logging.Logger

@Component
class MemcacheSessionRepository(private val memcacheService: MemcacheService) : SessionRepository<MapSession> {
    private val logger = Logger.getLogger(javaClass.simpleName)
    private val maxInactiveIntervalInSeconds: Int = 3600

    override fun createSession() = MapSession().also { session ->
        session.maxInactiveIntervalInSeconds = maxInactiveIntervalInSeconds
        logger.info { "createSession() = ${session.id}" }
    }

    override fun save(session: MapSession) {
        logger.info { "save(${session.id}) with expiration ${session.maxInactiveIntervalInSeconds}" }
        memcacheService.put(session.id, session, Expiration.byDeltaSeconds(session.maxInactiveIntervalInSeconds))
    }

    override fun getSession(id: String): MapSession? =
            (memcacheService.get(id) as? MapSession)?.also { session ->
                session.lastAccessedTime = System.currentTimeMillis()
            }.also { session ->
                        logger.info { "getSession($id) = ${session?.id}" }
                    }

    override fun delete(id: String) {
        logger.info { "delete($id)" }
        memcacheService.delete(id)
    }
}
AppengineConfiguration
package com.hometest.config;

import com.google.appengine.api.datastore.DatastoreService
import com.google.appengine.api.datastore.DatastoreServiceFactory
import com.google.appengine.api.memcache.MemcacheService
import com.google.appengine.api.memcache.MemcacheServiceFactory
import com.hometest.libs.appengine.session.MemcacheSessionRepository
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.session.MapSession
import org.springframework.session.web.http.SessionRepositoryFilter

@Configuration
class AppengineConfiguration {

    // AppEngine Session
    @Bean
    fun memcacheService(): MemcacheService {
        return MemcacheServiceFactory.getMemcacheService()
    }

    @Bean
    fun datastoreService(): DatastoreService {
        return DatastoreServiceFactory.getDatastoreService()
    }

    @Bean
    fun springSessionRepositoryFilter(sessionRepository: MemcacheSessionRepository): SessionRepositoryFilter<MapSession> {
        return SessionRepositoryFilter(sessionRepository)
    }
//    @Bean
//    fun springSessionRepositoryFilter(sessionRepository: DatastoreSessionRepository): SessionRepositoryFilter<MapSession> {
//        return SessionRepositoryFilter(sessionRepository)
//    }

}

しかし、今度はローカルサーバーで認証をかけてないページでセッションクッキーが吐かれなくなった(;´Д`)

こちらは未解決。

一旦本番では問題なくクッキー履かれていいるのでまた今度。

もし解決方法見つけたら、く、ください !

参考

0 件のコメント:

コメントを投稿