在 Web 开发中 Session 是非常常用也是必要的东西,本篇即来实现在 Ktor
下完成 Session 的操作。
首先我们需要将 Session 安装至框架,在 Ktor 中由于解藕的存在,各个功能均是被安装进去的,在不进行安装的情况下,无法使用 Session(上一篇讲到的 FreeMarker 也需要先安装)。
data class MySession(val name: String, val data: String)
@KtorExperimentalAPI
fun Application.main() {
install(Sessions) {
cookie("MySession", directorySessionStorage(File(".sessions"))) {
cookie.path = "/"
}
}
}
上面的 directorySessionStorage()
来自 Ktor 的 Session 库,并且需要注意的是,directorySessionStorage()
也是一个 Experimental 的 API,需要加入注解来使其能够顺利编译:
compile "io.ktor:ktor-server-sessions:$ktor_version"
在上面这段代码里,即表示了这个 Session 可以在文件系统里保存,并且作用范围是全站,即以 /
为路径的所有请求。这意味着我们可以通过请求路径来进行 Session 的隔离。
对于只需要使用 Session,而不需要其他配置的情况下,上面的代码也可以简单的写成:
fun Application.main() {
install(Sessions) {
cookie("MySession")
}
}
然后来写一段代码验证一下 Session 的工作情况:
data class MySession(val name: String, val data: String)
@KtorExperimentalAPI
fun Application.main() {
install(Sessions) {
cookie("MySession", directorySessionStorage(File(".sessions"))) {
cookie.path = "/"
}
}
get("/session") {
val s = call.sessions.get("MySession") as? MySession
if (s == null) {
call.sessions.set("MySession", MySession("rarnu", "init"))
call.respondText { "generated new session" }
} else {
call.respondText { "name: ${s.name}, data: ${s.data}" }
}
}
}
编译运行后,在浏览器内访问 http://localhost:8080/session
即可看到效果,第一次进入时,显示 generated new session
而页面刷新后显示 Session 的信息。
同时,我们可以在工程目录下发现被保存下来的 Session 内容:
你可以尝试删掉这个文件看看会发生什么。
在 Ktor 内,拥有几种不同的 Session 管理方式,上面用的是 Cookie,即把数据保存到本地。在很多场景下,我们还会有另外一种请求方式,即把相关的数据放在 Header
里,通常是用于 API 或 XHR 请求,这个时候我们可以使用 header()
来描述 Session:
fun Application.main() {
install(Sessions) {
header("MySession") {
transform(SessionTransportTransformerMessageAuthentication(SecretKeySpec(key, "HmacSHA256")))
}
}
}
这里的 key
是一个 ByteArray
对象,也就是加密用的 key,它可以是任意组合的 byte 串。后面的 HmacSHA256
是采用的算法,具体可以使用哪些算法可以查阅文档。当然此处在文档里有一处 bug,在 Ktor 官方文档内,用于 Header 的 transform
是 SessionTransportTransformerDigest
,而这个类并不安全,在 Ktor 内对它的描述如下:
@Deprecated(
"This authentication kind is potentially vulnerable with several hash functions." +
" Use SessionTransportTransformerMessageAuthentication instead or ensure you are using secure enough hash."
)
为了安全起见,应当使用此处的 SessionTransportTransformerMessageAuthentication
并配合相应的加密手段。
上面的讲的事情都是在服务器端做的,然而很多时候我们会将 cookie 保存在客户端,这个时候要怎么做呢?其实也很简单的,把上面讲的内容稍做结合就可以了:
fun Application.main() {
install(Sessions) {
cookie("MySession") {
val secretSignKey = hex("000102030405060708090a0b0c0d0e0f")
transform(SessionTransportTransformerMessageAuthentication(secretSignKey))
}
}
}
此时我们就拥有了写到客户端的 Cookie 了。当然了,对于客户端 Cookie 有一点很重要,就是校验是否过期,这里也提供一个简单的函数:
data class MySession(val name: String, val expiration: Long)
fun ApplicationCall.expiration(): Boolean {
var ret = true
val s = sessions.get(sessionName) as? MySession
if (s != null && System.currentTimeMillis() < s.expiration) {
ret = false
}
return ret
}
现在我们可以在 Ktor 程序内自由的使用 Session 了,本篇到此结束。
下一篇预告:《Ktor 从入门到放弃(六) WebSockets》