在 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>("CookieName", 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>("CookieName")
- }
- }
然后来写一段代码验证一下 Session 的工作情况:
- data class MySession(val name: String, val data: String)
- @KtorExperimentalAPI
- fun Application.main() {
- install(Sessions) {
- cookie<MySession>("CookieName", 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>("MySession2") {
- 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>("CookieName") {
- 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》
来源: http://www.jianshu.com/p/45225f64a4ae